WakeLift
wakelift / posts /

How to set up a static ikiwiki as a blog/articles website [update!]

Today, I'm going to demonstrate how I created this website using ikiwiki. I will discuss the theming, the templating and the configuration.

Update: I added the section "Instant previewing" below, as well as the section "Heredocs and triple-quoted arguments". Check it out, it's pretty nifty :)

Another Update: Since the new ikiwiki version, my code for here docs has been merged upstream. The text on it has been changed accordingly.

Workflow overview

My installation of ikiwiki uses a git repository as its backend. It was created by ikiwiki and has a post-receive hook that regenerates the websites new parts when I push to it. The git repo is then cloned to my laptop on which I do most of the work.

Creating a new blogpost is very simple with ikiwiki. All I have to do is create a new .mdwn file in posts/, commit the changes and push them. All the rest is done by the ikiwiki generator on the server.

Usually, posts and pages can be created on the website itself utilising the CGI, but I didn't want to bother with non-vim editors and since I'll be the only one working on the website, I decided to try to get rid of the CGI completely. That would also make my website much more resilient to high visitor counts, in case I ever create an interesting blog post or article by accident. To top that, uploading files is also easier when you can just copy them into the folder and git commit it later on.

Installation

The first step, obviously, is to install ikiwiki on the web server you want to use to serve your content. Your package management should do this for you, but if you want the latest and greatest version, you can just follow the download instructions on the website.

The next step is to set up all things needed. Ikiwiki comes with a "auto-blog" setup file, which is the one I used. It will ask questions and create a git repository and a checkout for it. You will then want to edit the setup file that has been created and remove some plugins:

[...]
add_plugins => [qw{goodstuff sidebar highlight format}],
[...]
disable_plugins => [qw{editpage recentchanges smiley}],
[...]
underlaydir => "/path/to/a/random/directory/",
[...]

This will remove the "edit page" links as well as the "recent changes" links. The latter will be re-added later with slight modifications. The underlaydir would have given us a subfolder "ikiwiki/" with a few basic pages, a sandbox and such things. Since removing the setting itself won't cause the underlay to disappear, we use a new empty folder instead.

Additionally, make sure to remove the config options cgiurl, cgi_wrapper and cgi_wrappermode, so that no cgi file will be created for us.

Templating

To get a template, I chose to dig around on some "free design" websites and stumbled upon Shades of Gray by Arcsin, which I quite liked. It just lacked a bit of color, so I took some inspiration from mxey of ghosthacking fame and added some colorful highlights.

The first step to turning the Shades of Gray template into an ikiwiki template was, in order to get familiarized with the ikiwiki template system, to copy the two templates page.tmpl and inlinepage.tmpl to the templates folder in your checked out git repo. This will automatically override the two global templates. Then I opened page.tmpl in a split view with the design index.html and started removing bits and pieces from the page.tmpl and copied stuff over and such.

The first step was to remove the optional html5 compatibility completely, because I don't know anything about html5 yet and I didn't want to mess it up. All the extra template IFs were making it really difficult to see what's what, and the template I was basing it off of was XHTML, so I thought that's good enough for now. The same thing was done for the inlinepage.tmpl.

You can get my versions of the templates from my website: inlinepage.tmpl and page.tmpl

I also removed lots of things that my website wouldn't have anyway, such as the edit link, the recent changes link and others. The recent changes link was later replaced by a link to a wiki page generated by ikiwiki with only the following content:

[[!meta  title="pages most recently changed"]]
[[!inline  pages="*" archive="yes" sort="mtime" show=20]]

Theming

Theming was pretty straight forward, I just took the css that came with the free theme and put it into the git repo named local.css and did a bit of wiggling back and forth. You can also download the css file here.

Instant previewing

The only feature I've really been missing, because I'm not using the ikiwiki webinterface is previewing the pages I've created. Fortunately, all that the ikiwiki CGI does is call ikiwiki with the setup file that was used to create the wrapper and pass the command to build a page to it.

This can easily be used to our advantage. I've written this simple script to simplify the few steps necessary to create a working preview and open it in my browser:

#!/bin/sh
# change this to the root of your website on your local disk
# make sure it does not end in a slash.
WEBSITE_ROOT=/home/timo/website

if [ "a${1:0:1}" = "a/" ]; then
    # if the argument was absolute, we just take it.
    FILE_POSITION=$(readlink -f $1)
else
    # for relative paths, pwd combined with $1 will give the
    # full path to the file
    #  e.g.: /home/timo/website/posts/ikiwiki_setup.mdwn
    # readlink will remove any .. or symlinks

    FILE_POSITION=$(readlink -f $(pwd)/$1)
fi

