summaryrefslogtreecommitdiff
path: root/docs/tutorials
diff options
context:
space:
mode:
authorPaul Everitt <paul@agendaless.com>2008-07-24 17:14:16 +0000
committerPaul Everitt <paul@agendaless.com>2008-07-24 17:14:16 +0000
commit1a7b14de4bc89ef0fd162ef8e62d01926e42b54e (patch)
tree8d9ccc8187ac29eb80ac310c6f82c17d548cdf54 /docs/tutorials
parentc1a6ef6f751a16b7a352bc3fdad13d4b5b73f212 (diff)
downloadpyramid-1a7b14de4bc89ef0fd162ef8e62d01926e42b54e.tar.gz
pyramid-1a7b14de4bc89ef0fd162ef8e62d01926e42b54e.tar.bz2
pyramid-1a7b14de4bc89ef0fd162ef8e62d01926e42b54e.zip
Add tutorial sections
Diffstat (limited to 'docs/tutorials')
-rw-r--r--docs/tutorials/cluegun/index.rst21
-rw-r--r--docs/tutorials/index.rst9
-rw-r--r--docs/tutorials/lxmlgraph/NOTES.txt6
-rw-r--r--docs/tutorials/lxmlgraph/background.rst113
-rw-r--r--docs/tutorials/lxmlgraph/index.rst21
-rw-r--r--docs/tutorials/lxmlgraph/installation.rst22
-rw-r--r--docs/tutorials/lxmlgraph/oxygen.xpr509
-rw-r--r--docs/tutorials/lxmlgraph/step00/simplemodel.xml26
-rw-r--r--docs/tutorials/lxmlgraph/step01.rst188
-rw-r--r--docs/tutorials/lxmlgraph/step01/myapp/__init__.py1
-rw-r--r--docs/tutorials/lxmlgraph/step01/myapp/configure.zcml13
-rw-r--r--docs/tutorials/lxmlgraph/step01/myapp/models.py18
-rw-r--r--docs/tutorials/lxmlgraph/step01/myapp/views.py7
-rw-r--r--docs/tutorials/lxmlgraph/step01/run.py8
-rw-r--r--docs/tutorials/lxmlgraph/step02.rst118
-rw-r--r--docs/tutorials/lxmlgraph/step02/myapp/__init__.py1
-rw-r--r--docs/tutorials/lxmlgraph/step02/myapp/configure.zcml11
-rw-r--r--docs/tutorials/lxmlgraph/step02/myapp/models.py41
-rw-r--r--docs/tutorials/lxmlgraph/step02/myapp/samplemodel.xml5
-rw-r--r--docs/tutorials/lxmlgraph/step02/myapp/views.py8
-rw-r--r--docs/tutorials/lxmlgraph/step02/run.py8
-rw-r--r--docs/tutorials/lxmlgraph/step03.rst175
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/__init__.py1
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/configure.zcml17
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/default.pt8
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/models.py41
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/samplemodel.xml5
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/views.py10
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/xsltview.xsl12
-rw-r--r--docs/tutorials/lxmlgraph/step03/run.py8
-rw-r--r--docs/tutorials/lxmlgraph/step04.rst98
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/__init__.py1
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/configure.zcml11
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/models.py41
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/samplemodel.xml44
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/views.py13
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl69
-rw-r--r--docs/tutorials/lxmlgraph/step04/run.py8
-rw-r--r--docs/tutorials/lxmlgraph/step05.rst12
39 files changed, 1728 insertions, 0 deletions
diff --git a/docs/tutorials/cluegun/index.rst b/docs/tutorials/cluegun/index.rst
new file mode 100644
index 000000000..4b8cacf52
--- /dev/null
+++ b/docs/tutorials/cluegun/index.rst
@@ -0,0 +1,21 @@
+======================================
+ Shared Clipboard with repoze.cluegun
+======================================
+
+repoze.cluegun is a "pastebin" application, showing how you can use
+the ``repoze.bfg`` framework for posting and managing paste sessions.
+
+In this tutorial, you'll see:
+
+ - Persistence
+
+ - Handling form data
+
+ - z3c.pt templating
+
+ - Security
+
+.. toctree::
+ :maxdepth: 2
+
+
diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst
new file mode 100644
index 000000000..0f3d798bb
--- /dev/null
+++ b/docs/tutorials/index.rst
@@ -0,0 +1,9 @@
+==============================
+``repoze.bfg`` Tutorials
+==============================
+
+.. toctree::
+ :maxdepth: 2
+
+ lxmlgraph
+
diff --git a/docs/tutorials/lxmlgraph/NOTES.txt b/docs/tutorials/lxmlgraph/NOTES.txt
new file mode 100644
index 000000000..60b4b9f55
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/NOTES.txt
@@ -0,0 +1,6 @@
+
+To Do
+------------
+
+- Dream up a ZPT/XSLT theme engine
+
diff --git a/docs/tutorials/lxmlgraph/background.rst b/docs/tutorials/lxmlgraph/background.rst
new file mode 100644
index 000000000..975c23a1d
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/background.rst
@@ -0,0 +1,113 @@
+Background
+====================
+
+This demo application presumes that you have an interest in XML
+technologies and might want to leverage them for a fast-as-hell, but
+rich and dynamic, website. In this demo application, we build up,
+bit-by-bit, the functionality. Thus, you don't have to know squatola
+about XML to follow along.
+
+In fact, the real purpose of this demo app is to teach its author how
+to use the stack (repoze.bfg, Paster, eggs, etc.)
+
+In summary:
+
+ - Represent a hierarchical site as hierarchical XML
+
+ - Inject ``repoze.bfg`` semantics into elements using ``lxml``
+
+ - Support flexible-but-fast rendering with XSLT
+
+.. warning::
+
+ If you dislike XML and related technologies such as XPath and XSLT,
+ you'll thoroughly detest this sample application. Just to be
+ stupendously clear, ``repoze.bfg`` is in no way dependent on XML.
+ On the other hand, ``repoze.bfg`` happens to make XML publishing
+ kind fun.
+
+What It Does
+-------------------
+
+Imagine you have a website that looks like this::
+
+ /
+ folder1/
+ doc1
+ doc2
+ image1
+ folder2/
+ doc2
+
+Meaning, a heterogenous, nested folder structure, just like your hard
+drive. (Unless you're one of those folks that uses your Windows
+Desktop as a flat filing system.) How might I get that information
+into a website?
+
+Using ``repoze.bfg``, of course. More specifically, with an XML file
+that models that hierarchy:
+
+.. literalinclude:: step00/simplemodel.xml
+ :language: xml
+
+How It Works
+-------------------
+
+To coerce ``repoze.bfg`` into publishing this model, I just need to
+sprinkle in some Python behavior. For example, ``repoze.bfg`` uses
+``__getitem__`` to traverse the model. I need my XML data to support
+this method. Moreover, I want some specific behavior: run an XPath
+express on the node to get the child with the ``@name`` attribute
+matching the URL hop.
+
+Fortunately ``lxml`` makes this easy. I can inject my nodes with a
+class that I write, thus providing my own ``__getitem__`` behavior.
+
+That class can also assert that my XML nodes provide an interface.
+The interface then lets me glue back into the standard ``repoze.bfg``
+machinery, such as associating views and permissions into the model.
+
+Neato torpedo. And stinking fast.
+
+Next up, I need to provide views for the elements in the model. I
+could, for example, use ZPT and manipulate the XML data using Python
+expressions against the lxml API. Or, I could use XSLT.
+
+For the latter, I could register a different XSLT for every "view" on
+every interface. Or, I could write one big XSLT, and let its template
+matching machinery decide who to render in a certain context.
+
+And finally, I could pass in just a single node and render it, or pass
+in the entire tree with a parameter identifying the context node.
+
+In the course of this writeup, we'll build ``repoze.lxmlgraph``
+step-by-step, starting with no XML. Each of those decisions will be
+analyzed an implemented. At the end, you'll see both the resulting
+demo application, plus the thought process that went along with it.
+
+What It Might Do
+--------------------
+
+This demo application has the potential to show some other interesting
+investigations:
+
+#. **Authorization**. By hooking up support for an ``__acl__``
+ property, I can store ACL information on a single node, on an
+ ancestor, on the ``<site>`` root, on the Python class, or any
+ combination thereof. Additionally, I can wire up the
+ ``__parent__`` attribute as a property that makes an lxml
+ ``node.getparent()`` call.
+
+#. **Multiple views**. Instead of just having a single default view
+ on a node, I can allow other view names, all pointing at the same
+ factory and XSLT. I simple grab that name and pass it in as a
+ paramter to the XSLT, which will run a different rule for
+ rendering.
+
+#. **Forms**. To edit data in the model, I need to render a form,
+ then handle post data on the way back in. For the former, it's
+ *really* easy in XSLT to make a very powerful, flexible, and
+ extensisible form rendering system. For the latter, I'll have to
+ learn more about POST handlers in ``repoze.bfg``.
+
+
diff --git a/docs/tutorials/lxmlgraph/index.rst b/docs/tutorials/lxmlgraph/index.rst
new file mode 100644
index 000000000..42c8ec503
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/index.rst
@@ -0,0 +1,21 @@
+Publishing XML with repoze.bfg
+==========================================
+
+``repoze.bfg`` is, well, fun. It brings back the good old days of
+Bobo, but with the good new days of WSGI. With ``repoze.bfg``,
+hierarchical websites are a cinch to develop.
+
+XML is also hierarchical. ``repoze.lxmlgraph`` is a demo application
+for ``repoze.bfg`` that shows publishing a tree of XML as a
+hierarchical website, while leveraging the facilities the gun gives
+you.
+
+.. toctree::
+ :maxdepth: 2
+
+ background
+ installation
+ step01
+ step02
+ step03
+ step04
diff --git a/docs/tutorials/lxmlgraph/installation.rst b/docs/tutorials/lxmlgraph/installation.rst
new file mode 100644
index 000000000..b30c058b8
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/installation.rst
@@ -0,0 +1,22 @@
+Installation
+=====================
+
+You can get the final form of the demo application, along with all the
+steps along the way, via the miracles of virtualenv, easy_install and Paster
+templates::
+
+ $ virtualenv --no-site-packages myapp
+ $ cd myapp
+ $ source bin/activate
+ $ easy_install repoze.lxmlgraph
+ $ paster create -t lxmlgraph_project
+
+Answer the questions, then run the demo:
+
+ cd <<projectname>>
+ python run.py
+
+At that point a URL such as ``http://localhost:5432/folder2/query1``
+should work.
+
+ \ No newline at end of file
diff --git a/docs/tutorials/lxmlgraph/oxygen.xpr b/docs/tutorials/lxmlgraph/oxygen.xpr
new file mode 100644
index 000000000..6b42af9e1
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/oxygen.xpr
@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <meta>
+ <filters directoryPatterns="" filePatterns=""
+ positiveFilePatterns="" showHiddenFiles="false"/>
+ <options>
+ <serialized>
+ <map>
+ <entry>
+ <String xml:space="preserve">scenario.associations</String>
+ <scenarioAssociation-array>
+ <scenarioAssociation>
+ <field name="name">
+ <String xml:space="preserve">xsltview</String>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XML</String>
+ </field>
+ <field name="url">
+ <String xml:space="preserve">step04/myapp/xsltview.xsl</String>
+ </field>
+ </scenarioAssociation>
+ <scenarioAssociation>
+ <field name="name">
+ <String xml:space="preserve">debugui-entryviewer</String>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XML</String>
+ </field>
+ <field name="url">
+ <String xml:space="preserve">../../../repoze.debug/trunk/repoze/debug/static/debugui-entryviewer.xsl</String>
+ </field>
+ </scenarioAssociation>
+ <scenarioAssociation>
+ <field name="name">
+ <String xml:space="preserve">Docbook PDF modified</String>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XSL</String>
+ </field>
+ <field name="url">
+ <String xml:space="preserve">../../../../playground/paul/psu2008/themes/Untitled5.xml</String>
+ </field>
+ </scenarioAssociation>
+ <scenarioAssociation>
+ <field name="name">
+ <String xml:space="preserve">docbook/docbook5.framework/DocBook 5/Docbook HTML</String>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XSL</String>
+ </field>
+ <field name="url">
+ <String xml:space="preserve">../../../../../guidetoec2.xml</String>
+ </field>
+ </scenarioAssociation>
+ </scenarioAssociation-array>
+ </entry>
+ <entry>
+ <String xml:space="preserve">scenarios</String>
+ <scenario-array>
+ <scenario>
+ <field name="name">
+ <String xml:space="preserve">Execute DDXQuery</String>
+ </field>
+ <field name="baseURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="footerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="fOPMethod">
+ <null/>
+ </field>
+ <field name="fOProcessorName">
+ <null/>
+ </field>
+ <field name="headerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="inputXSLURL">
+ <String xml:space="preserve">${currentFileURL}</String>
+ </field>
+ <field name="inputXMLURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="defaultScenario">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="isFOPPerforming">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">DD_XQUERY</String>
+ </field>
+ <field name="saveAs">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="openInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="outputFile">
+ <null/>
+ </field>
+ <field name="openOtherLocationInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="locationToOpenInBrowserURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="openInEditor">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInHTMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInXMLPane">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="showInSVGPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="useXSLTInput">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="xsltParams">
+ <list/>
+ </field>
+ <field name="cascadingStylesheets">
+ <String-array/>
+ </field>
+ <field name="xslTransformer">
+ <String xml:space="preserve">DataDirect</String>
+ </field>
+ <field name="extensionURLs">
+ <String-array/>
+ </field>
+ </scenario>
+ <scenario>
+ <field name="name">
+ <String xml:space="preserve">Execute SQL</String>
+ </field>
+ <field name="baseURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="footerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="fOPMethod">
+ <null/>
+ </field>
+ <field name="fOProcessorName">
+ <null/>
+ </field>
+ <field name="headerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="inputXSLURL">
+ <String xml:space="preserve">${currentFileURL}</String>
+ </field>
+ <field name="inputXMLURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="defaultScenario">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="isFOPPerforming">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">SQL</String>
+ </field>
+ <field name="saveAs">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="openInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="outputFile">
+ <null/>
+ </field>
+ <field name="openOtherLocationInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="locationToOpenInBrowserURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="openInEditor">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInHTMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInXMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInSVGPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="useXSLTInput">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="xsltParams">
+ <list/>
+ </field>
+ <field name="cascadingStylesheets">
+ <String-array/>
+ </field>
+ <field name="xslTransformer">
+ <String xml:space="preserve">JDBC</String>
+ </field>
+ <field name="extensionURLs">
+ <String-array/>
+ </field>
+ </scenario>
+ <scenario>
+ <field name="name">
+ <String xml:space="preserve">Execute XQuery</String>
+ </field>
+ <field name="baseURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="footerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="fOPMethod">
+ <null/>
+ </field>
+ <field name="fOProcessorName">
+ <null/>
+ </field>
+ <field name="headerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="inputXSLURL">
+ <String xml:space="preserve">${currentFileURL}</String>
+ </field>
+ <field name="inputXMLURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="defaultScenario">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="isFOPPerforming">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XQUERY</String>
+ </field>
+ <field name="saveAs">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="openInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="outputFile">
+ <null/>
+ </field>
+ <field name="openOtherLocationInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="locationToOpenInBrowserURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="openInEditor">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInHTMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInXMLPane">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="showInSVGPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="useXSLTInput">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="xsltParams">
+ <list/>
+ </field>
+ <field name="cascadingStylesheets">
+ <String-array/>
+ </field>
+ <field name="xslTransformer">
+ <String xml:space="preserve">Saxon9B XQuery</String>
+ </field>
+ <field name="extensionURLs">
+ <String-array/>
+ </field>
+ </scenario>
+ <scenario>
+ <field name="name">
+ <String xml:space="preserve">Docbook PDF modified</String>
+ </field>
+ <field name="baseURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="footerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="fOPMethod">
+ <String xml:space="preserve">pdf</String>
+ </field>
+ <field name="fOProcessorName">
+ <String xml:space="preserve">Built-in (Apache FOP)</String>
+ </field>
+ <field name="headerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="inputXSLURL">
+ <String xml:space="preserve">${frameworks}/docbook/xsl/fo/docbook.xsl</String>
+ </field>
+ <field name="inputXMLURL">
+ <String xml:space="preserve">${currentFileURL}</String>
+ </field>
+ <field name="defaultScenario">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="isFOPPerforming">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XSL</String>
+ </field>
+ <field name="saveAs">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="openInBrowser">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="outputFile">
+ <File xml:space="preserve">${cfd}/${cfn}.pdf</File>
+ </field>
+ <field name="openOtherLocationInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="locationToOpenInBrowserURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="openInEditor">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInHTMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInXMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInSVGPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="useXSLTInput">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="xsltParams">
+ <list>
+ <transformationParameter>
+ <field name="name">
+ <String xml:space="preserve">draft.mode</String>
+ </field>
+ <field name="value">
+ <String xml:space="preserve">no</String>
+ </field>
+ </transformationParameter>
+ <transformationParameter>
+ <field name="name">
+ <String xml:space="preserve">fop.extensions</String>
+ </field>
+ <field name="value">
+ <String xml:space="preserve">0</String>
+ </field>
+ </transformationParameter>
+ <transformationParameter>
+ <field name="name">
+ <String xml:space="preserve">fop1.extensions</String>
+ </field>
+ <field name="value">
+ <String xml:space="preserve">1</String>
+ </field>
+ </transformationParameter>
+ <transformationParameter>
+ <field name="name">
+ <String xml:space="preserve">linenumbering.extension</String>
+ </field>
+ <field name="value">
+ <String xml:space="preserve">1</String>
+ </field>
+ </transformationParameter>
+ <transformationParameter>
+ <field name="name">
+ <String xml:space="preserve">paper.type</String>
+ </field>
+ <field name="value">
+ <String xml:space="preserve">A4</String>
+ </field>
+ </transformationParameter>
+ <transformationParameter>
+ <field name="name">
+ <String xml:space="preserve">use.extensions</String>
+ </field>
+ <field name="value">
+ <String xml:space="preserve">1</String>
+ </field>
+ </transformationParameter>
+ </list>
+ </field>
+ <field name="cascadingStylesheets">
+ <String-array/>
+ </field>
+ <field name="xslTransformer">
+ <String xml:space="preserve">Saxon6.5.5</String>
+ </field>
+ <field name="extensionURLs">
+ <String-array/>
+ </field>
+ </scenario>
+ <scenario>
+ <field name="name">
+ <String xml:space="preserve">xsltview</String>
+ </field>
+ <field name="baseURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="footerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="fOPMethod">
+ <String xml:space="preserve">pdf</String>
+ </field>
+ <field name="fOProcessorName">
+ <String xml:space="preserve">Built-in (Apache FOP)</String>
+ </field>
+ <field name="headerURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="inputXSLURL">
+ <String xml:space="preserve">${currentFileURL}</String>
+ </field>
+ <field name="inputXMLURL">
+ <String xml:space="preserve">file:/Users/paul/projects/repozesvn/repoze.lxmlgraph/trunk/docs/step04/myapp/samplemodel.xml</String>
+ </field>
+ <field name="defaultScenario">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="isFOPPerforming">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="type">
+ <String xml:space="preserve">XML</String>
+ </field>
+ <field name="saveAs">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="openInBrowser">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="outputFile">
+ <null/>
+ </field>
+ <field name="openOtherLocationInBrowser">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="locationToOpenInBrowserURL">
+ <String xml:space="preserve"></String>
+ </field>
+ <field name="openInEditor">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInHTMLPane">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="showInXMLPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="showInSVGPane">
+ <Boolean xml:space="preserve">false</Boolean>
+ </field>
+ <field name="useXSLTInput">
+ <Boolean xml:space="preserve">true</Boolean>
+ </field>
+ <field name="xsltParams">
+ <list/>
+ </field>
+ <field name="cascadingStylesheets">
+ <String-array/>
+ </field>
+ <field name="xslTransformer">
+ <String xml:space="preserve">Xsltproc</String>
+ </field>
+ <field name="extensionURLs">
+ <String-array/>
+ </field>
+ </scenario>
+ </scenario-array>
+ </entry>
+ <entry>
+ <String xml:space="preserve">scenarios.load.from.project</String>
+ <Boolean xml:space="preserve">true</Boolean>
+ </entry>
+ </map>
+ </serialized>
+ </options>
+ </meta>
+ <projectTree name="oxygen.xpr">
+ <folder path="."/>
+ <folder path="../../../../../venvs/bfg/myapp-bak/"/>
+ </projectTree>
+</project>
diff --git a/docs/tutorials/lxmlgraph/step00/simplemodel.xml b/docs/tutorials/lxmlgraph/step00/simplemodel.xml
new file mode 100644
index 000000000..7dbc53951
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step00/simplemodel.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<site>
+ <title>My XMLGRAPH Website</title>
+ <folder xml:id="n1" name="folder1">
+ <document xml:id="n11" name="doc1">
+ <title>doc1 in folder1</title>
+ <body>
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>Welcome to the site. We have lots to say.</p>
+ <p>Or, <em>maybe</em> not.</p>
+ </div>
+ </body>
+ </document>
+ <document xml:id="n12" name="doc2">
+ <title>doc2 in folder1</title>
+ </document>
+ </folder>
+ <folder xml:id="n2" name="folder2">
+ <document xml:id="n21" name="doc1">
+ <title>doc1 in folder2</title>
+ </document>
+ <document xml:id="n22" name="doc2">
+ <title>doc2 in folder2</title>
+ </document>
+ </folder>
+</root>
diff --git a/docs/tutorials/lxmlgraph/step01.rst b/docs/tutorials/lxmlgraph/step01.rst
new file mode 100644
index 000000000..f44f48db9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step01.rst
@@ -0,0 +1,188 @@
+================================================
+Step 01: Non-XML Hello World for ``repoze.bfg``
+================================================
+
+Before we work on implementing an XML graph, we need a simple starting
+point for a basic ``repoze.bfg`` application. In this step we'll do
+the least amount possible to run a ``repoze.bfg`` sample application.
+
+.. note::
+
+ All steps in this writeup presume that you have a virtualenv setup
+ as shown in the Installation step. More specifically: *make sure
+ you are using that virtualenv's Python* !!
+
+Directory Layout
+====================
+
+Each step in this writeup has a subdirectory for the working
+application described in that step. Thus, starting at this docs
+directory, we have::
+
+ docs/
+ step01/
+ myapp/
+ __init__.py
+ configure.zcml
+ models.py
+ views.py
+ run.py
+ step02/
+ step03/
+
+Below we discuss each file in the ``step01``, then show how to run and
+use the demo application.
+
+
+Directory ``myapp``
+---------------------
+
+This directory contains the *package* to be published. That's right,
+I said *package*. ``repoze.bfg``, as we will see in a moment, uses
+Python packages as the unit of publishing.
+
+
+Module ``myapp/__init__.py``
+------------------------------
+
+This is (usually-empty) file that makes a directory into a Python
+"package." For ``repoze.bfg`` this is particularly important
+
+
+Module ``myapp/configure.zcml``
+--------------------------------
+
+Unlike other frameworks, ``repoze.bfg`` doesn't try to hide
+configuration. Instead, configuration using ZCML is the central
+wiring point. At the same time, ZCML is used in a very basic way, to
+avoid confusion it can cause.
+
+.. literalinclude:: step01/myapp/configure.zcml
+ :linenos:
+ :language: xml
+
+#. Lines 1-3 provide the root node and namespaces for the
+ configuration language. ``bfg`` is the namespace for
+ ``repoze.bfg``-specific configuration directives.
+
+#. Line 5 initializes those ``repoze.bfg``-specific configuration
+ directives.
+
+#. Lines 8-11 register a single view for model objects that support
+ the IMyModel interface. In this case we made a default view, which
+ means going to ``/a`` triggers the default view of instance ``a``.
+ Finally, the ``view`` attribute points at a Python function that
+ does all the work for this view.
+
+
+Module ``myapp/models.py``
+-----------------------------
+
+In our sample app, the ``models.py`` module provides the model data.
+We create an interface ``IMyModel`` that gives us the "type system"
+for our data. We then write a class ``MyModel`` that provides the
+behavior for instances of the ``IMyModel`` type.
+
+.. literalinclude:: step01/myapp/models.py
+ :linenos:
+
+#. Lines 5-6 define the interface.
+
+#. Lines 8-11 provide a class for that interface.
+
+#. Lines 13-15 make a small tree of sample data (``/a`` and ``/b`` for
+ URLs.)
+
+#. Line 17 is a function that will be grabbed by the server when it
+ wants to find the top of the model.
+
+
+Module ``myapp/views.py``
+---------------------------
+
+Much of the heavy liftin in a ``repoze.bfg`` application comes in the
+views. These are the bridge between the content in the model, and the
+HTML given back to the browser. Since ``repoze.bfg`` is very
+type-centric, URLs grab information of a certain type. Once we have
+the information and its type, we can grab a view that is registered
+for that type.
+
+.. note::
+
+ This "Step 01" doesn't use a template. The "view" we define is done
+ in Python, which generates the HTML. Most applications will use
+ templates to generate markup.
+
+.. literalinclude:: step01/myapp/models.py
+ :linenos:
+
+#. Lines 3 provide the ``my_hello_view`` that was registered as the
+ view. ``configure.zcml``, on Line 10, said that the default URL
+ for IMyModel content should run this ``my_hello_view`` function.
+
+ The function is handed two pieces of information: the ``context``
+ and the ``request``. The ``context`` is the data at the current
+ hop in the URL. (That data comes from the model.) The request is
+ an instance of a WebOb request.
+
+#. Lines 4-7 generate a WebOb Response object and return it.
+
+
+Module ``run.py``
+---------------------
+
+We need a small Python module that sets everything, fires up a web
+server, and handles incoming requests. Later we'll see how to use a
+Paste configuration file to do this work for us.
+
+.. literalinclude:: step01/run.py
+ :linenos:
+
+#. Line 1 uses the web server from the Paste project.
+
+#. Line 3 imports the big gun from ``repoze.bfg``.
+
+#. Line 4 grabs the function that hands back the top of the
+ application's model data.
+
+#. Line 5 imports the package that we want to "publish".
+
+#. Line 7 loads the big gun with the data and the package.
+
+#. And finally, line 8 starts the web server on port 5432.
+
+
+Running and Browsing the Application
+---------------------------------------
+
+We have our minimal application now in place. We can run the
+application as follows::
+
+ cd docs/step01
+ python run.py
+
+.. note::
+
+ Just to say it AGAIN: make sure the Python you are using is the
+ Python from your virtual environment. One way to ensure you always
+ get the right scripts is to do ``source bin/activate`` from the top
+ of your virtualenv. This will modify your PATH to look first in the
+ virtual env.
+
+You should then see::
+
+ $ python ./run.py
+ serving on 0.0.0.0:5432 view at http://127.0.0.1:5432
+
+If you connect in a web browser to ``http://localhost:5432/a`` you
+will see::
+
+ Hello from a @ /a
+
+This also works for ``http://localhost:5432/b``. However, if you
+connect to ``http://localhost:5432/c`` you will get::
+
+ 404 Not Found
+ The resource could not be found.
+
+ http://localhost:5432/c \ No newline at end of file
diff --git a/docs/tutorials/lxmlgraph/step01/myapp/__init__.py b/docs/tutorials/lxmlgraph/step01/myapp/__init__.py
new file mode 100644
index 000000000..792d60054
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step01/myapp/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/docs/tutorials/lxmlgraph/step01/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step01/myapp/configure.zcml
new file mode 100644
index 000000000..b139396fa
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step01/myapp/configure.zcml
@@ -0,0 +1,13 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:bfg="http://namespaces.repoze.org/bfg">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="repoze.bfg" />
+
+ <!-- the default view for a MyModel -->
+ <bfg:view
+ for=".models.IMyModel"
+ view=".views.my_hello_view"
+ />
+
+</configure>
diff --git a/docs/tutorials/lxmlgraph/step01/myapp/models.py b/docs/tutorials/lxmlgraph/step01/myapp/models.py
new file mode 100644
index 000000000..85d603d80
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step01/myapp/models.py
@@ -0,0 +1,18 @@
+from zope.interface import implements
+from zope.interface import Attribute
+from zope.interface import Interface
+
+class IMyModel(Interface):
+ __name__ = Attribute('Name of the model instance')
+
+class MyModel(dict):
+ implements(IMyModel)
+ def __init__(self, name):
+ self.__name__ = name
+
+root = MyModel('site')
+root['a'] = MyModel('a')
+root['b'] = MyModel('b')
+
+def get_root(environ):
+ return root
diff --git a/docs/tutorials/lxmlgraph/step01/myapp/views.py b/docs/tutorials/lxmlgraph/step01/myapp/views.py
new file mode 100644
index 000000000..13de3ae31
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step01/myapp/views.py
@@ -0,0 +1,7 @@
+from webob import Response
+
+def my_hello_view(context, request):
+ response = Response('Hello from %s @ %s' % (
+ context.__name__,
+ request.environ['PATH_INFO']))
+ return response
diff --git a/docs/tutorials/lxmlgraph/step01/run.py b/docs/tutorials/lxmlgraph/step01/run.py
new file mode 100644
index 000000000..1eac209dc
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step01/run.py
@@ -0,0 +1,8 @@
+from paste import httpserver
+
+from repoze.bfg import make_app
+from myapp.models import get_root
+import myapp
+
+app = make_app(get_root, myapp)
+httpserver.serve(app, host='0.0.0.0', port='5432')
diff --git a/docs/tutorials/lxmlgraph/step02.rst b/docs/tutorials/lxmlgraph/step02.rst
new file mode 100644
index 000000000..d4b6b261b
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02.rst
@@ -0,0 +1,118 @@
+================================================
+Step 02: Hello World as XML
+================================================
+
+We now have a website with ``/a`` and ``/b`` URLs. Each has a default
+view that returns a teensy weensy response.
+
+In this step we will do the exact some scope, but using an XML
+document as our model data. We will leverage the same ``repoze.bfg``
+machinery:
+
+ - Model data with interfaces that define "types"
+
+ - ZCML configuration to provide type-specific views
+
+We do, however, need to do some things differently:
+
+ - Our model class needs to use lxml to inject itelf into the XML
+ nodes
+
+ - That model class needs to implement the "handshake"
+
+Let's look at what changed.
+
+File ``myapp/samplemodel.xml``
+--------------------------------
+
+Our hierarchy in Step 01 was very simple. Mimicking it in XML is,
+thus, also very simple:
+
+.. literalinclude:: step02/myapp/samplemodel.xml
+ :linenos:
+ :language: xml
+
+#. Line 2 provides the root of the model as an XML ``<site>`` node.
+ The element name doesn't have to be ``<site>``.
+
+#. In lines 3-4, the ``<site>`` contains 2 top-level children: a and
+ b. These are provided as an element name ``<document>``. This,
+ also, is meaningfless as far as ``repoze.bfg`` is concerned.
+ However, this is where you compose th information model you are
+ publishing.
+
+The only special constraint is that a node that wants to be "found" by
+``repoze.bfg`` in during traversal *must* have an ``name`` attribute.
+(The use of ``@name`` corresponds to ``__name__`` in the
+``repoze.bfg`` handshake.) Each hop in the URL tries to grab a child
+with an attribute matching the next hop. Also, the value of the
+``@name`` should be unique in its containing node.
+
+
+Module ``myapp/models.py``
+------------------------------
+
+Here is the serious change: we have made an XML-aware model. Or is it
+a model-aware XML document? Such questions, harrumph.
+
+At a high level, we make write a class that "extends" lxml Element
+nodes, create an lxml parser, and register the custom class with the
+parser.
+
+.. literalinclude:: step02/myapp/models.py
+ :linenos:
+
+#. Line 4 imports lxml.
+
+#. Line 9 creates the custom class we are going to use to extend
+ etree.ElementBase. The lxml website has great documentation on the
+ various ways to inject custom Python behavior into XML.
+
+#. Just as before, line 12 says that instances of this class support a
+ certain content type (interface.) In our case, instances will be
+ XML nodes.
+
+#. ``repoze.bfg`` has a "protocol" where model data should have an
+ ``__name__`` attribute. Lines 14-16 implement this by grabbing the
+ ``@name`` attribute of the current node.
+
+#. URL traversal in ``repoze.bfg`` works via the ``__getitem__``
+ protocol. Thus, we need a method that implements this. Lines
+ 18-26 use XPath to look for a direct child that has an ``@name``
+ matching the item name that's being traversed to. If it finds it,
+ return it. If not, or if more than one is found, raise an error.
+
+#. As before, ``get_root`` is the function that is expected to return
+ the top of the model. In lines 30-32 we do the lxml magic to get
+ the custom Python class registered. We then load some XML and
+ return the top of the tree.
+
+
+Module `myapp/views.py``
+--------------------------
+
+We only made two changes here.
+
+.. literalinclude:: step02/myapp/views.py
+ :linenos:
+
+#. Line 5 grabs the element name (tag name) of the ``context``, which
+ is the current XML node that we're traversing through.
+
+#. Line 6 uses the special property we defined in our custom Python
+ class to get the ``__name__`` of the context.
+
+
+Browsing the Model
+------------------------
+
+We can use the same URLs from Step 01 to browser the model and see
+results::
+
+ http://localhost:5432/a
+ http://localhost:5432/b
+ http://localhost:5432/c (Not Found)
+
+In this case, each request grabs a node in the XML and uses it as the
+data for the view. ``repoze.bfg`` doesn't really know that, unlike
+Step 01, we no longer have "real" Python data. \ No newline at end of file
diff --git a/docs/tutorials/lxmlgraph/step02/myapp/__init__.py b/docs/tutorials/lxmlgraph/step02/myapp/__init__.py
new file mode 100644
index 000000000..792d60054
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02/myapp/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/docs/tutorials/lxmlgraph/step02/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step02/myapp/configure.zcml
new file mode 100644
index 000000000..540526ec9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02/myapp/configure.zcml
@@ -0,0 +1,11 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:bfg="http://namespaces.repoze.org/bfg">
+
+ <include package="repoze.bfg" />
+
+ <bfg:view
+ for=".models.IMyModel"
+ view=".views.my_hello_view"
+ />
+
+</configure>
diff --git a/docs/tutorials/lxmlgraph/step02/myapp/models.py b/docs/tutorials/lxmlgraph/step02/myapp/models.py
new file mode 100644
index 000000000..3c03de1a9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02/myapp/models.py
@@ -0,0 +1,41 @@
+from zope.interface import implements
+from zope.interface import Attribute
+from zope.interface import Interface
+from lxml import etree
+
+class IMyModel(Interface):
+ __name__ = Attribute('Name of the model instance')
+
+class BfgElement(etree.ElementBase):
+ """Handle access control and getitem behavior"""
+
+ implements(IMyModel)
+
+ @property
+ def __name__(self):
+ return self.xpath("@name")[0]
+
+ def __getitem__(self, child_name):
+ xp = "*[@name='%s']" % child_name
+ matches = self.xpath(xp)
+ if len(matches) == 0:
+ raise KeyError('No child found for %s' % child_name)
+ elif len(matches) > 1:
+ raise KeyError('More than one child for %s' % child_name)
+ else:
+ return matches[0]
+
+def get_root(environ):
+ # Setup the custom parser with our BfgElement behavior
+ parser_lookup = etree.ElementDefaultClassLookup(element=BfgElement)
+ parser = etree.XMLParser()
+ parser.set_element_class_lookup(parser_lookup)
+
+ # Now load the XML file
+ xmlstring = open("myapp/samplemodel.xml").read()
+ root = etree.XML(xmlstring, parser)
+
+ return root
+
+
+
diff --git a/docs/tutorials/lxmlgraph/step02/myapp/samplemodel.xml b/docs/tutorials/lxmlgraph/step02/myapp/samplemodel.xml
new file mode 100644
index 000000000..83678e5e9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02/myapp/samplemodel.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<site>
+ <document name="a"/>
+ <document name="b"/>
+</site>
diff --git a/docs/tutorials/lxmlgraph/step02/myapp/views.py b/docs/tutorials/lxmlgraph/step02/myapp/views.py
new file mode 100644
index 000000000..205b4b163
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02/myapp/views.py
@@ -0,0 +1,8 @@
+from webob import Response
+
+def my_hello_view(context, request):
+ response = Response('Hello to %s from %s @ %s' % (
+ context.tag,
+ context.__name__,
+ request.environ['PATH_INFO']))
+ return response
diff --git a/docs/tutorials/lxmlgraph/step02/run.py b/docs/tutorials/lxmlgraph/step02/run.py
new file mode 100644
index 000000000..1eac209dc
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02/run.py
@@ -0,0 +1,8 @@
+from paste import httpserver
+
+from repoze.bfg import make_app
+from myapp.models import get_root
+import myapp
+
+app = make_app(get_root, myapp)
+httpserver.serve(app, host='0.0.0.0', port='5432')
diff --git a/docs/tutorials/lxmlgraph/step03.rst b/docs/tutorials/lxmlgraph/step03.rst
new file mode 100644
index 000000000..c7298e41d
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03.rst
@@ -0,0 +1,175 @@
+================================================
+Step 03: Basic Rendering With ZPT and XSLT
+================================================
+
+Our XML-based model is now usable. However, we're using Python to
+generate the HTML, instead of a template. In this step, we'll look at
+wiring up some templates, using both ZPT and XSLT.
+
+In a nutshell, this means:
+
+ - Slight changes to the ZCML
+
+ - View functions that assemble information and call the template
+
+ZPT Templates
+========================
+
+Let's start with a ZPT-based default view for the nodes in the XML.
+The ZCML for this would look like this:
+
+.. code-block:: xml
+
+ <bfg:view
+ for=".models.IMyModel"
+ view=".views.zpt_default_view"
+ />
+
+Here we point to a function in ``myapp/views.py`` that looks like the
+following:
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.template import render_template_to_response
+ def zpt_default_view(context, request):
+ fn = "default.pt"
+ return render_template_to_response(fn, name=context.__name__, node=context)
+
+This function is relatively simple:
+
+#. Line 1 imports a ``repoze.bfg`` function that renders ZPT
+ templates. ``repoze.bfg`` uses the ``z3c.pt`` ZPT engine.
+
+#. Line 2, like our other view functions, gets passed a ``context``
+ (the current hop in the URL) and WebOb ``request`` object.
+
+#. Line 3 points at the filename of the ZPT.
+
+#. Line 4 calls the ``render_template_to_response`` function, passing in the
+ filename for the ZPT and two top-level variables that can be used
+ in the ZPT. The first is the name of the current URL hop
+ (context). The second is the XML node object for that hop
+ (context).
+
+In Step 01 and 02, we returned a WebOb Response object that we
+created. ``render_template_to_response`` makes a Response itself.
+
+Here's what the ZPT looks like:
+
+.. literalinclude:: step03/myapp/default.pt
+ :linenos:
+ :language: xml
+
+Look, a template! Life is better with templating:
+
+#. Lines 1-2 make an ``<html>`` node with a namespace for TAL.
+
+#. Line 5 inserts the value of the ``name`` that we passed into
+ ``render_template_to_response``.
+
+#. Line 6 sure looks interesting. It uses the ``node`` that we passed
+ in via ``render_template_to_response``. Since ``z3c.pt`` uses
+ Python as its expession language, we can put anything Python-legal
+ between the braces. And since ``node`` is an lxml Element object,
+ we just ask for its ``.tag``, like regular Python lxml code.
+
+Viewing the ZPT
+------------------
+
+With all of that in place, going to ``http://localhost:5432/a`` now
+generates, via the ZPT, the following::
+
+ My template is viewing item: a
+
+ The node has a tag name of: document.
+
+
+XSLT Templates
+====================
+
+So that's the ZPT way of rendering HTML for an XML document. How
+might XSLT look?
+
+.. note::
+
+ For the following, we'll switch back to showing the complete module
+ code, rather than snippets. You can then follow along by looking at
+ the files in ``docs/step03/myapp``.
+
+File ``myapp/configure.zcml``
+--------------------------------
+
+The ZCML statement for the XSLT template looks almost exactly the same
+as the ZPT template:
+
+.. literalinclude:: step03/myapp/configure.zcml
+ :linenos:
+ :language: xml
+
+#. Lines 10-14 wire up a new view, in addition to the default view.
+
+#. Line 13 provides the difference: ``name="xsltview.html"`` means
+ that all our URLs now can have ``/xsltview.xml`` appended to them.
+
+In the ZCML, there is no distinction between a ZPT view and an XSLT
+view. The difference is only in the function that is pointed to by
+the ``view=`` attribute.
+
+
+Module ``myapp/views.py``
+--------------------------------
+
+The ZCML says that our XSLT view (``xsltview.html`` on the URL) comes
+from the ``myapp.views.xslt_view`` function:
+
+.. literalinclude:: step03/myapp/views.py
+ :linenos:
+
+#. Line 9 starts the Python function which serves as the view for this
+ template. The function has the same signature as the
+ ``zpt_default_view`` function we defined for the ZPT template's
+ view.
+
+#. Line 10 implements the difference. We call
+ ``render_transform_to_response`` instead of
+ ``render_template_to_response``. This tells ``repoze.bfg`` to make
+ an XSLT processor for this template, instead of a ZPT. The second
+ argument passes in ``context`` to the XSLT transform. ``context```
+ is an instance of an Element node. Namely, a node from the XML
+ document that corresponds to the current hop in the URL.
+
+
+File ``myapp/xsltview.xsl``
+--------------------------------
+
+How different does the XSLT itself look? At this stage, not too different:
+
+.. literalinclude:: step03/myapp/xsltview.xsl
+ :linenos:
+ :language: xml
+
+#. Lines 1 and 2 are typical XSLT setup.
+
+#. Line 3 defines a rule to match on the node that is passed in. In
+ our case, a ``<document>`` node.
+
+#. Line 7 inserts the value of the ``@id`` attribute from the
+ "current" node at that point in the rule. We're sitting on the
+ ``<document>`` node (thanks to line 3). Thus, ``<xsl:value of
+ select="@id"/>`` inserts ``a`` or ``b``, depending on which
+ document we are sitting on.
+
+#. Line 8 shows the element name of the current node.
+
+
+Viewing the XSLT
+--------------------
+
+With this in place, runnning the application provides a URL such as
+``http://localhost:5432/a/xsltview.html``. Going to that URL should
+show::
+
+ My template is viewing item: a
+
+ The node has a name of: document.
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/__init__.py b/docs/tutorials/lxmlgraph/step03/myapp/__init__.py
new file mode 100644
index 000000000..792d60054
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step03/myapp/configure.zcml
new file mode 100644
index 000000000..ef340c283
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/configure.zcml
@@ -0,0 +1,17 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:bfg="http://namespaces.repoze.org/bfg">
+
+ <include package="repoze.bfg" />
+
+ <bfg:view
+ for=".models.IMyModel"
+ view=".views.zpt_default_view"
+ />
+
+ <bfg:view
+ for=".models.IMyModel"
+ view=".views.xslt_view"
+ name="xsltview.html"
+ />
+
+</configure>
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/default.pt b/docs/tutorials/lxmlgraph/step03/myapp/default.pt
new file mode 100644
index 000000000..3cc98ef92
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/default.pt
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <head></head>
+ <body>
+ <h1>My template is viewing item: ${name}</h1>
+ <p>The node has a tag name of: ${node.tag}.</p>
+ </body>
+</html>
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/models.py b/docs/tutorials/lxmlgraph/step03/myapp/models.py
new file mode 100644
index 000000000..3c03de1a9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/models.py
@@ -0,0 +1,41 @@
+from zope.interface import implements
+from zope.interface import Attribute
+from zope.interface import Interface
+from lxml import etree
+
+class IMyModel(Interface):
+ __name__ = Attribute('Name of the model instance')
+
+class BfgElement(etree.ElementBase):
+ """Handle access control and getitem behavior"""
+
+ implements(IMyModel)
+
+ @property
+ def __name__(self):
+ return self.xpath("@name")[0]
+
+ def __getitem__(self, child_name):
+ xp = "*[@name='%s']" % child_name
+ matches = self.xpath(xp)
+ if len(matches) == 0:
+ raise KeyError('No child found for %s' % child_name)
+ elif len(matches) > 1:
+ raise KeyError('More than one child for %s' % child_name)
+ else:
+ return matches[0]
+
+def get_root(environ):
+ # Setup the custom parser with our BfgElement behavior
+ parser_lookup = etree.ElementDefaultClassLookup(element=BfgElement)
+ parser = etree.XMLParser()
+ parser.set_element_class_lookup(parser_lookup)
+
+ # Now load the XML file
+ xmlstring = open("myapp/samplemodel.xml").read()
+ root = etree.XML(xmlstring, parser)
+
+ return root
+
+
+
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/samplemodel.xml b/docs/tutorials/lxmlgraph/step03/myapp/samplemodel.xml
new file mode 100644
index 000000000..83678e5e9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/samplemodel.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<site>
+ <document name="a"/>
+ <document name="b"/>
+</site>
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/views.py b/docs/tutorials/lxmlgraph/step03/myapp/views.py
new file mode 100644
index 000000000..c77eca8fe
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/views.py
@@ -0,0 +1,10 @@
+from repoze.bfg.template import render_template_to_response
+from repoze.bfg.template import render_transform_to_response
+
+def zpt_default_view(context, request):
+ return render_template_to_response("default.pt",
+ name=context.__name__,
+ node=context)
+
+def xslt_view(context, request):
+ return render_transform_to_response("xsltview.xsl", context)
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/xsltview.xsl b/docs/tutorials/lxmlgraph/step03/myapp/xsltview.xsl
new file mode 100644
index 000000000..4d759b15b
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/myapp/xsltview.xsl
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:template match="/document">
+ <html>
+ <head/>
+ <body>
+ <h1>My template is viewing item: <xsl:value-of select="@name"/></h1>
+ <p>The node has a name of: <xsl:value-of select="name()"/>.</p>
+ </body>
+ </html>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/docs/tutorials/lxmlgraph/step03/run.py b/docs/tutorials/lxmlgraph/step03/run.py
new file mode 100644
index 000000000..1eac209dc
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step03/run.py
@@ -0,0 +1,8 @@
+from paste import httpserver
+
+from repoze.bfg import make_app
+from myapp.models import get_root
+import myapp
+
+app = make_app(get_root, myapp)
+httpserver.serve(app, host='0.0.0.0', port='5432')
diff --git a/docs/tutorials/lxmlgraph/step04.rst b/docs/tutorials/lxmlgraph/step04.rst
new file mode 100644
index 000000000..8112610a2
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04.rst
@@ -0,0 +1,98 @@
+================================================
+Step 04: Hierarchical Rendering With XSLT
+================================================
+
+Now that we have basic templating for our XML graph in place, let's
+start doing some fun stuff with it. As we walk through use cases and
+build out patterns for implementing them, we'll get to leverage some
+features available in XML processors. For better or worse. [wink]
+
+In this step we take a look at the following:
+
+- Build a nested, folder-like website
+
+- Render the HTML common to all pages, then render the part specific
+ to a certain page
+
+- Show contents of a folder when sitting on a folder, and content of a
+ document when sitting on a document
+
+
+Pre-Flight Cleanup
+====================
+
+In the last example, we had a default template that used ZPT. We're
+shifting the rest of the steps over to XSLT. Thus, our
+``myapp/configure.zcml`` is now simpler:
+
+.. literalinclude:: step04/myapp/configure.zcml
+ :linenos:
+ :language: xml
+
+We also remove the ZPT view function from ``views.py``, as we'll see
+in a moment.
+
+Design Change: Trees and Context IDs
+========================================
+
+In ``repoze.bfg``, the ``context`` variable that is passed into our
+view function equates to the Python object that was grabbed on the
+current hop in the URL. For ``repoze.lxmlgraph``, that "context"
+object is a node in the XML document, found by traversing node
+children.
+
+For the XSLT in Step 03, we passed in the context node. From the
+XSLT's perpective, the universe started at the context node. It could
+only see information in that node and the children beneath it.
+
+If we could see the entire tree, however, we could put the other
+information to use: showing the name of the site in a header, listing
+the breadcrumbs to reach the document, and other portal-style boxes.
+
+To enable this, we need the following:
+
+#. A way to pass in the entire XML document tree.
+
+#. A way to uniquely point at the item in the XML that we are
+ currently sitting on, with the fastest performance possible.
+
+We will thus make the following changes in our approach:
+
+#. The XML document will support an ``xml:id`` attribute on each node
+ that has a ``name`` attribute. The ``xml:id`` uniquely identifies
+ the resource within the document. Moreover, it leverages built-in
+ support for high-speed lookups in XPath.
+
+#. We change the view function to pass in the root of the tree,
+ instead of the context node.
+
+#. We also pass in, via an XSLT parameter, the ``xml:id`` of the
+ context node.
+
+#. The XSLT will start at the top of the tree, generate the site-wide
+ look and feel, then render the context node.
+
+That's the big picture. Each of these changes will be explained in
+detail below.
+
+
+File ``myapp/samplemodel.xml``
+================================
+
+The XML document with the information for our website has quite a
+number of changes:
+
+.. literalinclude:: step04/myapp/samplemodel.xml
+ :linenos:
+ :language: xml
+
+#. Line 3 shows that our ``<site>`` now gets a ``<title>``.
+
+#. On Line 4 we make an index document at the root that contains a
+ document-wide unique value for its ``@xml:id``.
+
+#. In lines 5-11, our ``<document>`` gets some extra information: a
+ ``<title>``, plus some HTML-namespaced markup content inside a
+ ``<body>``.
+
+#.
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/__init__.py b/docs/tutorials/lxmlgraph/step04/myapp/__init__.py
new file mode 100644
index 000000000..792d60054
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/myapp/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step04/myapp/configure.zcml
new file mode 100644
index 000000000..1ba4c9155
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/myapp/configure.zcml
@@ -0,0 +1,11 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:bfg="http://namespaces.repoze.org/bfg">
+
+ <include package="repoze.bfg" />
+
+ <bfg:view
+ for=".models.IMyModel"
+ view=".views.xslt_view"
+ />
+
+</configure>
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/models.py b/docs/tutorials/lxmlgraph/step04/myapp/models.py
new file mode 100644
index 000000000..3c03de1a9
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/myapp/models.py
@@ -0,0 +1,41 @@
+from zope.interface import implements
+from zope.interface import Attribute
+from zope.interface import Interface
+from lxml import etree
+
+class IMyModel(Interface):
+ __name__ = Attribute('Name of the model instance')
+
+class BfgElement(etree.ElementBase):
+ """Handle access control and getitem behavior"""
+
+ implements(IMyModel)
+
+ @property
+ def __name__(self):
+ return self.xpath("@name")[0]
+
+ def __getitem__(self, child_name):
+ xp = "*[@name='%s']" % child_name
+ matches = self.xpath(xp)
+ if len(matches) == 0:
+ raise KeyError('No child found for %s' % child_name)
+ elif len(matches) > 1:
+ raise KeyError('More than one child for %s' % child_name)
+ else:
+ return matches[0]
+
+def get_root(environ):
+ # Setup the custom parser with our BfgElement behavior
+ parser_lookup = etree.ElementDefaultClassLookup(element=BfgElement)
+ parser = etree.XMLParser()
+ parser.set_element_class_lookup(parser_lookup)
+
+ # Now load the XML file
+ xmlstring = open("myapp/samplemodel.xml").read()
+ root = etree.XML(xmlstring, parser)
+
+ return root
+
+
+
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/samplemodel.xml b/docs/tutorials/lxmlgraph/step04/myapp/samplemodel.xml
new file mode 100644
index 000000000..36ab335d2
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/myapp/samplemodel.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<site>
+ <title>My XMLGRAPH Website</title>
+ <document xml:id="index" name="index.html">
+ <title>Site Home Page</title>
+ <body>
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>Welcome to the site. We have lots to say.</p>
+ <p>Or, <em>maybe</em> not.</p>
+ </div>
+ </body>
+ </document>
+ <folder xml:id="n1" name="folder1">
+ <title>Folder One</title>
+ <document xml:id="n11" name="doc1">
+ <title>doc1 in folder1</title>
+ <body>
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>I am in an HTML <code>div</code> so I can do <strong>LOTS</strong> of
+ formatting.</p>
+ </div>
+ </body>
+ </document>
+ <document xml:id="n12" name="doc2">
+ <title>doc2 in folder1</title>
+ <body>
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>Keep this on one line.</p>
+ </div>
+ </body>
+ </document>
+ </folder>
+ <folder xml:id="n2" name="folder2">
+ <title>The Second Folder</title>
+ <document xml:id="n21" name="doc1">
+ <title>doc1 in folder2</title>
+ <body>
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>This is a special folder. It's folder 2!</p>
+ </div>
+ </body>
+ </document>
+ </folder>
+</site>
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/views.py b/docs/tutorials/lxmlgraph/step04/myapp/views.py
new file mode 100644
index 000000000..fd8650e14
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/myapp/views.py
@@ -0,0 +1,13 @@
+from repoze.bfg.template import render_transform_to_response
+
+# Some constants
+XML_NAMESPACE='http://www.w3.org/XML/1998/namespace'
+XML_PREFIX= '{%s}' % XML_NAMESPACE
+
+def xslt_view(context, request):
+ # Grab the root of the tree, which should be a <site>
+ site = context.getroottree().getroot()
+ # Jot down which node we're sitting on as the <context>
+ contextid = "'%s'" % context.get(XML_PREFIX+'id')
+ return render_transform_to_response("xsltview.xsl", site,
+ contextid=contextid)
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl b/docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl
new file mode 100644
index 000000000..2406987d0
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:param name="contextid">n1</xsl:param>
+ <xsl:variable name="contextnode" select="id($contextid)"/>
+ <xsl:template match="/">
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="$contextnode/title"/>
+ </title>
+ </head>
+ <body>
+ <h2>
+ <xsl:value-of select="$contextnode/title"/>
+ </h2>
+ <xsl:apply-templates select="$contextnode"/>
+ <table border="1" cellpadding="6" cellspacing="0">
+ <tr>
+ <th>Type</th>
+ <th>@xml:id</th>
+ <th>@name</th>
+ <th>Parent Type</th>
+ <th>Parent @name</th>
+ </tr>
+ <tr>
+ <td>
+ <xsl:value-of select="name($contextnode)"/>
+ </td>
+ <td>
+ <xsl:value-of select="$contextnode/@xml:id"/>
+ </td>
+ <td>
+ <xsl:value-of select="$contextnode/@name"/>
+ </td>
+ <td>
+ <xsl:value-of select="name($contextnode/..)"/>
+ </td>
+ <td>
+ <xsl:value-of select="$contextnode/../@name"/>
+ </td>
+ </tr>
+ </table>
+ </body>
+ </html>
+ </xsl:template>
+ <xsl:template match="folder">
+ <p>
+ <em>Folders are special, they contain things.</em>
+ </p>
+ <xsl:if test="*[@xml:id]">
+ <h2>Folder Contents</h2>
+ <ul>
+ <xsl:for-each select="*[@xml:id]">
+ <li>
+ <a href="{../@name}/{@name}">
+ <xsl:value-of select="title"/>
+ </a>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:if>
+ </xsl:template>
+ <xsl:template match="document">
+ <p>
+ <em>Documents contain text.</em>
+ </p>
+ <xsl:copy-of select="body/*"/>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/docs/tutorials/lxmlgraph/step04/run.py b/docs/tutorials/lxmlgraph/step04/run.py
new file mode 100644
index 000000000..1eac209dc
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step04/run.py
@@ -0,0 +1,8 @@
+from paste import httpserver
+
+from repoze.bfg import make_app
+from myapp.models import get_root
+import myapp
+
+app = make_app(get_root, myapp)
+httpserver.serve(app, host='0.0.0.0', port='5432')
diff --git a/docs/tutorials/lxmlgraph/step05.rst b/docs/tutorials/lxmlgraph/step05.rst
new file mode 100644
index 000000000..83474adfd
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step05.rst
@@ -0,0 +1,12 @@
+================================================
+Step 05: Advanced Templating
+================================================
+
+
+- multiple "views" wired to the same function and template
+
+- breadcrumbs, sidebars
+
+- <query> support
+
+- type-based support for <document>, <folder>, <query>, <site> \ No newline at end of file