setting up this theme

Posted on 2015-01-10T10:20:00Z by aaron.kyle

Posted in pelican

Desiring to learn more about the Python programming language and how it can be applied in web design, I elected to build this site using the 'Pelican' static site generator. Pelican is a relatively simple web publication tool, so I figured it'd be a good way to get my head around things like using python for theming and templating (without the added complexity of a CMS).

To begin my adventure, I needed a template. Looking over the options available on the GitHub pelican themes repository, I opted to go with FrankV's port of the html5up template, 'twenty'.

FrankV's notes on theme deployment include a few caveats that were initially challenging for me (a novice) to figure out. In case you happen to stumble across this post while similarly trawling the Internet in search of guidance for theme installation, I hope the following materials help.

Caveat #1: skel.js

Frank's first caveat concerns the location of css and javascript files within the Pelican directory structure. He writes:

html5up uses skel.js to handle responsiveness of their templates. For skel to work right css has to be available at the {{ SITEURL }}/css/ path. I did not want to alter any of the files in the template (not even the init.js). So to fix this for development my fab script copies over the css and js from the static folder and places them into their corresponding locations in my output directory. Here's my fab script I call collectstatic:

def collectstatic():
  if os.path.isdir(DEPLOY_PATH):
    local('mkdir -p {deploy_path}/css/ {deploy_path}/js/ {deploy_path}/fonts/ {deploy_path}/images/'.format(**env))
    local('cp -rf {theme_path}/twenty/static/css/* {deploy_path}/css/'.format(**env))
    local('cp -rf {theme_path}/twenty/static/js/* {deploy_path}/js/'.format(**env))
    local('cp -rf {theme_path}/twenty/static/fonts/* {deploy_path}/fonts/'.format(**env))
    local('cp -rf {theme_path}/twenty/static/images/* {deploy_path}/images/'.format(**env))


I just call it from my build and rebuild scripts and I'm good. On my production server I let the web server handle this.

I must admit that haven't fully figured out how Frank's configuration script works. (I'll update this post as soon as I do). That is, I added in his script to my fabfile.py, but I don't see it copying around the files as it should.

From what I can gather, there needs to be four folders just under the site's root directory— css, js, fonts, and images—which Pelcan references when building the site (i.e. when running the command pelican content). Frank's note about not wanting to make any changes to the HTML5 template in order to keep his port neat and tidy; his collectstatic script is a means of allowing him to keep the template directory self-contained (and separate from the rest of his Pelican files).

Until I figure out how to correctly configure the fabfile.py so that it implements the collectstatic script, my preference is to use a work-around: linking my deployment process to Git and running the following 'post-update' hook.

#!/bin/sh

cd /srv/www/pelican/ || exit
unset GIT_DIR
git pull && pelican content
cp -R ./twenty/static/css/* ./output/images/
cp -R ./twenty/static/js/* ./output/js/
cp -R ./twenty/static/fonts/* ./output/fonts/
cp -R ./twenty/static/images/* ./output/images/


As you can see, my 'post-update' script does basically the same thing as Frank's: I tell the Git user to pull in the changes I made to the site via Git, then to re-generate the Pelican website and to copy over my static files to the 'output' directory. This seems to work well enough for the moment. The location of my template specified in the pelicanconf.py as THEME = "themes/twenty" and everything else seems to regenerate successfully with each push to the server.

Caveat #2: body classes

Frank's next caveat totally confused me for some time:

The original template uses a body class to specify the layout. This was a little tricky since for now I want to use two different layouts. For archive and category pages I wanted the right-sidebar layout, and for everything else, no-sidebar. The index class is implicit.

So to do this I had to write a little hack. Using a custom jinja filter I determine the type of page and then return the appropriate body class. In code it looks like this:

<body class="{% if page_name %}{{ page_name|sidebar }}{% endif %} loading">

There's a number of different ways to include a custom defined jinja filter in your pelican project. Here's the documentation for it.

And here's the filter code:

def sidebar(value):
  if value.startswith('archives') or value.startswith('category'):
    return 'right-sidebar'
  elif value == 'index':
    return 'index'
  else:
    return 'no-sidebar'


What appears to be happening here is that the template affords greater flexibility for specifying the layout of a given page than does the Pelican set-up. To help make things easy on Pelican, Frank decided that different 'classes' of page will be linked to a particular layout—no sidebar, right sidebar, etc.—which he defines in his jinja filter code.

The pelican documentation on theming is rather terse with respect to working with Jinja filters, so it took me a bit of effort to figure out how Frank got them working.

What went un-stated in the documentation is that the filter code given should be entered into a filters.py file that resides in a folder named utils. Then, before this filter can be picked up by Pelican / python, the following changes need to be made in your pelicanconf.py file:

First, add two lines of code near the top of pelicanconf.py:

import sys
sys.path.append('.')


A bit further down, add the code that explicitly references utils as the containing folder and then names the filter that should be loaded:

from utils import filters
JINJA_FILTERS = {'sidebar': filters.sidebar }


While it is apparent that the import command loads the specific *.py file, which python was able to find because of import sys command, and it's also clear that the JINJA_FILTERS = line then defines the specific filter within /utils/filters.py, I am still a bit confused by some of the shorthand and how it all works.

I.e.

import sys  ## What is sys?
sys.path.append('.')    ## What does it mean to append sys to the current directory?

import sidebar
JINJA_FILTERS = {'sidebar': filters.sidebar }   ## How does the syntax work here (and why)?


Caveat #3: blog and submenus

I wanted to have a category submenu underneath a permanent blog menu item. This code is currently located at line 54 in base.html. How it works is for all my articles in their own category sub-folder, a submenu and subsequent category page are built. Have a look at my pelicanconf.py file if it helps.

This is how I structured my content directory:

├── blog                      //standard articles
│   ├── article1.md
│   ├── article2.md
│   ├── article3.md
│   └── article4.md
├── pages
│   ├── about.md
│   └── portfolio.md
└── programming               //category
    └── article1.md

```



Template Info: As noted above, the template used as the basis for this site is Twenty, written by aj ‏@n33co and distributed by HTML5 UP. HTML5 UP generously allows for free personal and commercial use under the Creative Commons Attribution 3.0 License. The template is responsive and built on HTML5/CSS3/skel. Icons from Font Awesome.

social justice & hacking

This history of hackers and the 'hacker movement' is wild stuff. A guy figured out how to whistle his way through switchboard relays. A kid breached the pentagon.

setting up this theme

I would like to learn more about python and how it can be applied in web design. I figured a good way to start would be to learn Pelican and to generate a static site.

getting hacked!?!

Hello hackers! Please know that I appreciate your (collective) work. My own struggle with networked information systems, however, is less than inspiring.

Looking for me?

I welcome invitations to collaborate on interesting research projects—especially those related to OpenSource software development in support of applied social science research.
If you have an idea in mind that you'd like to bounce off someone, I'd be glad to hear from you.