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 reinout.vanrees.org) 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 http://doc.lizardsystem.nl . There are a couple of different kinds of pages in there:

  • Plain generated html index pages.

  • The python setup.py --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 setup.py install_requires list:

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

In my buildout.cfg, I added two parts:

[wsgiapp]
# 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

[wsgiconf]
# 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/paste.ini.in
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):

[composite:main]
# 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

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

[app:root]
# 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

[filter: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

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

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

[handlers]
keys = logfile

[formatters]
keys = logfile

[logger_root]
level = INFO
handlers = logfile

[handler_logfile]
class=logging.FileHandler
args=('/home/reinout/buildout/doc/var/log/xdv.log',)
level=INFO
formatter=logfile

[formatter_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="http://namespaces.plone.org/xdv"
       xmlns:css="http://namespaces.plone.org/xdv+css">
  <!-- 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"
           css:content="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" />

</rules>

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

<!DOCTYPE html>
<html>
  <head>
    <title>title</title>
    <!-- Blueprint css framework. -->
    <link rel="stylesheet"
          href="/static/blueprint/screen.css"
          type="text/css"
          media="screen, projection" />
    <link rel="stylesheet"
          href="/static/blueprint/print.css"
          type="text/css"
          media="print" />
    <!--[if IE]>
    {% compress css %}
    <link rel="stylesheet"
          href="/static/blueprint/ie.css"
          type="text/css"
          media="screen, projection" />
    {% endcompress %}
    <![endif]-->
  </head>
  <body>
    <div class="container">
      <h1>Hoera</h1>
      <p>Hurray!  Sample content that will disappear!</p>
    </div>
  </body>
</html>

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!