summaryrefslogtreecommitdiff
path: root/docs/tutorials
diff options
context:
space:
mode:
authorPaul Everitt <paul@agendaless.com>2008-07-25 15:24:07 +0000
committerPaul Everitt <paul@agendaless.com>2008-07-25 15:24:07 +0000
commit992ef769cbbcb7a47b53d299e551b70d80e993b9 (patch)
tree26cc57f79b8dfeec6c7224d99320d0468677f74a /docs/tutorials
parent3bb4cdcbd5c0a5ddb994d1f12f1f38066fc49cba (diff)
downloadpyramid-992ef769cbbcb7a47b53d299e551b70d80e993b9.tar.gz
pyramid-992ef769cbbcb7a47b53d299e551b70d80e993b9.tar.bz2
pyramid-992ef769cbbcb7a47b53d299e551b70d80e993b9.zip
Step 4 written up. I also made the small changes to the models.py that Chris had in Step 03.
Diffstat (limited to 'docs/tutorials')
-rw-r--r--docs/tutorials/lxmlgraph/index.rst1
-rw-r--r--docs/tutorials/lxmlgraph/step04.rst159
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/models.py6
3 files changed, 162 insertions, 4 deletions
diff --git a/docs/tutorials/lxmlgraph/index.rst b/docs/tutorials/lxmlgraph/index.rst
index 385968d42..a4d0016cc 100644
--- a/docs/tutorials/lxmlgraph/index.rst
+++ b/docs/tutorials/lxmlgraph/index.rst
@@ -14,3 +14,4 @@ describes publishing an XML document as a hierarchical website.
step01
step02
step03
+ step04
diff --git a/docs/tutorials/lxmlgraph/step04.rst b/docs/tutorials/lxmlgraph/step04.rst
index 966406319..35e0b9aa9 100644
--- a/docs/tutorials/lxmlgraph/step04.rst
+++ b/docs/tutorials/lxmlgraph/step04.rst
@@ -76,8 +76,8 @@ That's the big picture. Each of these changes will be explained in
detail below.
-File ``samplemodel.xml``
-===================================
+``samplemodel.xml``
+=====================
The XML document with the information for our website has quite a
number of changes:
@@ -95,4 +95,157 @@ number of changes:
``<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/models.py b/docs/tutorials/lxmlgraph/step04/myapp/models.py
index 3c03de1a9..a0d64ef59 100644
--- a/docs/tutorials/lxmlgraph/step04/myapp/models.py
+++ b/docs/tutorials/lxmlgraph/step04/myapp/models.py
@@ -1,3 +1,5 @@
+import os
+
from zope.interface import implements
from zope.interface import Attribute
from zope.interface import Interface
@@ -32,7 +34,9 @@ def get_root(environ):
parser.set_element_class_lookup(parser_lookup)
# Now load the XML file
- xmlstring = open("myapp/samplemodel.xml").read()
+ 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