What I'm trying to do
Jekyll can use front matter variables like tags and categories and access them with site.tags and site.categories to iterate over them using liquid.
Now my problem is that I can't do this with a custom front matter variable like author (as in site.authors) because Jekyll will not store it in a list format. This makes it very hard to paginate it.
The problem
Every solution I have looked at i.e.
how to handle multiple authors in Jekyll
adding authors to Jekyll
jekyll-author
requires me to hardcode a list of authors to _config.yml or some other .yml (i.e. in _data/authors.yml).
The problem here is that I don't use a fixed list of authors. The authors list needs to get updated when I throw in another post with a front-matter tag author: exampleAuthor or a list of authors (as in multiple authors per post, as currently only possible with categories and tags as well), while the server is running. It works with tags and categories splendidly, but not with custom tags like authors.
The easiest solution would to have a site.authors list to iterate over and just extending it with a ruby plugin.
I haven't found a plugin that provides me with a solution and thinking this was a common problem that I'm probably not the first to have.
What I tried
I then looked at writing my own ruby plugin (Which is hard on it's own because of the lack of documentation. Maybe I'm to dumb to google, but the resources I found where very limited and hardly enough to guide you through the process) but there has to be a reason why this is so hard to do that makes all the existing solutions require hard-coding the author list in a .yml (or .json, but most people go with .yml for some reason).
Doing this is out of the question for me, since I want to only throw in posts with author names in it later on and manipulating a .yml (I am under the impression that .yml-files don't get compiled once the server is started, like _config.yml, correct me if I'm wrong) would be counterproductive because it requires you to restart the server to have them compiled.
Even very advanced plugins like jekyll-paginate-v2 (which I use successfully to paginate posts by tags and categories) don't have a solution to this, as shown by this issue. As per this issue, it is getting recommended to misuse the category variable to paginate by author. In my opinion, that is desperate workaround and too hacky to be considered.
I have found suggestions that it could also be done with collections, but this would again
requiring to hard-coding the author list (again, I don't want that. I don't have a fixed list of authors. All of the author information has to come from the front-matter in the /_posts directory .md files)
As of now I don't see how it can be done with collections.
However I'm open to suggestions.
Edit: I found this dated issue on Jekylls github page which highlights that people are trying to do the same but to no avail.
Has this become viable in the last 4 years?
For someone still looking for a way to
Generate author pages automatically just by dropping author: name to post front matter,
Have pagination on the author pages (a good optimization).
I built a plugin jekyll-auto-authors that works in sync with jekyll-paginate-v2 to enable author auto pages along with pagination.
I wrote this guide and made this video to help with the setup.
The bare minimum setup instructions:
Install the plugin:
group :jekyll_plugins do
# other gems
gem "jekyll-paginate-v2" # reqiured for jekyll-auto-authors to work
gem "jekyll-auto-authors"
end
Enable it:
plugins:
# other plugins
- jekyll-paginate-v2
- jekyll-auto-authors
Make a data file with author data, for example using _data/authors.yml:
johndoe:
name: "John Doe"
bio: "John Doe is a software engineer."
email: "john#example.com"
socials:
github: "john-doe"
twitter: "john_doe"
janedoe:
name: "Jane Doe"
bio: "Jane Doe is a systems engineer."
email: "jane#example.com"
socials:
github: "jane-doe"
twitter: "jane_doe"
Make a layout for the author page, let's say _layout/author.html. Example layout can be taken from the article.
Enable pagination and auto pages for authors in _config.yml file:
pagination:
enabled: true
per_page: 9
permalink: '/page/:num/'
title: ':title - page :num'
sort_field: 'date'
sort_reverse: true
autopages:
enabled: true
# enable auto pages for tags/categories/collections as per need. Disabling for this demo.
tags:
enabled: false
categories:
enabled: false
collections:
enabled: false
authors:
enabled: true # adding false here stops the auto-generation
data: '_data/authors.yml' # Data file with the author details
layouts:
- 'author.html' # We'll define this layout later, will be used for each author
title: 'Posts by :author'
permalink: '/author/:author/'
slugify:
mode: 'default' # choose from [raw, default, pretty, ascii or latin]
cased: true # if true, the uppercase letters in slug will be converted to lowercase ones.
That's the initial setup!
Now drop in the author value in the front matter of posts:
---
# other configs
author: johndoe
---
This will generate a page as defined in the permalink block of the auto pages configuration. If there's pagination enabled, and per_page value is exceeded, the additional pages will be generated in /page/:num/ format.
To render the author values, the plugin exposes page.pagination.author_data value
{% assign author = page.pagination.author_data %}
<!-- Use {{ author.name }} or any such value, as defined inside the data file -->
To show next and previous buttons, you can use this logic that paginator exposes:
{% if paginator.total_pages > 1 %}
<ul>
{% if paginator.previous_page %}
<li>
Newer
</li>
{% endif %}
{% if paginator.next_page %}
<li>
Older
</li>
{% endif %}
</ul>
{% endif %}
The initial setup is overwhelming, but once done you can then just drop in author data inside _data/authors.yml file and add author: value inside post frontmatter and it is fairly easy then!
P.S. I developed this solution for Genics Blog as managing multiple authors got hard. To learn how I've implemented it at Genics, please check out the theme-files repository.
Update
I released v1.0.1 just now, which makes adding the data parameter to author autopage configuration optional.
If data isn't defined, you can still access the author username string with page.pagination.author. You can use it to show the username on the page.
If data is defined, page.pagination.author_data variable is available. This would be a hashmap that has data as defined in the data file.
This means that you just have to:
Add and enable the plugin.
Set up pagination and author pages config.
Make a layout file.
And you can just drop in author: username to post files to generate autopages for them with pagination!
Adding authors to Jekyll posts is easy with collections. Here's a proof of concept for you. Specifically, in this commit I add everything you need for it.
As for your question about pagination, will need to use a pagination plugin (paginate-v2 is good), as I believe the built in pagination only supports the posts collection.
Related
I'm using saltstack to manage my personal config. In particular I'm managing my ~/.profile programatically. I have various things throughout the setup which append text (mostly env var exports) to it, and they all work exactly as expected. I want to to use slsutil.banner to prepend a banner saying that the file is managed programatically by salt, and not to touch it by hand. So I have in my profile/init.sls:
profile-managed-banner:
file.prepend:
- name: {{ pillar['profile_file'] }}
- text: {{ salt['slsutil.banner']() }}
This should write the default banner to the beginning of the file.
When I then run run salt-call (the setup is masterless. Running salt-call as sudo if that's at all relevant) I get:
ID: profile-managed-banner
Function: file.prepend
Name: /home/modallyFragile/.saltProfile
Result: True
Comment: File /home/modallyFragile/.saltProfile is in correct state
Started: 15:59:07.760790
Duration: 1.757 ms
Changes:
So clearly salt can find and use all the functions (or at least it thinks it can) and the the file is having something prepended to it. If I check the file though, I get this:
None
[ ... further config here]
If I substitute the templating for a string (so - text: some string here) it works as expected (prepends 'some string' to the file). So the probelm is with the templating slsutil.banner then. I've tried passing (various combinations of) arguments explicitly and nothing seems to help.
Why might this be happening and what can I do about it? Failing anything more substantive, what could I do to further diagnose the problem (I'm pretty new to saltstack, is there a particular log I should be checking with all the relevant info, etc. etc.)?
I can't find any issues or problems by searching (github or more generally), so I'm drawing a blank. Literally any suggestions would be really helpful. Thanks!
Adding this as an answer. As per the official documentation:
Create a standardized comment block to include in a templated file.
And this does work inside a templated file.
Just for example files/user-profile.j2:
{{ salt.slsutil.banner() }}
# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
umask 022
PATH=$PATH:/my/local/bin
Used in a setup.sls:
create-user-profile:
file.managed:
- name: /home/user1/.profile
- source: salt://files/user-profile.j2
- template: jinja
Will create a file /home/user1/.profile with a default banner at the top and the remaining content as it is.
Then it starts making sense. If you want a banner on file that "it is managed by Salt", then the file should be managed by Salt (as template). Prepending the banner in a remote file, leaving the remaining file contents unmanaged contradicts the purpose of the banner.
So if you want a banner in the file, you can manage the ~/.profile file as a template, and write it to minions as above.
Update:
It might be worth reporting this as an issue if we want a custom banner prepended to a file without managing it.
The problem is that your Jinja, once rendered, produces invalid or incorrect YAML. In this case, what is happening is that the slsutil.banner() function returns several lines that all start with '#'. The YAML renderer considers them comments and throws them out.
The solution is to escape the comment characters so they are properly interpreted by the YAML renderer. Fortunately, Salt comes with a very helpful Jinja filter that does this for you: yaml_encode. Your state works fine with this small change.
profile-managed-banner:
file.prepend:
- name: {{ pillar['profile_file'] }}
- text: {{ salt['slsutil.banner']() | yaml_encode }}
I need to group some of the collections so that I can password protect that group. At the same time I want to keep the _posts collection open, so it needs to remain at the root of jekyll.
For example, this is group secure
collection:
notes:
output: true
hybrid:
output: true
collections_dir: secure
How can I exclude the collections such as _posts and also custom ones being grouped under secure?
Can I use permalinks to achieve this? For example
collection:
notes:
output: true
permalink: /secure/:path/
hybrid:
output: true
permalink: /secure/:path/
Answering my own question. The permalinks option is in fact best in this situation. That way _posts remains at the root of jekyll and other collections which do not need to be grouped are also behaving normally.
Once you build the site, all grouped collections will be under _site/secure/ locally and will be under http://example.com/secure/ on your server. Make sure to set up http authentication using htpasswd. There is plenty of material on the web on how to do this.
TL;DR: Can I say somehow to generate the content for a {% include %} once and just stamp it out in multiple places without having to regenerate it in every location?
I'm building a fairly big documentation site with Jekyll which has right now a bit over 50 articles on it. It has a sidebar where all articles are listed. the sidebar is built in a separate sidebar.html and then it is included into every page on the site with {% include sidebar.html %} in default.html.
The problem I have is that every single article runs the generation of sidebar.html separately, so I have over 50 generation passes on that piece of code. Every article I add adds another pass to this and make all the passes a bit slower, as generating the sidebar has to parse every single article in the project.
Build time has gone up from basically zero to over 100 seconds already, and if I remove the {% include sidebar.html %} then it drops down to 5 seconds. When I get all the articles in I'd estimate to have around 100-200 of them. Then I should have versioning in the future for all articles which means that there can easily be 1000+ articles in the long run. At that point I wouldn't be suprised if changing one letter in one file would take something like an hour to regenerate files in jekyll serveand jekyll build.
What I would like to do is to build sidebar.html once in the beginning of the build process and just stamp it out to every page when i generate said pages. Is this possible?
Fastest way to do this.
Move _includes/sidebar.html to sidebar-template.html
Add this front matter :
---
layout: null
permalink: sidebar-template.html
---
Create a Rakefile
TPL = "_site/sidebar-template.html"
TST = "_includes/sidebar.html"
task :default => :nav
desc "Generates sidebar then copy it to be used as an include"
task :nav do
if !File.exist?(TST)
puts "Creating dummy #{TST} file"
open(TST, 'w') do |f|
f.puts warning
end
end
puts "Building Jekyll 1st run"
system "jekyll build --trace"
# delete target file (TST) if exist
if File.exist?(TST)
puts "#{TST} exists deleting it"
rm TST
end
# copy generated file as an include
cp(TPL, TST)
puts "Building Jekyll AGAIN"
system "jekyll build --trace"
puts "task END"
end
Just run rake and you have your sidebar include generated.
There is now a better way to do this, thanks to Ben Balter.
instead of: {% include yourtemplate.html %}
use: {% include_cached yourtemplate.html %}
When used on larger items that need to be built once, such as a site hierarchy, the item will be cached. For other items that vary across pages, you'll still want to use the include as you always have done.
It is explained well here: https://github.com/benbalter/jekyll-include-cache
Definitely cuts down on site startup time!
Does Jekyll support setting multiple permalinks?
For example, I currently have the following in my _config.yml:
permalink: /:categories/:title/
What I would like to have, is the following:
permalink:
- /:categories/:title/
- /:year/:month/:day/:title/
What I'm trying to achieve it that a single post will have multiple URLs. I'm well aware that I can use the "redirect_from" plugin (I'm hosting in GitHub Pages), but that would require me to manually update all my posts to include the redirect_from in the YAML
have you checked out jekyll-archives? https://github.com/jekyll/jekyll-archives
you can create other permalinks like
permalinks:
year: '/:year/'
month: '/:year/:month/'
day: '/:year/:month/:day/'
tag: '/tag/:name/'
category: '/category/:name/'
i don't think you can use :title though. it's an index page that lists posts.
My goal is to create links from any published jekyll page back to its location on Github.
And so, I'd need access to a page's pathname when constructing this url. However, I don't see anything in the api for templates that provides this info. I looked at the source for page, but none of the file/path attributes had values, when accessed via the template.
Update: nowadays you can use {{page.path}}.
It seems like you can construct your link just fine using the liquid code: {{ page.url }}
For instance, if you want this page: http://railsdocs.org/pages/get-involved.html to link to this page: https://github.com/dogweather/railsdocs.org/blob/gh-pages/pages/get-involved.md
Seems like you could add something like:
[source](https://github.com/dogweather/railsdocs.org/blob/gh-pages/{{page.url | replace:'.html','.md'}})
to the markdown and get the link you want.
A more general solution:
Alternatively, we could write a generator that allows a page to access its file name directly. Add this to a .rb file in your _plugins directory:
module Jekyll
class PagePathGenerator < Generator
safe true
## See post.dir and post.base for directory information.
def generate(site)
site.posts.each do |post|
post.data['path'] = post.name
end
end
end
end
and then we can reliably get the post's filename as {{ page.path }}. This solution is more robust than converting from the URL, since page names can have characters that get 'sanitized' out when converting them to URLs. (Posts would also need their date information, etc added back in). Instead, this plugin gives us direct access to a post's name.
A similar strategy could allow us to get the path to the post if that data is also needed.
I'm not sure when this was added, but page.path gives the source file's path relative to the Jekyll root directory.