Taming the Wild Types Tool
Taming the Wild Types Tool
A fairly young addition to the PTK / CMF architecture, the
portal_types tool plays several important roles.
Understanding these roles is a key piece of zen for CMF
developers and site builders.
Roles of the Types Tool and TypeInformationObjects
Initially, the types tool was conceived as a simplified replacement for the "Wizards" folder of the "classic" PTK. Wizards were typically multi-page forms, which collected metadata and content from the user in order to construct a content object, or a folder. Separating the logic for constructing an object (into a Wizard, distinct from the object's class) from the logic used to edit it (typically, in the class itself), made for poor maintainability. In addition, the Wizards had to be distributed as through-the-web objects, which made updates to PTK/CMF-based sites quite painful.
The original versions of the type information objects stored in the types tool contained little more than the properties needed to construct an instance through a simple, polymorphic interface.
Having created a registry of "addable content types" for a CMF Site, other roles began quickly to accrete.
Skinning Content UI
Having successfully "skinned" the portal-wide UI
components via the creation of the
portal_skins tool, we
wanted very much to allow site builders and application
developers to customize the UI components for specific
content objects, as well. The type information objects
turned out to be logical containers for the mappings
between logical "actions" and the skin methods which enact
Holding Site-Specific Policies
Embedding policy decisions about content objects in their classes makes such content objects hard to reuse; sites whose policies (discussability, for instance, or allowed contents) are forced to subclass to override such policies, and may not be successful, at that. Type information objects make excellent "policy configuration points" for such choices.
Customizing Content Behavior in Instance Space
Because more than one type information object can "point" to the same underlying class, while imparting different construction semantics, UI, and policy settings, type information objects allow the site builder to leverage CMF content objects for multiple purposes.
Objects implementing this interface serve as the central registry for content type information in a CMF site.
# getType__roles__ = None # Public def getTypeInfo( self, contentType ): """ Return an instance which implements the ContentTypeInformation interface, corresponding to the specified 'contentType'. """ # listTypeInfo__roles__ = None # Public def listTypeInfo( self, container=None ): """ Return a sequence of instances which implement the ContentTypeInformation interface, one for each content type regisetered in the portal. If the container is specified, the list will be filtered according to the user's permissions. """ def constructContent( self, contentType, container, id ): """ Build an instance of the appropriate content class in 'container', using 'id'. """
Objects implementing this interface function as factories and configuration points for the classes they wrap.
def Metatype( self ): """ Return the Zope 'meta_type' for this content object. """ def Type( self ): """ Return the "human readable" type name (note that it may not map exactly to the 'meta_type', e.g., for l10n/i18n or where a single content class is being used twice, under different names. """ def Description( self ): """ Textual description of the class of objects (intended for display in a "constructor list"). """ def isConstructionAllowed( self, container ): """ Does the user have the permission to construct an instance of this type in 'container'? """ def allowType( self, contentType ): """ Can objects of 'contentType' be added to containers whose type object we are? """ def constructInstance( self, container, id ): """ Build a "bare" instance of the appropriate type in 'container', using 'id' as its id. Return the URL of its "immediate" view (typically the metadata form). """ def allowDiscussion( self ): """ Can this type of object support discussion? """ def getActionById( self, id ): """ Return the URL of the action whose ID is id. """ def getIcon(self): """ Returns the portal-relative icon for this type. """
In addition to implementing the
instances of this type are ObjectManagers: they hold type
information objects, as well as "helper" objects (e.g.
Python Scripts). They provide a UI for adding new type
information objects, and support for populating these
objects with default values registered by Zope products
(see "Implementing the
TypeInformation (base class)
This class implements a shared portion of the ContentTypeInformation interface (those portions not dealing with object construction):
- Action list bindings and UI
- Policy configuration
- Metadata about the type itself.
TypeInformation objects have several shared properties:
titleHuman-readable "type name".
descriptionFuller description, used in selection UIs.
content_meta_typeWhat is the underlying Zope meta_type for objects of this type?
iconWhat icon should we show for objects of this type?
immediate_view-- After construction, what method should we show first?
allowed_content_types-- If folderish, do objects of this type limit their contents to specific types?
allow_discussion-- Do objects of this type allow discussion?
This class implements the object construction portion of the ContentTypeInformation interface by invoking Zope's product-dispatcher/factory-dispatcher mechanism directly. These type objects are simplest to configure, and provide the easiest path to integrating content objects supplied "directly" by Zope products.
FTI objects have two unique properties:
- the name of the Zope product which holds the class and factory methods for the objects of this type.
- the name of the factory method (not
the Zope "factory object" created for ZClasses!) which
creates instances of the class. Notes:
- This should be a "direct" method, not a form.
- The user must have permission to invoke the method in the current location, according to the Zope security policy.
- The method will be called, effectively as though
manage_addProduct[ product ].factory( id )
Note that additional positional and keyword arguments might be passed, if the invoking method supplies them.
This class implements the object construction portion of the ContentTypeInformation interface by invoking a user-supplied method (typically a PythonScript). Somewhat more difficult to set up than FTI objects, these type objects provide for more flexible object creation, e.g.:
- Populate default metadata for the type.
- Supply default content for the type.
- Configure multiple objects, e.g., a folder with one or more content objects.
STI objects have two unique properties:
- the permission which protects their constructor method / script.
- a traversable path to the method or
script to be called to construct an instance. This
method must accept at least two arguments
id), and must return an instance, already added to
container, and wrapped in it.
This section describes several kinds of customizations which the types tool facilitates.
Adding / Removing Content Types
Unlike Zope products, whose factories show up "automagically" in the "Add..." list after product installation, CMF sites require explicit configuration to enable new product types. Likewise, a particular CMF site may suppress the creation of certain content types, merely by removing its type information object.
Creating "News-Only" Folders
Suppose you need to restrict the items created under your
/news folder to be only NewsItems? Create a new
type object, using
CMFDefault/Folder as your starting
point, and then check the 'Filter content types?" checkbox
and select only "NewsItem" in the
Allowed content types
Internationalizing Action URLs
Name property of each action on a type
Actions tab to internationalize the label which
shows up in the 'actions_box'; change the
property to select an internationalized skin method.
Binding Customized UI
Create custom skin methods for your objects, and bind them using the Actions tab (skin methods which reuse the names of the objects' existing actions don't need any re-binding).
- Using ZClasses as content
- Type objects + contained methods --> "local" ZClasses?