Step 2: ZCML

In this step, we will introduce Zope 3's component configuration language ZCML. With loose components hanging around in your software packages as the Component Architecture proclaims, ZCML plays an important role in wiring these components together to form an actual application.

Why ZCML?

Python code written for Zope 2 (often called Zope products) is often perceived as "unpythonic". The reason for that is because otherwise pure Python code is sprinkled with Zope 2-specified software policy code, such as component setup, registration, hookings and security declarations. This not only adds to a lot of often meaningless boiler-plate being copied from one Zope 2 "product" to another, it also hinders the reuse of these components.

Zope 3 separates all that policy from the actual code and moves it out to separate configuration files. The term "configuration" might be a bit misleading here. Better think of it as wiring. ZCML, the XML-based configuration language that is used for this, is tailored to do component registration and security declarations, for the most part. By enabling or disabling certain components in ZCML, you can configure certain policies of the overall application. In Zope 2, enabling and disabling components means to drop in or remove a certain Zope 2 product. When it's there, it's automagically imported and loaded. Not so in Zope 3. If you don't enable it explicitly, it will not be found.

Loading

There are a few conventions when doing wiring in ZCML. First, the default configuration file name for a package is configure.zcml. Therefore, when you use the following ZCML directive:

<include package="Products.FiveFeeds" />

the ZCML machinery will actually look for the configure.zcml file in the Products.FiveFeeds package. (Of course, you could tell it to load a different file from the package, but that's rarely used.)

In Zope 3, you need such a statement for every package that you want to have loaded. Zope 3 does not automatically load packages that are in a certain location because Zope 3 just doesn't have special locations like the Products package anymore. Instead you have to tell Zope 3 explicitly with such a ZCML statement as the above to load your package. Such a ZCML statement is sometimes called a ZCML slug and usually placed in a file in $INSTANCE_HOME/etc/package_includes.

Zope 2 and Five make a compromise regarding explicit and implicit loading of products and packages. Zope 2 developers are used to deploying products by dropping them into their instance's Products directory. Therefore, when a product contains a configure.zcml file, Five will load it automatically. A ZCML slug thus is not necessary for products. It is, however, necessary for regular Python packages (outside of Products) whose ZCML you would like to be loaded.

Namespaces

As an XML dialect, ZCML makes use of XML namespaces. Different directives have different namespaces according to their component domain. The most common namespace is the http://namespaces.zope.org/zope namespace. The most basic ZCML directives like configure (typically the document element of a ZCML file), include (see above) and registration directives like adapter, utility, etc. are part of this namespace. Another popular namespace is the http://namespaces.zope.org/browser namespace which is used for directives configuring browser pages, resources and forms.

There is also a Five-specific namespace, http://namespaces.zope.org/five, for all those directives that only occur in Five. These directives are usually necessary to do additional things with Zope 2 components that aren't Zope 3 ready yet in a particular way so that they become ready. We will see an example in the next section where we'll write a small ZCML file for our product.

A simple configuration file

Coming back to our example application: In the last step, we left off with having created a marker interface for all portal content. We now want to use this interface by marking the two most important base classes for Plone content objects, CMF's PortalObjectBase and Archetype's BaseObject, with it. Normally, when we write a class and and want to express that it implements a certain interface, we would use the zope.interface.implements() function. Obviously we can't do this with the existing code from CMF and Archetypes, which is why we'll use a Five-specific ZCML directive, five:implements, in our configure.zcml (download source):

<configure xmlns="http://namespaces.zope.org/zope"
           xmlns:five="http://namespaces.zope.org/five">

  <five:implements
      class="Products.CMFCore.PortalObject.PortalObjectBase"
      interface=".interfaces.IPortalObject"
      />
  <five:implements
      class="Products.Archetypes.public.BaseObject"
      interface=".interfaces.IPortalObject"
      />

</configure>

As you can see, we use the configure element as the document element where we also declare the appropriate namespaces. Then we call the five:implements directive twice, once for each base class. In the end, it has the same effect of using the zope.interface.implements() function in the class definition. Note that global Python objects residing in Python modules and packages are accessed using their dotted names (the Python import path). A leading dot represents the package the ZCML file is located in, so in this case Products.FiveFeeds, and lets you save some typing when referencing objects below the current package.

By having the two major base classes for Plone content implement the marker interface now, all content objects in Plone will now provide it. That means we can register components like a browser view generating an Atom feed to function on this interface and they will work with all content objects.

We will see how to create and register a simple browser page like that in Step 3.

Summary