Archetypes Developer Manual
An in-depth manual about how to create new content-types in Plone using the Archetypes framework.
1. Introduction
What is Archetypes? A bit of history and future direction.
1.1. What is Archetypes?
Brief presentation of Archetypes.
Archetypes is a framework for developing new content types for a Plone project. Most content management projects involve introducing new types of content, which in the non-trivial case requires an informed understanding of how Zope and the CMF work. Archetypes provides a simple, extensible framework that can ease both the development and maintenance costs of CMF content types while reducing the learning curve for the simpler cases.
Compared to building content types using the stock CMF (through subclassing), Archetypes gives you the following advantages:
- automatically generates forms and views;
- provides a library of stock field types, form widgets, and field validators;
- allows defining custom fields, widgets, and validators;
- automates transformations of rich content;
- a built-in reference engine that gives the ability to link two objects together with a relation; such a "link" from a given object to another one is a Python object called a reference.
Since Plone 2.1, Archetypes has become the de-facto way of developing new content types, and a majority of third party products that are released these days use Archetypes.
1.2. Archetypes schemas
Introducing Archetypes-based schemas and fields.
Archetypes provides a robust framework for storing data attributes on content objects. This framework consist of a number of Fields stored in a container called a Schema. Fields are simply specialized Python classes that allow you to store and retrieve data associated with an Archetypes object.
Fields provide a few functionalities. First, there are specialized field types for strings, lists of strings, integers, floating-point numbers, etc., that allow special handling of fields based on the type of data stored.
Some definitions
Before we go diving in, let's define some often-used terms:
- Field: An Archetypes Field. This refers to an instance of a Field class defined in a Schema.
- Schema: The "container" that Archetypes uses to store fields.
- Schemata: A named grouping of fields. One Schema can have many schematas.
- AT: Abbreviation for Archetypes.
Fields, Classes, and Objects
Archetypes Fields are Python objects contained within the Schema. A Field is defined once for an Archetypes content class. This single Field instance is used for every instance of that class. Therefore, the relationship between Field instances and content classes is described as such: "A field instance belongs to exactly one class." A class, however, can have many different Field instances. Furthermore, every instance of an AT class uses the same set of Fields. AT objects themselves do not contain unique Fields.
When Zope starts up, during product initialization, Archetypes reads the schema of the registered classes and "automagically" generates methods to read (the accessor) and change (the mutator) each of the fields defined.
Stock schemas
Archetypes includes three stock schemas:
- BaseSchema: defines a normal content type,
- BaseFolderSchema: defines a folderish content type (object can contain other objects),
- BaseBTreeFolderSchema: for folders which need to handle hundreds or thousands of objects (even up to millions).
All three include two
fields, id and title, as well as the
standard Dublin Core metadata fields.
Modifying the fields of an existing schema
Modifying an existing schema field is possible using the syntax schema['<field_name>'].attribute = value. For example, to change the label of the description field widget (already available in BaseSchema), you can write (in your defined schema definition that reuses BaseSchema):
schema['description'].widget.label = u'Summary'
The fields in the schema are ordered, and normally first fields come
first in "add" and "edit" forms. To rearrange a field within the schema
use the moveField method:
- Place it before a specific field:
schema.moveField('<field_to_move>', before='<field_to_place_it_before>') - Place it after a specific field:
schema.moveField('<field_to_move>', after='<field_to_place_it_after>') - Place it at the top of the schema:
schema.moveField('<field_to_move>', pos='top') - Place it at the bottom:
schema.moveField('<field_to_move>', pos='bottom') - Place it in a specific position:
schema.moveField('<field_to_move>', pos=0)
1.3. What is ATContentTypes?
ATContentTypes is the Plone core product that provides the default content types (since Plone 2.1).
One of the major changes introduced in Plone 2.1 was that the core content types (Page, Image, etc) were changed from being based on stock CMF types, to using Archetypes. The new core types are housed in the ATContentTypes product.
ATContentTypes introduces a number of base classes and tools that provide common "Plone-ish" behaviour. This includes things like support for the "display" menu and the "more..." menu and restrictions for the "add item" menu.
You can use ATContentTypes' base classes and tools in your own products. The RichDocument tutorial covers the core techniques, and is probably a good place to go when you have finished this reference.
2. A simple AT Product
A semi-realistic example Archetypes-based content type product.
2.1. Introduction
Introducing a sample AT Product and the contents of the tutorial.
In this part of the manual, we discuss a sample AT Product to explain CMF/Archetypes practices. We will be building a product called example.archetype, which will implement a content type (InstantMessage) that members with specific rights can use to add messages readable by other members. However, as you may have guessed, this is more a learning example than a usable product for a real website application.
What is a Product ? A product - a Zope product to be precise - is a third party add-on that can be integrated to provide additional functionality. It is a code package written using the Python language and conventions.
In order to understand this section you will need to have some prior knowledge of working on the file system and programming protocols common to Python and Zope.
The example.archetype product features the following CMF and Archetypes capabilities:
- basic fields and widgets;
- defining and using a vocabulary for a field with a selection widget;
- defining specific "Add" permissions for the contents.
The code of the product can be downloaded here: http://plone.org/products/example.archetype/
2.2. Product package layout
Conventions and techniques for organizing the package for an AT product.
Following Zope, Plone and AT's conventions, the content of our example product pakage will look like this:
- __init__.py
- configure.zcml
- config.py
- interfaces.py
- content
- __init__.py
- message.py
- profiles
- default
- browser
- __init__.py
- configure.zcml
- instantmessage.pt
- tests
- __init__.py
- base.py
- test_setup.py
What is the purpose of these files and directories?
- __init__.py: The usual "Python package" initialization module;
- configure.zcml: Using Zope's new Configuration Markup Language (ZCML), this file configures the services or behaviour the Zope server needs to load at startup;
- config.py: Provides configuration variables for the product;
- interfaces.py: Where you define interfaces describing what the packages' classes will do;
- content: Contains the modules providing the implementation of the content types.
In this case, it contains the message.py file where the
'InstantMessage' class should be defined;
- profiles/default:
Contains a set of XML files that are needed to provide the settings that will be used by Plone's Quick-Installer tool when installing the product within Plone; this is what we call an Extension Profile, an artifact of Zope CMF's GenericSetup technology. Note that this replaces the old way of doing based on the Extensions/Install. More precisely, since Plone 3.0, you do not need that old-style technique;
- browser: The sub-package where the developer can add specific presentation code such as browser views and templates; the contained configure.zcml is used to provide these components registration.
- tests: Contains the unit tests code for the product.
Now we will go through the files one by one and add what we need to produce our application.
2.3. The interfaces module
The module where you define interfaces describing what your content class(es) will do.
Why do you need interfaces?
Interfaces are useful to describe what a class will do. They are a kind of contract between a class and the components that class interact with. Starting a content management functionality package with writing interfaces is recommended practice as it helps document your code. In addition to that, Zope Component Architecture (ZCA) allows us to use interfaces as components for adapting a class (which is useful as new user requirements appear) and thus specializing its behaviour.
The interface for the Instant Message class
This is done by convention in the interfaces.py file, that you need to add at the root of the package.
First, we need an import from Zope's zope.interface module, which is included into Zope 2's distribution since version 2.8:
from zope.interface import Interface
Following ZCA naming conventions (interface names start with an I), we define the IInstantMessage interface we need for the InstantMessage class that we will define later:
class IInstantMessage(Interface):
"""
Interface for the InstantMessage class.
"""That's it!
We could add attribute definitions to it using the zope.interface.Attribute class, but this is not mandatory. When an interface is defined as above, without any function nor attribute, we call it a "marker interface" meaning that it will be used simply to "mark" the instances of the class that implements it.
More information about interfaces in the context of Archetypes can be found in the b-org tutorial - Interfaces section. For a detailed presentation of interfaces and their usage patterns, read the doctests document available from Zope's documentation site.
2.4. The configuration module
The configuration details for your content type, in config.py.
First, we have to import a class from Archetypes:
from Products.Archetypes.atapi import DisplayList
Displaylist is a data container we use when displaying
pulldowns/radiobuttons/checkmarks with different choices. Let's say we wanted
priorities on our instant messages, and we wanted those to be High, Normal
and Low. We will specify these later in the file.
The next two lines set the project (Product in Zope) name, and point to the
skin directory. PROJECTNAME should reference the name of the package: example.archetype.
PROJECTNAME = "example.archetype"
Now, we need to specify our 'Priority' pulldown. It should look like this, using the DisplayList utility class that Archetypes has provided for exactly that purpose:
MESSAGE_PRIORITIES = DisplayList((
('high', 'High Priority'),
('normal', 'Normal Priority'),
('low', 'Low Priority'),
))
Python notes:
The reason for double parantheses is that DisplayList is a class that you pass a tuple of tuples to.
We also need to define the "Add" permission(s) for the content type(s):
ADD_CONTENT_PERMISSIONS = {
'InstantMessage': 'example.archetype: Add InstantMessage',
}
We recommend using the standard way of naming permissions: '<ProductName>: <Permission>'. This will group the related permissions together within the ZMI (Security tab), and allow the Administrator to recognize which permissions belong to which Product.
Note that, unless you have an advanced case which needs custom security settings, you don't need to define your own permissions for the "edit" and "view" of the content. In this simple case you will just reuse, in the modules where needed, the generic permissions defined in CMFCore.permissions: "View", "Modify portal content"...2.5. The startup module
The initialization module (__init__.py) provides the script that is run when Zope is started.
Before starting the usual Zope product initialization code, we need to define a Message Factory for when this product is internationalized.
from zope.i18nmessageid import MessageFactory
exampleMessageFactory = MessageFactory('example.archetype')The defined MessageFactory object will be imported with the special name "_" in most modules, and strings like _(u"message") will then be extracted by i18n tools for translation.
Now, we import some useful stuff from the Archetypes API:process_types is useful to get the product's content types,
associated constructors, and Factory Type Information (FTI) data structures, while listTypes can be used to list the types available in the product.
We also need to import the utils module from CMFCore to be able to use its ContentInit class later.
from Products.Archetypes.atapi import process_types from Products.Archetypes.atapi import listTypes from Products.CMFCore import utils
Python notes:
Factory Type Information (FTI): Part of a CMF portal's configuration, the FTI for a content type is the data structure that holds the information needed to expose a content type within the portal. From the integrator's perspective, the FTI is the object (Factory-based Type Information object) within the portal_types component that tells CMF and Plone how to create a content from the type and how to display it.
How exactly does 'listTypes' work: See those registerType() calls in your content type modules? Notice how we also import those modules (but do nothing with the import) in the 'content' package's __init__.py. The registerType() call tells AT about the type so that listTypes() can find it later.
from content import messageNow, we import the configuration module, in order to have access to the variables it contains, such as the "Add" permission setting:
import config
Now for the real action. You define a function that is required by Zope and CMF internals to initialize our content type(s):
def initialize(context):
The first part of the code of this function generates the content types, the constructors and the Factory-based Type Informations (or FTIs) required to make your types work with the CMF:
content_types, constructors, ftis = process_types(
listTypes(config.PROJECTNAME),
config.PROJECTNAME)
The second part instantiates an object of the class ContentInit (from CMFCore), and registers your types in the CMF:
utils.ContentInit(
"%s Content" % config.PROJECTNAME,
content_types = content_types,
permission = config.ADD_CONTENT_PERMISSIONS['InstantMessage'],
extra_constructors = constructors,
fti = ftis,
).initialize(context)Handling several content types
There is a better way to write the code that initializes the content type class with its "Add" permission and constructor, so that it still works if you define several content types. This is useful if you plan to later augment your product with additional types.
Here is the improved code:
def initialize(context):
content_types, constructors, ftis = process_types(
listTypes(config.PROJECTNAME),
config.PROJECTNAME)
# We want to register each type with its own permission,
# this will afford us greater control during system
# configuration/deployment (credit : Ben Saller)
allTypes = zip(content_types, constructors)
for atype, constructor in allTypes:
kind = "%s: %s" % (config.PROJECTNAME, atype.portal_type)
utils.ContentInit(kind,
content_types = (atype,),
permission = config.ADD_CONTENT_PERMISSIONS[atype.portal_type],
extra_constructors = (constructor,),
fti = ftis,
).initialize(context)Python notes:
We can use the "ADD_CONTENT_PERMISSIONS[atype.portal_type]" construct because ADD_CONTENT_PERMISSIONS references a dictionary in which the keys are the potential content types names.
The zip() function is a Python built-in that pairs up elements of two lists. In this case, "allTypes" will be a list of tuples containing a content type from "content_types" and the corresponding constructor from "constructors".
If you have several content types, you should not forget to import each content module, as is done for the message example discussed here !
2.6. The content package and its modules
Now we are ready for the core of the product, i.e. the content class definition module (content/message.py).
Since it provides a Python (sub)package, the 'content' directory contains 2 modules:
- the usual __init__ module that initializes the package,
- the message module (message.py) where we will define the 'InstantMessage' class.
The message module
First imports we need
We start the message module by adding the general Zope-related imports we need, such as the implements function from the zope.interface module:
from zope.interface import implements
We need to use a few classes and/or functions provided by
the core of our codebase, i.e. CMF/Archetypes. It is possible to have access to all the classes and helper functions
made
publicly available by Archetypes, by importing its façade or API module (Products.Archetypes.atapi) this way:
from Products.Archetypes import atapi
i18n support
It is always a good idea to have an i18n-enabled application. To start using Zope's i18n support, let's import the MessageFactory object created in the product's startup module:
from example.archetype import exampleMessageFactory as _
The MessageFactory referenced with the _ symbol can now be used to provide i18nized labels, descriptions, and all the miscellaneous text snippets that are injected in the UI, also known as "messages". For a content type implementation, this is useful for UI widgets; for example to define the label of the content title field widget, we could define label = _(u'Title'). (See later for how we make use of this tool/practice.)
ATContentTypes-based schema definition
You can base your implementation directly on these stock Archetypes schemas. But you can add better support for Plone's UI and content management policies (such as the parameters that allow showing/hiding contents in the navigation menu), by basing the implementation on ATContentTypes' base schema, ATContentTypeSchema. To be compatible with that schema, you will also need to inherit from ATContentTypes' ATCTContent base class.
Let's add the import of modules we need for that:
from Products.ATContentTypes.content import base from Products.ATContentTypes.content import schemataThen, we import things internal to our product package, such as our defined interface(s) and the configuration module (for access to things such as
PROJECTNAME and MESSAGE_PRIORITIES):
from example.archetype.interfaces import IInstantMessage from example.archetype import config
Now, we have everything we need to start building the schema, and then the class that will use it. We start out by copying ATContentTypes' ATContentTypeSchema, and we extend it by adding our specific fields and/or overriden field properties.
schema = schemata.ATContentTypeSchema.copy() + atapi.Schema((
atapi.StringField('priority',
vocabulary = config.MESSAGE_PRIORITIES,
default = 'normal',
widget = atapi.SelectionWidget(label = _(u'Priority')),
),
atapi.TextField('body',
searchable = 1,
required = 1,
allowable_content_types = ('text/plain',
'text/structured',
'text/html',),
default_output_type = 'text/x-html-safe',
widget = atapi.RichWidget(label = _(u'Message body')),
),
))Notes:
To instantiate an Archetypes schema object, you pass a tuple of field objects to the 'Schema' class.
We define the body of the InstantMessage object using a RichWidget, so the user can use formatting with a WYSIWYG editor.
The full list of out-of-the-box available Fields and Widgets can be found in the Fields section at the end of the manual. You can find more 3rd party fields and widgets here.
Content-type class definition
The last step is to create the class for the InstantMessage content. It inherits from ATContentTypes' ATCTContent, which itself is based on AT's BaseContent, which automatically gives its 'id' and 'title' attributes, and the entire Dublin Core metadata set (Title, Description, Creator, CreationDate, etc):
class InstantMessage(base.ATCTContent):
"""An Archetype for an InstantMessage application"""
implements(IInstantMessage)
schema = schema
The first information we add for the class definition is saying that it implements the IInstantMessage interface that we have previously defined (in interfaces.py) and imported.
implements(IInstantMessage)
The next thing is assigning the reference of the Archetypes schema, using the schema class attribute.
schema = schema
The content class definition is done. Now, we are ready to activate the content type in Archetypes'
internal types registry. This is done using the helper function called registerType.
atapi.registerType(InstantMessage, config.PROJECTNAME)
Congratulations! You have just created your first Archetype for Plone! It allows you to handle the content of an instant message with Zope-based persistent objects which:
- can be added within your Plone site,
- published by the Zope Publisher, which means you can visit them via their URLs, etc...
- searched since they are automatically indexed,
- etc...
But wait! You have some final packaging work to do to ease installation of the product within your Plone site.
Notes:
At the content class level, you could also provide the 'actions' attribute useful for defining the settings of the type's actions (for the portal_actions tool). In Plone 3, this is no more needed, since this is part of the FTI's configuration details, and should be provided using GenericSetup, in the types-related XML files (i.e. 'profiles/default/types/InstantMessage.xml'). Same for the aliases.
The __init__ module
The trick here is to simply import the message module so that all the code of that module gets interpreted as soon as the Python interpreter initializes the package.
import message
2.7. Adding a custom view for the content
Providing the custom presentation template for the InsantMessage, using Zope's browser layer mechanism.
The browser layer concept
A browser layer is a concept introduced by Zope Component Architecture (Zope 3), and which can be used in Plone. It is useful for registering views and resources (images, CSS, JS) for the site, in a way that they can override default elements (which are implicitly registered for the default browser layer) or be overriden when needed, even through the ZMI. A browser layer is similar in purpose to a CMF skin layer, but is implemented differently.
To add a browser layer to your product, you need 3 steps:
- Define the marker interface for the browser layer (for example,
example.archetype.interfaces.IInstantMessageSpecific.) - Add an XML file in your extension profile named
browserlayer.xmlproviding the browser layer settings to the site. (This step is covered later as part of the various product setup details.) - Register (using ZCML) your browser views, templates and resources.
Defining the browser layer interface
Add a marker interface for the browser layer (in interfaces.py):
from plone.theme.interfaces import IDefaultPloneLayer class IInstantMessageSpecific(IDefaultPloneLayer): """Marker interface that defines a Zope 3 skin layer for this product. """
Adding and registering the browser template
To provide a custom view template for your content type, you need a page template called
instantmessage.pt in the browser/ directory, and a ZCML declaration in the configure.zcml to associate the template to the IInstantMessageSpecific Zope 3 skin layer.
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" i18n_domain="example.archetype" > <browser:page for="example.archetype.interfaces.IInstantMessage" layer="example.archetype.interfaces.IInstantMessageSpecific" name="instantmessage_view" template="instantmessage.pt" permission="zope2.View" /> </configure>
Here is the example template code:
<html metal:use-macro="here/main_template/macros/master"
i18n:domain="plone" >
<body>
<div metal:fill-slot="main"
tal:define="priority here/getPriority;
priority_color python:(priority == 'high' and 'red') or (priority == 'low' and 'green') or ''" >
<h1 tal:content="context/Title"
tal:attributes="style string:color:$priority_color" >
Title
</h1>
<p tal:content="structure here/getBody" />
<div class="documentByLine">
Message by <span tal:content="context/Creator" />
with <strong tal:content="priority" /> priority.
-
<span tal:replace="python:here.toLocalizedTime(context.CreationDate(),long_format=1)" />
</div>
</div>
</body>
</html>
Python notes:
The new methods we use on the content object (getPriority, getBody, etc), called the "accessors", are generated by Archetypes as part of its internal mechanisms, based on the field definition in the content schema; so if the field is called 'priority', there is a generated method called 'getPriority' responsible to return the stored value on the object. Note that the code of the method is not available somewhere for modification ; "generated" here means it is available in the server's memory, within Archetypes engine's registries, when the Zope server has started.
After the product installation step, which we still have to discuss (see later), Plone should be able to find this template and use it as the content object's default view when you invoke the content's URL.
2.8. Installing the product
Ensuring the product elements (types, browser layers, resources) are correctly installed.
In this part, we will provide the code to be executed when the integrator "adds", i.e. installs, the InstantMessage product to the Plone site. This aspect of the product code is called the "Extension Profile" (or "Setup Profile") and is managed under the hood by a machinery called GenericSetup.
For more about GenericSetup, its possibilities, and how a developer uses it, read the GenericSetup tutorial.The setup profile files (profiles/default)
The setup profile is composed of a set of GenericSetup XML files containing setup declarations.
Type declaration and definition
First, we provide the files needed for adding the types to CMF's types registry (portal_types): types.xml and types/InstantMessage.xml.
In types.xml, within the <object name="portal_types" ... /> element, add the setup code for the type(s) you want to install:
<?xml version="1.0"?>
<object name="portal_types" meta_type="Plone Types Tool">
<property
name="title">Controls the available content types in your portal</property>
<object name="InstantMessage"
meta_type="Factory-based Type Information with dynamic views"/>
</object> The name property of the <object> node constitutes the called portal type name of the content-type, a CMF concept which supports two things:
- Dynamic typing: objects can change their content
type during their lifetime. To do this use
_setPortalTypeName(<type>). - You can have arbitrarily many different content types
using the same base class (and having therefore the same
meta_type) but differing in their Factory Type Information (FTI) settings.
The portal type name was formerly set in a content-type class attribute called portal_type, which is no longer necessary.
The name of the file inside the profiles/default/types folder must match the portal type name, with spaces converted to underscores whenever necessary. So, in types/InstantMessage.xml, add the code for the InstantMessage FTI object:
<?xml version="1.0"?>
<object name="InstantMessage"
meta_type="Factory-based Type Information with dynamic views"
i18n:domain="example.archetype" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<property name="title" i18n:translate="">Example AT - InstantMessage</property>
<property name="description"
i18n:translate="">An example type (InstantMessage) discussed in the AT Developer Manual.</property>
In these first lines we give the content-type a title and a description.
The title property indicates the user-friendly name of the content-type. This is what's supposed to be used in the user
interface, and can be accessed using the <fti>.title_or_id() or the Type() methods, which
both return the content-type title if it exists or the content-type id
otherwise. Like portal type, this property was formerly set in a the content-type class attribute called archetype_name, which is no longer neccessary.
<property name="content_meta_type">InstantMessage</property> <property name="content_icon">document_icon.gif</property> <property name="product">example.archetype</property> <property name="factory">addInstantMessage</property>
The meta_type property of the object is a Zope concept to organize object classification or containment.
For historical reasons, it is used in
CMF in some places because first versions of
CMF didn't have today's portal_type. Also note that Archetypes uses the content-type class name as the meta_type value, unless given explicitly.
The content_icon property specifies the icon image file which will be shown in the Plone UI for this content-type. This icon image file must be accessible from the context of the content-type, and
therefore should be placed into a CMF skin layer (the CMF way) or in a browser resource directory (the Zope 3 way).
The factory property indicates the factory function which
will be used to create and initialize new content objects of this type.
This factory is automatically generated by the Archetypes framework,
when the product is initialized (via the code in the startup module), and is always named add<content-meta-type>. The factory is also associated with a certain product by means of the product property.
<property name="immediate_view">atct_edit</property> <property name="global_allow">True</property> <property name="filter_content_types">False</property> <property name="allow_discussion">False</property>
The global_allow property determines if the content-type will be available to be added from anywhere in the site.
The filter_content_types property, paired with allowed_content_types, controls what content-types will be addable inside the current one.
With allow_discussion, we specify whether or not comments will be allowed by default on this content-type.
<property name="default_view">@@instantmessage_view</property>
<property name="view_methods">
<element value="@@instantmessage_view" />
</property>
<alias from="(Default)" to="@@instantmessage_view" />
<alias from="edit" to="atct_edit" />
<alias from="sharing" to="@@sharing" />
<alias from="view" to="@@instantmessage_view" />
Here we define CMF views (templates) and aliases that map content-type methods to views.
<action title="View" action_id="view" category="object" condition_expr=""
url_expr="string:${object_url}/" visible="True">
<permission value="View" />
</action>
<action title="Edit" action_id="edit" category="object" condition_expr=""
url_expr="string:${object_url}/edit" visible="True">
<permission value="Modify portal content" />
</action>
</object>
The <action> elements register type-specific actions for the content-type. The object category makes the render as tabs in the Plone UI.
- The
url_expris a TALES expression that defines the URL from where the action will be triggered and should match one of the method aliases defined above. Hence, theeditaction points tostring:${object_url}/edit, which means that if you are at/path/to/objectand clickedit, you will go to/path/to/object/edit./editthen gets recognized as a method alias, which points to the page templateatct_edit, causing Zope to render/path/to/object/atct_edit. - The
<permission /> element specifies a guard permission for this
action. If the user's role doesn't have this permission, the action
won't be available and the corresponding action tab won't be shown.
- In addition to the former criteria, the
condition_expris a TALES expression which will be evaluated to decide if the action is available or not. - The
visibleattribute indicates wheter the action tab will be visible or hidden. If it's set to False, the tab won't appear even when the action is available, but the exposed page will still be accesible from the associated URL.
- Defining new content-type actions this way, i.e. using GenericSetup, supersedes the old
updateActionsfunction fromATContentTypes.content.base. - Don't worry. You don't have to type all this XML each time you create a new content-type; since most of it is boilerplate (XML is very verbose) you can copy & paste an already working example (like the CMFPlone ones) and modify only the changing bits.
Type factory
We also need the file useful for setting the type against Plone's factory tool (portal_factory): factorytool.xml.
This is needed so that when a user adds a content object and then
clicks Cancel in the edit form, a stale object won't be lying around.)
<?xml version="1.0"?> <object name="portal_factory" meta_type="Plone Factory Tool"> <factorytypes> <type portal_type="InstantMessage"/> </factorytypes> </object>
Roles - Permissions mapping
For our content type(s) to be usable, we need to assign the required "Add" permission to the Plone site's default roles: Contributor, Owner, and Manager. This is done using the rolemap.xml file as follows:
<?xml version="1.0"?>
<rolemap>
<permissions>
<permission name="example.archetype: Add InstantMessage" acquire="True">
<role name="Manager"/>
<role name="Owner"/>
<role name="Contributor"/>
</permission>
</permissions>
</rolemap>
Browser skin layer
In order to install our browser skin layer, we also add a browserlayer.xml file with the following code:
<?xml version="1.0"?>
<layers>
<layer name="example.archetype"
interface="example.archetype.interfaces.IInstantMessageSpecific" />
</layers>
Registering our setup profile
This last step ensures everything can work. We update the package's configure.zcml file with the code snippet that will load the extension profile:
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
i18n_domain="example.archetype" >
<five:registerPackage package="." initialize=".initialize" />
<include package=".browser" />
<genericsetup:registerProfile
name="default"
title="Example Archetype content - InstantMessage"
directory="profiles/default"
description="Extension profile for Example AT - InstantMessage"
provides="Products.GenericSetup.interfaces.EXTENSION"
/>
</configure>
Restarting Zope
Now that you have a first version of your product ready to be tested, and installed via your buildout, you need to (re)start Zope.
Quick-installing the product
Back in the Plone configuration (or Plone control panel), when you visit the "Add/Remove Products" interface or the portal_quickinstaller tool through the ZMI (at the root of the site), you can see the product show up under the category of "installable products".
Select and click the button to install the product. If everything goes fine, the product should be installed, and you're ready to start using it!
2.9. Basic integration tests
No product is complete without tests.
To build high-quality software, you must provide automatic tests - often known as "unit" tests (though tests for Archetypes products tend to be "integration" tests, strictly speaking).
The tutorial on testing and test-driven development is essential reading if you want to write high-quality software (and you don't know the techniques it advocates already). Please refer to it for details.
The example.archetype product contains basic tests that prove that the product is properly installed, that it registers its types, and that an InstantMessage object can actually be instantiated. If it contained more functionality, there would have been more tests, but even simple integration tests like this can be surprisingly useful - if you accidentally broke the content type with some change, you'd notice that it failed to install or instantiate.
The tests are in the "tests" directory. The file "base.py" contains some base classes that are used for tests, to ensure the site is properly set up:
import unittest
from zope.testing import doctestunit
from zope.component import testing
from Testing import ZopeTestCase as ztc
from Products.Five import zcml
from Products.Five import fiveconfigure
from Products.PloneTestCase import PloneTestCase as ptc
from Products.PloneTestCase.layer import PloneSite
from Products.PloneTestCase.layer import onsetup
@onsetup
def setup_product():
"""Set up the package and its dependencies.
The @onsetup decorator causes the execution of this body to be deferred
until the setup of the Plone site testing layer. We could have created our
own layer, but this is the easiest way for Plone integration tests.
"""
fiveconfigure.debug_mode = True
import example.archetype
zcml.load_config('configure.zcml', example.archetype)
fiveconfigure.debug_mode = False
ztc.installPackage('example.archetype')
setup_product()
ptc.setupPloneSite(products=['example.archetype'])
class InstantMessageTestCase(ptc.PloneTestCase):
"""Base class for integration tests.
This may provide specific set-up and tear-down operations, or provide
convenience methods.
"""
The actual tests are in "test_setup.py":
from base import InstantMessageTestCase
from example.archetype.interfaces import IInstantMessage
class TestProductInstall(InstantMessageTestCase):
def afterSetUp(self):
self.types = ('InstantMessage',)
def testTypesInstalled(self):
for t in self.types:
self.failUnless(t in self.portal.portal_types.objectIds(),
'%s content type not installed' % t)
def testPortalFactoryEnabled(self):
for t in self.types:
self.failUnless(t in self.portal.portal_factory.getFactoryTypes().keys(),
'%s content type not installed' % t)
class TestInstantiation(InstantMessageTestCase):
def afterSetUp(self):
# Adding an InstantMessage anywhere - can only be done by a Manager or Portal Owner
self.setRoles(['Manager'])
self.portal.invokeFactory('InstantMessage', 'im1')
def testCreateInstantMessage(self):
self.failUnless('im1' in self.portal.objectIds())
def testInstantMessageInterface(self):
im = self.portal.im1
self.failUnless(IInstantMessage.providedBy(im))
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(TestProductInstall))
suite.addTest(makeSuite(TestInstantiation))
return suite
To run these tests within your buildout environment:
./bin/instance test -s example.archetype
You may see output like:
Ran 4 tests with 0 failures and 0 errors in 0.119 seconds.
If there was an error with one or more of the tests, you'd be told here!
Please refer to the testing tutorial for more about writing tests - and writing good tests - and how to run them.
3. Other useful Archetypes features
Complementary features you'd be pleased to know about.
3.1. How to use events to hook the Archetypes creation process
Times have changed since the days of at_post_create_script(). Here is the way to hook into Zope3 (or Five's) event system in order to execute code during the Archetypes content creation and or editing process.
In the old days the only way to execute code during the object creation process for Archetypes was to add a method to your content type called at_post_create_script. In this script you would add any code that should execute after Archetypes was done creating the object.
The new method for hooking the Archetypes object creation and editing process is to use Zope3 style events, like Products.Archetypes.interfaces.IObjectInitializedEvent.
Prerequisites
Have a content type handy so we can add a post creation hook to it. To learn how to create a content type, check previous sections of this manual.
We're going to use a content type called ExampleContent with the interface IExampleContent for this how to. The code structure will look like this:
tutorial/configure.zcml tutorial/interfaces.py tutorial/content/examplecontent.py
Step by step
First let's create the interface for our ExampleContent type. In interfaces.py, add:
from zope.interfaces import Interface
class IExampleContent(Interface):
''' Interface for the ExampleContent type
'''You can store the implementation for your event handlers anywhere but for the purpose of this example we're going to put it in the same module as the ExampleContent type:
from zope.interface import implements
from Products.ATContentTypes import atct
def addSubFolder(obj, event):
obj.invokeFactory(type_name='Folder', id='subfolder')
class ExampleContent(atct.ATFolder):
implements(IExampleContent)
portal_type = archetype_name = 'ExampleContent' # <-- this is no longer needed with GenericSetup.All we need to do now is register the addSubFolder method as a handler for Products.Archetypes.interfaces.IObjectInitializedEvent and for anything implementing the IExampleContent interface. We do this in a configure.zcml file:
<subscriber for=".interfaces.IExampleContent
Products.Archetypes.interfaces.IObjectInitializedEvent"
handler=".content.examplecontent.addSubFolder" />Notice that there are two interfaces in the "for" attribute. This is because we are register a multi-adapter. Now when you add an ExampleContent type the addSubFolder method will be executed after Archetypes has created the object. The object itself will be passed to the handler and we can use the object reference to make additional modifications, in this case adding a sub folder.
You can register as many handlers as you need.
Warnings from your future
Having implemented all of your content type's event hooks you might then run off and try using invokeFactory somewhere in your code only to realize that your IObjectInitializedEvent handlers are not being executed. This is because invokeFactory does not notify Zope's event system that new objects are being created. You have to provide these notifications yourself. So here is an example:
import zope.event from Products.Archetypes.event import ObjectInitializedEvent some_folder.invokeFactory(type_name='ExampleContent', id='foobar') obj = getattr(some_folder, 'foobar') zope.event.notify(ObjectInitializedEvent(obj))
This will both create your object and invoke any IObjectInitializedEvent handlers you have registered. Notice that we are importing ObjectInitializedEvent, not the interface IObjectInitializedEvent. We want to actually instantiate an event passing it our newly created object as the single parameter and then pass the event to zope.event.notify. From there, Zope takes care of figuring out which handlers need to execute.
Further information
The IObjectInitializedEvent is fired once during the objects creation process. To hook the editing process for an object use IObjectEditedEvent.
The Sending and handling events tutorial is a little out of date but provides a broader explanation of the underlying mechanics. Walking through Five to Zope 3 - Events is another great introduction to events handling.
4. Fields
This section describes what Archetypes Fields are, how they work, and how to use them. It also contains reference sections listing all available field types and the related widgets.
4.1. Fields Reference
Attributes of standard Archetypes fields.
Topics | ||
|---|---|---|
| ||
Common Field Attributes
These attributes are common to nearly all fields. Field-specific attributes follow, and are listed by field. Particular fields have different defaults, types, and some other specialized attributes.
| Name | Description | Possible Values | Default |
|---|---|---|---|
| accessor | The name of a class method that will return the value of the field. Use this to
change how the field is retrieved. If you don't provide a custom method
name here, a default accessor, named getYourFieldName, is going to be created
that just returns the value of the Field. | A class method name; for example, specialGetMethod | None |
| default | The default value for the field. | Type should be appropriate to the field. | None |
| default_method | The name of a class method returning a value for the field. | A class method name; for example, getSpecialDescription. | None |
| edit_accessor | The name of a class method that returns the raw value of a field. | Any method name (for example, rawGetMethod). | None |
| enforceVocabulary | Determines whether or not values outside the vocabulary will be accepted.
If True, Archetypes will validate input for the field against the vocabulary.
Only values already in the vocabulary will be accepted. | True or False. | False |
| index (Plone < 3 only) | If you want this field to be placed in its own catalog index, then specify the type of index here as a string. If you append :schema onto the end of the schema, then this will also be added as a metadata column. (The actual index will be on the field accessor, typically "getFieldName".) Ignored in Plone 3+; use GenericSetup profile for similar functionality. | The name of any index, such as KeywordIndex or KeywordIndex:schema. | None |
| index_method | May be used to specify the method called when indexing a field. Use '_at_accessor' to use the default accessor, '_at_edit_accessor' to use the edit accessor, or the name of a method returning the value to be indexed. | _at_accessor, _at_edit_accessor, getIndexAccessor and getIndexAccessorName | _at_accessor |
| languageIndependent | Flag for Fields that are independent of the language, such as dates. True tells LinguaPlone that no translation is necessary for this field. | True or False | False |
| isMetadata | Marks metadata fields. This is currently only needed as a convenience for the filterFields method of Schema. Fields marked as metadata are not displayed in the uncustomized base view. | True or False | False |
| mode | The read/write mode of field, as a string; the default is to be read and write.
Accessors will not be created without the read mode, and Mutators will not be created without the write mode. | For read only: r, for write only: w, for read and write: rw. | rw |
| multiValued | Set this to True if the field can have multiple values. This is the case for fields like multiple-selection lists that allow the selection of multiple values. | True or False. | False |
| mutator | The string name of a class method that changes the value of the Field. If you don't provide a special method name here, a default mutator is generated with the name 'setYourFieldName' to simply store the value. | A class method name; for example, specialSetMethod. | None |
| name | A unique name for this field.
Usually specified as the first item in the field definition.
| Any string. Strongly recommended: lowercase, no punctuation or spaces,
conforming to standard Python identifier rules.
For example, description, user_name, or coffee_bag_6. | No default. |
| primary | If True, this will be the field that used for File Transfer Protocol (FTP) and WebDAV requests. There can be only field that does this; if multiple are defined, the first one in the schema will be used. You normally set this for the main body attribute. Only used for TextField and FileField field types. | True or False | False |
| read_permission | The permission required for the current user to allowed to view or access the field. Only useful if the read mode is activated. This read permission is checked when rendering the widget in read mode. | A permission identifier imported from Products.CMFCore.permissions | View |
| required | Specifies that some value for this field required. | True or False. | False |
| schemata | Use named schematas to organize fields into grouped views. | A short string that labels the group. | default |
| searchable | Specifies whether or not the field value will be indexed as part of the SearchableText for the content object. SearchableText is what is checked by the portal's main search. | True or False. | False |
| storage | The storage mechanism for the field. The default is Attribute Storage, which stores the field as an attribute of the object. | Any valid storage object such as AttributeStorage or SQLStorage.
You can find these in the Archetypes Application Programming Interface (API). | AttributeStorage |
| type | Provided by the field class.. Should never be changed in a Schema. | None | None |
| validators | A list or tuple of strings naming validators that will check field input.
If you only have one validator, you may specify it as a string. Validators may also be instances of a class implementing the IValidator interface from from Products.validation.interfaces.IValidator. Providing a class instance allows you more flexibility as you may set additional parameters. Validators are checked in order specified. | The names of validators registered via Products.validation; for example, isEmail. | () |
| vocabulary |
Provides the values shown in selection and multi-selection inputs.
This may be specified as a static list or as the name of a class method returning
the choice list. | A list of strings (in which case keys and values will be the same); a list of 2-tuples of strings [("key1","value 1"),("key 2","value 2"),...]; a Products.Archetypes.utils.DisplayList. Or, the name of a class method returning any of the above. | () |
| vocabulary_factory | Like the vocabulary attribute, in Plone 3 provides the values shown in selection and multi-selection inputs. | A string name of a Zope 3 style vocabulary factory (a named utility providing zope.schema.interfaces.IVocabularyFactory) | None |
| widget | The widget that will be used to render the field for viewing and editing.
See the widget reference for a list of available widgets. | An instance of a widget; for example, StringWidget(). | StringWidget() |
| write_permission | The permission required for the current user to edit the field value. Only interesting if the write mode is activated. The write permission is checked when rendering the widget in write mode. | A permission identifier imported from Products.CMFCore.permissions | ModifyPortalContent |
Standard Fields
BooleanField
Simple storage of True or False for a field.
Standard properties| Name | Type | Default | Description | example values |
|---|---|---|---|---|
| widget | widget | BooleanWidget | Implemented as a check box. | |
| default | boolean | False | ||
| type | boolean |
Note: The required attribute for the boolean field is often confusing. It does not require that the box be checked. Use a validator if you need to require the box be checked.
ComputedField
Read-only field, whose content cannot be edited directly by users, but is
computed instead from a Python expression. For example, it can be the result of
an operation on the contents from some other fields in the same schema,
e.g. calculating the sum of two or more currency amounts, or composing a
full name from first name and surname.
This field is usually not stored in the database, because its content is
calculated on the fly when the object is viewed.
| Name | Type | Default | description | some possible values |
|---|---|---|---|---|
| widget | widget | ComputedWidget | ||
| storage | storage | ReadOnlyStorage | ||
| type | computed | |||
| mode | string | r |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| expression | Evaluated on the object to compute a value. |
CMFObjectField
Used for storing values inside a CMF Object, which can have workflow. Can only be used for BaseFolder-based content objects.
Standard properties| Name | Type | Default | description | some possible values |
|---|---|---|---|---|
| widget | widget | FileWidget | ||
| storage | storage | ObjectManagedStorage | ||
| type | object |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| portal_type | File | |||
| workflowable | True | |||
| default_mime_type | application/octet-stream |
DateTimeField
Used for storing dates and times.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | CalendarWidget | ||
| default | DateTime | |||
| type | datetime |
Note: The default for the DateTimeField needs to be specified as a DateTime object. If you need to set the current date/time as the default, you'll need to use the default_method attribute to specify a class method returning the current date/time as a DateTime object.
Example:
from DateTime.DateTime import DateTime
...
# inside the schema definiton
DateTimeField('dateAdded',
searchable = 1,
required = 0,
default_method = 'getDefaultTime',
widget = CalendarWidget(
label = 'Date Added'
),
),
...
def getDefaultTime(): # function to return the current date and time
return DateTime()
FileField
Storage for large chunks of data such as plain-text files, office-automation documents, and so on.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | FileWidget | ||
| default | string | |||
| type | file |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| primary | False | |||
| default_content_type | application/octet | |||
| primary | boolean | False | Set this True to mark the field as primary for FTP or WebDAV. |
Note: File field values are stored as strings. It's a common practice to use streams to read/write the values as if they were files.
FixedPointField
For storing numerical data with fixed points.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | DecimalWidget | ||
| validators | validators | isDecimal | ||
| default | string | 0.00 | ||
| type | fixedpoint |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| precision | 2 |
FloatField
For storing numerical data with floating points.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| default | string | 0.0 | ||
| type | float |
ImageField
Stores an image and allows dynamic resizing of the image.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | ImageWidget | ||
| default | string | |||
| type | image | |||
| allowable_content_types | tuple of MIME strings | Specifies the types of images that will be allowed. | ('image/gif','image/jpeg','image/png') | ('image/jpeg','image/png') |
Note: Image field values are stored as strings. It's a common practice to use streams to read/write the values as if they were files.
Special properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| original_size | tuple (w,h) | None | The size to which the original image will be scaled. If it's None, then no scaling will take place; the original size will be retained. Caution: the aspect ratio of the image may be changed. | (640,480) |
| max_size | tuple (w,h) | None | If specified then the image is scaled to be no bigger than either of the given values of width or height. Aspect ratio is preserved. Useful to prevent storage of megabytes of unnecessary image data. | (1024,768) |
| sizes | dict | {'thumb':(80,80)} | A dictionary of scales in which the image will be available. Dictionary entries should be of the form 'scaleName':(width,height). | { 'mini' : (80,80), 'normal' : (200,200), 'big' : (300,300), 'maxi' : (500,500)} |
| pil_quality | integer | 88 | A JPEG quality setting (range 0 to 100). Lower numbers yield high compression and low image quality. High numbers yield low compression and better quality. | 50 (a medium quality) |
Using Image Scales
To display the original image (possibly rescaled if you used original_size or
max_size attributes), you may use a URL like:
http://url_of_content_object/imageFieldName
as the SRC attribute of an IMG tag where url_of_content_object is the
URL of the content object and imageFieldName is the name
of the image field.
To display one of the scales, use a URL like:
http://url_of_content_object/imageFieldName_scale
where scale is one of the keys of the sizes dictionary.
Attention: The direct attribute access as shown above works only together with AttributeStorage, which will be used by default. To avoid heavy memory consumption on sites with many images it is recommended to use AnnotationStorage for the ImageField.
You may also generate a ready-to-insert IMG tag with the python code:
obj.getField('image').tag(obj, scale='mini')
if obj is your content object, image the name of your image
field, and mini the name of your scale.
You may rescale to other sizes than those in the sizes field attribute with code like:
obj.getField('image').tag(obj, height=480, width=640, alt='alt text',
css_class='css_class_selector', title='html title attribute')
IntegerField
For storing numerical data as integers.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | IntegerWidget |
| |
| default | integer | 0 | ||
| type | integer |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| size | 10 | Sets the size of the input field. |
LinesField
Used for storing text as a list, for example a list of data such as keywords.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | LinesWidget | ||
| default | string | () | ||
| type | lines |
ReferenceField
Used for storing references to other Archetypes Objects.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| widget | widget | ReferenceWidget | ||
| index_method | _at_edit_accessor | |||
| type | reference | |||
| multiValued | boolean | False | Set multiValued True to allow multiple references (one-to-many), or False to allow only a single reference (one-to-one). |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| relationship | Specifes an identifier for the type of relationships associated with the field. This should be unique within your content type, but has no larger meaning. A ReferenceField allows you to edit the set of references with a particular relationship identifier from the current content object to other objects. | 'KnowsAbout', 'Owns', 'WorksWith' | ||
| allowed_types | tuple of portal types | () | Determines all the portal types that will be searched to find objects that the user can make a reference to. It also specifies the Types that should be allowed to be added directly from the reference widget. This is only activated if the addable property is set. An empty list or tuple will allow references to all portal types. | ('Document', 'File') |
| allowed_types_method | string | None | A string containing the name of a class method that will return a list of portal types to which references are allowed. | |
| vocabulary_display_path_bound | integer | 5 | Sets a limit for presentation of reference items. Up to this limit, only titles are displayed. Above the limit, the path to the referenced object is also displayed. The idea is that if there are a large number of referenced items, the user will need help to differentiate them. | |
| vocabulary_custom_label | string | None | A string containing a python expression that will be evaluated to get the displayed text for a referenced item. Your expression may use the variable "b" which will be a reference to the catalog brain returned by the reference lookup. | "b.getObject().title_or_id()" |
StringField
A field for plain-text, unformatted strings.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| default | string | |||
| type | string | |||
| widget | widget | StringWidget |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| default_content_type | string MIME type | text/plain | Rarely changed. |
TextField
A string field typically used for longer, multi-line strings. The string may also be transformed into alternative formats.
Standard properties| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| default | string | |||
| type | text | |||
| widget | widget | StringWidget |
| Name | Type | Default | Description | some possible values |
|---|---|---|---|---|
| primary | boolean | False |
Set this True to mark the field as primary for FTP or WebDAV.
| |
| default_content_type | string MIME type | text/plain | A string designating MIME the default input type for the field. | text/plain, text/html |
| allowable_content_types | tuple of MIME-type strings | ('text/plain',) | Used in the TextArea and Rich widgets to let the user choose between different text formats in which the content is entered. | ('text/plain', 'text/html',) |
| default_output_type | string MIME type | text/plain | This is used by the accessor (get) method to and decides which MIME-Type the content should be transformed into if no special MIME-Type is demanded. | 'text/html', 'text/x-html-safe' |
4.2. Widgets Reference
This page is a syntax reference and general guide for defining and using Widgets.
Widget Attribute Topics | ||
|---|---|---|
Common Widget Attributes
The table below describes attributes common to nearly all widgets. Illustrations and special attributes listings for each of the standard widgets follows.
| Name | Description | Possible Values |
|---|---|---|
| condition | A string containing a TALES expression to determine whether or not a field/widget is
included on a view or edit page.
This does not distinguish between view and edit mode. | Your TALES expression may referenc the current context as 'object' and the Plone site root as 'portal' |
| description | Help or explanatory text for the field. Usually shown on the edit form under the label and above the input field. | |
| description_msgid | The i18n identifier for the description message. Used to translate the message. Should be unique within your product's i18n domain. | 'help_type_field' |
| label | The label that will appear in the field. | Any string, for example, Start Date for a field start_date. Also label_msgid (takes string message ids for i18n.) |
| label_msgid | The i18n identifier for the label message. Should be unique within your product's i18n domain. | 'label_type_field' |
| i18n_domain | The i18n domain specifier for your product. This should be unique for your product, and will be used to find the translation catalogs for your product. | 'productname' |
| modes | The modes that this widget will be shown in; by default there are two modes: view and edit. | A list of modes as strings; by default ("view", "edit"). |
| populate | If this is enabled, the view and edit fields will be populated. Usually this is enabled, but for fields such as a password field, this shouldn't be the case. Usually this is true by default. | True or False |
| postback | If this is enabled, then when an error is raised, the field is repopulated; for fields such as a password field, this shouldn't be the case. Usually this is True by default. | True or False |
| visible | Determines whether or not the field is visible view and edit mode. This is a dictionary mapping the view mode to a string describing the visibility. Choices are visible, hidden (rendered in an HTML hidden form value), invisible (not rendered at all). | For example, {'view': 'visible', 'edit': 'hidden' } means that the view will show, but the edit page will hide the value. |
Standard Widgets
BooleanWidget
Renders an HTML checkbox, from which users can choose between two values such as on/off, true/false, yes/no.
CalendarWidget
Renders a HTML input box with a helper popup box for choosing dates.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| format | string | Defines the date/time format using strftime, e.g. '%d.%m.%Y', for the view.
(See the strftime section of the Python time documentation.
If this is not specified, the long form of the portal's local time format is used. | |
| future_years | integer | 5 | Specifies the number of future years offered by the year drop-down portion of the date widget. Do not use both future_year and end_year. (Plone 2.5+) |
| starting_year | integer | 1999 | The first year offered by the year drop-down. (Plone 2.5+) |
| ending_year | integer | None | The final year offered by the year drop-down. Do not use both future_years and end_year. (Plone 2.5+) |
| show_hm | boolean | True | Should the widget ask for a time as well as a date? (Plone 2.5+) |
ComputedWidget
Generally used for ComputedField field type, it renders the computed value. Note that if your field has a vocabulary, and the field value is a key in that vocabulary, the widget will lookup the key in the vocabulary and show the result.
Standard Properties
| Name | Type | Default | Description |
|---|---|---|---|
| modes | tuple | ('view', 'edit') | As ComputedField is a read-only field, this property can be used to prevent the widget from appearing in edit templates, by setting it to just ('view',). |
DecimalWidget
In edit mode, renders an HTML text input box which accepts a fixed point value.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| thousands_commas | boolean | False | In view mode, formats the value to shows commas for thousands. For example, when thousands_commas is True, "7632654849635.02" is displayed as "7,632,654,849,635.02". (Note: this feature is not localized; it uses commas independent of locale. |
| whole_dollars | boolean | False | Shows whole dollars in view, leaving out the cents. Enter "1.123", and "$1" is shown. (Note: this feature is not localized; it uses the dollar sign independent of locale.) |
| maxlength | 255 | Maximum input size; sets the HTML input tag's maxlength attribute. | |
| dollars_and_cents | boolean | False | In view mode, shows dollars and cents. Enter "123.123" and "$123.12" is shown. (Note: this feature is not localized; it always uses the dollar sign, period, and two digits precision.) |
| size | 5 | Size of the input field; sets the HTML input tag's size attribute. |
FileWidget
Renders an HTML widget so a user can upload a file.
ImageWidget
Renders an HTML widget that can be used to upload, display, delete, and replace images. You can provide a display_threshold that allows you to set the size of an image; if it's below this size, the image will display in the Web page.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| display_threshold | integer | 102400 | Only display the image inline if img.getSize() <= display_threshold |
InAndOutWidget
In edit mode, renders a widget for moving items from one list to another. Items are removed from the source list. This can be used to choose multiple values from a list. This provides a good alternative to the MultiSelectionWidget when the vocabulary is too long for checkboxes.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| size | 6 | Size of the select widget; sets the HTML select tag's size attribute. |
| Name | Type | Default | Description |
|---|---|---|---|
| maxlength | 255 | Maximum input size; sets the HTML input tag's maxlength attribute | |
| size | 5 | Size of the input field; sets the HTML input tag's size attribute. |
KeywordWidget
This widget allows the user to select keywords or categories from a list. It is
used for the Categories field in the Categorization Schema (Plone 3+)
or the equivalent Keywords field on the Properties Tab (Plone < 3)
of a content object.
Keywords are drawn from the field vocabulary and/or the unique values for the
field in a specified catalog.
Additional keywords may be added unless the enforceVocabulary property of the
field is True.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| vocab_source | portal_catalog | Sets the catalog to search for additional vocabulary to be combined with the vocabulary defined for the field. Additional keywords from existing content are found using catalog.uniqueValuesFor(fieldName). | |
| roleBasedAdd | True | Only shows the "New keywords" input for adding keywords if the current user has one of the roles stored in the allowRolesToAddKeywords property in the site_properties property sheet in portal_properties |
LabelWidget
Used to display labels on forms -- without values or form input elements.
LinesWidget
Displays a text area so that users can enter a list of values, one per line.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| rows | integer | 5 | Rows of the lines widget; sets the HTML textarea tag's rows attribute. |
| cols | integer | 40 | Columns of the lines widget; sets the HTML textarea tag's cols attribute. |
MultiSelectionWidget
A selection widget; by default it's an HTML select widget which can be used to choose multiple values. As a checkbox users can choose one or more values from a list (useful if the list is short).
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| format | string | select | Possible values: 'select' or 'checkbox'. Uses a either a series of checkboxes or a multi-selection list. Note that checkboxes have much better usability for short vocabularies. Consider using the InAndOutWidget for longer vocabularies. |
| size | 5 | Defines the size of the multi-select list. Does not apply for checkboxes. |
PasswordWidget
Renders an HTML password input.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| maxlength | 255 | Maximum input size; sets the HTML input tag's maxlength attribute. | |
| size | 20 | Size of the input field; sets the HTML input tag's size attribute. |
Standard Properties
| Name | Type | Default |
|---|---|---|
| populate | boolean | False |
| postback | boolean | False |
| modes | ('edit',) |
PicklistWidget
Similar to the InAndOutWidget, but the values stay in the source list after selection.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| size | integer | 6 | Size of the selection box; sets the HTML select tag's size attribute. |
ReferenceWidget
Renders an HTML text input box which accepts a list of possible reference
values. Used in combination with the Reference Field.
Note: In Plone 2.5 and above, the ReferenceBrowserWidget is
a usually a better choice for a reference widget due to its ability to browse for content
referenceable objects.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| checkbox_bound | 5 | When the number of items exceeds this value, multi-selection lists are used. Otherwise, radio buttons or checkboxes are used. | |
| destination | None | May be:
| |
| addable | False | Create createObject link for every addable type | |
| destination_types | None | Either a single type given as a string, or a list of types given as a string, defining what types we allow adding to. Only applies when addable is set on the widget. |
ReferenceBrowserWidget
A sophisticated widget for browsing, adding and deleting references.
Standard in Plone 2.5+, available for earlier versions as an add-on product.
Import from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| size | integer | Size of the field if not multiValued; sets the HTML input tag's size attribute. | |
| default_search_index | string | SearchableText | when a user searches in the popup, this index is used by default |
| show_indexes | boolean | False | If True, a drop-down list is shown in the popup to select the index used for searching. If set to False, default_search_index will be used. |
| available_indexes | dict | {} |
Optional dictionary containing all the indexes that
can be used for searching along with their friendly names.
Format: {'catalogindex':'Friendly Name of Index', ... }
The friendly names are shown in the widget. Caution: If you set show_indexes True, but do not use this property to specify indexes, then all the indexes will be shown. |
| allow_search | boolean | True | If True, a search form is included in the popup. |
| allow_browse | True | Allows the user to browse content to find referenceable content. | |
| startup_directory | string | '' | Directory shown when the popup opens. Optional. When empty, the current folder is used. See the ATReferenceBrowser readme.txt for advanced usage. |
| base_query | dict or name of method | Defines query terms that will apply to all searches, mainly useful to create specific restrictions when allow_browse=0. Can be either a dictonary with query parameters, or the name of a method or callable available in cotext that will return such a dictionary. | |
| force_close_on_insert | boolean | False | If true, closes the popup when the user choses insert. This overrides the default behavior in multiselect mode. |
| search_catalog | string | 'portal_catalog' | Specifies the catalog used for searches |
| allow_sorting | boolean | False | Allows changing the order of referenced objects (requires multiValued). |
| show_review_state | boolean | False | If True, popup will display the workflow state for objects. |
| show_path | boolean | False | If True, display the relative path (relative to the portal object) of referenced objects. |
| only_for_review_states | None | If set, content items are only referenceable if their workflow state matches one of the specified states. If None there will be no filtering by workflow state. | |
| image_portal_types | sequence | () | Use to specify a list of image portal_types. Instances of these portal types are previewed within the popup widget |
| image_method | string | None | Specifies the name of a method that is added to the image URL to preview the image in a particular resolution (e.g. 'mini' for thumbnails). |
| history_length | integer | 0 | If not zero, enables a history feature that show the paths of the last N visited folders. |
| restrict_browsing_to_startup_directory | boolean | False | If True, the user will not be able to browse above the starting directory. |
RichWidget
Allows the input of text, or upload of a file, in multiple formats that are then transformed as necessary for display. For example, allows you to type some content, choose formatting and/or upload a file. If available, the visual editor set in personal preferences is used for editing and formatting.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| rows | integer | 5 | Number of rows. (Since the visual mode of the RichWidget is controlled by JavaScript, this is not very useful.) |
| cols | integer | 40 | Number of columns. (Since the visual mode of the RichWidget is controlled by JavaScript, this is not very useful.) |
| allow_file_upload | boolean | True | If True, a file upload option is included with the field. |
SelectionWidget
Renders an HTML selection widget, which can be represented as a dropdown, or as a group of radio buttons.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| format | string | 'flex' | Possible values: 'flex', 'select', 'radio'. Uses radio buttons when set to radio, and a single-selection list when set to select. Using flex will automatically use single-selection lists for more than three settings at a time, and a single-select list for up to three settings. |
StringWidget
Renders an HTML text input box which accepts a single line of text. For simple text lines such as author.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| maxlength | integer | 255 | Maximum input length in characters; sets the HTML input tag's maxlength attribute. |
| size | 30 | Size of the input widget; sets the HTML input tag's size attribute. |
TextAreaWidget
Renders an HTML text area for typing a few lines of text. Also provides for the entry of the content in multiple formats when allowed_content_types in the enclosing TextField allows it.
Special Properties
| Name | Type | Default | Description |
|---|---|---|---|
| rows | integer | 5 | Number of rows for the edit widget; sets the HTML textarea tag's rows attribute. |
| cols | integer | 40 | Column width of the edit widget; sets the HTML textarea tag's cols attribute. |
| append_only | boolean | False | Set this attribute to True to make an append-only TextArea widget. New text gets added to the top of the existing text, dividing the new text from the existing text using the divider property. The existing text is shown below the TextArea, and is not editable. This currently works with TextArea widgets and using plain text format. |
| divider | string | ======================== | Divider text marker to use for append only text areas. Only used then the append_only property is True. |
| maxlength | integer | False | If non-zero, sets a maximum input length in characters. Since the HTML textarea tag has no maxlength property, this is enforced via a JavaScript snippet. So, it is is not applicable when JavaScript is unavailable. |
Add-on Widgets
To find all available add-on widgets contributed by the community, follow this link.
4.3. Validator Reference
A quick reference to the built-in Archetypes validators.
Using Validators
Archetypes fields may have validators specified in the Field schema. For example, the schema for the basic page type includes the stanza:
ATDocumentSchema = ATContentTypeSchema.copy() + Schema((
TextField('text',
...
validators = ('isTidyHtmlWithCleanup',),
...
),
This specifies that the isTidyHtmlWithCleanup test will be applied to validate form input.
You may specify a sequence of validators:
validators = ('isMaxSize', 'isTidyHtmlWithCleanup',),
and the validators will tested in order.
The validators sequence may contain two kinds of entries:
- The string names of validators registered with the validation service (see Products.validation);
- Instances of classes implementing the IValidator interface (Products.validation.interfaces.IValidator.IValidator).
A validation specification using a validator class instance can look like:
validators = ( ExpressionValidator('python: int(value) == 5'), )
Registered Validators
These are validators pre-registered with the validation service. They may be specified by name.
| Name | Use | Details |
|---|---|---|
| isDecimal | Is the input a decimal number. | Allows exponent notation. |
| isInt | Is the input an integer. | |
| isPrintable | Does not contain unprintable characters | r'[a-zA-Z0-9\s]+$' |
| isSSN | Is a well-formed social-security number | Very naive: r'^\d{9}$' |
| isUSPhoneNumber | Is a valid US phone number | Looks for 10 digits, ignores spaces, parens and dashes |
| isInternationalPhoneNumber | Is a valid international phone number | Looks for any number of digits, ignores spaces, parens and dashes |
| isZipCode | Very naive: is five or nine digits | |
| isURL | Is a valid URL | Recognizes most protocols |
| isEmail | Is a valid e-mail address | A pretty good regular expression test |
| isMailTo | Is an e-mail address preceded by "mailto:" | |
| isUnixLikeName | Passes the basic test to be a Unix-style name | r"^[A-Za-z][\w\d\-\_]{0,7}$" |
| isMaxSize | Tests if an upload, file or something supporting len() is smaller than a given max size value. | Tests against a maxsize attribute on the field |
| isValidDate | Tests whether or not input value can be converted to a DateTime object. | |
| isEmpty | Input value must be empty. | |
| isEmptyNoError | Input value must be empty. | Validation will fail if input value is not empty; but no error will show. |
| isValidId | Input value is a valid identifier. | |
| isTidyHtml | Uses mx.Tidy to validate HTML input. Fails on errors and warnings. | |
| isTidyHtmlWithCleanup | Uses mx.Tidy to validate HTML input. Fails only on errors; cleans up. | |
| isNonEmptyFile | The uploaded file is not empty. | |
| isTAL | Validates as Template Attribute Language |
Useful Validation Classes
These classes are useful for creating your own validation class instances. Imports and prototypes are shown. See source for details.
ExpressionValidator
Evaluates an expression to test the input value.
from Products.validation.validators.ExpressionValidator import ExpressionValidator def ExpressionValidator(expression=None, errormsg=None)
RegexValidator
Tests value against a regular expression after removing ignorecharacters.
from Products.validation.validators.RegexValidator import RegexValidator def RegexValidator(name, regex, title=name, description='', errmsg='fails tests of %s' % name, ignore=None)
RangeValidator
Tests to see if specified minval <= input_value < maxval
from Products.validation.validators.RangeValidator import RangeValidator def RangeValidator(name, minval=0.0, maxval=0.0, title='', description='')















