Sort jekyll posts by hash value? - jekyll

Using Jekyll 3.1.1. Given posts with headers containing:
categories: works
sitemap:
priority: 0.8
chagefreq: yearly
I don't seem to be able to do this...
{% assign prioritized = site.categories.works | sort: 'page.sitemap.priority' | reverse %}
{% for work in prioritized %}...{% endfor %}
A | sort: 'priority' would work just fine, if I instead
defined a single variable.
priority: 0.8
but I'd rather my sitemap priority double for this purpose. DRY, ya know?
Is something like this possible?

Related

Can I get posts by using an array of tags using Jekyll?

I'm working on a liquid script for my Jekyll website to retrieve related posts. I'd like to use the array of tags used for the current post (post.tags) to do a query that matches other posts on the site that also have one (or more) of those tags. Such as:
assign relatedPosts = site.posts | where 'tags', page.tags
However, in the documentation it only mentions passing in a string, not an array of strings? I would ideally like to accomplish this using the where operator (or something similar), instead of iterating through every post on the site.
You are correct that you can't supply an array for the where operator, as per the documentation, you can only pass a string there.
By the nature of your requirement, it looks like iterating through every post on the website is not something you can avoid, since basically you want to check all your posts for a couple of different tags.
One possible solution would be to iterate through your post tags first, then check that tag against all site post tags, something like this:
{% assign new_array = "" | split: "" %}
{% for tag in page.tags %}
{% assign tag_found = site.posts | where "tags": tag %}
{% assign new_array = new_array | concat: tag_found %}
{% endfor %}
OUTPUT: {{ new_array }}

How do I show a Jekyll Collection item on a specific date? (Liquid syntax issue?)

I guess the most basic question I have is why does using a number at the beginning of a Liquid if statement seem not to work.
This works:
{% if quip.show == "foo" %}
This does not:
{% if quip.show == "1" %}
…but of course there's a lot more to it, here's what I have going on.
I have a Jekyll Collection of little quips I want to show when the site builds on a specific date. A daily status type thing. I setup a collection with the files, made my template, things are marvelous. The issue is making it so only one shows up at a time on the assigned date.
My first thought was to add date: 2020-06-17 to the front matter then run a check to see if that matches today's date but that seems to be conflicting with the Jekyll implied date. So I changed the front matter tag to show: 2020-06-17 just to troubleshoot.
So now I have a for loop that shows all of my quips like this:
{% for quip in site.quips %}
(this is where my HTML is and it loops and renders each collection file perfectly)
{% if quip.show == "2020-06-17" %}
It's working. (this is where the quip HTML will go but I'm isolating it)
{% endif %}
{% endfor %}
That's all manual, the front matter literally says show: 2020-06-17 but obviously I'd like it to automatically get the date, more on that in a bit. …but it doesn't print "It's working.". Oddly, when I change the front matter to show: foo in one of my collection files it does show "It's working". Also, when I change it to just the number 1 it doesn't work. So it seems like Jekyll doesn't like numbers as the first character in a ``==`.
So basically this is what I would like to do:
I have a variable set using this:
{% capture my_var %}{{ "now" | date: "%Y-%m-%d"}}{% endcapture %}
I added this at the top of my page to grab the date (after some other things didn't seem to work)
…this outputs cleanly to 2020-06-17 (as confirmed by calling the variable and looking at the HTML output.)
So then I'd like to put this in my collection loop:
{% if quip.show == "{{ my_var }}" %}
It's Working
{% endif %}
(the quotes around {{ my var }} are tripping me up a bit because the compiler complains if they aren't there, but things don't reliably work without them. No clue there, not the main issue but one that I need to solve).
I mean, the idea is to wrap all of my quip template with this date detector if statement instead of printing "It's working" but that would be a nice proof of concept.
I'm really not opposed to using Jekyll's built in date frontmatter or even the filename -- there's only one per day. I've tried so many things and for some reason I just can't get this to work when I'm using a numeric value in the == statement.
Any help would be much appreciated.
This is happening because the front matter is YAML, and YAML might be more clever than you thought it would.
date: 2020-06-17
Is an actual valid date for YAML.
So if I JSON print such a date defined in a front matter, then, it would show as a date:
{{ quip.date | json }}
2020-06-17 00:00:00 +0200
So if you want to compare dates from the front matter and from the day, you'll have to format both dates.
{% assign now = "now" | date: "%Y-%m-%d" %}
{% assign show = quip.show | date: "%Y-%m-%d" %}
{% if show == now %}
<p class="fresh-from-the-day">quip.title</p>
{% endif %}

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

Conctenating two collections in a liquid assignment

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.

How can I have multiple authors for one post in Jekyll?

I have the following header on a jekyll post with a single author "usman" which generates the this article. I would like to have something like "authors: usman, someone_else" so a colleague can also contribute to the article. Is this possible? How would I set it up.
---
layout: post
title: The pitfalls of building services using Google App Engine -- Part I
date: 2013-02-24 08:18:17
author: usman
categories:
- System Admin
tags:
- GAE
---
I took a look at the post template for the theme I am using and it has the following line:
{% assign author = site.authors[page.author] %}
This will obviously only support one author, Is there a way to get both authors of the page? i.e. page.author[0] for example?
If you want to specify multiple authors in your YAML Frontmatter, then you are going to want to use YAML's list syntax like you did with categories and tags, like this:
author:
- usman
- someone_else
This will be useful for dynamically injecting author information into each of the posts.
As for allowing multiple people to contribute to the same article, I don't think this has anything to do with Jekyll or what is specified in the Frontmatter. This is an issue of having your Jekyll content hosted in a shared place (such as on GitHub, like many do) where both you and your collaborator can both work on the file. That being said, be aware that you may run into nasty merge conflicts if you work on that same markdown file in parallel.
Update
This is an update based on the OP's edits to the original question.
A simple hacked approach would be to set your author tag like this:
author: Usman and Someone_Else
This doesn't give you much flexibility though. A better solution, which would require you to modify the template you are using, would be to do something like the following:
First, setup your YAML Front Matter so that it can support multiple authors.
authors:
- Usman
- Someone_else
Now, you modify the template to go through the authors specified in the YAML Front Matter.
<p>
{% assign authorCount = page.authors | size %}
{% if authorCount == 0 %}
No author
{% elsif authorCount == 1 %}
{{ page.authors | first }}
{% else %}
{% for author in page.authors %}
{% if forloop.first %}
{{ author }}
{% elsif forloop.last %}
and {{ author }}
{% else %}
, {{ author }}
{% endif %}
{% endfor %}
{% endif %}
</p>
Resulting HTML:
If no authors are specified:
<p>No author</p>
If one author is specified:
<p>Usman</p>
If two authors are specified:
<p>Usman and Someone_Else</p>
If more than two authors are specified:
<p>Usman, Bob, and Someone_Else</p>