I had a template with a handful of tables that basically just displayed the same tabular data, just different data sets depending on the table. In case your curious, my data is segregated by workflow state.
I’ve had this problem before but in the past I had a skin layer registered too (thanks to ArchGenXML).
Defining macros is pretty easy, the Zope Book does a good job of explaining how to define them. It’s actually easier than it looks. If you don’t have a need for slots, it basically just comes down to slapping a
metal:define-macro attribute into any arbitrary block of code.
Here’s a quick example:
<html> <body> <p metal:define-macro="title"> <span tal:replace="view/context/Title" /> </p> </body> </html>
I’ve left out most of the usual ZPT window-dressings (like a i18n domain declaration), but I’ll assume you already know how to do that :).
This is also of course, a pretty stupid example. But it does illustrate an important concept in METAL: when your macro is being expanded, it’s as if you wrote the code right where the
metal:use-macro attribute is used. So any variables that are available to the template that’s calling the macro are available here.
You have to be careful of this though, because it does limit how much your macro can be re-used.
In the past, because of the skin layer, I was able to drop a ZPT file into my skins directory, and call them like this (assume I put the last macro into a file called “macros.pt” in the skins directory along side this template):
<html> <body> <p metal:use-macro="here/macros/macros/title" /> </body> </html>
I saw a lot of examples where the name of the file itself was used (e.g.
here/macros.pt/macros/title), but I don’t recall doing it that way.
As far as I can tell, this doesn’t work with templates on the filesystem that aren’t registered in a skin layer. So when you aren’t using a skins folder, because you’ve had the forethought to put that sort of stuff in a policy or theme product, this sort of macro expansion doesn’t seem to work.
I found a solution to this by accident. I don’t have my Zope 3 book (the essential Web Component Development with Zope 3) with me today, and was poking around Google aimlessly to solve this problem. I stumbled upon a page from a hereto unidentified Zope 3 course that showed me what I needed to do.
Zope 3 registers page templates, much like Zope 2/Plone does with the skins concept. But in Zope 3 you do it with ZCML as opposed to the CMF/GenericSetup glue that you need to register a Plone skin layer.
So in a handy
configure.zcml (I put mine in the
browser directory of my product), you first need to register the template containing your macros as a browser page:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser"> ... <browser:page for="*" name="my.product.macros" template="macros.pt" permission="zope.Public" /> </configure>
Note that I since my macro file (
macros.pt) is in the same directory as my
configure.zcml, I didn’t have to specify any sort of relative path information in the
Since the macro template is being registered like any other browser view, you can specify a particular interface that is only allowed to use it, using the
for attribute. Likewise, you can set a specific permission that will restrict access to the macros using the
permission attribute. I haven’t tested either attribute with a browser page that’s being used for macros, so I don’t know what the expected behavior is.
OK, so with the ZCML tag above added to
configure.zcml and a quick restart of the server (I had trouble with plone.reload in this case, FYI), any macros in
macros.pt are available, to any view on any content type that feels inclined to use them.
To access the macros within a browser view/page (or viewlet, etc), the syntax is similar to what it was in the “skins layer” scenario mentioned earlier, but it’s very Zope 3:
<html> <body> <p metal:use-macro="view/context/@@my.product.macros/title" /> </body> </html>
You access the macro by getting the browser page defined in the ZCML above applied to the current context. That’s it.
So to summarize: you can use a browser page like a sort of template/code snippet resource. And as a bonus, said resource can contain METAL macros. METAL macros are a nice way to clean up your code, enforce DRY, and reduce bugs.
I’ve also come to realize that the same
@@viewname syntax works to apply a view to an object inside of a page template. This is a natural predecessor to the macro stuff I was just talking about, but I didn’t realize it was possible until now. Whoo-hoo!