From ba9b0e647bff1bf0c437ab204ddf11783ed698f8 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 1 May 2009 10:26:57 +0000 Subject: Merge "c-free" branch to trunk. --- docs/tutorials/lxmlgraph/NOTES.txt | 6 - docs/tutorials/lxmlgraph/background.rst | 83 ------- docs/tutorials/lxmlgraph/index.rst | 17 -- docs/tutorials/lxmlgraph/step00/simplemodel.xml | 26 --- docs/tutorials/lxmlgraph/step01.rst | 24 -- docs/tutorials/lxmlgraph/step01/myapp/__init__.py | 1 - .../lxmlgraph/step01/myapp/configure.zcml | 12 - docs/tutorials/lxmlgraph/step01/myapp/models.py | 18 -- docs/tutorials/lxmlgraph/step01/myapp/views.py | 7 - docs/tutorials/lxmlgraph/step01/run.py | 8 - docs/tutorials/lxmlgraph/step02.rst | 132 ----------- docs/tutorials/lxmlgraph/step02/myapp/__init__.py | 1 - .../lxmlgraph/step02/myapp/configure.zcml | 10 - docs/tutorials/lxmlgraph/step02/myapp/models.py | 42 ---- .../lxmlgraph/step02/myapp/samplemodel.xml | 5 - docs/tutorials/lxmlgraph/step02/myapp/views.py | 8 - docs/tutorials/lxmlgraph/step02/run.py | 8 - docs/tutorials/lxmlgraph/step03.rst | 209 ----------------- docs/tutorials/lxmlgraph/step03/myapp/__init__.py | 1 - .../lxmlgraph/step03/myapp/configure.zcml | 16 -- docs/tutorials/lxmlgraph/step03/myapp/models.py | 42 ---- .../lxmlgraph/step03/myapp/samplemodel.xml | 5 - .../lxmlgraph/step03/myapp/templates/default.pt | 8 - .../lxmlgraph/step03/myapp/templates/xsltview.xsl | 12 - docs/tutorials/lxmlgraph/step03/myapp/views.py | 10 - docs/tutorials/lxmlgraph/step04.rst | 250 --------------------- docs/tutorials/lxmlgraph/step04/myapp/__init__.py | 1 - .../lxmlgraph/step04/myapp/configure.zcml | 10 - docs/tutorials/lxmlgraph/step04/myapp/models.py | 45 ---- .../lxmlgraph/step04/myapp/samplemodel.xml | 44 ---- docs/tutorials/lxmlgraph/step04/myapp/views.py | 13 -- docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl | 69 ------ docs/tutorials/lxmlgraph/step04/run.py | 8 - docs/tutorials/lxmlgraph/step05.rst | 11 - 34 files changed, 1162 deletions(-) delete mode 100644 docs/tutorials/lxmlgraph/NOTES.txt delete mode 100644 docs/tutorials/lxmlgraph/background.rst delete mode 100644 docs/tutorials/lxmlgraph/index.rst delete mode 100644 docs/tutorials/lxmlgraph/step00/simplemodel.xml delete mode 100644 docs/tutorials/lxmlgraph/step01.rst delete mode 100644 docs/tutorials/lxmlgraph/step01/myapp/__init__.py delete mode 100644 docs/tutorials/lxmlgraph/step01/myapp/configure.zcml delete mode 100644 docs/tutorials/lxmlgraph/step01/myapp/models.py delete mode 100644 docs/tutorials/lxmlgraph/step01/myapp/views.py delete mode 100644 docs/tutorials/lxmlgraph/step01/run.py delete mode 100644 docs/tutorials/lxmlgraph/step02.rst delete mode 100644 docs/tutorials/lxmlgraph/step02/myapp/__init__.py delete mode 100644 docs/tutorials/lxmlgraph/step02/myapp/configure.zcml delete mode 100644 docs/tutorials/lxmlgraph/step02/myapp/models.py delete mode 100644 docs/tutorials/lxmlgraph/step02/myapp/samplemodel.xml delete mode 100644 docs/tutorials/lxmlgraph/step02/myapp/views.py delete mode 100644 docs/tutorials/lxmlgraph/step02/run.py delete mode 100644 docs/tutorials/lxmlgraph/step03.rst delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/__init__.py delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/configure.zcml delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/models.py delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/samplemodel.xml delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/templates/default.pt delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/templates/xsltview.xsl delete mode 100644 docs/tutorials/lxmlgraph/step03/myapp/views.py delete mode 100644 docs/tutorials/lxmlgraph/step04.rst delete mode 100644 docs/tutorials/lxmlgraph/step04/myapp/__init__.py delete mode 100644 docs/tutorials/lxmlgraph/step04/myapp/configure.zcml delete mode 100644 docs/tutorials/lxmlgraph/step04/myapp/models.py delete mode 100644 docs/tutorials/lxmlgraph/step04/myapp/samplemodel.xml delete mode 100644 docs/tutorials/lxmlgraph/step04/myapp/views.py delete mode 100644 docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl delete mode 100644 docs/tutorials/lxmlgraph/step04/run.py delete mode 100644 docs/tutorials/lxmlgraph/step05.rst (limited to 'docs/tutorials') diff --git a/docs/tutorials/lxmlgraph/NOTES.txt b/docs/tutorials/lxmlgraph/NOTES.txt deleted file mode 100644 index 60b4b9f55..000000000 --- a/docs/tutorials/lxmlgraph/NOTES.txt +++ /dev/null @@ -1,6 +0,0 @@ - -To Do ------------- - -- Dream up a ZPT/XSLT theme engine - diff --git a/docs/tutorials/lxmlgraph/background.rst b/docs/tutorials/lxmlgraph/background.rst deleted file mode 100644 index aec4cda26..000000000 --- a/docs/tutorials/lxmlgraph/background.rst +++ /dev/null @@ -1,83 +0,0 @@ -Background -==================== - -In this demo application, we build up, bit-by-bit, the functionality -for a website based on a single XML document. You don't have to know -much about XML to follow along. In fact, the real purpose of this -demo app is to teach its author how to use the stack -(:mod:`repoze.bfg`, ``paster``, eggs, etc.) - -.. 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, :mod:`repoze.bfg` is in no way dependent on XML. - On the other hand, :mod:`repoze.bfg` happens to make XML publishing - kinda fun. - -In summary: - - - Represent a hierarchical website as an XML document - - - Inject :mod:`repoze.bfg` semantics into elements using - :term:`lxml` - - - Support rendering with :term:`XSLT` - -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 :mod:`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 :mod:`repoze.bfg` into publishing this model, I just need to -sprinkle in some Python behavior. For example, :mod:`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 :term:`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 :mod:`repoze.bfg` -machinery, such as associating views and permissions into the model. - -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 :term:`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. - diff --git a/docs/tutorials/lxmlgraph/index.rst b/docs/tutorials/lxmlgraph/index.rst deleted file mode 100644 index cc79f7e73..000000000 --- a/docs/tutorials/lxmlgraph/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -``lxmlgraph``: Publishing An XML Tree with :mod:`repoze.bfg` -============================================================ - -Hierarchical websites are easy to develop with :mod:`repoze.bfg`. -That hierarchy doesn't need to be defined by any particular sort of -graph or databsase system. To demonstrate this, we present -``repoze.lxmlgraph``, a demo application for :mod:`repoze.bfg` that -describes publishing an XML document as a hierarchical website. - -.. toctree:: - :maxdepth: 2 - - background - step01 - step02 - step03 - step04 diff --git a/docs/tutorials/lxmlgraph/step00/simplemodel.xml b/docs/tutorials/lxmlgraph/step00/simplemodel.xml deleted file mode 100644 index 7dbc53951..000000000 --- a/docs/tutorials/lxmlgraph/step00/simplemodel.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - My XMLGRAPH Website - - - doc1 in folder1 - -
-

