Browser layers

by Martin Aspeli last modified Nov 03, 2009 01:42 PM

Customisation Zope 3-style

When Zope 3 was designed, many of the lessons from CMF were incorporated and the technology refined. The idea of a single, global namespace with customisation possible by id only was supplanted by the notion of named resources being registered for a context type (so that the view called @@view when invoked on a Page looks different to the view with the same name invoked on a Folder, say), and possibly registered for a browser layer. A Zope 3 browser layer is similar in purpose to a CMF skin layer, but is implemented differently.

A browser layer is just a marker interface which is applied to the request upon traversal. A Zope 3 browser resource is a multi-adapter on the context and the request, and when the request is marked with a particular interface, the Component Architecture may find a more specific adapter in a view registered for that particular layer. If that made no sense, don't worry. All you need to know is this:

  • We define a marker interface (which is just a class with no body, deriving from zope.interface.Interface) representing the browser layer.
  • We ensure that the interface is applied to the request automatically, enabling the browser layer in our site.
  • We register (using ZCML) browser resources, views, viewlets and portlets for this new layer, allowing them to override the defaults (which are implicitly registered for the default browser layer).

In Plone, there are two main ways of enabling a custom browser layer interface. The first is to use the mechanisms of plone.theme. This package, which ships with Plone by default, allows us to link a browser layer with a particular theme (skin) in portal_skins (not to be confused with a skin layer). When the theme is enabled in portal_skins, the layer is in effect. This is useful for products that install a whole new theme in Plone. It's less useful for general (non-theme) products, since only one plone.theme-installed layer is active at any given time. We really want layer installation to be additive, so that we can install any number of products, each providing its own layer.

To do that, we need to use the plone.browserlayer package, which is part of Plone 3.1 and later (it is possible to install it as an add-on in Plone 3.0, too).

In example.customization, you will find a layer marker interface in browser/interfaces.py:

from zope.interface import Interface

class IExampleCustomization(Interface):
    """This interface is registered in profiles/default/browserlayer.xml,
    and is referenced in the 'layer' option of various browser resources.
    When the product is installed, this marker interface will be applied
    to every request, allowing layer-specific customisation.
    """

This is then installed when the package is installed, via the browserlayer.xml GenericSetup import step, found in profiles/default:

<?xml version="1.0"?>
<!-- Register the package-specific browser layer, so that it will be activated
when this product is installed. -->
<layers>
    <layer name="example.customization.layer" 
           interface="example.customization.browser.interfaces.IExampleCustomization" />
</layers>

The examples later in this tutorial will reference this marker interface.