When we last visited the little todo application for Zope X3, we had made some very basic views for the Zope Management Interface (ZMI). At this point, no HTML had been written. No other view/controller code had been written. And we didn't have much of a todo application, but we did have editable Todo objects being stored in a Todo List in the object database. But what it needs now is its own look. To do this, we need to make a skin.
Zope 3 has a concept of View Components, which could be any sort of view on an object - HTML, FTP, XMLRPC, or even native widgets for an operating system. Views and controllers specific to an HTML interface are typically thought of as "browser" views. There's a browser
ZCML namespace, where most web views are configured, and it seems to be a common practice to group code for web browsers in a python package or module called 'browser' to connote the type of view that it is. XMLRPC views are typically bundled in a module or package called xmlrpc
, etc. There is no enforcement of these names.
So what we're interested in now is making a custom look for a web browser. Zope 3's browser UI system uses a concept of skins, which are made up of layers. Browser views are assigned to layers. When a view needs to be looked up, the Zope 3 framework goes through the layers defined for the current skin and looks up the requested view until it finds it. This is similar, I suppose, to how a CSS class is looked up. The HTML code may say to use class 'highlight', and the style sheets are gone through in order until 'highlight' is found. Replacement style sheets may be used (such as print or presentation) that display the 'highlight' class differently.
To make the todolist's skin, I made a new directory in the 'todo' package named 'browser', and made it into a Python package by adding an empty __init__.py
. I then made a sub-package of 'browser' called 'skin', and added an empty __init__.py
there as well.
Once there, I made a new file called template.pt
. Using The 'Developing New Skins' chapter of the Zope 3 Developers Book for guidance, I created the following template (Note that this is done using Zope Page Templates):
<metal:block define-macro="page"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title metal:define-slot="title">Todo List for Zope X3</title> <style type="text/css" media="all" tal:content="string: @import url(${context/++resource++todo.css});;"> @import url(todo.css); </style> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> </head> <body> <div id="todo_header">Todo List for Zope 3</div> <div id="workspace"> <div metal:define-slot="message" id="message"></div> <div id="content"> <metal:block define-slot="body"> This is the content. </metal:block> </div> </div> <div id="footer"> <div id="actions"> <metal:block define-slot="actions" /> </div> </div> </body> </html> </metal:block>
I followed that with a CSS file called todo.css
, with the following content:
body { font-family: Verdana, Arial, Helvetica, sans-serif; background: white; color: black; margin: 0; padding: 0px; } h1, h2, h3, h4, h5, h6 { font-weight: bold; color: black; } #todo_header { background: black; color: white; padding: 1em; font-size: 150%; } #footer { background: black; color: white; padding: 1em; font-size: 150%; }
Now for the important part - wiring these two files into Zope so that we can use them in the todo application. To do this, we need to define a couple of things:
todo/browser/skin
called configure.zcml
with the following contents:
<configure xmlns="http://namespaces.zope.org/browser"> <layer name="todo"/> <skin name="todo" layers="todo rotterdam default"/> <resource name="todo.css" file="todo.css" layer="todo"/> <page for="*" name="skin_macros" permission="zope.View" layer="todo" template="template.pt" /> </configure>As you can see, it pretty much maps to the list of items above. A layer is made, named 'todo', and a skin of the same name is made with the layers 'todo', 'rotterdam', and 'default'. When the 'todo' skin is used, views will be looked up in that order, starting from the beginning of the chain. After that, we define the CSS file as a resource to be used in the 'todo' layer. And then we define a page for the template, also in the 'todo' layer. Note that there's a
for="*"
in the page declaration. That means the page is a browser view that can be used for any object in the system. We want this because this page defines the basic ZPT METAL macros that all other pages/templates will use.
Finally, in todo/configure.zcml
, add the following line at the bottom to import the skin configuration:
<include package=".browser.skin"/>And with that, we have a custom skin defined - a simple template to use, and a named layer to start writing custom views for. The next post will talk about implementing some of those browser views.