Welcome to the site. We have lots to say.

-

Or, maybe not.

-
- -
- - doc2 in folder1 - -
- - - doc1 in folder2 - - - doc2 in folder2 - - - diff --git a/docs/tutorials/lxmlgraph/step01.rst b/docs/tutorials/lxmlgraph/step01.rst deleted file mode 100644 index 578f13608..000000000 --- a/docs/tutorials/lxmlgraph/step01.rst +++ /dev/null @@ -1,24 +0,0 @@ -======================= -Step 1: Getting Started -======================= - -To get started, using the ``paster`` command from a :term:`virtualenv` -you've created that has :mod:`repoze.bfg` installed, run ``paster -create -t bfg`` as described in :ref:`project_narr` to create your -``lxmlgraph`` project:: - - $ paster create -t bfg Selected and implied templates: repoze.bfg#bfg - repoze.bfg starter project - - Enter project name: lxmlgraph - ... - $ - -You now have a project named ``lxmlgraph`` in your current directory. -We'll add to this project in subsequent steps. - -To get your project ready for development and execution, use the -``setup.py develop`` command within the same virtualenv as bfg is -installed as documented in :ref:`project_narr` . You'll need to do -this in order to run the ``lxmlgraph`` application in subsequent -steps. diff --git a/docs/tutorials/lxmlgraph/step01/myapp/__init__.py b/docs/tutorials/lxmlgraph/step01/myapp/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/docs/tutorials/lxmlgraph/step01/myapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/docs/tutorials/lxmlgraph/step01/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step01/myapp/configure.zcml deleted file mode 100644 index e06023c24..000000000 --- a/docs/tutorials/lxmlgraph/step01/myapp/configure.zcml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/docs/tutorials/lxmlgraph/step01/myapp/models.py b/docs/tutorials/lxmlgraph/step01/myapp/models.py deleted file mode 100644 index 85d603d80..000000000 --- a/docs/tutorials/lxmlgraph/step01/myapp/models.py +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 13de3ae31..000000000 --- a/docs/tutorials/lxmlgraph/step01/myapp/views.py +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 1eac209dc..000000000 --- a/docs/tutorials/lxmlgraph/step01/run.py +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 1dc1ebcd3..000000000 --- a/docs/tutorials/lxmlgraph/step02.rst +++ /dev/null @@ -1,132 +0,0 @@ -================================================ -Step 2: Hello World as XML -================================================ - -We now have a project named ``lxmlgraph``. It contains a *package* -(also) named ``lxmlgraph``. - -In this step we will add an XML document to the *package* as our model -data. We will leverage the following :mod:`repoze.bfg` machinery: - - - Model data with interfaces that define "types" - - - ZCML configuration to provide type-specific views - -Our application will need to do these things: - - - Use :term:`lxml` Element classes to inject :mod:`repoze.bfg` - behavior into ``lxml`` nodes - - - That model class needs to implement the :mod:`repoze.bfg` - publishing contract - -All of the below filenames are relative to the ``lxmlgraph`` *package* -rather than the *project*. - -``samplemodel.xml`` ------------------------------------ - -We're going to add an XML document that will serve as a source for -model data named ``samplemodel.xml``. Put the content of this file in -your package: - -.. literalinclude:: step02/myapp/samplemodel.xml - :linenos: - :language: xml - -#. Line 2 provides the root of the model as an XML ```` node. - The element name doesn't have to be ````. It has a name of - ``site``. - -#. In lines 3-4, the ```` contains 2 top-level children: a and - b. These are provided as an element name ````. This, - also, is meaningless as far as :mod:`repoze.bfg` is concerned. - However, this is where you compose the information model you are - publishing. - -The only special constraint is that an XML node that wants to be -"found" by :mod:`repoze.bfg` in during traversal *must* have a -``name`` attribute. (The use of ``@name`` corresponds to ``__name__`` -in the :mod:`repoze.bfg` sense of :term:`location` ). 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 ``models.py`` ------------------------------- - -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. Replace the contents of the autogenerated -``models.py`` with the content we show below. - -.. literalinclude:: step02/myapp/models.py - :linenos: - -#. Line 4 imports :term:`lxml`. - -#. Line 9 creates the custom class we are going to use to extend - etree.ElementBase. The ``_ 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. - -#. :mod:`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 :mod:`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+ we do the :term:`lxml` magic to - get the custom Python class registered. We then load some XML and - return the top of the tree. - -Module ``views.py`` ------------------------------ - -Replace the autogenerated ``views.py`` code in the ``lxmlgraph`` -package with the following: - -.. literalinclude:: step02/myapp/views.py - :linenos: - -Here's what that file does: - -#. 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. - -We don't need to change the ``configure.zcml`` because the -autogenerated one is still correct for this configuration. It -includes: - -.. literalinclude:: step02/myapp/configure.zcml - :linenos: - :language: xml - -Browsing the Model ------------------------- - -We're done changing code. Start the application by executing ``paster -serve lxmlgraph.ini`` (the ``.ini`` file is in the project directory). -It will listen on port 5432. We can use these URLs to browse the -model graph and see results:: - - http://localhost:5432/a (Hello to document from a @ /a) - - http://localhost:5432/b (Hello to document from b @ /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. diff --git a/docs/tutorials/lxmlgraph/step02/myapp/__init__.py b/docs/tutorials/lxmlgraph/step02/myapp/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/docs/tutorials/lxmlgraph/step02/myapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/docs/tutorials/lxmlgraph/step02/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step02/myapp/configure.zcml deleted file mode 100644 index 2414ae6b2..000000000 --- a/docs/tutorials/lxmlgraph/step02/myapp/configure.zcml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/docs/tutorials/lxmlgraph/step02/myapp/models.py b/docs/tutorials/lxmlgraph/step02/myapp/models.py deleted file mode 100644 index 1d93ccdd0..000000000 --- a/docs/tutorials/lxmlgraph/step02/myapp/models.py +++ /dev/null @@ -1,42 +0,0 @@ -import os - -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 - here = os.path.join(os.path.dirname(__file__)) - samplemodel = os.path.join(here, 'samplemodel.xml') - xmlstring = open(samplemodel).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 deleted file mode 100644 index bbe474c63..000000000 --- a/docs/tutorials/lxmlgraph/step02/myapp/samplemodel.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/tutorials/lxmlgraph/step02/myapp/views.py b/docs/tutorials/lxmlgraph/step02/myapp/views.py deleted file mode 100644 index 75e390efb..000000000 --- a/docs/tutorials/lxmlgraph/step02/myapp/views.py +++ /dev/null @@ -1,8 +0,0 @@ -from webob import Response - -def my_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 deleted file mode 100644 index 1eac209dc..000000000 --- a/docs/tutorials/lxmlgraph/step02/run.py +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index ce88b78c8..000000000 --- a/docs/tutorials/lxmlgraph/step03.rst +++ /dev/null @@ -1,209 +0,0 @@ -================================================ -Step 3: 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. -Change your project's ``configure.zcml`` so that it looks like this: - -.. code-block:: xml - - - - - - - - - - -In other words, replace the default view function with -``.views.zpt_view``. This view stanza indicates that the *default -view* for a model that implements ``lxmlgraph.models.IMyModel`` should -be the ``lxmlgraph.views.zpt_view`` function. It is the *default* -view because this stanza does not have a ``name`` attribute. - -Additonally, add a template to your project's ``templates`` directory -named ``default.pt`` with this content: - -.. literalinclude:: step03/myapp/templates/default.pt - :linenos: - :language: xml - -Also add a function in ``views.py`` that looks like the following: - -.. code-block:: python - :linenos: - - from repoze.bfg.chameleon_zpt import render_template_to_response - def zpt_view(context, request): - return render_template_to_response('templates/default.pt', - name=context.__name__, - node=context) - -This function is relatively simple: - -#. Line 1 imports a :mod:`repoze.bfg` function that renders ZPT - templates to a response. :mod:`repoze.bfg` uses the - :term:`chameleon.zpt` 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 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 02, we returned a :term:`WebOb` Response object that we -created. ``render_template_to_response`` makes a Response itself. -The response's status is always ``200 OK`` and the content-type is -always ``text/html`` if you use this shortcut function. - -Here's what the ZPT looks like again: - -.. literalinclude:: step03/myapp/templates/default.pt - :linenos: - :language: xml - -Life is better with templating: - -#. Lines 1-2 make an ```` node with a namespace for TAL. - -#. Line 5 inserts the value of the ``name`` that we passed into - ``render_template_to_response``. - -#. Line 6 looks interesting. It uses the ``node`` that we passed in - via ``render_template_to_response``. Since :term:`chameleon.zpt` - 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, restarting the application and visiting -``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. - -If you visit ``http://localhost:5432/`` you will see:: - - My template is viewing item: site - - The node has a tag name of: site. - -We've successfully rendered a view that uses a template against a -model using the ZPT templating language. - - -XSLT Templates -============== - -So that's the ZPT way of rendering HTML for an XML document. We can -additonally use XSLT to do templating. How might XSLT look? - -``configure.zcml`` ----------------------------------- - -Make your ``configure.zcml`` look like so: - -.. 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 URLs invoked against our model can have ``/xsltview.html`` - appended to them, which will invoke our XSLT view. - -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. The view itself controls which templating -language is in use. - -``views.py`` --------------------------------- - -The ZCML says that our XSLT view (``xsltview.html`` on the URL) comes -from the ``lxmlgraph.views.xslt_view`` function, which you should add -to your ``views.py`` file: - -.. 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 :mod:`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. - - -``xsltview.xsl`` --------------------------------- - -Add a file named ``xsltview.xsl`` to your application's ``templates`` -directory and give it the following contents: - -.. literalinclude:: step03/myapp/templates/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 ```` 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 - ```` node (thanks to line 3). Thus, ```` 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 those changes in place, restart the application. Visiting to the -``http://localhost:5432/a/xsltview.html`` URL should show:: - - My template is viewing item: a - - The node has a name of: document. - -We've successfully run an XSL template against our model object. - -We've now seen how to use ZPT and XSL templates against model objects -created via an XML tree. - - diff --git a/docs/tutorials/lxmlgraph/step03/myapp/__init__.py b/docs/tutorials/lxmlgraph/step03/myapp/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/docs/tutorials/lxmlgraph/step03/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step03/myapp/configure.zcml deleted file mode 100644 index 43b766cfb..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/configure.zcml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/docs/tutorials/lxmlgraph/step03/myapp/models.py b/docs/tutorials/lxmlgraph/step03/myapp/models.py deleted file mode 100644 index 1d93ccdd0..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/models.py +++ /dev/null @@ -1,42 +0,0 @@ -import os - -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 - here = os.path.join(os.path.dirname(__file__)) - samplemodel = os.path.join(here, 'samplemodel.xml') - xmlstring = open(samplemodel).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 deleted file mode 100644 index bbe474c63..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/samplemodel.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/tutorials/lxmlgraph/step03/myapp/templates/default.pt b/docs/tutorials/lxmlgraph/step03/myapp/templates/default.pt deleted file mode 100644 index 3cc98ef92..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/templates/default.pt +++ /dev/null @@ -1,8 +0,0 @@ - - - -

