Trying to get Jekyll collections to paginate using jekyll-paginate-v2 but I'm getting some odd behaviour where it uses the layout in the first pagination request it finds for all pages.
So for instance I have a number of different collections and for each collection I have a a folder with an index that is the same name as the collection but without the underscore.
So my events collection has _events with my markdown files and events with index.html that should display that collection. I have the same for "gallery" and "results" collections.
If I only activate pagination for one collection it works fine. As soon as I activate it for more than one the generated index.html's for every collection appear to use the first one it finds (in alphabetical order).
I've tested this by renaming folders and moving the pagination using index.html's around. Whichever index.html it hits first with pagination enabled becomes the one used for all of my index.html's that have collections enabled. Even if its in the root and called something else the same thing still happens.
My index.html's have similar front matter and pagination logic which is basically:
---
layout: page
title: Results
pagination:
enabled: true
collection: results
---
{% for result in paginator.posts %}
<a href="{{ result.url }}">
<h1 class="title results">
{{ result.title }}
</h1>
<h6 class="date results">
{{ result.date | date: "%A, %B %e, %Y at %I:%M%P" }}
</h6>
</a>
{% endfor %}
Related
I wonder if anyone can illuminate this puzzle. I am using a new installation of vanilla jekyll on a Mac. Everything seems to work fine, but I discovered that some text being shown in my page footer was rendering differently on posts and all other pages. On most pages the text would render as HTML, but in posts it was rendering as Markdown. I found a workaround, but it left me with even more questions.
Context
I have defined footer_sections as a collection to hold portions of the footer. In my _config.yml this looks like:
collections:
footer_sections:
output: false
A footer section is then defined in a Markdown file such as _footer_sections/address.md as:
---
title: Address
order: 1
---
**My Name**
123 My Street
My Town, ST 12345
123-555-5555
In my default.html I had a footer section in my HTML something like this:
<div id="footer">
{{ site.footer_sections | where: "title", "Address" }}
</div>
And my posts are set up like this example:
---
title: Silly new post
date: 2017-02-27T12:33:53+00:00
author: Eric Celeste
layout: post
---
Silly post.
And finally, the post layout is connected to the default layout like this:
---
layout: default
---
<h1>{{ page.title }}</h1>
<p class="meta">{{ page.date | date_to_string }}</p>
<div class="post">
{{ content }}
</div>
The Problem
Notice that the address.md file is defined in Markdown and then its content is shown in the footer by the inclusion of the section in default.html. On all regular pages this would render as HTML (a bold name, a plain address), but on posts like the silly post above, it would render as Markdown (a name surrounded by stars and an address without like breaks).
I thought maybe it had to do with different procedural steps between posts and pages, maybe the Markdown rendering is happening "later" on pages but has already happened "earlier" in posts. I am only two days old on Jekyll, so I really don't know how it works.
In order to test that theory, I tried forcing the Markdown rendering with the markdownify filter. I changed the liquid tags in default.html so that they read:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.content | markdownify }}
Oddly, this produced a worse result everywhere. Now no text of any sort appeared in the footer of regular or post pages.
On the theory that maybe the where filter is actually different from looping through members of an array with foreach I tried another approach:
{% for section in site.footer_sections %}
{% if section.title == "Address" %}
{{ section.content | markdownify }}
{% endif %}
{% endfor %}
That worked! Now the content of the footer sections rendered as HTML on both regular pages and posts.
My Questions
Why didn't the initial approach work? What is the difference between rendering of posts and other pages in Jekyll?
While I found workaround, I don't understand why it works. In what ways does pulling out an item from an array with a where filter differ from using a member from a foreach loop? How does this affect the results of the markdownify filter?
Is there a cleaner, simpler way to grab the HTML-rendered content from my sections than looping through them each time I want to use one of them?
Thanks for any insights you may have!
site.footer_sections is an array and the output of the 'where' filter is still an array (but only containing the values that match your condition).
In your case, you are getting a single-element array but it's still an array object.
To see this for yourself use the inspect filter:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.content | inspect }}
On the other hand, when you loop through the elements with a for loop, at each iteration you get the individual elements of the array. Try using inspect inside your loop to see how the two types of your section variable differ.
For the 'where' method to work you need to get the actual element from the array either with first or [0]:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.first.content | markdownify }}
OR
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section[0].content | markdownify }}
links:
array documentation
first documentation
where documentation
I'm modifying a Pelican template and I have the code below which adds url every time a page is found. I can see that the p object has the attributes url and title.
However I only knew this because I copied the code from another template shown below. Is there any way to inspect objects in jinja2 or Pelican to understand what information is contained within them?
{% for p in pages %}
<h1 class = "sidebar-title">
<a href="{{ SITEURL }}/{{ p.url }}">
{{ p.title }}
</a>
</h1>
https://github.com/getpelican/pelican-themes/blob/master/backdrop/templates/base.html
<li{% if p == page %} class="active"{% endif %}>{{ p.title }}</li>
I am not aware of an official resource explaining all variables, objects, attributes and properties in detail.
But for a start, I think the following start points suffice:
Common variables available for the standard templates
pelican.contents.py:
This is the module which contains (most of) the data structures pelican uses and which are made available in the templates. Have a look for properties (#property, these are functions which act like they are attributes) and attributes. At lines 367ff there are some very simple subclass definitions which could be of use.
pelican.writers.py: This module brings together the templating engine jinja2, the templates and the data to be inserted in the templates. Of special interest for you could be lines 138ff, as this seems like a good point to simply insert some small debug prints to see the real data which is present in the data structures.
I'm rebuilding my company's current site in Jekyll and attempting to set up the structure around a content model using objects (files in a collection) that have attributes (key/value pairs in YAML front-matter). Simple conceptual stuff designed to demonstrate effective content modeling to my team.
Anything on the site that gets reused becomes an object, with the type of object defined by the specific collection that contains its file. So, I have a "services" collection for services offered by the company, a "clients" collection, and a "people" collection with a file for each person.
The problem I'm having is referencing a specific item in a collection. For example, I want my posts to have authors. I've found a ton of solutions for this using _data, but I want my authors to have pages and collections automatically outputs a page for each person. I've been able to get _data to generate pages, but I have no control over the order in which items are listed, whereas with a collection there are a lot of ways for me to control the order.
My current solution feels hacky. I'm giving each person an id which is equal to "firstname-lastname" so in the YAML front-matter it would say id: steve-hickey. Then I use the following code to loop through every person on the site and return any that match the author-id specified in the post.
The post layout template:
---
layout: default
---
<header class="intro post-header">
<h1>{{ page.title }}</h1>
{% assign people = site.people | where:"id", page.author-id %}
{% for person in people %}
<p>Written by {{ person.first-name }} {{ person.last-name }} on <p>{{ page.date | date: '%B %d, %Y' }}</p></p>
{% endfor %}
</header>
<div class="post-body">
{{ content }}
</div>
A post's front-matter:
---
title: User Offboarding
layout: post
nav-area: blog
author-id: steve-hickey
---
A person file from the people collection:
---
id: steve-hickey
first-name: Steve
last-name: Hickey
job: User Experience Strategist & AUX Director
layout: person
nav-area: about
portrait-url:
---
It seems like there should be a way to identify a specific file or object based on a unique attribute that it already possesses, such as its name or url. That way I can just point to a specific object instead of evaluating all of them for a unique property I have to write. But after 3 days of searching I can't find a good answer to this.
If people are unique in your collection {% assign author = site.people | where:"id", page.author-id %} will return an array with one element.
In order to directly access this element do :
{% assign author = site.people | where:"id", page.author-id | first %}
This grab the first (and only) element in you array. You now do {{ author.anykey }} directly.
I'm not familiar with Ruby and I know there's a common Jekyll plugin for doing this but I tried all day and have not been able to get it working.
I added this plugin:
module Jekyll
class TagIndex < Page
def initialize(site, base, dir, tag)
#site = site
#base = base
#dir = dir
#name = 'index.html'
self.process(#name)
self.read_yaml(File.join(base, '_layouts'), 'tag_index.html')
self.data['tag'] = tag
tag_title_prefix = site.config['tag_title_prefix'] || 'Posts Tagged “'
tag_title_suffix = site.config['tag_title_suffix'] || '”'
self.data['title'] = "#{tag_title_prefix}#{tag}#{tag_title_suffix}"
end
end
class TagGenerator < Generator
safe true
def generate(site)
if site.layouts.key? 'tag_index'
dir = site.config['tag_dir'] || 'tag/tag'
site.tags.keys.each do |tag|
write_tag_index(site, File.join(dir, tag), tag)
end
end
end
def write_tag_index(site, dir, tag)
index = TagIndex.new(site, site.source, dir, tag)
index.render(site.layouts, site.site_payload)
index.write(site.dest)
site.pages << index
end
end
end
and to _config.yml I added
tag_dir: /tag
I display the list of tags with this include file:
<div class="tags modal">
<ul>
{% for tag in site.tags %}
<li>
<a href="/tag/{{ tag | first | slugize }}/">
{{ tag | first }}
</a>
</li>
{% endfor %}
</ul>
</div>
And when I try to select a tag, the url changes appropriately but nothing else changes. It does not display my tag_index template or filter the tags.
The site can be previewed and full source is available here. I've been banging my head against the wall for a while and can't for the damned of me figure out what I'm not doing right.
If one single tag page with all posts for all tags (like this one on my blog) is enough for you, you can just generate it with Jekyll/Liquid, without using any plugins at all.
If you absolutely want a separate page for each tag, then you have only two choices:
David Jacquel's answer (generate the page with the plugin on your local machine and push only the generated HTML to GitHub)
Each time you're writing a post where you use a new tag for the first time, manually create a new page with the tag name where you load all posts for that tag
Both choices are a bit more work than the tag page from my blog (see the first link), but there's no other way when you want one page per tag on GitHub Pages.
Only some plugins are supported by github pages. Your tag pages are not currently generated.
If you want to use your tag plugin, you have to
generate you site locally
add an empty .nojekyll file
push the generated files in you master branch
you can also push you code in an other branch like code
If you can't solve the trouble of sorting your posts by tags, you may consider
collections.
Collections in Jekyll is a set of items which has a certain relation between individual items but may not have a chronological arrangement.
For example, let’s consider you have a blog which is about movie reviews. You write your reviews as posts. So for every movie you create a post. Let’s say you want to make a list of Top 25 must watch movies or Top 10 scary movies which will have all the details of each movie.
In this case, posts or pages are not a good choice to go with. Use collections instead.
You can have any number of collections. It is a collection of certain kind of things. A collection of fruits, a collection of veggies, collections of beverages etc. Do not create collections for subsets, for example, say exotic fruits. They come under the fruit collection. In such cases use categories.
Maybe things have changed since this was posted, but sorting via tags (or categories) is built into Jekyll. You can do this by using the proposed method in the docs on Jekyll's website.
From the site:
Jekyll makes the categories available to us at site.categories. Iterating over site.categories on a page gives us another array with two items, the first item is the name of the category and the second item is an array of posts in that category... For tags it’s exactly the same except the variable is site.tags.
{% for tag in site.tags %}
<h3>{{ tag[0] }}</h3>
<ul>
{% for post in tag[1] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endfor %}
That ☝️ will give you each tag and its associated posts of your Jekyll site. You could go a step further and only return a specific tag(s). For example only list the tag nsfw and its associated posts.
{% for tag in site.tags %}
{%- if tag[0] == "nsfw" -%}
<h3>{{ tag[0] }}</h3>
<ul>
{% for post in tag[1] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{%- endif -%}
{% endfor %}
I am using Jekyll as a static generator for a website (not a blog), and I want to have an automatically generated list of all pages on my index page. Specifically, I want to have different categories and list all articles in each category separately. Here's an example of what I'm describing, if you're having trouble following. Is there any way to do this in Jekyll (e.g. GitHub pages)? I've seen the variables documentation page but that seems specific to the blog post format.
While building my own site I came across this very same problem, and I have found an (IMHO) easy and robust solution.
The Problem
Given a subset of pages (not posts) on the site, list them under headings based on their categories. For example: given a set of pages which we consider resource pages (or reference pages, or whatever logical grouping of pages that you want to display are), we want to list them under their categories (ex. code, explanation, et cetera).
The Solution
To get the behaviour that we want, we have to make modifications in three places:
_config.yml
resources.md
resource-file-X.md
_config.yml
In _config.yml, we must add a list of all of the categories/keywords/tags (or whatever you want to call it) that will appear in the resource files. Here is what I have in mine:
category-list: [code, editors, math, unix]
You can call the variable anything, I chose category-list, just make sure that you use the same variable in the resource.md file.
Note: The order that you place the items in the list is the order they will be listed on the resource.md page.
resource-file-X.md
These are the files that you want to have indexed and linked to on the resources.md page. All that you need to do is add two file variables to the top of each of these files. The first is to indicate that this file is a resource file.
resource: true
The second is to indicate what categories you want this file to be indexed under. You can index it under as many categories as you would like, and if you want a page un-indexed, leave the list blank. My reference for proper EINTR handling in C has the following categories:
categories: [code, unix]
resources.md
This is the file that will generate the list of pages based on their respective categories. All you need to do is add the following code to this file (or whatever file you want the list to be on):
{% for cat in site.category-list %}
### {{ cat }}
<ul>
{% for page in site.pages %}
{% if page.resource == true %}
{% for pc in page.categories %}
{% if pc == cat %}
<li>{{ page.title }}</li>
{% endif %} <!-- cat-match-p -->
{% endfor %} <!-- page-category -->
{% endif %} <!-- resource-p -->
{% endfor %} <!-- page -->
</ul>
{% endfor %} <!-- cat -->
Code Breakdown
Just a quick explanation of how this works:
Loop through each of the categories specified in _config.yml.
Display a heading with that category name.
Start an unordered list for the pages that belong in that category.
Loop through the pages on the site.
If the page is a resource file as indicated by the file variable resource, then for each of the categories that the file belongs to, if one of them matches the current category being listed, display a link to that page.
Note: the variables category-list in _config.yml and categories in the resource files can be called whatever you want, just make sure that you use the same variables in the file generating the list.
Another Note: When you modify _config.yml, you have to completely restart Jekyll, even if you have the --watch option, you have to stop and restart it. It took me a while to figure out why my changes weren't taking effect!
The Final Product
You can see the final product on the resources page on my site, although I just put this together today so at the time of this writing, it's far from complete, but you can check out my bio if you want on the home page.
There's a cleaner way to do this using the liquid "contains" property:
In _config.yml, add your index of categories
categories: [fruit, meat, vegetable, cheese, drink]
In your page.md inside the front matter, add one or more of the categories available in the _config.yml
---
layout: page
title: Orange juice
description: Orange juice is juice from oranges. It's made by squeezing oranges.
categories: [fruit, drink]
---
In your template to get all the pages in the fruit category you do:
{% for page in site.pages %}
{% if page.categories contains 'fruit' %}
<div class="item">
<h3>{{page.title}}</h3>
<p>{{page.description}}</p>
</div>
{% endif %}
{% endfor %}
You should differentiate between pages and posts (articles). Listing all posts sorted by category is not a problem at all. You can loop through site.categories. It contains the category name and a list of all posts in that category.
Listing all pages is possible, too. You can loop through site.pages. But a page does not belong to a specific category (only posts do).
When I take a look at your posted example, using categories on posts and then looping through site.categories seems to be the way to go. It will get you exactly the desired output.
There are some variations/ simplifications possible (answer of felipesk). Maybe due to improvements in Jekyll.
There is NO index needed in _config.yml.
If the list of pages are not listed in a page but for example in a doc, you can add the category also to the doc:
---
layout: doc
title: Fruit List
categories: [fruit]
---
And then use it like this:
{% for p in site.pages %}
{% if p.categories contains page.category %}
* [{{ p.title }}]({{ p.url | absolute_url }})
<small>{{ p.excerpt }}</small>
{% endif %}
{% endfor %}
With posts this can even be shorter:
{% for post in site.categories[page.category] %}
* [{{ post.title }}]({{ post.url | absolute_url }})
<small>{{ post.excerpt }}</small>
{% endfor %}
Why this only works for posts, I could not figure out yet.
The interesting point is that this snippet can be used everywhere (if you mix docs/pages/posts)!
So just add it as an _includes and use it like:
## Further Reading
{% include pages-list.md %}
I work with the theme Minimal Mistakes