# removing the website root from the beginning of the absolute path will give
# us a path that ikiwiki will understand
RELATIVE=$(echo $FILE_POSITION | sed 's,'$WEBSITE_ROOT'/,,')

# split relative into PAGE, the filename itself, and relative, the path
# relative to the WEBSITE_ROOT.
PAGE=$(basename $RELATIVE)
RELATIVE=$(dirname $RELATIVE)
OUTFILE=$(mktemp --tmpdir ikiwiki_preview.XXXXXX)

# use our locally adapted setup
# --no-usedirs will give us one less pair of ../, so that links will line up.
ikiwiki -setup $WEBSITE_ROOT/.setup \
        --no-usedirs \
        --render $WEBSITE_ROOT/$RELATIVE/$PAGE > $OUTFILE

# change all links to absolute links on our local filesystem,
# so that styles etc. work
sed -i 's,\(src\|href\)=",\1="file://'$WEBSITE_ROOT'/'$RELATIVE/',g' $OUTFILE

# display the result in the browser.
$BROWSER $OUTFILE

# ask the user to remove the temp file.
echo -n "delete the temporary file $OUTFILE? [Y/n] "
read ANSWER
[ ! "${ANSWER:0:1}" = "n" ] && rm $OUTFILE

To use the script, first copy the setup file into your website git root. I've called it .setup and added it to the .gitignore, so it would get out of my way, but you can name it anything you like, as long as you modify the script above to fit. Then you need to change srcdir in the setup file as well.

The script replaces all relative references like stylesheet inclusions or such to absolute links. Images, though, don't work yet, as ikiwiki can apparently not find links in preview mode and since we disabled cgi above, it isn't even able to link to the cgi, which usually does link finding for complicated scenarios. But since a .cgi would probably not be executed properly by our browser if invoked via file://, that wouldn't help anyway.

Vim integration

If you want to quickly run the preview script from normal mode in your vim, try this mapping. If you don't have mapleader set, it will be bound to \pv in normal mode. It will run the script above (if it's anywhere in PATH, executable, and called iki_preview.sh.)

nnoremap <leader>pv :!iki_preview.sh %<cr>

Emacs integration

Thanks to my good friends florolf and andi, even emacs users can enjoy this neat little script.

(defun preview-ikiwiki ()
    (interactive)
    (if
        (buffer-file-name)
        (start-process "ikiwiki" "ikiwiki" "/path/to/iki_preview.sh" (buffer-file-name))
      (error "Buffer has no file name")))

(global-set-key "\C-c\C-v" 'preview-ikiwiki)

Don't forget to change the path to the script and maybe the keyboard shortcut!

Heredocs and triple-quoted arguments

One thing that really bothered me about ikiwiki was, that the "more" directive, that prevents the complete blogpost to be included on the front page would require the rest of the text to be quoted. Since ikiwiki only allows """, ' or " quotes, that got problematic fast, as I'd put code examples inside [[!format directives, that also had to be quoted. And since I like using """ and similar quotes in python code, that would have to be quoted. Except it wasn't possible. So i had to change all quotes. Not good! So I delved into the ikiwiki sourcecode late at night and added support for Heredoc style parameters, as well as triple-single-quotes.

Here is an example of how you would use heredoc quotes to reduce escaping.

This is my most fabulous blogpost about some really interesting topic!

Click the "more" link for more text and sourcecode examples!

[[!more text=<<MORETEXT
hey, this is the rest of the blogpost!

[[!inline pages='''posts/*''' show='''1''']]

And this is the example I promised:

[[!format py <<PYTHONCODE
class spam(object):
    def __init__(self):
        """This class does absolutely nothing except print spam once."""
        print "spam!"
        print 'haha, i lied to you in the docstring!'
        print '''now you're surprised, huh?'''
PYTHONCODE]]

And this is the end of the blog post!
MORETEXT

The support for this is in any ikiwiki release after and including 3.20110431, which was just released as I wrote this update. Its usage is explained with examples on the official ikiwiki website.

If you don't want to update all of ikiwiki, it will be enough, to update just IkiWiki.pm. Depending very much on what version you're using now, there might be incompatibilities. You have been warned.

Unfortunately I'm not good enough at vim syntax to figure out how to make the here docs correctly work. If someone would like to take up the task, that would be brilliant. The code can be obtained here.

Instant Feedback

A little something I developed for my old website already was the "instant feedback" widget, that would allow any visitor to quickly send a jabber message my way including the name, what page they sent from and the text. Obviously I got some spam through it, so I decided to implement reCAPTCHA support for it. You can download the result, even though it is very rough around the edges. It really barely works and maybe I'd be better off using a python microwebframework instead of a cgi application for it. Just grab the code from the feedback page.