ZCML

Description

What Plone programmers should know about ZCML.

Introduction

ZCML stands for the Zope Configuration Mark-up Language. It is an XML-based language used to extend and plug into systems based on the Zope Component Architecture (ZCA).

It provides:

  • conflict resolution (e.g. two plug-ins cannot overlap);
  • extensible syntax based on namespaces.

Downsides of ZCML are:

  • it is cumbersome to write by hand;
  • lack of end-user documentation.

Plone uses ZCML to:

  • register components with various places in the system, both core and add-ons.

Note

Everything you can do in ZCML can also be done in Python code.

More info:

ZCML workflow

Each Plone component (core, add-on) has a base configure.zcml in the package root. This ZCML file can include additional nested configuration files using the <include> directive.

When Plone is started all ZCML files are read.

  • New way: Python egg setup.py file contains a autoinclude hint and is picked up automatically when all the packages are scanned.
  • Old way: ZCML reference must be manually added to the zcml = section in buildout.cfg

If ZCML contains errors Plone does not start up in the foreground

Overrides

Besides layer overrides, ZCML provides more hardcore ways to override things in buildout. These overrides can also override utilities etc. and overrides take effect during ZCML parsing, not when site is run.

  • Create overrides.zcml file in your egg to the same folder as configure.zcml
  • Syntax is 100% same as in configure.zcml
  • Restart Plone.

Note

Before Plone 3.3, ZCML directives could not be automatically picked up from eggs. To make Plone pick up the directions in overrides.zcml, you'd have to add this line in buildout.cfg:

zcml =
    ...
    myegg-overrides

Since Plone 3.3, the z3c.autoinclude plugin can do this (https://plone.org/products/plone/roadmap/247/).

Specify files and code from another package

If you ever find yourself needing to use a template from another package, you can do so with using the configure tag which will then run the block of ZCML in the context of that package.

Here's an example of overriding the BrowserView 'folder_contents'. It is defined in package plone.app.content in directory browser with this ZCML statement:

<browser:page
    for="Products.CMFCore.interfaces._content.IFolderish"
    class=".folder.FolderContentsView"
    name="folder_contents"
    template="templates/folder_contents.pt"
    permission="cmf.ListFolderContents"
/>

In your own package my.package, you want to override the class, but keep the template. Assuming you created a class MyFolderContentsView inside foldercontents.py in the browser directory of your package, add this ZCML statement:

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser"
    i18n_domain="my.package">

  <!-- override folder_contents -->
  <configure package="plone.app.content.browser">
      <browser:page
          for="Products.CMFCore.interfaces._content.IFolderish"
          class="my.package.browser.foldercontents.MyFolderContentsView"
          name="folder_contents"
          template="folder_contents.pt"
          layer="my.package.interfaces.IMyPackageLayer"
          permission="cmf.ListFolderContents"
      />
  </configure>
</configure>

Basically, you re-define the BrowserView in the context of its original package, so that the relative path to the template stays valid. But using the full path in dotted notation, you can let it point to your own class.

Conditionally run ZCML

You can conditionally run ZCML if a certain package or feature is installed.

First, include the namespace at the top of the ZCML file:

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:zcml="http://namespaces.zope.org/zcml"
    i18n_domain="my.package">
....

Examples

conditionally run for package:

<include zcml:condition="installed some.package" package=".package" />
<include zcml:condition="not-installed some.package" package=".otherpackage" />

conditionally run for feature:

<include zcml:condition="have plone-4" package=".package" />
<include zcml:condition="not-have plone-4" package=".otherpackage" />