5.04.2007

Using z3c pagelet/template/layout paradigms

(This post is liable to change in the near future as I fix mistakes)
I began work on trying to use Stephan Richter's forthcoming form library, z3c.form (not yet public) with my existing yet unstable zobby code. z3c.form is built on the idea that you are already using other z3c components for your web application - which apparently is now the standard among Zope experts for advanced application development. This was difficult for me as I have never used any z3c components before. The following are steps I had to take to move what I already had in zobby to be ready for z3c.form stuff.


  1. Make sure you have your own skin and layer.

    If you use Zope's default skin/layer then you will not have access to various templates and resources used by z3c.form, as they are registered on their own layer (z3c.form.interfaces.IFormLayer). Your custom layer must inherit from z3c.form.interfaces.IFormLayer. Specifically, widgets are looked up by adapting a view class, a request and some context. But the request must implement IFormLayer, which means you must be browsing to your pages through your custom skin that extends IFormLayer.

    For Zobby I have

    from z3c.form.interfaces import IFormLayer
    from z3c.formui.interfaces import IDivFormLayer
    from z3c.layer.pagelet import IPageletBrowserLayer

    class IZobbyBrowserLayer(IDivFormLayer, IFormLayer, IPageletBrowserLayer):
    """Like IMinimalBrowserLayer including widget layer."""

    This is also inheriting from some other layers. IPageletBrowserLayer provides some simple error handling pages for your skin and IDivFormLayer provides templates for doing form layouts with divs (versus tables or something).

    Then of course you have to register the layer:

    <interface
    interface="zobby.layer.IZobbyBrowserLayer"
    type="zope.publisher.interfaces.browser.IBrowserSkinType"
    />

    but there is nothing special about that.

    Now of course you have to go through your application and make sure all the resources and pages are registered on this layer and not the default zope layer (which will no longer be accessible through the skin)

    Just to be complete, here is the code for the skin

    import zobby.layer

    class IZobbyBrowserSkin(zobby.layer.IZobbyBrowserLayer):
    """The ``Zobby`` browser skin."""

    and the registration

    <zope:interface
    interface="zobby.skin.IZobbyBrowserSkin"
    type="zope.publisher.interfaces.browser.IBrowserSkinType"
    name="ZobbySkin"
    />


  2. Register a layout template for your skin.

    In standard zope, skins always have macros that define where things are supposed to go on the page. Then each of templates for various views uses the macro for things like headers and footers and general layout. However, with z3c.form you will want to use z3c.template for this functionality. So rather than having this complicated thing with standard macros and overriding certain other macros, you just have

    <z3c:layout
    for="*"
    layer="zobby.layer.IZobbyBrowserLayer"
    template="template.pt"
    />

    which somehow magically does everything for you. I'm sure it wouldn't be magic if I just took the time to read the README.txt files in z3c.template. Which is here if you have the time: http://svn.zope.org/z3c.template/trunk/src/z3c/template/README.txt?rev=72085&view=markup.

    The one thing that differs from the older standard macro definitions, is that instead of having a big fat metal:define-macro attribute on some tag near the top of your macro, you just include a tag somewhere in the middle of your document (where you want the content to go) like so:
    <tal:block replace="structure provider:pagelet" />

    This references a content provider called pagelet, which is actually registered somewhere in z3c.pagelet. This will probably make a lot of sense if you read the README.txt file in z3c.pagelet which is here: http://svn.zope.org/z3c.pagelet/trunk/src/z3c/pagelet/README.txt?rev=72087&view=auto.

    This of course suggests that you can no longer use your typical page templates from standard zope as the pagelet content provider won't pick them up (I think). Which brings us to the next step.

  3. Change all your page registrations to z3c:pagelet registrations, and change the templates accordingly.

    z3c.pagelet and z3c.template serve the purpose of splitting up the registration for templates and views. Where as in standard zope it is the practice to register a template for a view with the page directive. However, this is not entirely pluggable so some folks came up with z3c.pagelet and z3c.template. From what I can tell, a pagelet is just like a regular page, except it does not have a template associated with it, and must have a view class set for it. The registration for a pagelet looks like this:

    <z3c:pagelet
    name="index.html"
    for="zobby.interfaces.IZobbyApplication"
    class=".browser.ZobbyApplicationDisplayForm"
    permission="zope.Public"
    layer="zobby.layer.IZobbyBrowserLayer"
    />

    Then you have to register a template separately, that gets registered for the view class - not the content component (which the pagelet is registered for). This registration looks like this:

    <z3c:template
    template="client.pt"
    for=".browser.ZobbyApplicationDisplayForm"
    layer="zobby.layer.IZobbyBrowserLayer"
    />

    This seems a bit superfluous and you might ask the question, why not just put template=whatever in the pagelet registration since the template registration clearly has the same layer as the pagelet and is registered for the same class used by the pagelet. Well, yes this is true, but now you can register multiple templates for different views, that each live on a different layer/skin.

    Now I think I can explain how the provider:pagelet thing from the previous step works. It actually figure out what to display by doing an multi adapter lookup, which tries to adapt (view, request, context) to something providing ITemplate. The z3c:template directive essentially registers this adapter. So that is pretty cool.

    So what does client.pt look like? Well rather than having a big fat metal:use-macro at the top of the page template, it is just a plain old template with macro funny business. (If you want to use macro funny business, then you should use z3c.macro instead.) In fact, client.pt could just be

    Hi! I am the zobby client

    and when the page gets rendered, Hi! I am the zobby client will appear surrounded by everything defined with the z3c:layout directive (i.e. whatever is in template.pt).

  4. Pay Attention to Layers

    When I was first trying this stuff out, I forgot to register my first the template that went with my first pagelet in my custom layer. Thus when it did the adapter lookup for ITemplate, it got back template.pt, which of course makes it do another adapter lookup for ITemplate (trying to find the pagelet's template rather than the layout template) and in a tenth of a second you have a difficult to debug maximum recursion depth exceeded error in your face.



And that is the end of my brief introduction to using z3c stuff.

1 comment:

roy said...

thanks, Paul - nice work describing the issues.