I decided to drop tags and categories during the migration from WordPress to Jekyll, but it was not the best decision as it turned out.
I decided to use topics to organize entries as it feels just right because I can quickly separate a group of entries and automatically provide an RSS feed.
How to define one or more topics inside the blog post?
Use topics key to list topics.
--- layout: post title: Jekyll - How to use custom taxonomy or post parameters topics: - Jekyll - Plugins --- <p>contents</p>
The origin
I will use the generator code available at the Jekyll’s plugins page and just modify it slightly each time to display topics, list posts, and create an RSS feed.
module Jekyll class CategoryPage < Page def initialize(site, base, dir, category) @site = site @base = base @dir = dir @name = 'index.html' self.process(@name) self.read_yaml(File.join(base, '_layouts'), 'category_index.html') self.data['category'] = category category_title_prefix = site.config['category_title_prefix'] || 'Category: ' self.data['title'] = "#{category_title_prefix}#{category}" end end class CategoryPageGenerator < Generator safe true def generate(site) if site.layouts.key? 'category_index' dir = site.config['category_dir'] || 'categories' site.categories.keys.each do |category| site.pages << CategoryPage.new(site, site.source, File.join(dir, category), category) end end end end end
List of the topics
To create a list of the topics (/topics/index.html file), look at the following plugin source code, which uses topic_list.html layout.
module Jekyll class TopicList < Page def initialize(site, base, dir, topics) @site = site @base = base @dir = dir @name = 'index.html' self.process(@name) self.read_yaml(File.join(base, '_layouts'), 'topic_list.html') self.data['topics'] = topics self.data['title'] = "Topics" end end class TopicListGenerator < Generator safe true def generate(site) if site.layouts.key? 'topic_list' dir = 'topics' # get list of the unique topics topics = []; site.posts.each do |p| p.data['topics'].each do |t| topics << t if !topics.include? t end if p.data['topics'] end write_topic_list(site, dir, topics) end end def write_topic_list(site, dir, topics) index = TopicList.new(site, site.source, dir, topics) index.render(site.layouts, site.site_payload) index.write(site.dest) site.pages << index end end end
Notice that generate function inside TopicListGenerator class uses a topics array to pass further the list of the unique topics.
Use page.topics inside topic_list.html layout to access the above-mentioned topic list.
--- layout: index --- {% for topic in page.topics %} <div class="topic"> <h2> <a href="/topic/{{ topic | downcase }}/">{{ topic }}</a> </h2> <div> <small> <a href="/topic/{{ topic | downcase }}/atom.xml">RSS</a> </small> </div> </div> {% endfor %}
Index pages
To create index pages for each topic (/topic/jekyll/index.html file and so on) containing only selected posts, look at the following plugin.
Notice that we pass further the topic and posts belonging to this topic.
class TopicIndex < Page def initialize(site, base, dir, topic) @site = site @base = base @dir = dir @name = 'index.html' # get topic's posts topic_posts = [] site.posts.each do |p| ((topic_posts << p) if p.data['topics'].include? topic) if p.data['topics'] end self.process(@name) self.read_yaml(File.join(base, '_layouts'), 'topic_index.html') self.data['posts'] = topic_posts.reverse self.data['topic'] = topic self.data['title'] = "Topic \""+topic+"\"" end end class TopicIndexGenerator < Generator safe true def generate(site) if site.layouts.key? 'topic_index' dir = 'topic' # get list of the unique topics topics = []; site.posts.each do |p| p.data['topics'].each do |t| topics << t if !topics.include? t end if p.data['topics'] end # create page for each topic topics.each do |topic| write_topic_index(site, File.join(dir, topic.downcase), topic) end end end def write_topic_index(site, dir, topic) index = TopicIndex.new(site, site.source, dir, topic) index.render(site.layouts, site.site_payload) index.write(site.dest) site.pages << index end end end
topic_index.html layout doesn’t contain anything new.
--- layout: index --- <h1 class="page-title">{{page.title}}</h1> {% for post in page.posts %} {% include content-on-the-list.html %} {% endfor %}
Atom feeds
It is almost identical to the above plugin as I just changed names and layout (/topic/jekyll/atom.xml file and so on).
class TopicAtom < Page def initialize(site, base, dir, topic) @site = site @base = base @dir = dir @name = 'atom.xml' # get topic's posts topic_posts = [] site.posts.each do |p| ((topic_posts << p) if p.data['topics'].include? topic) if p.data['topics'] end self.process(@name) self.read_yaml(File.join(base, '_layouts'), 'topic_atom.xml') self.data['posts'] = topic_posts.reverse self.data['topic'] = topic self.data['title'] = "Topic \""+topic+"\"" end end class TopicAtomGenerator < Generator safe true def generate(site) if site.layouts.key? 'topic_atom' dir = 'topic' topics = []; # get list of the unique topics site.posts.each do |p| p.data['topics'].each do |t| topics << t if !topics.include? t end if p.data['topics'] end # create RSS feed for each topic topics.each do |topic| write_topic_atom(site, File.join(dir, topic.downcase), topic) end end end def write_topic_atom(site, dir, topic) index = TopicAtom.new(site, site.source, dir, topic) index.render(site.layouts, site.site_payload) index.write(site.dest) site.pages << index end end end
topic_atom.xml does not use any other layout as it is just a simple Atom feed.
--- layout: nil --- <?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>sleeplessbeastie's notes - {{ page.topic }}</title> <link href="{{ site.production_url }}/topic/{{ page.topic | downcase }}/atom.xml" rel="self" /> <link href="{{ site.production_url }}/topic/{{ page.topic | downcase }}/" /> <updated>{{ page.posts.first.date | date_to_xmlschema }}</updated> <id>{{ site.production_url }}/</id> <author> <name>Milosz Galazka</name> </author> {% for post in page.posts offset: 0 limit: 10 %} <entry> <title>{{ post.title }}</title> <link href="{{ site.production_url }}{{ post.url }}/" /> <updated>{{ post.date | date_to_xmlschema }}</updated> <id>{{ site.production_url }}{{ post.url }}/</id> <content type="html">{{ post.content | postmorefilter: post.url | xml_escape }}</content> </entry> {% endfor %} </feed>