I'd like to convert the printed category names of my posts into title case. I couldn't find a Liquid filter that would work. I tried using dashes and the camelcase filter, but no dice.
Alternatively, I'd like to print the category name as it's written in the YAML frontmatter.
For instance, for a post with:
category: Here's the Category
When I reference the name:
{% for cat in site.categories %}
<h1>{{ cat[0] }}</h1>
{% endfor %}
I see "here's the category" on the page. I would like to see "Here's the Category" or even "Here's The Category," and I could replace (replace: 'The', 'the') the few articles that I wanted to be downcase.
EDIT
For anyone as desperate as I am, this disgusting hack works, where n is the max number of words you have in a category title.
{% for cat in site.categories %}
{% assign words = cat[0] | split: ' ' %}
<h1>{{ words[0] | capitalize | replace:'The','the'}} {{ words[1] | capitalize }} {{ words[2] }} {{ words[3] | capitalize }} {{ words[4] | capitalize }} {{ words[n] | capitalize }}</h1>
{% endfor %}
I'm going to leave the question unanswered in case someone knows a more elegant method.
You can achieve a part of what you want by using the capitalize filter:
Input
{{ 'capitalize me' | capitalize }}
Output
Capitalize me
Source.
Another possibility, which I didn't test for its edge cases, is to use join and camelize:
{{ "here's the category" | join: '-' | camelize }}
It should print "Here's The Category", but camelize might have a problem with here's.
Just use {{ category.first | capitalize }} is OK for my case, without additional '' on category.
Instead of manually stepping through words 0 to n, one can use another for loop as follows:
{% for category in site.categories %}
{% assign words = category | first | split: ' ' %}
{% for word in words %}
{{ word | capitalize | replace: 'The','the' }}
{% endfor %}
{% endfor %}
I know that this is old but it helped me to solve my problem and it could help others.
The solution I came was:
{% for category in site.categories %}
<a href="/{{category[0] | slugify }}/">
{{category[0]}}
</a>
{% endfor %}
This list all the blog categories (most used for building a menu)
It turns into:
<a href="/dicas/">
Dicas
</a>
<a href="/diário-de-bordo/">
Diário de bordo
</a>
Thank you for giving me a clue.
You can check another filters in: http://jekyllrb.com/docs/templates/#filters
Name pages in Kabab-case as you want them to appear, then use replace:
{{ category.name | replace: '-', ' '}}
Don't-Capitalize-the becomes Don't Capitalize the and Capitalize-The becomes Capitalize The
This solutions is a little more overwrought, but you can adapt the author data file solution from the Jekyll site to work with categories.
In your _data folder, make a file called categories.yml. Put in something like this:
my-first-category:
name: My First Category
another-category:
name: Another Category
Then, you can loop through your categories like this:
{% for category in site.categories %}
{% assign category_slug = category[0] %}
{% assign category_data = site.data.categories[category_slug] %}
{% if category_data %}
{{ category_data.name }}
{% endif %}
{% endfor %}
Side note: I'm not sure why I needed the category_slug assign. I didn't need it when looping through post.categories.
This increases overhead by adding an extra file, but opens the door for other data that can get attached to objects like a category image or description. You can be more granular over your names. This is a pretty readable solution too, which is nice.
I went the other way around. I hard coded my page title in Title case and use downcase, upcase and capitalize liquid filter in other pages.
Related
Short Version:
Why does the following code not produce an output when navbox.next_article is the string '2018-01-05-man-command'?!
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
Details
My post 2018-01-05-man-command.md has a YAML front matter:
---
layout : post
title : 'Man Command'
tags : [RHCSA, RHCSA_mod, Using Essential Tools, Man Command]
categories: [RHCSA]
navbox:
# prev_article:
next_article: 2018-01-05-understanding-globbing-and-wildcards
---
This is accessed by the _includes/post.html file through:
{% unless include.excerpt %}
{{ post.content }}
{% include navbox.html navbox=page.navbox %}
{% endunless %}
This is used by the _layout/post.html which sets the layout for the post:
{% include post.html post=page link_title=false %}
My navbox.html contains:
{% assign navbox = include.navbox %}
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
However, all I get when I run bundle exec jekyll serve is:
The next post is
Why does that line not work? I'm new to jekyll so it's possible I've made a blunder somewhere that's intuitive to most. Please tell me what I can fix.
I believe that the capture tag only captures strings, not posts. See here for more info.
I'm not convinced that a where filter supports the contains syntax you're using. See here for more info.
On top of that, where returns an array. You have to get the first item from that array.
You need to fix these issues. Use an assign instead of a capture to store a post. And change your where filter to not use the contains syntax, which isn't valid. (Unless it's been added since the issue I just linked.)
Here is how I've done it:
{% assign post = site.posts | where:"url", targetUrl | first %}
I have a collection _colletion. In there is a file _collection/path/topic.md and a folder _collection/path/topic/ that includes lots of .md files with content. The permalinks for these files are /path/topic and /path/topic/file-x - so a parent page with a folder with the same name with multiple random pages in it.
Now I want to output a link to /path/topic in all these .md files with the title of topic.md as link text:
---
title: This is the page title defined in topic.md
---
should become
This is the page title defined in topic.md
How do I do that most easily?
Can I somehow access the folder name topic of the .md files and use this to read topic.md and output it's title and also generate a link to it?
My current manual "solution" (or workaround):
Add a parent entry to the frontmatter of all pages in /topic/ that contains the title and relative URL for the topic.md:
parent: ['Topic Title', '../topic']
In the template of the pages:
{% if page.parent %}
<p>« {{ page.parent[0] }}</p>
{% endif %}
Works, but of course duplicates this information n times and has to be maintained manually.
How about this (option 1)?
{% assign pageurl_array = page.url | split: "/" %}
{% assign path = pageurl_array[0] %}
{% assign topic = pageurl_array[1] %}
<p>« <a href="{{ path }}/{{ topic }}/{{ topic }}.html">
{{ topic | capitalize | replace: "-", " " }}
</a></p>
If you do not mind crazy build times, do this (option 2):
{% assign pageurl_array = page.url | split: "/" %}
{% assign path = pageurl_array[0] %}
{% assign topic = pageurl_array[1] %}
{% capture parent_url %}{{ path }}/{{ topic }}/{{ topic }}.html{% endcapture %}
<p>« <a href="{{ parent_url }}">
{% for i in site.pages %}
{% if i.url == parent_url %}
{{ i.title }}
{% endif %}
{% endfor %}
</a></p>
I would go for the first option (much faster) and use this javascript to get the capitals and special characters right:
$('a').each( function() {
var str = $(this).html();
str = str.replace('Topic from url', 'Topic from URL');
$(this).html(str);
});
I admit that the javascript solution is far from pretty, but it solves the build time problem pretty well.
Note that Jekyll is pretty slow. I would advice you to dig into Hugo if you require faster build times.
During discussion in the comments on my question and the other answers I noticed that what I wanted to build was actually a very common thing: A breadcrumb navigation! Just a very "small" one, with only one level.
With this newfound knowledge I could google "breadcrumb" plugins for Jekyll:
This solution uses the path of the page to extract the "crumbs":
https://www.mikestowe.com/blog/2017/08/adding-breadcrumbs-in-jekyll.php
It uses the folder name for the link text.
Another similar implementation:
https://stackoverflow.com/a/9633517/252627
Another one:
https://stackoverflow.com/a/37448941/252627
So no title link text in all of these.
This solution actually reads the page title, but can also read breadcrumb frontmatter from the pages, and uses these as link text:
https://github.com/comsysto/jekyll-breadcrumb-for-github-pages/
https://comsysto.com/blog-post/automatic-breadcrumb-for-jekyll-on-github-pages
https://gist.github.com/csgruenebe/8f7beef9858c1b8625d6
This one might be a valid solution.
There are also real plugins (that unfortunately don't work with Github Pages):
https://github.com/git-no/jekyll-breadcrumbs
My solution, based on JoostS code:
{% assign url = page.url | remove:'.html' | split: "/" %}
{% assign path = url | pop %}
{% if path.size == 1 %}
<a class="back" href="/home/">home</a>
{% else %}
<a class="back" href="/{% for dir in path offset: 1 %}{{ dir | append: "/" }}{% endfor %}">{{ path | last }}</a>
{% endif %}```
I am migrating my Wordpress blog to Jekyll, which I like a lot so far. The current setup in the new site is like this:
use category to distinguish two types of posts (e.g., blog and portfolio)
use tag as normal
The challenge right now is to display all tags within a category because I want to create two separate tag clouds for two types of posts.
As far as I know, Liquid supports looping over all tags in a site like this:
{% for tag in site.tags %}
{{ tag | first }}
{% endfor %}
But I want to limit the scope to a specific category and am wishing to do something like this:
{% for tag in site['category'].tags %}
{{ tag | first }}
{% endfor %}
Any advice will be appreciated.
This seems to work for all kinds of filters like category or other front matter variables - like "type" so I can have type: article or type: video and this seems to get tags from just one of them if I put that in the 'where' part.
{% assign sorted_tags = site.tags | sort %}
{% for tag in sorted_tags %}
{% assign zz = tag[1] | where: "category", "Photoshop" | sort %}
{% if zz != empty %}
<li><span class="tag">{{ tag[0] }}</span>
<ul>
{% for p in zz %}
<li>{{ p.title }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
zz is just something to use to filter above the first tag[0] since all it seems to have is the tag itself, so you can filter anything else with it. tag[1] has all of the other stuff.
Initially I was using if zz != null or if zz != "" but neither of them worked.
This will work, it will list only the tags on post of category 'X'. Replace X with the name of the category.
{% for post in site.categories['X'] %}
{% for tag in post.tags %}
{{ tag }}
{% endfor %}
{% endfor %}
I'm using jekyll-bootstrap to maintain a blog on GitHub.
I'd like to have a sorted tags_list. The tag with the most posts comes first. Then I can have a display that shows the first tags with bigger font-size and last tags with smaller font-size. And I also want a splice function.
If in python/Jinja2, I'd like some code like this:
{% for tag in sorted_tags[:10] %}
<li style="font-size:{{ tag.count }}px;">{{ tag.name }}</li>
{% endfor %}
What's the equivalent implementation in ruby/jekyll?
This is how I'm sorting by the number of posts in a tag (descending), without any plugins (i.e. GitHub Pages compatible).
It also works when your tag names contain spaces; only , and : are forbidden characters (but you can easily change those).
{% capture counts_with_tags_string %}{% for tag in site.tags %}{{ tag[1] | size | prepend:"000000" | slice:-6,6 }}:{{ tag[0] }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign counts_with_tags = counts_with_tags_string | split:"," | sort | reverse %}
<ol>
{% for count_with_tag in counts_with_tags %}
{% assign tag = count_with_tag | split:":" | last %}
{% assign count = site.tags[tag] | size %}
<li>{{ tag }} ({{ count }})</li>
{% endfor %}
</ol>
It's super gross. What it does:
counts_with_tags_string is set to a string like 000005:first_tag,000010:second_tag,000002:third_tag. The zero-padded numbers are generated using the filter chain | prepend:"000000" | slice:-6,6.
This is split on commas and sorted lexicographically, which works because of the zero padding. The result is assigned to counts_with_tags.
Finally, we iterate over the elements and split each on : to find the original tag name. We could find the count in the same way, but because it's zero padded, it's easier to look it up using site.tags[tag] | size instead.
I thought that the tags array is sorted. Assuming so, you can do this:
{% for tag in site.tags %}
<li style="font-size: {{ tag[1].size }}px">{{ tag[0] }}</li>
{% endfor %}
That feels a little hacky, but it should work. Unfortunately, Liquid doesn't currently allow you to sort arrays within your templates. If you want to do any sorting on the array, you'd probably have to write a plugin to do so - it shouldn't be too complex. In fact, there's an existing plugin for sorting accessors that may do it: https://github.com/krazykylep/Jekyll-Sort
I only needed to do this in one place, on the page where my list of Tags was listed, so I wrote it as a Jekyll filter:
tag_index.html
<h2>Posts by Tag</h2>
<ul class="tags-list">
{{ site.tags | render_tags_list }}
</ul>
_plugins/filters.rb
module Jekyll
module Filters
def render_tags_list(tags)
sorted_tags = tags.keys.sort_by! { |tag| tag.downcase }
str = ''
sorted_tags.each { |tag|
str << '<li>' + tags[tag].size.to_s + ' - ' + tag + '</li>'
}
str
end
end
end
You could certainly just allow the filter to return sorted_tags above if you wanted to keep a better separation between "view" logic and programming logic, but my case was very simple. Trying to re-access a hash value by a specific key using Liquid templating wasn't a very concise process, or maybe I was just doing it wrong, but was much easier in Ruby.
I'm hosting my blog on github and wanted a solution to sorting a tag list that did not involve any jekyll plugins since Github doesn't allow for custom plugins (jekyll bootstrap attempts this as well). My post here doesn't really answer the question since I sort by tag name, and NOT by size. You can adapt this method to output the tag size as well in the string and then do some fancier spliting to get a different sort order (but it will be messy)
I was able to do this with the following code:
{% capture tagString %}{% for tag in site.tags %}{{ tag[0] }}{% unless forloop.last %}|{% endunless %}{% endfor %}{% endcapture %}
{% assign tags = tagString | split: '|' | sort: 'downcase' %}
<div id="cloud">
{% for tag in tags %}
{% assign number = site.tags[tag].size %}
{% assign slug = tag | downcase | replace: ' ', '_' %}
<span class="{% if number == 1 %}small{% elsif number <= 5 %}medium{% elsif number <= 10 %}large{% else %}huge{% endif %}">
{{ tag | downcase }}
</span>
{% endfor %}
</div>
It's kind of odd since I capture a string of tags (using | as separator) and then use that to create an array. After that point (in the loop) I can refer to the tag as tag and the list of sites that use that tag as site.tags[tag].
I use this on my blog:
https://github.com/kelsin/kelsin.github.io/blob/master/tags/index.html
The rest of the code is just how I've chosen to make a tag cloud on my tag page. Hope this helps someone else looking for a solution (without plugins)!
I have a network of nodes, some of which are related to one another. I'm using Jekyll to power a website, and was hoping to use liquid tags to map those relationships.
So, the way I've been trying to do this is as follows.
A, which is in the category of 1, is related to B
B, which is in the category of 2, is related to A, and C
When I visit the page for A, I'd like to see B listed as being related.
I've defined the YAML front matter as such:
title: A
category: 1
tags:
- B.html
title: B
category: 2
tags:
- A.html
- C.html
My liquid template looks like:
<h2>{{ page.title }} <span class="label important">{{ page.category }}</span></h2>
<p>Last edited: {{ post.date | date_to_string }}</p>
<p><em>{{ content }}</em></p>
<h4>Related</h4>
<ul>
{% for post in site.tag.{{ post.url }} %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
To me, that looks like it should work. In reality, it doesn't.
Suggestions are welcome!
Also, relevant Github pages are here:
https://raw.github.com/salmonhabitat/salmonhabitat.github.com/master/_posts/2011-12-12-bycatch.md
https://raw.github.com/salmonhabitat/salmonhabitat.github.com/master/_posts/2011-12-12-accidental.md
https://github.com/salmonhabitat/salmonhabitat.github.com/blob/master/_layouts/post.html
What I was intending to happen was for 'Accidental injury' to show up under 'Marine bycatch's' Related nodes...
The first problem is that posts are basically "blind" to other posts in jekyll. It's impossible to the url (or title) of one post from inside another post in jekyll, with only the id of the former. Your site.tag.{{ post.url }}, while creative, would not work :) .
First, your front matter needs to be (unfortunately) a bit more complicated to be able to accomplish that:
title: A
category: 1
related_posts:
- title: B
href: /2010/01/01/B.html
- title: C
href: /2011/11/11/C.html
Notice that I've changed the name from "tags" to "related_posts". I feel it's more clear this way.
Now you can try this code:
<h2>{{ post.title }} <span class="label important">{{ post.category }}</span></h2>
<p>Last edited: {{ post.date | date_to_string }}</p>
<p><em>{{ content }}</em></p>
{% if post.related_posts %}
<h4>Related</h4>
<ul>
{% for related_post in post.related_posts %}
<li>{{ related_post.title }}</li>
{% endfor %}
</ul>
{% endif %}
While this is a bit more verbose than your version, it has an advantage - you can specify your own title in related posts, without being forced to use B or C.
Let me know if you have problems with this, and good luck!