You are on page 1of 3

How the system works

====================

There are lots of design decisions that have been made to make this
project possible. Some of them are crucial to make the system work.

I will try to describe here how some parts of it work, like the
publisher, the traversal adapters and the xslt theme/pipeline.

Publisher
---------

We have a custom `server type`, which has a custom


PublicationRequestFactory. The `server type` is defined in
``snap/server.py``, and the `request factory` is in
``snap/publication.py``.

Why we did this?


++++++++++++++++

Because we wanted to have a ZODB-less Zope 3 application, but we are


using Zope 3.1 so that's the only way to do it. In Zope 3.2 will be
possible to write this as a WSGI app and apparently there will be also
other ways to providing a custom PublicationRequestFactory, though I'm
not that sure on the latter.

What it does?
+++++++++++++

The request factory basically returns the ``root`` or ``application``


object equivalent to the ``application`` object in Zope 2. This is
done by (you guessed?) the ``getApplication`` method in the request
factory.

For LSTech we just return a dummy singleton object which has a marker
interface, and from there on Zope 3 uses traversal adapters to find
the next objects in the traversal path.

Another thing it does is to run the result of ``callObject``, which


(as you might have guessed) calls the published object pretty much the
same way it's done in Zope 2, by using ``mapply``. Note that in Zope 3
though, the published object is (almost?) always a ``view``.

So, all things keeping sane, ``callObject`` will receive a view, call
it and get some stream/string of content. If it smells like HTML or
XML, we run it through the pipeline, otherwise it's returned
unmodified.

The rest of the code in there is pretty much copied over from
``BrowserPublicationRequestFactory`` (?) just because we did want some
features from there but not all, and although Zope 3 uses adapters
everywhere, the publication part is not one of them.

Alright, if you got this far lets jump now to the next part of the
puzzle which is traversing objects.

Traversal
---------
For traversal we pretty much rely on the standard behaviour of Zope 3,
which is to lookup a ``ITraversable`` adapter, and sometimes a
``IPublishTraverse`` view depending on the way traversal happens. Yes,
it's more complicated than it should and we should kick Jim for that.

The theory is that if a traversal is being done by the publisher, then


there is a request, and a IPublishTraverse view is looked up, which is
just a multiadapter between the object and request. If you don't know
what that means don't be scared. It's non-trivial, really.

To simplify the matters a little, we registered a IPublishedTraverse


for all our objects which tries a ITraversable adapter first and then
tries to lookup a view, and if all fails, tries a
``resource``. Resources are usually CSS and images used in the skin.

Application Traversable
+++++++++++++++++++++++

From the application object, the first thing we do is to see if the


name being looked for is one of the top-level 'Topics' on the site.

If that's the case, we do a XPath query on the ``map.xml`` file which


is generated by Entransit to see if a Topic or Location with that name
exists.

If a Topic or Location is found, a corresponding object is created and


some metadata is stitched on it by calling the ``metadata_setup``
function. More on that later.

Otherwise we delegate down to the Directory Traversable adapter.

Directory Traversable
+++++++++++++++++++++

This little beast now checks if the name being traversed is not a
attribute or item on the adapted object. If it is, then it returns
immediately.

If a attribute/item is not found, then a lookup is done on the


filesystem. The path on the filesystem is computed from a prefix,
which is the snap/data directory path + the relative url being
traversed from the application root.

If the path exists and it's a directory, then a appropriate object is


returned.

Otherwise we first look for the path and return a Content object, or
look for path + '.data' and return a Metadata object.

If all fails, a NotFoundError is raised.

Once the full path has been traversed, the last item in the traversal
path will go through the IPublishTraverse and either a view or a
resource.

Metadata Setup
--------------
There's a function called `metadata_setup` that populates the objects
created by traversal. This function first reads the file created by
Entransit using xmlrpclib.loads then does a query on the ``cmf_meta``
table and stitches all those values into the object.

The last step then is to declare a marker interface on the object


based on the `portal_type` attribute that was populated from the
``cmf_meta`` table.

XSLT Pipeline
-------------

There is some magic going out there that probably Cameron understands
better than I. The basic idea is that you have a 'template' which is a
plain XML/XHTML file with the look and feel of the website.

On top of that a XSLT is applied which generates another XSLT (think


Escher), which is then applied to what Zope returns from
``callObject``.

The 'template' file is in ``snap/browser/theme/index.xml``. It does


define a couple ``<div id="something">``. Then comes
``snap/browser/theme/rules.xsl`` which defines how the content from
Zope will be stitched into the template. For example::

<rule themeid="discussion-body"
contentxpath="/html:html/html:body//html:div[@id='discussion']/*"/>

The rule above basically says that whatever is inside a ``<div


id="discussion">`` tag in the content returned by Zope, will be
stitched into a ``<div id="discussion-body">`` tag in the
``index.xml`` template.

If you think that's complex, don't. It's actually pretty trivial but
your brain might not be wired to think that way.