[z3-checkins] r54962 - in z3/deliverance/sandboxes/paul/dvng: . step01 step02 step03
paul at codespeak.net
paul at codespeak.net
Mon May 19 21:35:19 CEST 2008
Author: paul
Date: Mon May 19 21:35:17 2008
New Revision: 54962
Added:
z3/deliverance/sandboxes/paul/dvng/conf.py
z3/deliverance/sandboxes/paul/dvng/index.rst
z3/deliverance/sandboxes/paul/dvng/step01/
z3/deliverance/sandboxes/paul/dvng/step01.rst
z3/deliverance/sandboxes/paul/dvng/step01/content.html
z3/deliverance/sandboxes/paul/dvng/step01/content.xml (contents, props changed)
z3/deliverance/sandboxes/paul/dvng/step01/dvfinalstage.xsl
z3/deliverance/sandboxes/paul/dvng/step01/result.html
z3/deliverance/sandboxes/paul/dvng/step01/theme.html
z3/deliverance/sandboxes/paul/dvng/step01/xform.py
z3/deliverance/sandboxes/paul/dvng/step02/
z3/deliverance/sandboxes/paul/dvng/step02.rst
z3/deliverance/sandboxes/paul/dvng/step02/compiler.xsl
z3/deliverance/sandboxes/paul/dvng/step02/content.html
z3/deliverance/sandboxes/paul/dvng/step02/dvfinalstage.xsl
z3/deliverance/sandboxes/paul/dvng/step02/rules.xml
z3/deliverance/sandboxes/paul/dvng/step02/theme.html
z3/deliverance/sandboxes/paul/dvng/step02/xform-a.py
z3/deliverance/sandboxes/paul/dvng/step02/xform.py
z3/deliverance/sandboxes/paul/dvng/step03/
z3/deliverance/sandboxes/paul/dvng/step03.rst
z3/deliverance/sandboxes/paul/dvng/step03/compiler.xsl
z3/deliverance/sandboxes/paul/dvng/step03/content2.html
z3/deliverance/sandboxes/paul/dvng/step03/dvfinalstage-a.xsl
z3/deliverance/sandboxes/paul/dvng/step03/dvfinalstage-b.xsl
z3/deliverance/sandboxes/paul/dvng/step03/rules.rng
z3/deliverance/sandboxes/paul/dvng/step03/rules.xml
z3/deliverance/sandboxes/paul/dvng/step03/theme2.html
z3/deliverance/sandboxes/paul/dvng/step03/xform.py
Log:
Source code
Added: z3/deliverance/sandboxes/paul/dvng/conf.py
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/conf.py Mon May 19 21:35:17 2008
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+#
+# Sample Stample documentation build configuration file, created by
+# sphinx-quickstart on Sun May 11 11:35:19 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('some/directory'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+#extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'DVNG Walkthrough'
+copyright = '2008, Paul Everitt'
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = '0.0.0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.0.0.1'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be searched
+# for source files.
+#exclude_dirs = []
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+html_style = 'default.css'
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+#html_logo = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['.static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
Added: z3/deliverance/sandboxes/paul/dvng/index.rst
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/index.rst Mon May 19 21:35:17 2008
@@ -0,0 +1,70 @@
+===========================================
+DVNG - Rewriting the XSLT Renderer
+===========================================
+
+Deliverance is a simple system for applying a common look-and-feel to all
+pages across site, no matter what system generated the pages. Currently,
+Deliverance has two modes it uses to render:
+
+ - The Python Renderer uses lxml to manipulate HTML node trees during a
+ request. This is the default renderer and the most supported.
+
+ - The XSLT Renderer generates an XSLT stylesheet that is applied to the
+ content, transforming the content page into a themed result. For this
+ renderer, XSLT is nothing but an internal, intermediate language:
+ Deliverance integrators need never see a single line of XSLT.
+
+DVNG is an experiment in rewriting the XSLT Renderer to achieve some new
+goals, while also prototyping the redesign of the Deliverance
+specification.
+
+Design and Goals
+------------------------
+
+In a nutshell, DVNG uses a multistage approach to generate a standalone
+XSLT stylesheet. The stages break the work into smaller, more easily
+debugged chunks, using a combination of Pythonic lxml programming and
+XSLT transformation to produce the final stage.
+
+This final stage can be run with any XSLT processor and is no longer tied
+to the original theme, rules, or Deliverance processing logic. Stated
+differently, the final stage could be checked into Subversion and used
+without even installing Deliverance on a production server.
+
+The goals include:
+
+- *Speed*. The current Deliverance renderers do a lot of work on each
+ request. DVNG plans to do a lot of work once, with the result being
+ usable not just between requests, but for as long as the theme and
+ rules do not change.
+
+- *Reliability*. Much less complex.
+
+- *Debuggability*.
+
+- *Extensibility*.
+
+About This Document
+---------------------------
+
+This walkthrough is aimed first at the Deliverance mailing list, where we
+are considering design of the new specification and implementations.
+
+As such, we'll gradually build the result, step by step, to show the
+thinking that went into this approach. In some ways, this is an advocacy
+document, meant not just to explain, but also to persuade. Apologies in
+advance. (wink)
+
+Requirements
+--------------------
+
+Some of the work produced herein can run directly in Firefox 3.0b5+.
+Others can be run with a fairly recent version of ``xsltproc``. For the
+complete effect, though, you might want to install the Python lxml
+extension, version 2.0 or higher.
+
+.. toctree::
+
+ step01.rst
+ step02.rst
+ step03.rst
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step01.rst
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01.rst Mon May 19 21:35:17 2008
@@ -0,0 +1,146 @@
+===========================================
+Step 01 - The Minimum
+===========================================
+
+We have a number of things to introduce for DVNG. So in this first step
+we take it pretty easy:
+
+- View and explain the final step in the production of a "compiled
+ themeset"
+
+- See how to run that compiled themeset on a content page
+
+Sample Data and Desired Output
+----------------------------------------
+
+In this first step, we'll support a very simple case: taking a "page
+heading" from the content and putting in the right box of the theme.
+
+For this step, imagine a very simple theme:
+
+.. literalinclude:: step01/theme.html
+ :language: html
+ :linenos:
+
+And a very simple content page:
+
+.. literalinclude:: step01/content.html
+ :language: html
+ :linenos:
+
+On line 6 of the theme, we want to replace the ``Theme Page Heading``
+text inside the ``<h1>`` with the ``DVNG Walkthrough`` content from line
+2 of the content page. That is, we want the following markup as the
+themed result:
+
+.. literalinclude:: step01/result.html
+ :language: html
+ :linenos:
+
+Compilation
+------------------
+
+Under Deliverance, we would effect this transform by writing a rules
+file and doing the Deliverance processing.
+
+For DVNG, though, let's invert the thinking: let's start at the end and
+discuss what should the "final stage" XSLT look like? We can then work
+the problem backwards, writing stages that gradually produce that
+result, using the theme file and the rules file as the ultimate source.
+
+Here is an XSLT that prepares the way for later DVNG features:
+
+.. literalinclude:: step01/dvfinalstage.xsl
+ :language: xslt
+ :linenos:
+
+There are 3 major sections to this:
+
+#. *Compiled Theme*. Lines 4-19 handle the compiled theme, generated by
+ earlier stages of DVNG. We first make an ``xsl:variable`` that holds
+ the mixture of HTML (from the theme file) and XSLT instructions (by
+ way of the rules file). We then use EXSLT to turn this into a named
+ nodeset that we can do further work on. Note that, when the variable
+ is initialized, the XSLT instructions are processed and thus, the
+ theme processes the content.
+
+#. *Start processing*. Line 20 is the XSLT rule that matches on the top
+ of the incoming content page, and thus, the start of processing the
+ transform. This template rule is very simple: grab the first node in
+ the *compiled theme* (instead of the incoming content page), start
+ processing, and thus pass control over to other XSLT templates.
+
+#. *Identity transform*. Lines 25-28 are the classic XSLT pattern known
+ as the "identity transform." This least-common-denominator rule
+ matches when nothing else does, and simply copies the result to the
+ output, recursively.
+
+So in a nutshell:
+
+- At runtime, create a variable that holds the themed content.
+
+- Copy that content to the result document.
+
+Running
+----------------
+
+You can try this quite easily using ``xsltproc`` in the ``step01``::
+
+ $ /usr/bin/xsltproc dvfinalstage.xsl content.html
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading">
+ DVNG Walkthrough
+ </h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+
+You can also time the performance::
+
+ $ /usr/bin/xsltproc --repeat --timing dvfinalstage.xsl content.html
+ Parsing stylesheet dvfinalstage.xsl took 0 ms
+ Parsing document content.html took 0 ms
+ Applying stylesheet 20 times took 6 ms
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading">
+ DVNG Walkthrough
+ </h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+
+Additionally,here is a Python script that uses lxml to apply the result
+to the content:
+
+
+.. literalinclude:: step01/xform.py
+ :linenos:
+
+And finally, you can point Firefox (version 3.0b5 or later) or Safari at
+the ``step01\content.xml``. It will transform the input using the XSLT
+and let you view the results of your compiled themeset.
+
+.. note::
+
+ How did that work? Firefox (and IE, and Opera, and Safari) have long
+ supported client-side XSLT. You send back an XML document with a
+ "processing instruction" that points at a CSS or XSLT to help
+ visualize the XML. In the case of XSLT, the browser transforms the XML
+ document into HTML and shows the result.
+
+ We had to copy the ``content.html`` to a ``content.xml`` to trick
+ Firefox/Safari into opening the file in XML mode.
+
+ Why such a recent Firefox? Because this experiment relies on features
+ from the EXSLT extensions to XSLT, which are only recently supported
+ on Firefox.
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step01/content.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01/content.html Mon May 19 21:35:17 2008
@@ -0,0 +1,3 @@
+<div id="page-content">
+ <h1>DVNG Walkthrough</h1>
+</div>
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step01/content.xml
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01/content.xml Mon May 19 21:35:17 2008
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="dvfinalstage.xsl"?>
+<div id="page-content">
+ <h1>DVNG Walkthrough</h1>
+</div>
Added: z3/deliverance/sandboxes/paul/dvng/step01/dvfinalstage.xsl
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01/dvfinalstage.xsl Mon May 19 21:35:17 2008
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common" version="1.0">
+ <xsl:variable name="compiledtheme">
+ <!-- This is generated from earlier stages -->
+ <html>
+ <head>
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading">
+ <!-- The rules file determines the XPath here -->
+ <xsl:value-of select="/div[@id='page-content']"/>
+ </h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <!-- Match on the top of the content page, then switch
+ processing control to the nodes in the compiled theme. -->
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>
Added: z3/deliverance/sandboxes/paul/dvng/step01/result.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01/result.html Mon May 19 21:35:17 2008
@@ -0,0 +1,12 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>Theme Page Title</title>
+</head>
+<body>
+<h1 id="pageheading">
+ DVNG Walkthrough
+</h1>
+<p>Some theme text here.</p>
+</body>
+</html>
Added: z3/deliverance/sandboxes/paul/dvng/step01/theme.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01/theme.html Mon May 19 21:35:17 2008
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading">Theme Page Heading</h1>
+ <p>Some theme text here.</p>
+ </body>
+</html>
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step01/xform.py
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step01/xform.py Mon May 19 21:35:17 2008
@@ -0,0 +1,11 @@
+from lxml import etree
+
+def main():
+ contentdoc = etree.ElementTree(file="content.html")
+ xsldoc = etree.ElementTree(file="dvfinalstage.xsl")
+ processor = etree.XSLT(xsldoc)
+ result = processor(contentdoc)
+ print result
+
+if __name__ == "__main__":
+ main()
Added: z3/deliverance/sandboxes/paul/dvng/step02.rst
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02.rst Mon May 19 21:35:17 2008
@@ -0,0 +1,191 @@
+===========================================
+Step 02 - A "Compiler" for DVNG
+===========================================
+
+We saw in Step 01 how an XSLT could be produced that handled the simple
+case. Before going into more complicated cases, though, let's work the
+problem backwards. Remember, we want to start with the ``theme.html``
+file on disk with a ``rules.xml`` file lying around somewhere.
+
+In this step we'll work on:
+
+- An XSLT "compiler" that produces the ``dvfinalstage.xsl`` in Step 01
+
+- An input document for that XSLT that is the theme file...with a little
+ help from lxml
+
+- And of course, the rules file
+
+Sample Data and Desired Output
+----------------------------------------
+
+We'll use the (almost) same ``theme.html`` and ``content.html`` files
+from Step 01. Additionally, we want to use the beginnings of a rules
+file:
+
+.. literalinclude:: step02/rules.xml
+ :language: xml
+ :linenos:
+
+This is the minimum to express our goal, which is to keep everything in
+the theme but replace the theme's "pageheading" text with text from the
+content page. We'll add other rules and more rule attributes later.
+
+Regarding the desired output, this is simple: we want this stage to
+produce the ``dvfinalstage.xsl`` as shown in Step 01. Again, once we
+have that XSLT, we can use it anywhere that supports XSLT processing
+(albeit with EXSLT extensions.)
+
+Overview and theme "hints"
+-------------------------------
+
+Our strategy in this stage goes a little bit like this:
+
+- Start with a "hinted" theme file (explained next)
+
+- Have a `compiler.xsl` that converts the theme file into the final
+ stage
+
+- This compiler.xsl also reads in the rules file
+
+This stage expects the theme file to have some "hints" in it. Namely, we
+expect an earlier stage to have found and marked the nodes in the theme
+that were matched by a rule's @themeid attribute. As an example::
+
+ <h1 id="pageheading" dv:ruleid="1" dv:ruletype="replace">Theme
+ Page Heading</h1>
+
+This adds two attributes to the spot in the theme where a rule should
+operate:
+
+- dv:ruleid points to the rule number (position()) that matched this
+ theme node
+
+- dv:ruletype indicates what kind of rule (append, prepend, etc.) was
+ matched. The reason for this will be explained when we look at the
+ compiler.
+
+Compilation
+----------------------
+
+Now let's take a look at the "compiler", which is nothing more than an
+XSLT:
+
+.. literalinclude:: step02/compiler.xsl
+ :language: xslt
+ :linenos:
+
+Walking through the thinking of the compiler:
+
+#. **Lines 2-5**. Lots of namespaces in here. These point to extended
+ functionality from the EXSLT (exslt.org) standard add-ons. Also, we
+ need a trick to make it easy to generate XSLT nodes in the output
+ without interpreting them as part of the transform. namespace-alias,
+ in combination with binding the processing to the "x:" prefix, does
+ this.
+
+#. **Line 7**. Read the rules file in as an input document in addition
+ to the ``theme.html`` that will be the regular input document.
+
+#. **Lines 9-24**. Generate most of the boilerplate for the desired
+ output, which is the ``dvfinalstage.xsl`` in Step 01.
+
+#. **Line 12**. Pass control back to the XSLT processor, which starts
+ recursively processing all the nodes in the input document
+ (theme.html), looking for rules to match.
+
+#. **Lines 25-34**. Ah *HA*! The heart of the matter. Match on an HTML
+ node in the theme that has those funny hints. In this case, handle a
+ case where we are supposed to do a Deliverance "replace". This is,
+ essentially, the implementation of a Deliverance "rule".
+
+#. **Line 27**. Copy all the attributes from the theme node to the
+ output, *except* those funny "hint" attributes in the ``dv:``
+ namespace.
+
+#. **Lines 28-32**. The ``replace`` rule says to take out all the theme
+ node's children and insert all the nodes matched in the content. This
+ maps to an ``<xsl:value-of select="somexpath">`` expression, so
+ create one of those. Use a convenience function (``dv:getrule()``,
+ explained next) to grab the rule's ``@content`` attribute for the
+ value of "somexpath".
+
+#. **Lines 35-37**. Define an extension function to make it easy to find
+ the rule that placed the hints in the theme node.
+
+Running
+----------------
+
+Again, you can try this quite easily using ``xsltproc`` in the
+``step02`` directory. Remember to run it against the ``theme.html``
+document::
+
+ $ /usr/bin/xsltproc dvfinalstage.xsl theme.html
+ <?xml version="1.0"?>
+ <xsl:stylesheet xmlns:exsl="http://exslt.org/common"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:dv="http://openplans.org/deliverance"
+ xmlns:dyn="http://exslt.org/dynamic" version="1.0">
+ <xsl:variable name="compiledtheme">
+ <html>
+ <head>
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading"><xsl:value-of select="/div[@id='page-content']/h1"/></h1>
+ </body>
+ </html>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+ </xsl:stylesheet>
+
+You can run this in, save to a file, and then run the final stage::
+
+ $ /usr/bin/xsltproc compiler.xsl theme.html > dvfinalstage.xsl
+ $ /usr/bin/xsltproc dvfinalstage.xsl content.html
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading">DVNG Walkthrough</h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+
+This is the output we expected: the ``<h1>`` gets its value from the
+content page instead of the theme.
+
+Python For First Stage
+------------------------------
+
+We will start with a Python file that does the same work as the
+``xsltproc`` example:
+
+.. literalinclude:: step02/xform-a.py
+ :language: python
+ :linenos:
+
+This example uses a small class, instead of one big function.
+
+Next up, we need to add the logic that produces the "hints" in the
+theme. Just for fun, we turn the result of the first stage into the
+final processor and use it to theme some content:
+
+.. literalinclude:: step02/xform.py
+ :language: python
+ :linenos:
+
+This has plenty of docstrings and comments, but let's walk through the
+lines:
+
+#. **Line 4-6**. Setup some XML namespace-oriented constants.
Added: z3/deliverance/sandboxes/paul/dvng/step02/compiler.xsl
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/compiler.xsl Mon May 19 21:35:17 2008
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:func="http://exslt.org/functions"
+ xmlns:dv="http://openplans.org/deliverance" xmlns:xsl="anything" extension-element-prefixes="func"
+ xmlns:dyn="http://exslt.org/dynamic" xmlns:exsl="http://exslt.org/common" version="1.0">
+ <x:namespace-alias stylesheet-prefix="xsl" result-prefix="x"/>
+ <x:output indent="yes" method="xml"/>
+ <x:variable name="rf" select="document('rules.xml')/*"/>
+ <x:variable name="dvuri">http://openplans.org/deliverance</x:variable>
+ <x:template match="/">
+ <xsl:stylesheet xmlns:exsl="http://exslt.org/common" version="1.0">
+ <xsl:variable name="compiledtheme">
+ <x:apply-templates select="node()|@*"/>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+ </xsl:stylesheet>
+ </x:template>
+ <x:template match="node()[@dv:ruletype='replace']">
+ <x:copy>
+ <x:apply-templates select="@*[namespace-uri()!=$dvuri]"/>
+ <xsl:value-of>
+ <x:attribute name="select">
+ <x:value-of select="dv:getrule()/@content"/>
+ </x:attribute>
+ </xsl:value-of>
+ </x:copy>
+ </x:template>
+ <func:function name="dv:getrule">
+ <func:result select="$rf/*[position()=current()/@dv:ruleid]"/>
+ </func:function>
+ <x:template match="node()|@*">
+ <x:copy>
+ <x:apply-templates select="node()|@*"/>
+ </x:copy>
+ </x:template>
+</x:stylesheet>
Added: z3/deliverance/sandboxes/paul/dvng/step02/content.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/content.html Mon May 19 21:35:17 2008
@@ -0,0 +1,3 @@
+<div id="page-content">
+ <h1>DVNG Walkthrough</h1>
+</div>
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step02/dvfinalstage.xsl
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/dvfinalstage.xsl Mon May 19 21:35:17 2008
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:exsl="http://exslt.org/common" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dv="http://openplans.org/deliverance" xmlns:dyn="http://exslt.org/dynamic" version="1.0">
+ <xsl:variable name="compiledtheme">
+ <html>
+ <head>
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 xmlns:ns0="http://openplans.org/deliverance" id="pageheading"><xsl:value-of select="/div[@id='page-content']/h1"/></h1>
+ <p>Some theme text here.</p>
+ </body>
+</html>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>
+
Added: z3/deliverance/sandboxes/paul/dvng/step02/rules.xml
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/rules.xml Mon May 19 21:35:17 2008
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rules xmlns="http://openplans.org/deliverance">
+ <replace
+ theme="/html/body/h1[@id='pageheading']"
+ content="/div[@id='page-content']/h1"/>
+</rules>
Added: z3/deliverance/sandboxes/paul/dvng/step02/theme.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/theme.html Mon May 19 21:35:17 2008
@@ -0,0 +1,9 @@
+<html xmlns:dv="http://openplans.org/deliverance">
+ <head>
+ <title>Theme Page Title</title>
+ </head>
+ <body>
+ <h1 id="pageheading" dv:ruleid="1" dv:ruletype="replace">Theme Page Heading</h1>
+ <p>Some theme text here.</p>
+ </body>
+</html>
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step02/xform-a.py
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/xform-a.py Mon May 19 21:35:17 2008
@@ -0,0 +1,20 @@
+from lxml import etree
+
+class DVNG:
+
+ def __init__(self):
+ self.compilerdoc = etree.ElementTree(file="compiler.xsl")
+ self.compiler = etree.XSLT(self.compilerdoc)
+
+ def __call__(self, themefn):
+ themedoc = etree.ElementTree(file=themefn)
+ result = self.compiler(themedoc)
+ return result
+
+def main():
+ dvng = DVNG()
+ result = dvng("theme.html")
+ print result
+
+if __name__ == "__main__":
+ main()
Added: z3/deliverance/sandboxes/paul/dvng/step02/xform.py
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step02/xform.py Mon May 19 21:35:17 2008
@@ -0,0 +1,54 @@
+from lxml import etree
+
+# Constants
+DV_NAMESPACE = "http://openplans.org/deliverance"
+DV = "{%s}" % DV_NAMESPACE
+NSMAP = {"dv": DV_NAMESPACE}
+
+class DVNG:
+
+ compilerfn = "compiler.xsl"
+
+ def compileTheme(self, themefn, rulesfn):
+ """Given theme and rule files, make a compiled themeset"""
+
+ # Make the compiler XSLT
+ compilerdoc = etree.ElementTree(file=self.compilerfn)
+ compiler = etree.XSLT(compilerdoc)
+
+ # Load the theme doc and rules doc
+ self.themedoc = etree.ElementTree(file=themefn)
+ self.rulesdoc = etree.ElementTree(file="rules.xml")
+
+ # Iterate over the rules, find the theme nodes that
+ # match, and add the "hint" attributes.
+ position = 1
+ for rule in self.rulesdoc.xpath("/*/*", namespaces=NSMAP):
+ themexpath = rule.get("theme")
+ ruletype = rule.tag.split("}")[1]
+ for themenode in self.themedoc.xpath(themexpath):
+ themenode.set(DV + "ruleid", str(position))
+ themenode.set(DV + "ruletype", ruletype)
+ position = position +1
+
+ # themedoc now hacked to provide hints, so transform.
+ self.finalstage = compiler(self.themedoc)
+ self.processor = etree.XSLT(self.finalstage)
+
+ def __call__(self, contentfn):
+ """Apply a compiled themeset against a contentdoc"""
+
+ # Turn the result into a processor
+ content = etree.ElementTree(file=contentfn)
+ result = self.processor(content)
+
+ return result
+
+def main():
+ dvng = DVNG()
+ dvng.compileTheme("../step01/theme.html", "rules.xml")
+ result = dvng("../step01/content.html")
+ return result
+
+if __name__ == "__main__":
+ print main()
Added: z3/deliverance/sandboxes/paul/dvng/step03.rst
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03.rst Mon May 19 21:35:17 2008
@@ -0,0 +1,291 @@
+===========================================
+Step 03 - Multi-theme support
+===========================================
+
+We've come quite a way so far. Although only for one kind of theme rule,
+we have support for processing a theme and rule file, then applying the
+theme to a content page.
+
+DVNG, though, can serve as a way to investigate new capabilities for
+Deliverance. Theming different pages in different ways is one of the
+biggest points under consideration for Deliverance. In this step
+investigate it:
+
+- Support theme switching in the ``dvfinalstage.xsl`` compiled themeset
+
+- Extend the compiler to support a rules file that specifies themes and
+ conditions
+
+- Provide rich information from the environment, such as HTTP headers,
+ for theme switching
+
+To do this, we'll gradually build up the support working backwards from
+the "compiled themeset".
+
+Sample Data and Desired Output
+----------------------------------------
+
+For our main theme, we'll continue using ``step01/theme.html``. We'll
+leave the file in that directory, as well as ``step01/content.html`` as
+an example of a content page that should be styled with that theme.
+
+We'll also introduce a second theme, one that includes a table layout
+with a left-column navigation:
+
+.. literalinclude:: step03/theme2.html
+ :language: html
+ :linenos:
+
+We will use ``step03/content2.html`` to represent a content page that
+should be themed with Theme 2.
+
+.. literalinclude:: step03/content2.html
+ :language: html
+ :linenos:
+
+This is a very different content page:
+
+- It looks more like a full HTML page, with an ``<html>`` root node
+
+- It's more semantic-oriented: the ``<title>`` is in the ``<title>``,
+ not also repeated as an ``<h1>`` in the ``<body>``.
+
+- There is some document-ish content in the ``<body>``.
+
+- And key to our scenario, we pack in some information about the
+ "section" that the content page occurs in, as a ``<meta>`` element in
+ the ``<head>``. We'll use this to use a different theme, one for
+ subordinate pages.
+
+For the desired output, we want the first theme to do as before, but
+also take the ``<title>`` from the content page. The second theme,
+though, should do a bit more:
+
+- Merge the content's ``<title>`` into both the ``<title>`` of the
+ themed result and the ``<h1>`` heading
+
+- Copy everything from the ``<body>`` of the content into the
+ "pagecontent" of the themed result
+
+
+Compiled Themeset
+-----------------------
+
+As before, the goal is to have an XSLT transform that is self-contained
+and can be used in any context that supports applying XSLT
+transformations.
+
+Using ``step01/dvfinalstage.xsl`` as the starting point, let's add
+support for multiple themes. First up:
+
+.. literalinclude:: step03/dvfinalstage-a.xsl
+ :language: xslt
+ :linenos:
+
+#. **Lines 4-49**. Bind a theme to a variable.
+
+#. **Line 7**. The key to multitheme: use ``<xsl:choose>`` to select
+ which theme to bind to the variable. In this case, if the content
+ page has a ``<meta>`` tag named ``dv.section``, then use the theme
+ in that ``<xsl:when>``.
+
+#. **Line 33**. If nothing else matches, use the final theme.
+
+Everything else in the compiled theme is the same as in Step 01.
+
+
+Running Part A
+-------------------
+
+We now have support in the final stage for multiple themes. How can we
+run it? Same as before::
+
+ $ xsltproc dvfinalstage-a.xsl content2.html
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>A Deeper Look At DVNG</title>
+ </head>
+ <body><table border="0"><tr>
+ <td><h2>Navigation</h2></td>
+ <td>
+ <h1 id="pageheading">A Deeper Look At DVNG</h1>
+ <div id="pagecontent">
+ <p>I have some content <em>in here</em> to merge.</p>
+ <p>Perhaps an image will be in here later.</p>
+ </div>
+ </td>
+ </tr></table></body>
+ </html>
+
+ $ xsltproc dvfinalstage-a.xsl ../step01/content.html
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>DVNG Walkthrough</title>
+ </head>
+ <body>
+ <h1 id="pageheading">DVNG Walkthrough</h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+
+As you can see, the content in the first page (the ``<meta>`` tag)
+triggers the table-based theme. The performance is the same: 20
+transforms in 7 milliseconds.
+
+
+Theme Choice From Other Data
+-------------------------------------
+
+In this first case, we chose the theme based on information *in* the
+content page. But what if we want to choose from information in the URL,
+an HTTP header such as the content type, etc.?
+
+We'll add support for this in a relatively simple way: using XSLT
+*parameters*, or arguments the XSLT processor can supply to the
+transform. At the top of ``step03/dvfinalstage-b.xsl`` we see::
+
+ <xsl:param name="req.domain_url">look-at-me.com</xsl:param>
+
+This "declares" a transformation parameter and assigns a default value.
+
+.. note::
+
+ The ``req`` sure looks a lot like WebOb, right? Yes, the idea is
+ to match the documentation for WebOb and have it able to
+ supply values easily as parameters. In this case, ``domain_url``
+ isn't something provided by WebOb (as it isn't part of the
+ WSGI spec). Perhaps we can add it to WebOb.
+
+ Also, note that use of parameters places more work on the
+ XSLT environment that will apply the stylesheet. Use of
+ this feature (themeswitching based on the environment instead
+ of content) likely means a compiled themeset that only works
+ in Deliverance.
+
+The test condition on line 8 then changes. Instead choosing a template
+based on nodes in the content page, we choose content based on the value
+of the parameter::
+
+ <xsl:when test="$req.domain_url='look-at-me.com'">
+
+How will we implement this? That's the topic of the next section, but in
+summary:
+
+- The rules file will indicate where a certain condition will match
+
+- Based on what the rules file asks for, the ``compiler.xsl`` will put
+ ``<xsl:param>`` declarations in the compiled themeset
+ (``dvfinalstage.xsl``)
+
+- During operation, an XSLT processor will be expected to pass values to
+ this parameter to change the default value on a per-request basis
+
+You can run this with ``xsltproc`` as follows::
+
+ $ xsltproc --stringparam req.domain_url foo.com dvfinalstage-b.xsl content2.html
+
+The ``--stringparam`` is a way to pass a parameter value on the
+commandline. In lxml you pass keyword parameters to the ``XSLT()``
+function, as we'll see in the next section.
+
+
+Rulefile Support For Theme Switching
+--------------------------------------------
+
+We want theme switching to be declarative. We don't want Deliverance
+users to express it in Python and we definitely don't want them to
+express it in XSLT.
+
+That means support in the ``rules.xml`` file. For example:
+
+.. literalinclude:: step03/rules.xml
+ :language: xml
+ :linenos:
+
+The rules file has more features now:
+
+#. **Line 1**. We wrap everything in a ``<themeset>`` to contain
+ each defined theme.
+
+#. **Lines 4-15**. A ``<theme>`` contains an ``@href`` pointing to
+ the HTML file, a ``<match>`` which defines any of the conditions
+ to which this applies, and a ``<rules>`` which contains the
+ Deliverance rules. Note that precedence matters: the first
+ ``<theme>`` containing a ``<match>`` that matches will be
+ triggered.
+
+#. **Line 7-8**. If the content contains a ``<meta>`` tag of
+ ``dv.section`` **OR** there the ``Request`` has a ``domain_url``
+ value equal to ``look-at-me.com``, then use this theme. Note:
+ we can map to XPath string functions such as contains,
+ starts-with, and substring if we find a suitable expression
+ language.
+
+#. **Lines 10-14**. The rules, as before.
+
+#. **Lines 16-22**. This last theme has no ``<match>`` (which
+ is optional), indicating that this is to be used when nothing
+ else matches.
+
+Note also that we have added support for a minimal Relax NG schema that
+helps us validate our rules file. This is particularly helpful for tools
+(such as oXygen, which inserted that second line that references the
+schema) that do autocomplete, element documentation via the schema, and
+validate-as-you-type.
+
+Compiling the New Rule File
+-----------------------------------
+
+As in Step 02, we need a Python module which, given the theme files and
+the rules file, will generate the final stage XSLT.
+
+Along the way we'll make some changes to the Step 02 ``xform.py`` to
+make it more flexible:
+
+- Presume that the ``compiler.xsl`` is in the same directory as the
+ ``xform.py``.
+
+- Merge the rules and all themes into a single input document.
+
+- Let the ``theme/@href`` values define the location of the theme with
+ support for the ``here`` keyword.
+
+- Pass the location of the content file to be themed as a command-line
+ argument.
+
+- Load the content page using the HTML parser, in case it is messy.
+
+- Provide debug support to write to disk all the intermediate stages.
+
+This leads to a very different strategy for the Python module and the
+``compiler.xsl``. Let's start with the new Python module:
+
+.. literalinclude:: step03/xform.py
+ :language: python
+ :linenos:
+
+Here is the new compiler file:
+
+.. literalinclude:: step03/compiler.xsl
+ :language: xslt
+ :linenos:
+
+Running the module against one of the sample content pages is easy::
+
+ $ python ./xform.py content2.html
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Second Theme Page Title</title>
+ </head>
+ <body><table border="0"><tr>
+ <td><h2>Navigation</h2></td>
+ <td>
+ <h1>A Deeper Look At DVNG</h1>
+ <div id="pagecontent">Theme content to replace.</div>
+ </td>
+ </tr></table></body>
+ </html>
+
Added: z3/deliverance/sandboxes/paul/dvng/step03/compiler.xsl
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/compiler.xsl Mon May 19 21:35:17 2008
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform"
+ xmlns:dv="http://openplans.org/deliverance" xmlns:xsl="anything"
+ xmlns:exsl="http://exslt.org/common" version="1.0">
+ <x:namespace-alias stylesheet-prefix="xsl" result-prefix="x"/>
+ <x:output indent="yes" method="xml"/>
+ <x:key name="getRule" match="dv:replace" use="@id"/>
+ <x:template match="/">
+ <xsl:stylesheet xmlns:exsl="http://exslt.org/common" version="1.0">
+ <!-- Find any <req> match rules and generate parameters -->
+ <x:apply-templates select="/*/*/dv:match/dv:req" mode="params"/>
+ <xsl:variable name="compiledtheme">
+ <xsl:choose>
+ <!-- Based on certain conditions, use one of the themes -->
+ <x:apply-templates select="/*/dv:theme"/>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+ </xsl:stylesheet>
+ </x:template>
+ <!--
+ Theme switching. If there are <match> conditions, make an
+ <xsl:when> and put "or" between all the conditions.
+ -->
+ <x:template match="dv:theme[dv:match]">
+ <xsl:when>
+ <x:attribute name="test">
+ <x:for-each select="dv:match/*">
+ <x:if test="position() > 1"> or </x:if>
+ <x:apply-templates select="."/>
+ </x:for-each>
+ </x:attribute>
+ <x:apply-templates select="dv:source/*"/>
+ </xsl:when>
+ </x:template>
+ <x:template match="dv:theme[not(dv:match)]">
+ <xsl:otherwise>
+ <x:apply-templates select="dv:source/*"/>
+ </xsl:otherwise>
+ </x:template>
+ <!--
+ Handle the kinds of match conditions by generating predicates
+ to OR together in an <xsl:when>.
+ -->
+ <x:template match="dv:content">
+ <x:value-of select="."/>
+ </x:template>
+ <x:template match="dv:req">$req.<x:value-of select="@key"/>='<x:value-of select="."/>'</x:template>
+ <!--
+ Next up, support generating <xsl:param> nodes at the top of
+ the XSLT, for cases where we themeswitch based on information
+ outside the content page (e.g. the headers.)
+ -->
+ <x:template match="dv:req" mode="params">
+ <xsl:param name="req.{@key}">
+ <x:value-of select="."/>
+ </xsl:param>
+ </x:template>
+ <!--
+ Now we have support for each kind of rule.
+ -->
+ <x:template match="@ruletype[.='replace']">
+ <xsl:value-of>
+ <x:attribute name="select">
+ <x:value-of select="key('getRule',../@ruleid)/@content"/>
+ </x:attribute>
+ </xsl:value-of>
+ </x:template>
+ <!-- End implementation of rule types. -->
+
+ <x:template match="*">
+ <!-- Translate the html nodes from the dv namespace into
+ the null numespace -->
+ <x:element name="{local-name(.)}">
+ <x:choose>
+ <x:when test="@ruletype">
+ <!-- This calls a handler for each ruletype -->
+ <x:apply-templates select="@ruletype"/>
+ </x:when>
+ <x:otherwise>
+ <x:apply-templates select="node()|@*"/>
+ </x:otherwise>
+ </x:choose>
+ </x:element>
+ </x:template>
+ <!--
+ Pass everything else through.
+ -->
+ <x:template match="@*">
+ <x:copy>
+ <x:apply-templates select="node()|@*"/>
+ </x:copy>
+ </x:template>
+ <x:template match="@ruleid|@ruletype"/>
+</x:stylesheet>
Added: z3/deliverance/sandboxes/paul/dvng/step03/content2.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/content2.html Mon May 19 21:35:17 2008
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>A Deeper Look At DVNG</title>
+ <meta name="dv.section" content="DVNG Articles"/>
+ </head>
+ <body>
+ <p>I have some content <em>in here</em> to merge.</p>
+ <p>Perhaps an image will be in here later.</p>
+ </body>
+</html>
Added: z3/deliverance/sandboxes/paul/dvng/step03/dvfinalstage-a.xsl
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/dvfinalstage-a.xsl Mon May 19 21:35:17 2008
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common" version="1.0">
+ <xsl:variable name="compiledtheme">
+ <xsl:choose>
+ <!-- Based on certain conditions, use one of the themes -->
+ <xsl:when test="/html/head/meta[@name='dv.section']">
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="/html/head/title"/>
+ </title>
+ </head>
+ <body>
+ <table border="0">
+ <tr>
+ <td>
+ <h2>Navigation</h2>
+ </td>
+ <td>
+ <h1 id="pageheading">
+ <xsl:value-of select="/html/head/title"/>
+ </h1>
+ <div id="pagecontent">
+ <xsl:apply-templates select="/html/body/*"/>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </body>
+ </html>
+ </xsl:when>
+ <xsl:otherwise>
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="/div[@id='page-content']/h1"/>
+ </title>
+ </head>
+ <body>
+ <h1 id="pageheading">
+ <xsl:value-of select="/div[@id='page-content']/h1"/>
+ </h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <!-- Match on the top of the content page, then switch
+ processing control to the nodes in the compiled theme. -->
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>
Added: z3/deliverance/sandboxes/paul/dvng/step03/dvfinalstage-b.xsl
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/dvfinalstage-b.xsl Mon May 19 21:35:17 2008
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common" version="1.0">
+ <xsl:param name="req.domain_url">look-at-me.com</xsl:param>
+ <xsl:variable name="compiledtheme">
+ <xsl:choose>
+ <!-- Based on certain conditions, use one of the themes -->
+ <xsl:when test="$req.domain_url='look-at-me.com'">
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="/html/head/title"/>
+ </title>
+ </head>
+ <body>
+ <table border="0">
+ <tr>
+ <td>
+ <h2>Navigation</h2>
+ </td>
+ <td>
+ <h1 id="pageheading">
+ <xsl:value-of select="/html/head/title"/>
+ </h1>
+ <div id="pagecontent">
+ <xsl:apply-templates select="/html/body/*"/>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </body>
+ </html>
+ </xsl:when>
+ <xsl:otherwise>
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="/div[@id='page-content']/h1"/>
+ </title>
+ </head>
+ <body>
+ <h1 id="pageheading">
+ <xsl:value-of select="/div[@id='page-content']/h1"/>
+ </h1>
+ <p>Some theme text here.</p>
+ </body>
+ </html>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="ct" select="exsl:node-set($compiledtheme)"/>
+ <xsl:template match="/">
+ <!-- Match on the top of the content page, then switch
+ processing control to the nodes in the compiled theme. -->
+ <xsl:apply-templates select="$ct/*"/>
+ </xsl:template>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>
Added: z3/deliverance/sandboxes/paul/dvng/step03/rules.rng
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/rules.rng Mon May 19 21:35:17 2008
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar
+ xmlns="http://relaxng.org/ns/structure/1.0"
+ xmlns:dv="http://openplans.org/deliverance"
+ xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <element name="dv:themeset">
+ <oneOrMore>
+ <element name="dv:theme">
+ <attribute name="href"/>
+ <zeroOrMore>
+ <element name="dv:match">
+ <interleave>
+ <zeroOrMore>
+ <element name="dv:content"><text/></element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="dv:req">
+ <attribute name="key"/>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ <element name="dv:rules">
+ <interleave>
+ <zeroOrMore>
+ <element name="dv:replace">
+ <attribute name="theme"/>
+ <attribute name="content"/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </element>
+ </oneOrMore>
+ </element>
+ </start>
+</grammar>
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step03/rules.xml
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/rules.xml Mon May 19 21:35:17 2008
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<themeset xmlns="http://openplans.org/deliverance">
+ <theme href="file:///%(here)s/step03/theme2.html">
+ <match>
+ <!-- These are OR'd together -->
+ <content>/html/head/meta[@name='dv.section']</content>
+ <req key="domain_url">look-at-me.com</req>
+ </match>
+ <rules>
+ <replace
+ theme="//td/h1[@id='pageheading']"
+ content="/html/head/title"/>
+ </rules>
+ </theme>
+ <theme href="file:///%(here)s/step01/theme.html">
+ <rules>
+ <replace
+ theme="//h1[@id='pageheading']"
+ content="/div[@id='page-content']/h1"/>
+ </rules>
+ </theme>
+</themeset>
\ No newline at end of file
Added: z3/deliverance/sandboxes/paul/dvng/step03/theme2.html
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/theme2.html Mon May 19 21:35:17 2008
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <title>Second Theme Page Title</title>
+ </head>
+ <body>
+ <table border="0">
+ <tr>
+ <td>
+ <h2>Navigation</h2>
+ </td>
+ <td>
+ <h1 id="pageheading">Theme Page Heading</h1>
+ <div id="pagecontent">Theme content to replace.</div>
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
Added: z3/deliverance/sandboxes/paul/dvng/step03/xform.py
==============================================================================
--- (empty file)
+++ z3/deliverance/sandboxes/paul/dvng/step03/xform.py Mon May 19 21:35:17 2008
@@ -0,0 +1,134 @@
+import lxml.html
+from lxml import etree
+import os
+import sys
+
+# Constants
+DV_NAMESPACE = "http://openplans.org/deliverance"
+DV = "{%s}" % DV_NAMESPACE
+NSMAP = {"dv": DV_NAMESPACE}
+
+class DVNG:
+
+ def __init__(self, heredir, rulesuri):
+
+ # Some paths and filenames
+ self.heredir = heredir
+ absdir = os.path.dirname(os.path.abspath(__file__))
+ self.compilerfn = os.path.join(absdir, "compiler.xsl")
+
+ # Make the compiler XSLT
+ self.compilerdoc = etree.ElementTree(file=self.compilerfn)
+ self.compiler = etree.XSLT(self.compilerdoc)
+
+ # Load the rules doc then the theme docs. We want
+ # both the original (for later introspection), plus
+ # a "compiled rules" which will act as the input document
+ # for the compilation transform.
+ self.origrulesdoc = etree.ElementTree(file=rulesuri)
+ self.cr = etree.ElementTree(file=rulesuri)
+
+ # Add hints and load HTML source
+ self._makeRuleIds()
+ self._loadThemes()
+
+ # Themes now hacked to provide hints, so transform.
+ self.finalstage = self.compiler(self.cr)
+ self.processor = etree.XSLT(self.finalstage)
+
+ def __call__(self, contentfn):
+ """Apply a compiled themeset against a contentdoc"""
+
+ # Turn the result into a processor
+ content = etree.ElementTree(file=contentfn)
+ kw = {'req.domain_url': 'look-at-me.com'}
+ result = self.processor(content, **kw)
+
+ return result
+
+
+ def _loadThemes(self):
+
+ # First, find all the <theme href=""> nodes,
+ # retrieve the HTML, process it, and merge into the
+ # compiled rules file.
+
+ for themeuri in self._getNode("/dv:themeset/dv:theme/@href"):
+ # Grab the dv:theme node, then its dv:match child
+ themenode = themeuri.getparent()
+ match = themenode.find(DV + "match")
+
+ # Make nice URI to theme source HTML and retrieve
+ parturi = self._expandURI(themeuri)
+ themehtml = lxml.html.parse(parturi).getroot()
+
+ # Find all theme nodes referenced by rules and put in
+ # hints
+ self._makeThemeHints(themehtml, themenode)
+
+ # Graft in the hinted HTML source into the compiled rules
+ themesource = etree.SubElement(themenode, DV + "source")
+ themesource.append(themehtml)
+
+ def _makeThemeHints(self, htmlroot, themenode):
+ """Given raw HTML, add @ruleid and @ruletype to correct nodes"""
+
+ for themexpath in self._getNode("dv:rules/*/@theme", themenode):
+ ruleid = themexpath.getparent().get("id")
+ ruletype = themexpath.getparent().tag.split("}")[1]
+ themenode = htmlroot.xpath(themexpath)[0]
+ themenode.set("ruleid", ruleid)
+ themenode.set("ruletype", ruletype)
+
+
+ def _makeRuleIds(self):
+ """Put identifiers in rules to use as a primary key"""
+
+ rulenum = 0
+ for rule in self._getNode("/dv:themeset/dv:theme/dv:rules/*"):
+ rulenum = rulenum + 1
+ ruleid = "r%s" % rulenum
+ rule.set("id", ruleid)
+
+
+ def _getNode(self, xp, node=None):
+ """Easier way to get namespaced XPaths on compiled rules doc"""
+
+ if node is None:
+ # Allow passing a different starting point
+ node = self.cr
+
+ return node.xpath(xp, namespaces=NSMAP)
+
+
+ def _expandURI(self, themeuri):
+ """Given a file:///%(here)s URI, return a usable reference"""
+
+ fulluri = themeuri % {'here': self.heredir}
+ return fulluri[7:] # Cheat, as lxml.html can't do file:///
+
+ def debug(self):
+ """Write out key artifacts to disk"""
+
+ self._writeDoc(self.cr, "cr.xml")
+ self._writeDoc(self.finalstage, "finalstage.xsl")
+
+
+ def _writeDoc(self, doc, filename):
+
+ f = open("xxx-%s" % filename, "w")
+ f.write(etree.tostring(doc, pretty_print=True))
+ f.close()
+
+
+def main():
+ moduledir = os.path.dirname(os.path.abspath(__file__))
+ heredir = os.path.dirname(moduledir)[1:]
+ dvng = DVNG(heredir, "rules.xml")
+ dvng.debug()
+ result = dvng(sys.argv[1])
+ return result
+
+if __name__ == "__main__":
+ result = main()
+ print result
\ No newline at end of file
More information about the z3-checkins
mailing list