I followed the approach to install paginate v2 and paginate a collection, but I get an additional "Example Collection" top level page link for every page created by paginator.
Here is example.md
---
layout: page
title: Example Collection
permalink: /example/
pagination:
enabled: true
---
{% for post in paginator.posts %}
<h1>{{ post.title }}</h1>
{% endfor %}
{% if paginator.total_pages > 1 %}
<ul>
{% if paginator.previous_page %}
<li>
Newer
</li>
{% endif %}
{% if paginator.next_page %}
<li>
Older
</li>
{% endif %}
</ul>
{% endif %}
And this is what I added to my config.yml
# Collections
collections:
examplecol:
output: true
permalink: /:collection/:path/
# Plugin: Pagination (jekyll-paginate-v2)
pagination:
collection : 'examplecol'
enabled : true
debug : false
per_page : 3
#permalink : "/page/:num/"
title : ":title - Page :num of :max"
limit : 0
sort_field : "date"
sort_reverse : true
Now, if there are more than 3 files in the _examplecol folder, I get more than 1 instance of the example.md as a page in my header.
How can I have just one instance of Example Collection in the header that holds all of the paginated pages? I think I'm missing something silly.
I tried deleting the permalink entry in the example.md YAML, but that just made it so that the jekyll processor could not find examplecol/index.html.
It took me a lot of trial and error, but I found the solution in the header.
When the paginator makes pages with some items, the site sees them as pages and renders them.
Therefore, the site finds all the true responses to my_page.title and creates page-links.
<div class="trigger">
{% for my_page in site.pages %}
{% if my_page.title %}
<a class="page-link" href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{% endif %}
{% endfor %}
</div>
Since the paginator pages are autogen, you can filter them out:
<div class="trigger">
{% for my_page in site.pages %}
{% if my_page.title and my_page.autogen == nil %}
<a class="page-link" href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{% endif %}
{% endfor %}
</div>
Related
I am very new to jekyll. Currently, I noticed that there is only one /_posts folder in my project. All the posts I wrote in this folder will create a site in /_site/posts.
I wonder is there a way that I can write posts for different topics and generate the sites at different folders? For example, I want to write some posts related to 'travel', so I hope to put the markdown files in a folder like /_travel. I also want to write some posts related to 'work', so I hope to have a folder like /_work. And I hope jekyll can generate these websites separately, like /_site/travel and /_site/work.
The reason I want to solve this is I hope to create a path at the head of my posts. Currently, I am using
{% assign paths = page.url | split: '/' %}
{% for item in paths %}
{% if forloop.first %}
<span>
<a href="{{ '/' | relative_url }}">
{{ site.data.locales[site.lang].tabs.home | capitalize }}
</a>
</span>
{% elsif forloop.last %}
{% if page.collection == 'tabs' %}
<span>{{ site.data.locales[site.lang].tabs[item] | default: page.title }}</span>
{% else %}
<span>{{ page.title }}</span>
{% endif %}
{% elsif %}
<span>
<a href="{{ item | relative_url }}">
{{ site.data.locales[site.lang].tabs[item] | default: page.title }}
</a>
</span>
{% endif %}
{% endfor %}
But apparently, all my work and travel posts are located in the same folder _site/posts
First, you can create your own collections in _config.yml:
collections:
travel:
output: true
permalink: /:collection/:name
work:
output: true
permalink: /:collection/:name
output: true will render a page for each document in the collection.
Then you can create two folders named _travel and _work where you can put your posts.
You access the content of posts in each folder like this:
{% for travel_post in site.travel %}
...
<p>{{ travel_post.content }}</p>
...
{% endfor %}
Lastly, permalink: /:collection/:name should create a link at /travel/name_of_post. See Permalinks for collections for a list of placeholders for the permalink configuration variable.
I have been looking at multiple answers to similar questions here on stack overflow and other sources, but simply cannot solve my problem.
I have a page consisting of index.md which has the following frontmatter:
# Feel free to add content and custom Front Matter to this file.
# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults
title: title
layout: default
pagination:
enabled: true
---
And this is what I do to list my post:
<!--
Here is the main paginator logic called.
All calls to site.posts should be replaced by paginator.posts
-->
{% for post in paginator.posts %}
<li>
<span class="post-meta">{{ post.date | date: "%b %-d, %Y" }}</span>
<h2>
<a class="post-link" href="{{ post.url | relative_url }}">{{ post.title | escape }}</a>
</h2>
</li>
{% endfor %}
</ul>
<!--
Showing buttons to move to the next and to the previous list of posts (pager buttons).
-->
{% if paginator.total_pages > 1 %}
<ul class="pager">
{% if paginator.previous_page %}
<li class="previous">
← Newer Posts
</li>
{% endif %}
{% if paginator.next_page %}
<li class="next">
Older Posts →
</li>
{% endif %}
</ul>
{% endif %}
<div class="pagination">
{% if paginator.previous_page %}
<a href="{{ paginator.previous_page_path }}" class="previous">
Previous
</a>
{% else %}
<span class="previous">Previous</span>
{% endif %}
<span class="page_number ">
Page: {{ paginator.page }} of {{ paginator.total_pages }}
</span>
{% if paginator.next_page %}
Next
{% else %}
<span class="next ">Next</span>
{% endif %}
</div>
I have added the gem to plugin list and to the gem file and run bundle install, and my configuration looks like this:
pagination:
enabled: true
per_page: 3
offset: 2
permalink: '/page/:num/'
title: ':title - page :num of :max'
limit: 0
sort_field: 'date'
sort_reverse: true
However when I run bundle exec jekyll s my test post is not listed.
But if I use:
{% for post in site.posts%}
{{post.title}}
{% endfor %}
My test post is listed as I intent. Anyone who can help me towards, what I am doing wrong, I simply cannot spot it.
Do you have a specific reason for including offset: 2 in the _config.yml? This will exclude the first 2 posts from appearing in the pagination so if you don't have at least 3 posts in your project nothing will be displayed.
Try removing the offset line from your config file, rerun bundle exec jekyll serve, and see if the functionality works.
For offset usage check the jekyll-paginate-v2 README section "Offsetting posts".
I have a bunch of documentation pages in a collection, which all have a Category, which I am using a group_by filter on like so:
{% assign docs_by_category = site.documentation | group_by: "category" %}
Then I use these groupings to create a menu structure, where items for each category are listed under a header. This is all good.
However, my problem lies in, being able to sort, which category is shown first, so I would like to be able to somehow prioritize them.
Say, if I have a the following categories: tutorials, getting started, advanced. I wouldn't want advanced to show up as the first category, but rather getting started.
The current code for generating the menu looks something like:
{% assign navurl = page.url | remove: 'index.html' %}
{% assign docs_by_category = site.documentation | group_by: "category" | reverse %}
{% for category in docs_by_category %}
<div class="category_wrapper">
<div class="category">{{ category.name }}</div>
<ul>
{% for item in category.items %}
<li class="collapsed">
<a href="{{ site.baseurl }}{{ item.url }}">
{% if item.url == navurl %}
<u>{{ item.title }}</u>
{% else %}
{{ item.title }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
TL;DR: How do I sort groups by some kind of priority?
You can use a configuration array to store categories order :
# _config.yml
categories-order:
- getting started
- tutorials
- advanced
Then :
{% assign docs_by_category = site.documentation | group_by: "category" %}
{% for cat in site.categories-order %}
{% assign currentCat = docs_by_category | where: 'name', cat | first %}
<div class="category_wrapper">
<div class="category">{{ currentCat.name }}</div>
<ul>
{% for item in currentCat.items %}
<li class="collapsed">
<a href="{{ site.baseurl }}{{ item.url }}">
{% if item.url == navurl %}
<u>{{ item.title }}</u>
{% else %}
{{ item.title }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
This implies that you reference all you categories in you config, otherwise, they will not appear in this listing.
In my index of blog posts I'd like to grab the first image from the post to display it in the index using just liquid so it works on github pages.
I have a feeling split is the way to go, but I'm not good with liquid.
I'd like to be able to get the image url and put it in a variable to style it.
The ideal solution would be something like:
{% for post in site.posts %}
<li>
{{post.content | first_image}}
</li>
{% endfor %}
Any ideas?
You can define a custom variable to your Front Matter called "image", so it's going to work like Wordpress's posts Featured Image:
---
image: featured-image.jpg
---
Note to remember where is your image saved. In my case, I created a directory called "imagens" (PT-BR here). Then, go to your index.html and add the image to your template, wherever you want. In my site it looks like this:
<ul class="post-list">
{% for post in site.posts %}
<li>
<h2>
<a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
</h2>
<span class="post-meta">{{ post.date | date: "%b %-d, %Y" }},</span>
<span class="post-meta">por</span>
<span class="post-meta">{{ post.author }}</span>
</li>
//IMAGE
<img src="{{ site.baseurl }}/imagens/{{ post.image }}">
//IMAGE
{{ post.excerpt }}
<a class="btn btn-default" href="{{ post.url | prepend: site.baseurl }}">Continuar lendo</a>
{% endfor %}
</ul>
That's it.
Some solutions to your problem :
1 - Use the Post Excerpt tag Documentation is here
Your post :
---
layout: post
title: Testing images
---
## Title
Intro Text
![Image alt]({{ site.baseurl }}/assets/img/image.jpg "image title")
More intro text
Some more text blah !
Your template :
<ul>
{% for post in site.posts %}
<li>
{{ post.title }}
{{ post.excerpt }}
</li>
{% endfor %}
</ul>
As your image tag appears before the excerpt_separator (\n\n = two newlines) it will be in the post excerpt.
2 - Use your post's Yaml front matter to store your image's datas
Post :
---
layout: post
title: Testing images
images:
- url: /assets/img/cypripedium-calceolum.jpg
alt: Cypripedium Calceolum
title: Cypripedium Calceolum
- url: /assets/img/hello-bumblebee.jpg
alt: Hello bumblebee !
title: Hello bumblebee !
---
Intro here yo ! <-- THIS IS THE EXCERPT
Post body begin, and first image not in excerpt
{% assign image = page.images[0] %} <-- first element of the array is zero
{% include image.html image=image %}
Some more text blah !
{% assign image = page.images[1] %}
{% include image.html image=image %}
_includes/image.html (centralized in an include for standardization, but can be in the template) :
<img src="{{ site.baseurl }}{{ include.image.url }}" alt="{{ include.image.alt }}" title="{{ include.image.title }}">
The index page :
<ul class="posts">
{% for post in site.posts %}
<li>
<span class="post-date">{{ post.date | date: "%b %-d, %Y" }}</span>
<a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
{{ post.excerpt }}
{% assign image = post.images[0] %}
{% include image.html image=image %}
</li>
{% endfor %}
</ul>
Got it to work. Not sure how it will scale, but this liquid code loops through all the posts and grabs the source for the first image from a post and displays that post. I tested it with multiple images and it works as expected.
<ul>
{% for post in site.posts %}
<li>
{% assign foundImage = 0 %}
{% assign images = post.content | split:"<img " %}
{% for image in images %}
{% if image contains 'src' %}
{% if foundImage == 0 %}
{% assign html = image | split:"/>" | first %}
<img {{ html }} />
{% assign foundImage = 1 %}
{% endif %}
{% endif %}
{% endfor %}
{{ post.title }}
</li>
{% endfor %}
</ul>
If you just need the image URL instead of the whole thing in img tag, you can use the following method.
Install Liquid filter match_regex:
gem install match_regex
Then add it to your Jekyll config:
plugins:
- match_regex
Create a capture snippet in your template:
{% capture post_first_image %}
{% assign hero_image = page.content | match_regex: '<img .*?src="([^"]+)"' %}
{% if hero_image == nil %}
{% assign hero_image = "/placeholder-image.png" | prepend: site_base %}
{% endif %}
{{ hero_image }}
{% endcapture %}
Template:
<meta property="og:image" content="{{ post_first_image | strip }}">
You can simply remove the if condition if you don't need placeholder image.
I've taken David's answer and found a way to get just the src attribute from the img tag.
{% assign foundImage = 0 %}
{% assign images = post.content | split:"<img " %}
{% for image in images %}
{% if image contains 'src' %}
{% if foundImage == 0 %}
{% assign html = image | split:"/>" | first %}
{% assign tags = html | split:" " %}
{% for tag in tags %}
{% if tag contains 'src' %}
<img {{ tag }} />
{% endif %}
{% endfor %}
{% assign foundImage = 1 %}
{% endif %}
{% endif %}
{% endfor %}
I'm constructing a static site (no blog) with Jekyll/Liquid. I want it to have an auto-generated navigation menu that lists all existing pages and highlight the current page. The items should be added to the menu in a particular order. Therefore, I define a weight property in the pages' YAML:
---
layout : default
title : Some title
weight : 5
---
The navigation menu is constructed as follows:
<ul>
{% for p in site.pages | sort:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
This creates links to all existing pages, but they're unsorted, the sort filter seems to be ignored. Obviously, I'm doing something wrong, but I can't figure out what.
Since Jekyll 2.2.0 you can sort an array of objects by any object property. You can now do :
{% assign pages = site.pages | sort:"weight" %}
<ul>
{% for p in pages %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
And save a lot of build time compared to #kikito solution.
edit:
You MUST assign your sorting property as an integer weight: 10 and not as a string weight: "10".
Assigning sorting properties as string will ends up in a a string sort like "1, 10, 11, 2, 20, ..."
Your only option seems to be using a double loop.
<ul>
{% for weight in (1..10) %}
{% for p in site.pages %}
{% if p.weight == weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
Ugly as it is, it should work. If you also have pages without a weight, you will have to include an additional internal loop just doing {% unless p.weight %} before/after the current internal one.
Below solution works on Github (doesn't require a plugin):
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li>{{node.title}}</li>
{% endfor %}
Above snippet sorts pages by file name (name attribute on Page object is derived from file name). I renamed files to match my desired order: 00-index.md, 01-about.md – and presto! Pages are ordered.
One gotcha is that those number prefixes end up in the URLs, which looks awkward for most pages and is a real problem in with 00-index.html. Permalilnks to the rescue:
---
layout: default
title: News
permalink: "index.html"
---
P.S. I wanted to be clever and add custom attributes just for sorting. Unfortunately custom attributes are not accessible as methods on Page class and thus can't be used for sorting:
{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
I've written a simple Jekyll plugin to solve this issue:
Copy sorted_for.rb from https://gist.github.com/3765912 to _plugins subdirectory of your Jekyll project:
module Jekyll
class SortedForTag < Liquid::For
def render(context)
sorted_collection = context[#collection_name].dup
sorted_collection.sort_by! { |i| i.to_liquid[#attributes['sort_by']] }
sorted_collection_name = "#{#collection_name}_sorted".sub('.', '_')
context[sorted_collection_name] = sorted_collection
#collection_name = sorted_collection_name
super
end
def end_tag
'endsorted_for'
end
end
end
Liquid::Template.register_tag('sorted_for', Jekyll::SortedForTag)
Use tag sorted_for instead of for with sort_by:property parameter to sort by given property. You can also add reversed just like the original for.
Don't forget to use different end tag endsorted_for.
In your case the usage look like this:
<ul>
{% sorted_for p in site.pages sort_by:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endsorted_for %}
</ul>
The simplest solution would be to prefix the filename of your pages with an index like this:
00-home.html
01-services.html
02-page3.html
Pages are be ordered by filename. However, now you'll have ugly urls.
In your yaml front matter sections you can override the generated url by setting the permalink variable.
For instance:
---
layout: default
permalink: index.html
---
Easy solution:
Assign a sorted array of site.pages first then run a for loop on the array.
Your code will look like:
{% assign links = site.pages | sort: 'weight' %}
{% for p in links %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
This works in my navbar _include which is simply:
<section id="navbar">
<nav>
{% assign tabs = site.pages | sort: 'weight' %}
{% for p in tabs %}
<span class="navitem">{{ p.title }}</span>
{% endfor %}
</nav>
</section>
I've solved this using a generator. The generator iterates over pages, getting the navigation data, sorting it and pushing it back to the site config. From there Liquid can retrieve the data and display it. It also takes care of hiding and showing items.
Consider this page fragment:
---
navigation:
title: Page name
weight: 100
show: true
---
content.
The navigation is rendered with this Liquid fragment:
{% for p in site.navigation %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">{{ p.navigation.title }}</a>
</li>
{% endfor %}
Put the following code in a file in your _plugins folder:
module Jekyll
class SiteNavigation < Jekyll::Generator
safe true
priority :lowest
def generate(site)
# First remove all invisible items (default: nil = show in nav)
sorted = []
site.pages.each do |page|
sorted << page if page.data["navigation"]["show"] != false
end
# Then sort em according to weight
sorted = sorted.sort{ |a,b| a.data["navigation"]["weight"] <=> b.data["navigation"]["weight"] }
# Debug info.
puts "Sorted resulting navigation: (use site.config['sorted_navigation']) "
sorted.each do |p|
puts p.inspect
end
# Access this in Liquid using: site.navigation
site.config["navigation"] = sorted
end
end
end
I've spent quite a while figuring this out since I'm quite new to Jekyll and Ruby, so it would be great if anyone can improve on this.
I can get the code below works on with Jekyll/Liquid match to your requirement with category:
creates links to all existing pages,
sorted by weight (works as well on sorting per category),
highlight the current page.
On top of them it shows also number of post. All is done without any plug-in.
<ul class="topics">
{% capture tags %}
{% for tag in site.categories %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
<li class="topic-header"><b>{{ tag }} ({{ site.categories[tag] | size }} topics)</b>
<ul class='subnavlist'>
{% assign posts = site.categories[tag] | sort:"weight" %}
{% for post in posts %}
<li class='recipe {% if post.url == page.url %}active{% endif %}'>
{{ post.title }}
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Check it on action on our networking page. You may click a post to highlight the navigation, as well a given link to bring you to the source page where their weight is assigned.
If you're trying to sort by weight and by tag and limit the number to 10, here's code to do it:
{% assign counter = '0' %}
{% assign pages = site.pages | sort: "weight" %}
{% for page in pages %}
{% for tag in page.tags %}
{% if tag == "Getting Started" and counter < '9' %}
{% capture counter %}{{ counter | plus:'1' }}{% endcapture %}
<li>{{page.title}}</li>
{% endif %}
{% endfor %}
{% endfor %}
The solution above by #kikito also worked for me. I just added a few lines to remove pages without weight from the navigation and to get rid of white space:
<nav>
<ul>
{% for weight in (1..5) %}
{% unless p.weight %}
{% for p in site.pages %}
{% if p.weight == weight %}
{% if p.url == page.url %}
<li>{{ p.title }}</li>
{% else %}
<li>{{ p.title }}</li>
{% endif %}
{% endif %}
{% endfor %}
{% endunless %}
{% endfor %}
</ul>
</nav>