My template is viewing item: ${name}

-

The node has a tag name of: ${node.tag}.

- - diff --git a/docs/tutorials/lxmlgraph/step03/myapp/templates/xsltview.xsl b/docs/tutorials/lxmlgraph/step03/myapp/templates/xsltview.xsl deleted file mode 100644 index 4d759b15b..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/templates/xsltview.xsl +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - -

My template is viewing item:

-

The node has a name of: .

- - -
-
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/views.py b/docs/tutorials/lxmlgraph/step03/myapp/views.py deleted file mode 100644 index e18c04415..000000000 --- a/docs/tutorials/lxmlgraph/step03/myapp/views.py +++ /dev/null @@ -1,10 +0,0 @@ -from repoze.bfg.chameleon_zpt import render_template_to_response -from repoze.bfg.xslt import render_transform_to_response - -def zpt_view(context, request): - return render_template_to_response("templates/default.pt", - name=context.__name__, - node=context) - -def xslt_view(context, request): - return render_transform_to_response('templates/xsltview.xsl', context) diff --git a/docs/tutorials/lxmlgraph/step04.rst b/docs/tutorials/lxmlgraph/step04.rst deleted file mode 100644 index f96a0712f..000000000 --- a/docs/tutorials/lxmlgraph/step04.rst +++ /dev/null @@ -1,250 +0,0 @@ -================================================ -Step 4: 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 -``configure.zcml`` can now be madesimpler. Change your -``configure.zcml`` to lok like so: - -.. literalinclude:: step04/myapp/configure.zcml - :linenos: - :language: xml - -We'll also remove the ``zpt_view`` function from ``views.py``, as -we'll see in a moment. - -Design Change: Trees and Context IDs -======================================== - -In :mod:`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 ``lxmlgraph``, that "context" object is a -node in the XML document, found by traversing node children. - -For the XSLT in Step 3, 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. - -``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 ```` now gets a ````. - -#. 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>``. - -#. Lines 13-32 show a nested XML hierarchy. A ``<folder>`` gets an - ``@name``, making the folder itself published as a possible URL. - It also gets an ``@xml:id``, allowing it to be uniquely addressed - within the entire document. The ``<folder>`` gets a ``<title>`` - child node, but then gets two more ``__getitem__``-enabled child - nodes of type ``<document>``. ("Enabled", as in, has a ``@name`` - attribute.) - -Thus the major changes: - -- The root contains ``<folder>`` nodes which contain ``<document>`` - nodes. - -- The root could contain documents too, and the folder could contain - sub-folders. There's nothing special about the arrangement. - -- The only thing special is the presence of ``@name`` attributes, - which allow ``__getitem__`` (via XPath) to traverse to that child. - -- As we'll see in a second, the ``@xml:id`` allows jumping through the - hierarchy directly to a node. - -- Finally, for UI-framework-ish purposes, having a child ``<title>`` - allows us to show in a browser what we're looking at. - -The ``models.py`` hasn't changed, so let's move to the small changes -in the ``views.py``. - -``views.py`` -============ - -As noted above, we removed the ZPT views, and thus ``views.py`` is -shorter, and focused only on a function that provides an XSLT -template. Although there aren't many lines in that function, there -are some concepts to explain: - -.. literalinclude:: step04/myapp/views.py - :linenos: - -#. We are going to be using a feature from XML called ``xml:id``, - which we explained above in the ``samplemodel.xml`` section. In - lines 4 and 5, we make constants that point to the namespace - needed. - -#. As the comment says, line 9 grabs the "root" node of the site. - Inside this function, we are traversing, node-by-node, through a - hierarchy. Thus our context node is an lxml Element object, which - supports a method to grab the tree (and thus root) in which the - context sits. - -#. Next, to support the XSLT approach we show next, we want to let the - XSLT know the ``xml:id`` that we are sitting on. Since we are - passing it in as an XSLT paramter, we need some special handling: - - - The *value* of the parameter is the ``@xml:id`` of the node we are - sitting on. - - - Per lxml's needs, that value needs to be quoted. Thus, the value - of ``contextid`` will be something like ``"'n1'"`` - -#. Finally, on line 12, we call the XSLT processor, passing in keyword - arguments that become XSLT parameters. Unlike before, the node we - pass in is the top of the tree, rather than the current (context) - node. - -In summary, we render the XSLT by handing it the root node of the -tree, plus a flag that says which node we are currently sitting on. - -``xsltview.xsl`` -================= - -The XSLT template gets the most substantive changes, as we both have -to support this root-and-contextid idea, as well as some features that -put this to use: - -- Having a common look-and-feel across all pages, along with "rules" - that handle each content type - -- Show the context's ``<title>`` in the same place - -- Show some general information about the node - -The following XSLT accomplishes these features: - -.. literalinclude:: step04/myapp/xsltview.xsl - :linenos: - :language: xml - -#. Line 3 accepts the ``@xml:id`` passed in as a parameter of the XSLT - transformation. This can differ between requests, as different - nodes are traversed. - -#. Line 4 jumps directly to the tree node that has that ``@xml:id`` by - using XPath's ``id()`` function. This is a high-speed lookup, - similar to ``document.getElementById()`` in JavaScript. We then - assign the node to a global XSLT variable, to avoid paying that - price again. - -#. Line 5 gets into XSLT's rule-oriented mumbo-jumbo. This template - rule says: "Match on the root of the tree that was passed in, then - do some work." Think of this as a CSS rule that matches on ``body - {}``. - -#. Lines 7-11 output HTML for the document and ``<head>``. - -#. Line 9 (and line 14) inserts the value of the context node's - ``<title>`` using ``<xsl:value-of>``. - -#. Line 16 does some XSLT mumbo jumbo. It says "Find a rule that - handles the context node, which might be a ``<folder>`` or might be - a ``<document>``." Control is then passed to an ``<xsl:template>`` - that meets the conditions. Once that rule is finished, control - returns to line 17. - -#. Lines 17-42 then format some basic information about the context - node. The HTML generated for this, however, appears *after* the - type-specific handler in the resulting HTML. - -#. Line 46 is an ``<xsl:template>`` rule that handles ``<folder>`` - nodes. It only gets control when something else hands control to it. - - In this case, the rule makes a paragraph then lists the contents of - the folder. - -#. Line 50 checks to see if the folder contains any "publishable" - content. We wouldn't want a heading to appear saying "Folder - Contents" with empty space under it. - -#. Line 53 then iterates over all the child nodes which have an - ``@xml:id``. - -#. Lines 55-57 make an ``<li>`` with an ``<a>`` for each item in the - folder. Inside the ``<xsl:for-each``, the "current" node is the - current item in the iteration. The ``@href`` uses what XSLT calls - "attribute value template" (curly braces) to let the XSLT processor - operate inside an attribute. - -#. Line 63 handles ``<document>`` nodes when handed control. - -#. Line 67 recursively copies the nodes in the ``<document>`` content. - -To recap, this XSLT handles any node passed in, and generates a UI -that can handle the global styling, the navigational elements, and the -content for the current traversal hop. - -Conclusion -===================== - -Though not very much code, this is the basis for a useful amount of -features. A hierarchical website with templating that can handle -global styling and navigation, as well as type-driven templating, all -at reasonable (albeit in-memory) performance. - - diff --git a/docs/tutorials/lxmlgraph/step04/myapp/__init__.py b/docs/tutorials/lxmlgraph/step04/myapp/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/docs/tutorials/lxmlgraph/step04/myapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/docs/tutorials/lxmlgraph/step04/myapp/configure.zcml b/docs/tutorials/lxmlgraph/step04/myapp/configure.zcml deleted file mode 100644 index c2d59effb..000000000 --- a/docs/tutorials/lxmlgraph/step04/myapp/configure.zcml +++ /dev/null @@ -1,10 +0,0 @@ -<configure xmlns="http://namespaces.repoze.org/bfg"> - - <include package="repoze.bfg.includes" /> - - <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 deleted file mode 100644 index a0d64ef59..000000000 --- a/docs/tutorials/lxmlgraph/step04/myapp/models.py +++ /dev/null @@ -1,45 +0,0 @@ -import os - -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 - here = os.path.join(os.path.dirname(__file__)) - samplemodel = os.path.join(here, 'samplemodel.xml') - xmlstring = open(samplemodel).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 deleted file mode 100644 index 36ab335d2..000000000 --- a/docs/tutorials/lxmlgraph/step04/myapp/samplemodel.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<site> - <title>My XMLGRAPH Website - - Site Home Page - -
-

Welcome to the site. We have lots to say.

-

Or, maybe not.

-
- -
- - Folder One - - doc1 in folder1 - -
-

I am in an HTML div so I can do LOTS of - formatting.

-
- -
- - doc2 in folder1 - -
-

Keep this on one line.

-
- -
-
- - The Second Folder - - doc1 in folder2 - -
-

This is a special folder. It's folder 2!

-
- -
-
-
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/views.py b/docs/tutorials/lxmlgraph/step04/myapp/views.py deleted file mode 100644 index f079cea8c..000000000 --- a/docs/tutorials/lxmlgraph/step04/myapp/views.py +++ /dev/null @@ -1,13 +0,0 @@ -from repoze.bfg.xslt 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 = context.getroottree().getroot() - # Jot down which node we're sitting on as the - 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 deleted file mode 100644 index 2406987d0..000000000 --- a/docs/tutorials/lxmlgraph/step04/myapp/xsltview.xsl +++ /dev/null @@ -1,69 +0,0 @@ - - - n1 - - - - - - <xsl:value-of select="$contextnode/title"/> - - - -

- -

- - - - - - - - - - - - - - - - -
Type@xml:id@nameParent TypeParent @name
- - - - - - - - - -
- - -
- -

- Folders are special, they contain things. -

- -

Folder Contents

- -
-
- -

- Documents contain text. -

- -
-
diff --git a/docs/tutorials/lxmlgraph/step04/run.py b/docs/tutorials/lxmlgraph/step04/run.py deleted file mode 100644 index 1eac209dc..000000000 --- a/docs/tutorials/lxmlgraph/step04/run.py +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index c3ba037d2..000000000 --- a/docs/tutorials/lxmlgraph/step05.rst +++ /dev/null @@ -1,11 +0,0 @@ -================================================ -Step 5: Advanced Templating -================================================ - -- multiple "views" wired to the same function and template - -- breadcrumbs, sidebars - -- support - -- type-based support for , , , -- cgit v1.2.3