Notable Octopress tweaks: Copyright, Tags, & Feed Excerpts

October 28, 2013 · 8 min read

This is almost pointless to mention but the standard templates give you a very specific copyright with respect to atom feeds (Copyright (c) x-y). The file source\_includes\custom\footer.html includes a way of gathering the system time in the form:

Copyright © {{ site.time | date: "%Y" }}

I simply replaced the current hard-coded year with the ruby code above so that when the site is generated, it always gives the current year.

In case any of you are wondering how to insert code blocks with liquid syntax, see this post.

Tags

This is more involved but luckily gist makes it stupid simple to include here. Everything is pretty much a rip off of the category_generator plugin and includes with just minor tweaks to use the tag object.

_config.yml

tag_dir: tags
view raw _config.yml hosted with ❤ by GitHub

plugins/tag_generator.rb (an almost complete copy of category_generator.rb)

# encoding: utf-8
#
# Jekyll tag page generator.
# Completely stolen from the category generator, where I copied the file and replaced text here:
# http://recursive-design.com/projects/jekyll-plugins/
#
# Version: 0.1.4 (201101061053)
#
# Copyright (c) 2010 Dave Perrett, http://recursive-design.com/
# Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
#
# A generator that creates tag pages for jekyll sites.
#
# Included filters :
# - tag_links: Outputs the list of tags as comma-separated <a> links.
# - date_to_html_string: Outputs the post.date as formatted html, with hooks for CSS styling.
#
# Available _config.yml settings :
# - tag_dir: The subfolder to build tag pages in (default is 'tags').
# - tag_title_prefix: The string used before the tag name in the page title (default is
# 'Tag: ').
require 'stringex'
module Jekyll
# The TagIndex class creates a single tag page for the specified tag.
class TagIndex < Page
# Initializes a new TagIndex.
#
# +base+ is the String path to the <source>.
# +tag_dir+ is the String path between <source> and the tag folder.
# +tag+ is the tag currently being processed.
def initialize(site, base, tag_dir, tag)
@site = site
@base = base
@dir = tag_dir
@name = 'index.html'
self.process(@name)
# Read the YAML data from the layout page.
self.read_yaml(File.join(base, '_layouts'), 'tag_index.html')
self.data['tag'] = tag
# Set the title for this page.
title_prefix = site.config['tag_title_prefix'] || 'Tag: '
self.data['title'] = "#{title_prefix}#{tag}"
# Set the meta-description for this page.
meta_description_prefix = site.config['tag_meta_description_prefix'] || 'Tag: '
self.data['description'] = "#{meta_description_prefix}#{tag}"
end
end
# The TagFeed class creates an Atom feed for the specified tag.
class TagFeed < Page
# Initializes a new TagFeed.
#
# +base+ is the String path to the <source>.
# +tag_dir+ is the String path between <source> and the tag folder.
# +tag+ is the tag currently being processed.
def initialize(site, base, tag_dir, tag)
@site = site
@base = base
@dir = tag_dir
@name = 'atom.xml'
self.process(@name)
# Read the YAML data from the layout page.
self.read_yaml(File.join(base, '_includes/custom'), 'tag_feed.xml')
self.data['tag'] = tag
# Set the title for this page.
title_prefix = site.config['tag_title_prefix'] || 'Tag: '
self.data['title'] = "#{title_prefix}#{tag}"
# Set the meta-description for this page.
meta_description_prefix = site.config['tag_meta_description_prefix'] || 'Tag: '
self.data['description'] = "#{meta_description_prefix}#{tag}"
# Set the correct feed URL.
self.data['feed_url'] = "#{tag_dir}/#{name}"
end
end
# The Site class is a built-in Jekyll class with access to global site config information.
class Site
# Creates an instance of TagIndex for each tag page, renders it, and
# writes the output to a file.
#
# +tag_dir+ is the String path to the tag folder.
# +tag+ is the tag currently being processed.
def write_tag_index(tag_dir, tag)
index = TagIndex.new(self, self.source, tag_dir, tag)
index.render(self.layouts, site_payload)
index.write(self.dest)
# Record the fact that this page has been added, otherwise Site::cleanup will remove it.
self.pages << index
# Create an Atom-feed for each index.
feed = TagFeed.new(self, self.source, tag_dir, tag)
feed.render(self.layouts, site_payload)
feed.write(self.dest)
# Record the fact that this page has been added, otherwise Site::cleanup will remove it.
self.pages << feed
end
# Loops through the list of tag pages and processes each one.
def write_tag_indexes
if self.layouts.key? 'tag_index'
dir = self.config['tag_dir'] || 'tags'
self.tags.keys.each do |tag|
self.write_tag_index(File.join(dir, tag.to_url), tag)
end
# Throw an exception if the layout couldn't be found.
else
raise <<-ERR
===============================================
Error for tag_generator.rb plugin
-----------------------------------------------
No 'tag_index.hmtl' in source/_layouts/
Perhaps you haven't installed a theme yet.
===============================================
ERR
end
end
end
# Jekyll hook - the generate method is called by jekyll, and generates all of the tag pages.
class GenerateTags < Generator
safe true
priority :low
def generate(site)
site.write_tag_indexes
end
end
# Adds some extra filters used during the tag creation process.
module Filters
# Outputs a list of tags as comma-separated <a> links. This is used
# to output the tag list for each post on a tag page.
#
# +tags+ is the list of tags to format.
#
# Returns string
#
def tag_links(tags)
tags = tags.sort!.map { |c| tag_link c }
case tags.length
when 0
""
when 1
tags[0].to_s
else
"#{tags[0...-1].join(', ')}, #{tags[-1]}"
end
end
# Outputs a single tag as an <a> link.
#
# +tag+ is a tag string to format as an <a> link
#
# Returns string
#
def tag_link(tag)
dir = @context.registers[:site].config['tag_dir']
"<a class='tag' href='/#{dir}/#{tag.to_url}/'>#{tag}</a>"
end
# Outputs the post.date as formatted html, with hooks for CSS styling.
#
# +date+ is the date object to format as HTML.
#
# Returns string
def date_to_html_string(date)
result = '<span class="month">' + date.strftime('%b').upcase + '</span> '
result += date.strftime('<span class="day">%d</span> ')
result += date.strftime('<span class="year">%Y</span> ')
result
end
end
end

