Output link to "parent page"? - jekyll

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 %}```

Related

Jekyll how to write posts for different topics

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.

Why does this jekyll template not work?

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 %}

Dynamically add and filter images in Jekyll for github pages?

I am trying out Jekyll to help someone who's not all that technical maintain their own static site. I would like to be able to have a images directory in the app's root /images containing images following a naming convention:
post_one_1.jpg, post_one_2.jpg, post_two_1.jpg, post_two_2.jpg ... etc.
I would then like for the user to create a post (post_one) and dynamically grab all of the images pertaining to that post from the images directory.
This plugin (https://gist.github.com/jgatjens/8925165) does almost exactly what I need, but isn't compatible with github pages.
Is there a solution in which I can hand the site off to a user and they would only need to add images to the image directory following the naming convention and then create a post and have access to the images?
Given you have a post file _posts/2015-05-28-post_one.md
From inside this post you have :
page.id = /2015/05/29/post_one
page.dir = /2015/05/29
In order to extract post_one whe do :
{% assign imgNameStart = page.id | remove: page.dir | remove: "/" %}
We now generate the base path we search for :
{% assign imgBasePath = imgNameStart | prepend: "/images/" %}
in this case it will be imgBasePath = "/images/post_one"
Loop over all our static files (files that are not pages or posts).
{% for img in site.static_files %}
And print images that have /images/post_one in their path like /images/post_one-01.jpg or /images/post_one-wathever-you-want.jpg
{% if img.path contains imgBasePath %}
<img src="{{ site.baseurl }}{{ img.path }}">
{% endif %}
{% endfor %}
All together :
{% assign imgNameStart = page.id | remove: page.dir | remove: "/" %}
{% assign imgBasePath = imgNameStart | prepend: "/images/" %}
{% for img in site.static_files %}
{% if img.path contains imgBasePath %}
<img src="{{ site.baseurl }}{{ img.path }}">
{% endif %}
{% endfor %}
Beware of code indentation if your post is a markdown file, four space indentation can be transformed to code snippet.

Reuse file path in Jekyll

I am very new to writing websites and jekyll so I apologize if my terminology is unclear or wrong.
I have a bunch of saved files (foo1, foo2, etc) in a subdirectory called 'savedfiles' of my root jekyll folder. Suppose I am editing index.md which is in a sub folder called 'subfolder" and want to link to each of these. One way I can do this is to use
{{ site.url }}/savedfiles/foo1
{{ site.url }}/savedfiles/foo2
etc
Is there a way of saving the file path in a variable say pathfoo so that I could write
{{ pathfoo }}/foo1
etc
And sort of related to this can I get the file path of the subfolder that index.md is in? I know that
{{ page.path }}
will give me {{ site.url }}/subfolder/index.md but I want {{ site.url }}/subfolder. Is this possible?
Saving a path in a variable
{% capture path %}{{ site.url }}{{ site.baseurl}}/savedfiles/{% endcapture %}
you can now use this variable like this :
Link to foo
Getting file path from a file
It is a little bit tricky. But here is a way :
{% assign pathParts = page.path | split: "/" %}
{% assign length = pathParts.size | minus: 2 %}
{% assign path = "" %}
{% for c in (0..length) %}
{% capture path %}{{ path }}/{{pathParts[c]}}{% endcapture %}
{% endfor %}
You now have a path variable like /folder/subfolder. This could be simplest with pop or shift filters, but they are not working as expected and will change in Jekyll 3.0.

How to change the default order pages in Jekyll?

My blog is built with Jekyll on Github. In the navigation bar, the default order is Pages, Messages, About, Archives. I want to change the list to Pages, Archives, About, Messages. What should I do?
I think it is related to the code below
{% assign pages_list = site.pages %}
I think site.pages is what I should change, but I don't know how.
You can create custom order of your menu items like this:
In your pages front matter add the order field (you can name it as you prefer)
---
layout: default
published: true
title: Page title
order: 1
---
When getting pages, apply the 'sort' filter
{% assign sorted_pages = site.pages | sort:"order" %}
{% for node in sorted_pages %}
<li>{{node.title}}</li>
{% endfor %}
You'll end up with an ordered (ASC) list of pages, based on the 'order' field value you add to each page.
Update: Some ordering functionality seems to have been added to Jekyll: https://github.com/plusjade/jekyll-bootstrap/commit/4eebb4462c24de612612d6f4794b1aaaa08dfad4
Update: check out comment by Andy Jackson below – "name" might need to be changed to "path".
This seems to work for me:
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li>{{node.title}}</li>
{% endfor %}
name is file name. I renamed pages to 00-index.md, 01-about.md etc. Sorting worked, but pages were generated with those prefixes, which looked ugly especially for 00-index.html.
To fix that I override permalinks:
---
layout: default
title: News
permalink: "index.html"
---
Sadly, this won't work with custom attributes, because they are not accessible as methods on Page class:
{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
The order of your navbar menu is determined by the HTML template in _layout (which may be pulling in HTML fragments from _includes.
It sounds like your navbar is being programatically generated from the list of pages provided in site.pages using the liquid code
{% assign pages_list = site.pages %}
If you have only a small number of pages, you may prefer to just write the list out manually. site.pages is Jekyll's alphabetical list of all pages. Nothing stops you from just hardcoding this instead:
<div class="navbar" id="page-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="/">EverCoding.net</a>
<ul class="nav">
<li>Pages</li>
<li>Archive</li>
<li>About</li>
<li>Messages</li>
Whereas I'm guessing at the moment you have that list generated programmatically, perhaps by following the way Jekyll-bootstrap does with liquid code:
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="{{ HOME_PATH }}">{{ site.title }}</a>
<ul class="nav">
{% assign pages_list = site.pages %}
{% assign group = 'navigation' %}
{% include JB/pages_list %}
</ul>
</div>
</div>
</div>
The liquid code in this second example is handy if you really want to determine the menu each time, but if you have a static menu in a static order you are probably best coding it by hand as in my first example, rather than modifying the liquid code to sort.
If you could link to the Jekyll source, rather than the published blog, we could be more specific.
I'm using Jekyll v2.5.3 and you can also number your actual markdown files (order them that way) and since you're using the Front Matter block you can still keep the titles and permalinks as you want them.
The parser will order your page links that way.
I.e.:
01_about.md
02_photos.md
03_projects.md
99_contact.md
I made pages.yml file in the _data directory it is look like similar:
- url: pages/test.html
title: Pages
group: navigation
- url: pages/front.html
title: Front
group: navigation
And I changed the default.html (from site.pages to site.data.pages):
<ul class="nav">
{% assign pages_list = site.data.pages %}
{% assign group = 'navigation' %}
{% include JB/pages_list %}
</ul>
And now I can use this yml file for the menu.
You could see the documentation: http://jekyll.tips/jekyll-casts/navigation/
There are good examples and explanations with navigation_weight.
---
layout: page
title: About
permalink: /about/
navigation_weight: 10
---
For minima:
<div>
{% assign navigation_pages = site.pages | sort: 'navigation_weight' %}
{% for p in navigation_pages %}
{% if p.navigation_weight %}
{% if p.title %}
<a class="page-link" href="{{ p.url | relative_url }}">{{ p.title | escape }}</a>
{% endif %}
{% endif %}
{% endfor %}
</div>
For minima theme
Put:
header_pages:
- pages.md
- archive.md
- about.md
- messages.md
in _config.yml to override default order. That's all.
Minima README:
Customize navigation links
This allows you to set which pages you want to appear in the
navigation area and configure order of the links.
For instance, to only link to the about and the portfolio page,
add the following to you _config.yml:
- about.md
- portfolio.md
You can see how it works in header.html file from minima _includes.
You were on the right path. You could sort by a custom variable named, say, 'order'.
In header.html insert and extra row:
{% assign pages_list = (site.pages | sort: 'order') %}
Then replace site.pages with pages_list in the for statement:
{% for my_page in pages_list %}
{% if my_page.title %}
<a class="page-link" href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{% endif %}
{% endfor %}
Then add 'order' into the YAML front matter for each page, and set it a suitable value:
---
layout: page
title: About
permalink: /about/
order: 0
---
The Jekyll Bootstrap 3 template requires that you include group navigation in the Jekyll header. Building on #Wojtek's answer, you can modify JB3's pages_list to use this group field to both filter, and sort.
Before calling pages_list, sort by group:
{% assign sorted_pages = site.pages | sort:"group" %}
Then, simply change one line in pages_list:
{% if group == null or group == node.group %} -> {% if group == null or node.group contains group %}
Now you can specify the group to be navigation-00, navigation-01, without having to rename your files or set up any permalinks, and you get sorting for free.
I made a simple plugin some time ago to sort pages according to a page_order array you can define your _config.yml:
pages_order: ['index', 'summary', 'overview', 'part1', 'part2', 'conclusion', 'notes']
It exposes page.prev and page.next in templates to allow navigation:
{% if page.prev %}
<a id="previous-page" href="{{page.prev}}.html">Previous</a>
{% endif %}
{% if page.next %}
<a id="next-page" href="{{page.next}}.html">Next</a>
{% endif %}
Note: Does not work on Github Pages.