.. index:: z3c.layout, z3c, pagelet, z3c.template, layout, pagelet, template
Skinning with z3c.layout, z3c.pagelet, and z3c.template
*******************************************************
The next topic we will look at is creating a nice skin for our
application. In "regular" Zope 3, this is done using macros, which
are then used in every page template. Typically these macros appear
under a ``standard_macros`` view. With z3c.* components however, page
templates become completely unaware of any macro they get placed in.
Dependencies
============
First things first, we need to include configuration for the new
dependencies we will have with skinning. Open of
``src/zcontact/configure.zcml`` and add ```` towards the top with the
rest of the meta includes. This will give us the new ``z3c:pagelet``
zcml directive. Farther down you can then add two more includes:
.. code-block:: xml
Creating a Layout
=================
Now to the interesting part. We can create what is called a layout,
which is similar to a macro but much simpler. Rather than having a
complicated macro which each page must use (i.e. use-macro and
fill-slot), we can just create a plain page template with one
particular tag set asside for the content of the actual pages. We can
use different layouts for different skins, and even for different
object interfaces. Here we go! Open up the file
``src/zcontact/layout.pt`` and make it look something like this (feel
free to get creative):
.. code-block:: xml
ZContact
ZContact
Page Content
ZContact Tutorial Application
Notice in particular the line towards the bottom that says:
.. code-block:: xml
Page Content
As you can guess, this is where the magic happens. The
``provider:pagelet`` part tells zope to look up a *content provider*
called ``pagelet``, which via z3c.pagelet ends up being the content
for the page we want to look at, whether it is a form, or another page
template. So that is pretty cool, but now we need to register this
layout for our skin in zcml. Open up ``zcontact/skin.zcml`` and add
the following directive, which is pretty self explanatory:
.. code-block:: xml
Also, don't forget to add the z3c xml namespace to the configure tag
with ``xmlns:z3c="http://namespaces.zope.org/z3c"``.
Using Layouts with Pagelets (and Templates)
===========================================
Now that our layout is made, we have to do a slight conversion of our
existing pages so that they will actually use the new layout
machinery. Step one is to open up ``zcontact/browser/configure.zcml``
and add the z3c xml namespace to the configure tag:
``xmlns:z3c="http://namespaces.zope.org/z3c"``. Now, wherever you use
a page tag, replace the page tag with ``z3c:pagelet`` without changing
anything else. Now we are 90% there. The one caveat with pagelets
that makes them really different from regular pages is that *they have
nothing to do with page templates*. The pagelet directive does *not*
take a template attribute and *requires* a class attribute. For most
of our pages, which just use auto-generated form content, this isn't a
problem. For the front page though, we want our own template.
Start by replacing the last directive for the front page with the
following:
.. code-block:: xml
Notice that I got rid of the template attribute and added a class
attribute for our yet to be written ``FrontPage`` class. Let's go
write that class so we can finally be done with this skin conversion.
Open up ``zcontact/browser/contact.py`` and down at the bottom add the
following:
.. code-block:: python
class FrontPage(BrowserPagelet):
"""Pagelet for the front page."""
along with ``from z3c.pagelet.browser import BrowserPagelet`` at the
top. At this point you should be thinking, "what happened to our page
template and this class looks useless." Despite not having any real
substance, this class does quite a bit because it inherits from
BrowserPagelet. The BrowserPagelet class provides a special __call__
method which uses an adapter lookup to find the... template. You
might say, "An adapter lookup to find a template? Does that mean we
can register different templates for the same page(let)?" The answer
is a resounding yes. We can either specify the template for the
pagelet in the pagelet class (using ``template =
ViewPageTemplateFile('frontpage.pt')``), or we can register a template
to use in zcml for a particular skin. For demonstrative purposes,
we'll do the latter. Go back to ``zcontact/browser/configure.zcml``
and add the following directive right under the front page pagelet:
.. code-block:: xml
Using Layouts with Forms
========================
There is one more caveat left before our application can be all
sparkly. The z3c.form package was designed in such a way to be usable
without all this new layout and pagelet business (a good thing).
Fortunately, the z3c.formui package provides support for layouts with
its own form module. To convert our existing forms to use the layout
mechanism, we have to switch our imports so that instead of ``from
z3c.form import form``, we use ``from z3c.formui import form``. The
imports at the top of ``zcontact/browser/contact.py`` should now look
this this:
.. code-block:: python
from z3c.form import field, button
from z3c.form.interfaces import DISPLAY_MODE
from z3c.formui import form
from z3c.pagelet.browser import BrowserPagelet
from zope.traversing.browser.absoluteurl import absoluteURL
from zope.traversing.api import getParent, getName
from zcontact import interfaces
from zcontact.contact import Contact
Finally, we just need to change our zcml page declarations to be
pagelet declarations. In other words, in ``browser/configure.zcml``
you would change
.. code-block:: xml
to become
.. code-block:: xml
That concludes our skin transformation, so you should now be able to
restart the web server and jump into a beautiful skin (unless you
copied my CSS unwaveringly). Here are some screen shots of our new
pages:
.. image:: images/skinFrontPageScreenShot.png
.. image:: images/skinAddFormScreenShot.png