Conctenating two collections in a liquid assignment - jekyll

Based on this tutorial, I built a multi-lingual Jekyll site. In every page, the following code links to the versions of the same page in different languages:
{% assign pages=site.pages | where:"id", page.id | sort: 'path' %}
{% for p in pages %}
<img src="{{ site.baseurl }}/images/{{ p.lang }}.png" alt="This page in {{ p.lang }}"/>
{% endfor %}
This works well, but I want to do the same in pages that belong to other collections besides "site.pages". For example, I have a collection called "site.TOPICS". I would like to replace the first line with something like:
{% assign pages=site.pages + site.TOPICS | where:"id", page.id | sort: 'path' %}
or:
{% assign pages=site.EVERYTHING | where:"id", page.id | sort: 'path' %}
But this does not work.
Is there a way to concatenate two collections? Or alternatively, to access all objects in the site regardless of type?

Right now (Jekyll 3.1.x I don't think you can do it.
I found: https://github.com/Shopify/liquid/issues/427
Which seems to show it is coming - this is an issue that looks like it is solved in Liquid, but a higher version of liquid that Jekyll uses.
I also found this: https://github.com/mpc-hc/mpc-hc.org/commit/624a4bf63710ce00d98c80f8b3655a71c0468747#diff-23a9de36055c1aa13a62d73b9c318ebd
where parker says this is a potential 4.x feature.
It looks like if you have more than one collection you could use site.documents to do this, I did a little test and while it sounds like site.documents is all docs in the site, it looks like it is really all docs in collections.
There is a push filter that maybe you could use to push one array into another, but I don't think it is really made to add 2 arrays together.
In the meantime I think just duplicating your code and having one for pages and one for collections would work. If I understand correctly your id would only be found if it matched so one would find something the other wouldn't.

Related

Setting URL variable for all posts

I'm trying to reference an image from a GitHub pages post. I have been referencing images from the layout, and to do so I have used the following code in my _layouts/default.html:
{% assign custom_url = site.url | append: site.baseurl %}
{% assign full_base_url = custom_url | default: site.github.url %}
… href="{{ "/images/logo.png" | prepend: full_base_url }}" …
(I'm not sure how exactly I ame up with the first two lines, but they seem to work well in both the live pages and my local preview so I'm inclined to keep their semantics.)
Trying to duplicate only the last row in the body of a post failed, leading to nothing being prepended. I assume that the variable is not present when the post gets rendered, only when that rendering gets plugged into the layout. Using all three rows in every post with images feels a bit repetitive.
I have read Jekyll Front Matter Defaults which describes how to perform settings for all posts, but that does seem to only allow setting static values, not computed values like I do in my assignments.
I have also read How to define global variables in Liquid? which seems to be addressing the same Liquid problem. But the question is very bare-bones, and tagged for Shopify not Jekyll so some of the answers don't feel applicable to me. The gist I get from the answers there is that maybe I might be able to include some “snippets” somehow somewhere, but I have no clue how to do that, let alone do that in a way that would not require adding lines to every post. The fact that the term “snippet” tends to refer to code snippets does not make searching for guides any easier.
In one of my Jekyll project, I had the same kind of requirement and as you, I did not find a really clear way to do it.
I battled a long time trying to fit this into _config.yaml, but because of the processing order, realised it would never work.
What I finally ended up with is maybe not the cleanest of the solution, and I would really like someone coming with a better solution than this one, but here it is:
In the _includes folder I created a variables.html file containing all those global variables I knew I could use on all pages.
In your case the file _includes/variables.html would be
{% assign custom_url = site.url | append: site.baseurl %}
{% assign full_base_url = custom_url | default: site.github.url %}
Then in all the layouts I would need those variables, I would just include the variables.html files right after the DOCTYPE, here is a simplified example of _layouts/default.html:
<!DOCTYPE html>
{% include variables.html %}
<html>
<head></head>
<body>
{{ content }}
Some image
</body>
</html>
This way your variables custom_url and full_base_url are globally accessible in everywhere you are using the default layout.

Markdown rendering differs when invoked on members of foreach or via a where filter

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

Liquid 'If Contains'

I am fairly new to the coding language and what I am trying todo is probably fairly difficult but I would like to attempt it if I can.
I have dispatch notes which I would like to display an image if it is express delivery, so it is easily noticeable. The dispatch notes are in html/ liquid. This is where I have got to so far but it doesn't seem to work
{{% if contains_Standard UK %}} {{ 'express.png'> | | img_tag }}
it is for the information that comes from this {{ order.shipping_lines[0].title }}
First, double {{ is for displaying something. To execute something, you must start with {%.
Then, as any language, when you want to check a condition, it is necessry to compare two values.
Here, you should have something like:
{% if order.shipping_lines[0].title contains 'Standard UK' %}
Do something
{% endif %}
HTH

How do I inspect Pelican variables

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.

Generating a list of pages (not posts) in a given category

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