I Used Macros In My Browser Views And Saved A Bunch of Money on My Car Insurance!

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

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.

Side Note:
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!

Advertisements
This entry was posted in Uncategorized and tagged , , , , , . Bookmark the permalink.

10 Responses to I Used Macros In My Browser Views And Saved A Bunch of Money on My Car Insurance!

  1. Chris Rossi says:

    This is a nice, succinct article. You explain things well.

    This is just showing my bias towards doing things in Python code versus configuration, but another way you could have done it is load the template with your macros directly in your view:

    class MyView(BrowserView):


    blah blah

    @property
    @memoize
    def macros(self):
    return ViewPageTemplateFile(“macros.pt”)

    Then in your page template you just call:

    This particular example is untested, so mileage may vary.

    • jjmojojjmojo says:

      Good point! The negative is that you can’t use the macros in another view though… or you’d have to instantiate the view with the macros property to do so…

      looks like WP chopped off your template code…

      Is this (more or less) what you wanted to put after “you just call:”:

      <span metal:use-macro=”view/macros/blah” />

      Nobody’s ever called me succinct before… thanks!!

  2. Chris Rossi says:

    Yes, there are advantages to actually registering it. In my case, if I needed it in more than one view I’d probably just replicate the macros method. Or I’d do it your way. I just thought it was neat the different ways to approach the same problem.

    And yes, that template code is pretty close to what I typed in.

  3. Do you hear that tapping sound?

    It’s those hundreds of Plone newbies patting your back :)

    Thanks!

    • YulKa says:

      Not only newbies… Four years later, this remains a seemingly unique post on macros in combination with browser views. Thank you for taking your time to share the solution!

      • jjmojojjmojo says:

        Thank you so much – if you happen to find any inaccuracies or a more modern way to do this, please comment or contact me privately and I’ll update the post.

  4. Josh, nice post. Mikko, thank you thank you THANK YOU for all of your excellent tutorials on plone.org.

  5. nuffer says:

    Thanks for this – someone needs to update the METAL docs to reflect how METAL interacts with Zope3. I could not figure it out until I saw your page.

  6. kinkynj1 says:

    Thank you so much!!!!!!!

  7. infoCatch says:

    Ooh nifty! Thank you! I suddenly started getting Unauthorized errors after moving a macro from a browser view to a separate page template in /skins. The macro was using restricted Python to access catalog results. Looks like since PloneHotfix20130618 this won’t work anymore. Moving the template to /templates and registering it with a zope2.View permission fixed my issue.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s