Simple XDV theming setup

Tags: python, deliverance

A bit of background

“Deliverance” or xdv is something that I’ve been using for a while. It started in 2007 when hearing a presentation at a Plone sprint.

Basically, you have three parts:

  • The actual website(s) you want to theme.
  • A html file with the layout you want to have.
  • A rules file that tells xdv/deliverance which items to take from the source website and where to place them in the layout.

I’ve got two websites (a plone one for my church and a sphinx + plain html one that’s that use deliverance/xdv

  • The Plone one uses an older xdv version that I hooked up in a PasteScript .ini file.
  • My own website uses an old branch I once made of deliverance with some custom fixes. That deliverance version can be run on its own (with a cronjob to start it when the server starts).

It is all a bit brittle, somehow. It all runs perfectly, mind you, but I don’t dare touching it. So many moving parts and small differences. And nagging doubts about the small things that went wrong when deploying it.

Time for a fresh start

Time for a fresh start. I really like the xdv-idea and I had a website that was just perfect for xdv. I recently started generating documentation for all our products and started putting it online at . There are a couple of different kinds of pages in there:

  • Plain generated html index pages.
  • The python --long-description output, rendered as html. Basically the same you’d get when releasing a python package on pypi.
  • Coverage output.
  • Planned, but not there yet: sphinx documentation. Two packages already have it, but I just haven’t hooked it up in the automatic process.

Problem: I’m not going to make a nice sphinx layout and a regular html page layout and, yep, another long-description layout. The layout isn’t the problem, but you have to hook it up into at least three different documentation generation steps.

Solution: xdv. Just make one layout and write up a decent rule file that copy/pastes the correct bits and pieces out of the various source formats into the layout’s proper elements.

Current setup

Googling and reading my way through various websites, I settled for the following setup.

  • XDV, not deliverance. Xdv is a simpler, smaller version of deliverance and I don’t need the big version. And xdv has the interest and manpower of Plone behind it, so that’s solid. (Warning: they’re renaming it to diazo at the moment...)
  • dv.xdvserver as a piece of wsgi middleware for actually applying the xdv transformation.
  • collective.recipe.modwsgi to easily set up that wsgi middleware and to get an wsgi script I can feed to apache.

What I added to my install_requires list:

'dv.xdvserver',  # This pulls in xdv itself, btw.

In my buildout.cfg, I added two parts:

# Generate a script that's a wsgi script that runs the
# paste.ini config file.
recipe = collective.recipe.modwsgi
eggs = ${buildout:eggs}
config-file = ${buildout:directory}/etc/paste.ini

# Generate the paste.ini config file with the wsgi config.
# collective.recipe.template makes sure all the absolute
# paths are correct.
recipe = collective.recipe.template
input = ${buildout:directory}/etc/
output = ${buildout:directory}/etc/paste.ini

The wsgi configuration file paste.ini looks like this. Irritating is that you have to specify a full logging setup at the end: otherwise it won’t start (that’s because collective.recipe.modwsgi requires it):

# Main url mapper, this is the actual start of the site.
# This section tells paster to serve everything in /static
# with [app:theme_resources] and the rest with [app:root].
use = egg:Paste#urlmap
/static = theme_resources
/ = root

# Serve everything in /theme from the static directory.
use = egg:Paste#static
document_root = /home/reinout/buildout/doc/theme/static

# Likewise, our actual content (coverage, long-description,
# sphinx) is in var/www/.  Just serve it as static files,
# but first run it through our actual xdv filter.
use = egg:Paste#static
document_root = /home/reinout/buildout/doc/var/www
filter-with = xdv

# The actual xdv filter which is the purpose of this entire
# file!  Give it a ruleset (=configuration) and our base
# theme .html file.
use = egg:dv.xdvserver#xdv
theme = /home/reinout/buildout/doc/theme/index.html
rules = /home/reinout/buildout/doc/theme/rules.xml
# live=true means changes to the rules or theme are applied
# instantly, otherwise you'd need to restart apache.
# There's a bit of perfomance hit, of course.
live = true

# Development server, call it with
# "bin/paster server etc/paste.ini".
use = egg:Paste#http
host =
port = 8000

# Mandatory logging config below, otherwise
# collective.recipe.modwsgi refuses to work.
keys = root

keys = logfile

keys = logfile

level = INFO
handlers = logfile


format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt = %H:%M:%S

And our rules.xml file with - for the moment- just a couple of very basic transformations:

<rules xmlns=""
  <!-- Note: these prepend/append parts use the "old"
       xpath selectors. I can change them to css selectors
       later on. -->
  <prepend theme="//head" content="//head/base"
           nocontent="ignore" />
  <prepend theme="//head" content="//head/link"
           nocontent="ignore" />
  <prepend theme="//head" content="//head/style"
           nocontent="ignore" />
  <append theme="//head" content="//head/script"
          nocontent="ignore" />
  <append theme="//head" content="//head/meta"
          nocontent="ignore" />

  <!-- Hey look here! Nice new css selectors that everybody
       can understand!  -->
  <replace css:theme="title"
           nocontent="ignore" />
  <!-- For now, just copy the body text into a div we've got
       in there for our blueprint css styling. -->
  <copy css:theme="div.container"
        css:content="body > *"
        nocontent="ignore" />


The theme hasn’t been fleshed out yet. For now I only apply the blueprint css stylesheets, basically:

<!DOCTYPE html>
    <!-- Blueprint css framework. -->
    <link rel="stylesheet"
          media="screen, projection" />
    <link rel="stylesheet"
          media="print" />
    <!--[if IE]>
    {% compress css %}
    <link rel="stylesheet"
          media="screen, projection" />
    {% endcompress %}
    <div class="container">
      <p>Hurray!  Sample content that will disappear!</p>

In the end, I’m pretty happy with the setup. I’ll probably port my two other projects over. And I’ll keep an eye open for more usecases for xdv!

blog comments powered by Disqus logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):