.. 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