12.7.05. Adapting content attributes to Zope Dublin Core attributes

In many content oriented web applications - regardless of whether its a weblog, a simple knowledge base, a full bore CMS, a snippets library - there are often a number of common attributes that all items share - creation date, title, description, and so forth. In Zope 3, there are some built in components and utilities to provide these core elements to your content object, without using inheritance or having to implement all of these elements on your own.

As far as I understand it, this is done through an Annotations Adapter. When configuring and registering a content component through Zope 3's configuration language, ZCML, you (the developer) have the option of declaring additional marker interfaces. The most common way of supporting annotations is with the IAttributeAnnotatable marker interface.

    <implements
        interface="zope.app.annotation.interfaces.IAttributeAnnotatable"/>
This allows other content components to add their own data to your object in a transparent way - annotations are marked by a special key. WebDAV supports this in its properties system (if I recall correctly), allowing applications like Adobe GoLive to store their own interesting properties without the host storage system / application having to be impacted. In this Zope 3 case, annotations are all stored under a single hidden persistent attribute which holds the keys to the extra annotations.

You must forgive me. It's 1AM and I doubt I'm explaining this as well as I could be. In any case - the annotations system is a way of allowing objects to be extensible for use in other applications without the target object having to be impacted. This is done through adapters, which are other ways of extending objects. Adapters are commonly used in Zope 3, and I'm sure I've touched on them in other posts already.

So the common Zope Dublin Core implementation for Zope 3 uses annotation adapters to store and retrieve dublin core metadata. But what if your objects provide some bits of common dublin core data themselves? Zope 3 has an answer to that - partial annotatable adapter factories, which support the Dublin Core using a mixture of annotations and data on the context object. (source: zope.app.dublincore.annotatableadapter).

So for this snippets application, which I'm monkeying with as time permits, my IPost implementations provide a title and description (both common dublin core elements), and my IComment implementations provide a title and a body that could potentially be used as a description. I'd like to tell Zope that it can get these properties directly from my objects when it asks for dublin core data. Here's my implementation:

from zope.app.dublincore import annotatableadapter

def PostDCMapper(context):
    """
    Uses the partialAnnotatableAdapterFactory to map certain DC attributes
    to content attributes.
    
    Adapter.
    """
    # map - {dublin core name -> our name}
    map = {'title': 'title', 'description': 'description'}
    return annotatableadapter.partialAnnotatableAdapterFactory(map)(context)

def CommentDCMapper(context):
    # map - {dublin core name -> our name}
    map = {'title': 'title', 'description': 'body'}
    return annotatableadapter.partialAnnotatableAdapterFactory(map)(context)
Notice how the 'CommentDCMapper' supplies the comment's 'body' property for the 'description' element. Here's the ZCML configuration to register these as adapters (well, as factories that provide adapters):
  <adapter
      factory="snippets.PostDCMapper"
      provides="zope.app.dublincore.interfaces.IZopeDublinCore"
      for=".interfaces.IPost"
      permission="zope.Public"
      trusted="True"/>
  <adapter
      factory="snippets.CommentDCMapper"
      provides="zope.app.dublincore.interfaces.IZopeDublinCore"
      for=".interfaces.IComment"
      permission="zope.Public"
      trusted="True"/>
I found out about this from looking through the source code for Cubic, a mini-CMS that aims to be built using as many core Zope 3 components as possible (and looks to be a good example for me to look at as I need plan on building a simple and full indexed application using Zope's catalog and index capabilities). Cubic's implementation provides a few extra tricks for mapping the directly provided dublin core elements on a per-content-type basis, but I wanted to circumvent that for this round and provide direct adapters.