source/_includes/archive_post_tags.html

{% capture tag %}{{ post.tags | size }}{% endcapture %}
<div class="row-fluid">
<div class="span1">
<h1 class="date-time"><time datetime="{{ post.date | datetime | date_to_xmlschema }}" pubdate>{{ post.date | date: "<span class='month'>%b</span> <span class='day'>%d</span><span class='year'><!--%Y--></span>"}}</time></h1>
</div>
<div class="span10">
<h1><a href="{{ root_url }}{{ post.url }}">{{post.title}}</a></h1>
</div>
</div>
{% if tag != '0' %}
<div class="row-fluid">
<div class="span1">
</div>
<div class="span10">
<footer class="archive">
<span class="tags">posted in {{ post.tags | tag_links }}</span>
</footer>
</div>
</div>
{% endif %}

source/_includes/custom/tag_feed.xml

---
layout: nil
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text" xml:lang="en"><![CDATA[{{ site.title }}]]></title>
<link type="application/atom+xml" href="{{ site.url }}/{{ page.feed_url }}" rel="self"/>
<link type="text" href="{{ site.url }}/"/>
<updated>{{ site.time | date_to_xmlschema }}</updated>
<id>{{ site.url }}/</id>
<author>
<name><![CDATA[{{ site.author | strip_html }}]]></name>
{% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
</author>
<rights>Copyright (c) 2010-{{ site.time | date_to_string | date: "%Y" }} <![CDATA[{{ site.author | strip_html }}]]></rights>
<generator uri="http://octopress.org/">Octopress</generator>
{% for post in site.tags[page.tag] limit: 5 %}
<entry>
<title type="html"><![CDATA[{{ post.title | cdata_escape }}]]></title>
<link href="{{ site.url }}{{ post.url }}"/>
<updated>{{ post.date | date_to_xmlschema }}</updated>
<id>{{ site.url }}{{ post.id }}</id>
{% if post.has_excerpt %}<summary type="html">{{ post.excerpt | xml_escape }}</summary>{% endif %}
<content type="html"><![CDATA[{{ post.content | expand_urls: site.url | markdownify | cdata_escape }}]]></content>
</entry>
{% endfor %}
</feed>
view raw tag_feed.xml hosted with ❤ by GitHub

source/_includes/post/tags.html

{% capture tag %}{% if post %}{{ post.tags | tag_links | size }}{% else %}{{ page.tags | tag_links | size }}{% endif %}{% endcapture %}
{% unless tag == '0' %}
{% if post %}
{% for tag in post.tags %}
<a class="tag" href="{{ root_url }}/{{ site.tag_dir }}/{{ tag | to_url }}/"><span class="badge">{{ tag }}</span></a>
{% endfor %}
{% else %}
{% for tag in page.tags %}
<a class="tag" href="{{ root_url }}/{{ site.tag_dir }}/{{ tag | to_url }}/"><span class="badge">{{ tag }}</span></a>
{% endfor %}
{% endif %}
{% endunless %}
view raw tags.html hosted with ❤ by GitHub

source/_layouts/tag_index.html

---
layout: page
footer: false
---
<div id="blog-archives" class="tag">
{% for post in site.tags[page.tag] %}
{% capture this_year %}{{ post.date | date: "%Y" }}{% endcapture %}
<div class="row-fluid">
<div class="span1">
{% capture this_year %}{{ post.date | date: "%Y" }}{% endcapture %}
{% unless year == this_year %}
{% assign year = this_year %}
<h2>{{ year }}</h2>
{% endunless %}
</div>
<div class="span11">
<article>
{% include archive_post_tags.html %}
</article>
</div>
</div>
{% endfor %}
</div>
view raw tag_index.html hosted with ❤ by GitHub

Feed excerpts

The following goes on line 23 in my atom.xml file, or right before the content tag. A better alternative would be to detect an excerpt and only display that or the content, not both.

{% if post.has_excerpt %}<summary type="html">{{ post.excerpt | xml_escape }}</summary>{% endif %}