<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><Project><name>plone.z3cform</name>
<shortdesc>plone.z3cform is a library that allows use of z3c.form with Zope 2 and the CMF.</shortdesc>
<description>=============
plone.z3cform
=============

plone.z3cform is a library that allows use of `z3c.form`_ with Zope 2
and the CMF_.

.. _z3c.form: http://pypi.python.org/pypi/z3c.form
.. _CMF: http://www.zope.org/Products/CMF


Quick start
===========

A quick example:

  &gt;&gt;&gt; from zope import interface, schema
  &gt;&gt;&gt; from z3c.form import form, field, button
  &gt;&gt;&gt; from plone.z3cform.layout import wrap_form

  &gt;&gt;&gt; class MySchema(interface.Interface):
  ...     age = schema.Int(title=u"Age")

  &gt;&gt;&gt; class MyForm(form.Form):
  ...     fields = field.Fields(MySchema)
  ...     ignoreContext = True # don't use context to get widget data
  ...     label = u"Please enter your age"
  ... 
  ...     @button.buttonAndHandler(u'Apply')
  ...     def handleApply(self, action):
  ...         data, errors = self.extractData()
  ...         print data['age'] # ... or do stuff

  &gt;&gt;&gt; MyView = wrap_form(MyForm)

Then, register ``MyView`` as a ``browser:page``.

The ``wrap_form`` function returns a browser view that embeds your
form in a CMF layout template.  See the ``layout`` module for details.

For more examples, please refer to the `z3c.form docs`_ and to `this
how-to`_.


.. _z3c.form docs: http://docs.carduner.net/z3c.form
.. _this how-to: http://plone.org/documentation/how-to/easy-forms-with-plone3

Crud
====

This module gives you an abstract base class to make CRUD forms with.
These forms give you by default a tabular view of the objects, where
attributes of the object can be edited in-place.  Please refer to the
``ICrudForm`` interface for more details.

  &gt;&gt;&gt; from plone.z3cform.crud import crud

Setup
-----

  &gt;&gt;&gt; from plone.z3cform.tests import setup_defaults
  &gt;&gt;&gt; setup_defaults()

A simple form
-------------

First, let's define an interface and a class to play with:

  &gt;&gt;&gt; from zope import interface, schema
  &gt;&gt;&gt; class IPerson(interface.Interface) :
  ...     name = schema.TextLine()
  ...     age = schema.Int()

  &gt;&gt;&gt; class Person(object):
  ...     interface.implements(IPerson)
  ...     def __init__(self, name=None, age=None):
  ...         self.name, self.age = name, age
  ...     def __repr__(self):
  ...         return "&lt;Person with name=%r, age=%r&gt;" % (self.name, self.age)

For this test, we take the the name of our persons as keys in our
storage:

  &gt;&gt;&gt; storage = {'Peter': Person(u'Peter', 16),
  ...            'Martha': Person(u'Martha', 32)}

Our simple form looks like this:

  &gt;&gt;&gt; class MyForm(crud.CrudForm):
  ...     update_schema = IPerson
  ... 
  ...     def get_items(self):
  ...         return sorted(storage.items(), key=lambda x: x[1].name)
  ... 
  ...     def add(self, data):
  ...         person = Person(**data)
  ...         storage[str(person.name)] = person
  ...         return person
  ... 
  ...     def remove(self, (id, item)):
  ...         del storage[id]

This is all that we need to render a combined edit add form containing
all our items:

  &gt;&gt;&gt; from z3c.form.testing import TestRequest
  &gt;&gt;&gt; print MyForm(None, TestRequest())() \
  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  &lt;div class="crud-form"&gt;...Martha...Peter...&lt;/div&gt;

Editing items with our form
---------------------------

Before we start with editing objects, let's log all events that the
form fires for us:

  &gt;&gt;&gt; from zope.lifecycleevent.interfaces import IObjectModifiedEvent
  &gt;&gt;&gt; from plone.z3cform.tests import create_eventlog
  &gt;&gt;&gt; log = create_eventlog(IObjectModifiedEvent)

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; request.form['crud-edit.Martha.widgets.select-empty-marker'] = u'1'
  &gt;&gt;&gt; request.form['crud-edit.Peter.widgets.select-empty-marker'] = u'1'
  &gt;&gt;&gt; request.form['crud-edit.Martha.widgets.name'] = u'Martha'
  &gt;&gt;&gt; request.form['crud-edit.Martha.widgets.age'] = u'55'
  &gt;&gt;&gt; request.form['crud-edit.Peter.widgets.name'] = u'Franz'
  &gt;&gt;&gt; request.form['crud-edit.Peter.widgets.age'] = u'16'
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.edit'] = u'Apply changes'
  &gt;&gt;&gt; html = MyForm(None, request)()
  &gt;&gt;&gt; "Successfully updated" in html
  True

Two modified events should have been fired:

  &gt;&gt;&gt; event1, event2 = log.pop(), log.pop()
  &gt;&gt;&gt; storage['Peter'] in (event1.object, event2.object)
  True
  &gt;&gt;&gt; storage['Martha'] in (event1.object, event2.object)
  True
  &gt;&gt;&gt; log
  []

If we don't make any changes, we'll get a message that says so:

  &gt;&gt;&gt; html = MyForm(None, request)()
  &gt;&gt;&gt; "No changes made" in html
  True
  &gt;&gt;&gt; log
  []

Now that we renamed Peter to Franz, it would be also nice to have
Franz use 'Franz' as the id in the storage, wouldn't it?

  &gt;&gt;&gt; storage['Peter']
  &lt;Person with name=u'Franz', age=16&gt;

We can override the CrudForm's ``before_update`` method to perform a
rename whenever the name of a person is changed:

  &gt;&gt;&gt; class MyRenamingForm(MyForm):
  ...     def before_update(self, item, data):
  ...         if data['name'] != item.name:
  ...             del storage[item.name]
  ...             storage[str(data['name'])] = item

Let's rename Martha to Maria.  This will give her another key in our
storage:

  &gt;&gt;&gt; request.form['crud-edit.Martha.widgets.name'] = u'Maria'
  &gt;&gt;&gt; html = MyRenamingForm(None, request)()
  &gt;&gt;&gt; "Successfully updated" in html
  True
  &gt;&gt;&gt; log.pop().object == storage['Maria']
  True
  &gt;&gt;&gt; log
  []
  &gt;&gt;&gt; sorted(storage.keys())
  ['Maria', 'Peter']

Next, we'll submit the form for edit, but we'll make no changes.
Instead, we'll select one time.  This shouldn't do anything, since we
clicked the 'Apply changes' button:

  &gt;&gt;&gt; request.form['crud-edit.Maria.widgets.name'] = u'Maria'
  &gt;&gt;&gt; request.form['crud-edit.Maria.widgets.age'] = u'55'
  &gt;&gt;&gt; request.form['crud-edit.Maria.widgets.select'] = [u'selected']
  &gt;&gt;&gt; html = MyRenamingForm(None, request)()
  &gt;&gt;&gt; "No changes" in html
  True
  &gt;&gt;&gt; log
  []

And what if we do have changes *and* click the checkbox?

  &gt;&gt;&gt; request.form['crud-edit.Maria.widgets.age'] = u'50'
  &gt;&gt;&gt; html = MyRenamingForm(None, request)()
  &gt;&gt;&gt; "Successfully updated" in html
  True
  &gt;&gt;&gt; log.pop().object == storage['Maria']
  True
  &gt;&gt;&gt; log
  []

If we omit the name, we'll get an error:

  &gt;&gt;&gt; request.form['crud-edit.Maria.widgets.name'] = u''
  &gt;&gt;&gt; html = MyRenamingForm(None, request)()
  &gt;&gt;&gt; "There were some errors" in html
  True
  &gt;&gt;&gt; "Required input is missing" in html
  True

We expect an error message in the title cell of Maria:

  &gt;&gt;&gt; checkbox_pos = html.index('crud-edit.Maria.widgets.select-empty-marker')
  &gt;&gt;&gt; "Required input is missing" in html[checkbox_pos:]
  True

Delete an item with our form
----------------------------

We can delete an item by selecting the item we want to delete and
clicking the "Delete" button:

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; request.form['crud-edit.Peter.widgets.select'] = ['selected']
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.delete'] = u'Delete'
  &gt;&gt;&gt; html = MyForm(None, request)()
  &gt;&gt;&gt; "Successfully deleted items" in html
  True
  &gt;&gt;&gt; 'Franz' in html
  False
  &gt;&gt;&gt; storage
  {'Maria': &lt;Person with name=u'Maria', age=50&gt;}

Add an item with our form
-------------------------

  &gt;&gt;&gt; from zope.lifecycleevent.interfaces import IObjectCreatedEvent
  &gt;&gt;&gt; from plone.z3cform.tests import create_eventlog
  &gt;&gt;&gt; log = create_eventlog(IObjectCreatedEvent)

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; request.form['crud-add.form.widgets.name'] = u'Daniel'
  &gt;&gt;&gt; request.form['crud-add.form.widgets.age'] = u'28'
  &gt;&gt;&gt; request.form['crud-add.form.buttons.add'] = u'Add'
  &gt;&gt;&gt; html = MyForm(None, request)()
  &gt;&gt;&gt; "Item added successfully" in html
  True

Added items should show up right away:

  &gt;&gt;&gt; "Daniel" in html
  True

  &gt;&gt;&gt; storage['Daniel']
  &lt;Person with name=u'Daniel', age=28&gt;
  &gt;&gt;&gt; log.pop().object == storage['Daniel']
  True
  &gt;&gt;&gt; log
  []

What if we try to add "Daniel" twice?  Our current implementation of
the add form will simply overwrite the data:

  &gt;&gt;&gt; save_daniel = storage['Daniel']
  &gt;&gt;&gt; html = MyForm(None, request)()
  &gt;&gt;&gt; "Item added successfully" in html
  True
  &gt;&gt;&gt; save_daniel is storage['Daniel']
  False
  &gt;&gt;&gt; log.pop().object is storage['Daniel']
  True

Let's implement a class that prevents this:

  &gt;&gt;&gt; class MyCarefulForm(MyForm):
  ...     def add(self, data):
  ...         name = data['name']
  ...         if name not in storage:
  ...             return super(MyCarefulForm, self).add(data)
  ...         else:
  ...             raise schema.ValidationError(
  ...                 u"There's already an item with the name '%s'" % name)

  &gt;&gt;&gt; save_daniel = storage['Daniel']
  &gt;&gt;&gt; html = MyCarefulForm(None, request)()
  &gt;&gt;&gt; "Item added successfully" in html
  False
  &gt;&gt;&gt; "There's already an item with the name 'Daniel'" in html
  True
  &gt;&gt;&gt; save_daniel is storage['Daniel']
  True
  &gt;&gt;&gt; len(log) == 0
  True

Render some of the fields in view mode
--------------------------------------

We can implement in our form a ``view_schema`` attribute, which will
then be used to view information in our form's table.  Let's say we
wanted the name of our persons to be viewable only in the table:

  &gt;&gt;&gt; from z3c.form import field
  &gt;&gt;&gt; class MyAdvancedForm(MyForm):
  ...     update_schema = field.Fields(IPerson).select('age')
  ...     view_schema = field.Fields(IPerson).select('name')
  ...     add_schema = IPerson

  &gt;&gt;&gt; print MyAdvancedForm(None, TestRequest())() \
  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  &lt;div class="crud-form"&gt;...Daniel...Maria...&lt;/div&gt;

We can still edit the age of our Persons:

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; request.form['crud-edit.Maria.widgets.age'] = u'40'
  &gt;&gt;&gt; request.form['crud-edit.Daniel.widgets.age'] = u'35'
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.edit'] = u'Apply Changes'
  &gt;&gt;&gt; html = MyAdvancedForm(None, request)()
  &gt;&gt;&gt; "Successfully updated" in html
  True

  &gt;&gt;&gt; storage['Maria'].age
  40
  &gt;&gt;&gt; storage['Daniel'].age
  35

We can still add a Person using both name and age:

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; request.form['crud-add.form.widgets.name'] = u'Thomas'
  &gt;&gt;&gt; request.form['crud-add.form.widgets.age'] = u'28'
  &gt;&gt;&gt; request.form['crud-add.form.buttons.add'] = u'Add'
  &gt;&gt;&gt; html = MyAdvancedForm(None, request)()
  &gt;&gt;&gt; "Item added successfully" in html
  True
  &gt;&gt;&gt; len(storage)
  3
  &gt;&gt;&gt; storage['Thomas']
  &lt;Person with name=u'Thomas', age=28&gt;

Our form can also contain links to our items:

  &gt;&gt;&gt; class MyAdvancedLinkingForm(MyAdvancedForm):
  ...     def link(self, item, field):
  ...         if field == 'name':
  ...             return 'http://en.wikipedia.org/wiki/%s' % item.name

  &gt;&gt;&gt; print MyAdvancedLinkingForm(None, TestRequest())() \
  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  &lt;div class="crud-form"&gt;...
  ...&lt;a href="http://en.wikipedia.org/wiki/Daniel"...
  ...&lt;a href="http://en.wikipedia.org/wiki/Maria"...
  ...&lt;a href="http://en.wikipedia.org/wiki/Thomas"...
  &lt;/div&gt;

What if we wanted the name to be both used for linking to the item
*and* for edit?  We can just include the title field twice:

  &gt;&gt;&gt; class MyAdvancedLinkingForm(MyAdvancedLinkingForm):
  ...     update_schema = IPerson
  ...     view_schema = field.Fields(IPerson).select('name')
  ...     add_schema = IPerson

  &gt;&gt;&gt; print MyAdvancedLinkingForm(None, TestRequest())() \
  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  &lt;div class="crud-form"&gt;...
  ...&lt;a href="http://en.wikipedia.org/wiki/Thomas"...Thomas...&lt;/a&gt;...
  &lt;/div&gt;

We can now change Thomas's name and see the change reflected in the
Wikipedia link immediately:

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; for name in 'Daniel', 'Maria', 'Thomas':
  ...     request.form['crud-edit.%s.widgets.name' % name] = unicode(storage[name].name)
  ...     request.form['crud-edit.%s.widgets.age' % name] = unicode(storage[name].age)
  &gt;&gt;&gt; request.form['crud-edit.Thomas.widgets.name'] = u'Dracula'
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.edit'] = u'Apply Changes'

  &gt;&gt;&gt; print MyAdvancedLinkingForm(None, request)() \
  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  &lt;div class="crud-form"&gt;...
  ...&lt;a href="http://en.wikipedia.org/wiki/Dracula"...Dracula...&lt;/a&gt;...
  &lt;/div&gt;
  &gt;&gt;&gt; storage['Thomas'].name = u'Thomas'

Don't render one part
---------------------

What if we wanted our form to display only one part, that is, only the
add *or* the edit form.  Our CrudForm can implement
``editform_factory`` and ``addform_factory`` to override one or both
forms.  Seeting one of these to ``crud.NullForm`` will make them
disappear:

  &gt;&gt;&gt; class OnlyEditForm(MyForm):
  ...     addform_factory = crud.NullForm
  &gt;&gt;&gt; html = OnlyEditForm(None, TestRequest())()
  &gt;&gt;&gt; 'Edit' in html, 'Add' in html
  (True, False)

  &gt;&gt;&gt; class OnlyAddForm(MyForm):
  ...     editform_factory = crud.NullForm
  &gt;&gt;&gt; html = OnlyAddForm(None, TestRequest())()
  &gt;&gt;&gt; 'Edit' in html, 'Add' in html
  (False, True)

Render only in view, and define own actions
-------------------------------------------

Sometimes you want to present a list of items, possibly in view mode
only, and have the user select one or more of the items to perform
some action with them.  We'll present a minimal example that does this
here.

We can simply leave the ``update_schema`` class attribute out (it
defaults to ``None``).  Furthermore, we'll need to override the
ediform_factory with our custom version that provides other buttons
than the 'edit' and 'delete' ones:

  &gt;&gt;&gt; from pprint import pprint
  &gt;&gt;&gt; from z3c.form import button

  &gt;&gt;&gt; class MyEditForm(crud.EditForm):
  ...     @button.buttonAndHandler(u'Capitalize', name='capitalize')
  ...     def handle_capitalize(self, action):
  ...         self.status = u"Please select items to capitalize first."
  ...         selected = self.selected_items()
  ...         if selected:
  ...             self.status = u"Capitalized items"
  ...             for id, item in selected:
  ...                 item.name = item.name.upper()

  &gt;&gt;&gt; class MyCustomForm(crud.CrudForm):
  ...     view_schema = IPerson
  ...     editform_factory = MyEditForm
  ...     addform_factory = crud.NullForm     # We don't want an add part.
  ... 
  ...     def get_items(self):
  ...         return sorted(storage.items(), key=lambda x: x[1].name)

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; html = MyCustomForm(None, TestRequest())()
  &gt;&gt;&gt; "Delete" in html, "Apply changes" in html, "Capitalize" in html
  (False, False, True)
  &gt;&gt;&gt; pprint(storage)
  {'Daniel': &lt;Person with name=u'Daniel', age=35&gt;,
   'Maria': &lt;Person with name=u'Maria', age=40&gt;,
   'Thomas': &lt;Person with name=u'Thomas', age=28&gt;}

  &gt;&gt;&gt; request.form['crud-edit.Thomas.widgets.select'] = ['selected']
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.capitalize'] = u'Capitalize'
  &gt;&gt;&gt; html = MyCustomForm(None, request)()
  &gt;&gt;&gt; "Capitalized items" in html
  True
  &gt;&gt;&gt; pprint(storage)
  {'Daniel': &lt;Person with name=u'Daniel', age=35&gt;,
   'Maria': &lt;Person with name=u'Maria', age=40&gt;,
   'Thomas': &lt;Person with name=u'THOMAS', age=28&gt;}

We *cannot* use any of the other buttons:

  &gt;&gt;&gt; del request.form['crud-edit.form.buttons.capitalize']
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.delete'] = u'Delete'
  &gt;&gt;&gt; html = MyCustomForm(None, request)()
  &gt;&gt;&gt; "Successfully deleted items" in html
  False
  &gt;&gt;&gt; 'Thomas' in storage
  True

Customizing sub forms
---------------------

The EditForm class allows you to specify an editsubform_factory-a classs 
inherits from EditSubForm.  This allows you to say, override the crud-row.pt
page template and customize the look of the fields.

  &gt;&gt;&gt; import zope.schema
  &gt;&gt;&gt; class MyCustomEditSubForm(crud.EditSubForm):
  ...
  ...     def _select_field(self):
  ...         """I want to customize the field that it comes with..."""
  ...         select_field = field.Field(
  ...         zope.schema.TextLine(__name__='select',
  ...                              required=False,
  ...                              title=u'select'))
  ...         return select_field

  &gt;&gt;&gt; class MyCustomEditForm(MyEditForm):
  ...     editsubform_factory = MyCustomEditSubForm

  &gt;&gt;&gt; class MyCustomFormWithCustomSubForm(MyCustomForm):
  ...     editform_factory = MyCustomEditForm

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; html = MyCustomFormWithCustomSubForm(None, TestRequest())()

Still uses same form as before
  &gt;&gt;&gt; "Delete" in html, "Apply changes" in html, "Capitalize" in html
  (False, False, True)

Just changes the widget used for selecting...
  &gt;&gt;&gt; 'type="checkbox"' in html
  False

Using batching
--------------

The CrudForm base class supports batching.  When setting the
``batch_size`` attribute to a value greater than ``0``, we'll only get
as many items displayed per page.

  &gt;&gt;&gt; class MyBatchingForm(MyForm):
  ...     batch_size = 2
  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; html = MyBatchingForm(None, request)()
  &gt;&gt;&gt; "Daniel" in html, "Maria" in html
  (True, True)
  &gt;&gt;&gt; "THOMAS" in html
  False

  &gt;&gt;&gt; request.form['crud-edit.form.page'] = '1'
  &gt;&gt;&gt; html = MyBatchingForm(None, request)()
  &gt;&gt;&gt; "Daniel" in html, "Maria" in html
  (False, False)
  &gt;&gt;&gt; "THOMAS" in html
  True

Let's change Thomas' age on the second page:

  &gt;&gt;&gt; request.form['crud-edit.Thomas.widgets.name'] = u'Thomas'
  &gt;&gt;&gt; request.form['crud-edit.Thomas.widgets.age'] = '911'
  &gt;&gt;&gt; request.form['crud-edit.form.buttons.edit'] = u'Apply changes'
  &gt;&gt;&gt; html = MyBatchingForm(None, request)()
  &gt;&gt;&gt; "Successfully updated" in html
  True
  &gt;&gt;&gt; "911" in html
  True

Fieldsets and extensible forms
==============================

The ``fieldsets`` package provides support for groups/fieldsets and other
modifications via "extender" adapters. The idea is that a third party
component can modify the fields in the form and the way that they are grouped
and ordered.

This support relies on a mixin class, which is itself a subclass of 
z3c.form's GroupForm.

    &gt;&gt;&gt; from plone.z3cform.fieldsets import group, extensible

To use this, you have to mix it into another form as the *first* base class:

  &gt;&gt;&gt; from zope.annotation import IAttributeAnnotatable
  &gt;&gt;&gt; from z3c.form import form, field, tests, group
  &gt;&gt;&gt; from zope.interface import Interface, implements
  &gt;&gt;&gt; from zope import schema

  &gt;&gt;&gt; class ITest(Interface):
  ...     title = schema.TextLine(title=u"Title")

  &gt;&gt;&gt; class Test(object):
  ...     implements(ITest, IAttributeAnnotatable)
  ...     title = u""

  &gt;&gt;&gt; class TestForm(extensible.ExtensibleForm, form.Form):
  ...     fields = field.Fields(ITest)

Here, note the order of the base classes. Also note that we use an ordinary
set of fields. This known as the default fieldset.

This form should work as-is, i.e. we can update it:

  &gt;&gt;&gt; from z3c.form.testing import TestRequest

  &gt;&gt;&gt; request = TestRequest()
  &gt;&gt;&gt; context = Test()

  &gt;&gt;&gt; form = TestForm(context, request)
  &gt;&gt;&gt; form.update()
  &gt;&gt;&gt; _ = form.render()

Now let's register an adapter that adds two new fields - one in the
default fieldset as the first field, and one in a new group. To do this,
we only need to register a named multi-adapter. However, we can use a 
convenience base class to make it easier to manipulate the fields of the
form.

  &gt;&gt;&gt; from plone.z3cform.fieldsets.interfaces import IFormExtender
  &gt;&gt;&gt; from zope.component import adapts, provideAdapter
  
  &gt;&gt;&gt; class IExtraBehavior(Interface):
  ...     foo = schema.TextLine(title=u"Foo")
  ...     bar = schema.TextLine(title=u"Bar")
  ...     baz = schema.TextLine(title=u"Baz")
  ...     fub = schema.TextLine(title=u"Fub")
  
One plausible implementation is to use an annotation to store this data.

  &gt;&gt;&gt; from zope.annotation import factory
  &gt;&gt;&gt; from zope.annotation.attribute import AttributeAnnotations
  &gt;&gt;&gt; from persistent import Persistent
  &gt;&gt;&gt; class ExtraBehavior(Persistent):
  ...     implements(IExtraBehavior)
  ...     adapts(Test)
  ...     
  ...     foo = u""
  ...     bar = u""
  ...     baz = u""
  ...     fub = u""
  
  &gt;&gt;&gt; ExtraBehavior = factory(ExtraBehavior)
  &gt;&gt;&gt; provideAdapter(ExtraBehavior)
  &gt;&gt;&gt; provideAdapter(AttributeAnnotations)
 
We can now write the extender. The base class gives us some helper methods
to add, remove and move fields. Here, we do a bit of unnecessary work just
to exercise these methods.
 
  &gt;&gt;&gt; class ExtraBehaviorExtender(extensible.FormExtender):
  ...     adapts(Test, TestRequest, TestForm) # context, request, form
  ...     
  ...     def __init__(self, context, request, form):
  ...         self.context = context
  ...         self.request = request
  ...         self.form = form
  ...     
  ...     def update(self):
  ...         # Add all fields from an interface
  ...         self.add(IExtraBehavior, prefix="extra")
  ...         
  ...         # Remove the fub field
  ...         self.remove('fub', prefix="extra")
  ...         
  ...         all_fields = field.Fields(IExtraBehavior, prefix="extra")
  ...         
  ...         # Insert fub again, this time at the top
  ...         self.add(all_fields.select("fub", prefix="extra"), index=0)
  ...         
  ...         # Move 'baz' above 'fub'
  ...         self.move('baz', before='fub', prefix='extra', relative_prefix='extra')
  ...         
  ...         # Move 'foo' after 'bar' - here we specify prefix manually
  ...         self.move('foo', after='extra.bar', prefix='extra')
  ...         
  ...         # Remove 'bar' and re-insert into a new group
  ...         self.remove('bar', prefix='extra')
  ...         self.add(all_fields.select('bar', prefix='extra'), group='Second')
  ...         
  ...         # Move 'baz' after 'bar'. This means it also moves gropu.
  ...         self.move('extra.baz', after='extra.bar')

  
  &gt;&gt;&gt; provideAdapter(factory=ExtraBehaviorExtender, name=u"test.extender")
    
With this in place, let's update the form once again.

  &gt;&gt;&gt; form = TestForm(context, request)
  &gt;&gt;&gt; form.update()

At this point, we should have a set of default fields that represent the
ones set in the adapter.

  &gt;&gt;&gt; form.fields.keys()
  ['extra.fub', 'title', 'extra.foo']
  
And we should have one group created by the group factory:

  &gt;&gt;&gt; form.groups # doctest: +ELLIPSIS
  (&lt;plone.z3cform.fieldsets.group.Group object at ...&gt;,)

Note that the created group is of a subtype of the standard z3c.form group,
which has got support for a separate label and description as well as a 
canonical name.

  &gt;&gt;&gt; isinstance(form.groups[0], group.Group)
  True

This should have the group fields provided by the adapter as well.

  &gt;&gt;&gt; form.groups[0].fields.keys()
  ['extra.bar', 'extra.baz']

Changelog
=========

0.5.6 - 2009-09-25
------------------

* Added title_required msgid in macros.pt to be the same as plone.app.z3cform
  because macros.pt from plone.app.z3cform uses plone.z3cform translations.
  Added French translation and fixed German and Dutch translations
  for label_required and title_required messages.
  [vincentfretin] 

0.5.5 - 2009-07-26
------------------

* Removed explicit &lt;includeOverrides /&gt; call from configure.zcml. This causes
  race condition type errors in ZCML loading when overrides are included 
  later.
  [optilude]

0.5.4 - 2009-04-17
------------------

* Added monkey patch to fix a bug in z3c.form's ChoiceTerms on z3c.form 1.9.0.
  [optilude]

* Fix obvious bugs and dodgy naming in SingleCheckBoxWidget.
  [optilude]

* Use chameleon-based page templates from five.pt if available.
  [davisagli]

* Copied the basic textlines widget from z3c.form trunk for use until
  it is released.
  [davisagli]

0.5.3 - 2008-12-09
------------------

* Add translation marker for batch, update translation files.
  [thefunny42]

* Handle changed signature for widget extract method in z3c.form &gt; 1.9.0
  [davisagli]

* Added wildcard support to the 'before' and 'after' parameters of the
  fieldset 'move' utility function.
  [davisagli]

* Fixes for Zope 2.12 compatibility.
  [davisagli]

* Don't display an 'Apply changes' button if you don't define an
  update_schema.
  [thefunny42]

* Declare xmlnamespace into 'layout.pt' and 'subform.pt' templates

* Added support for an editsubform_factory for an EditForm so you can
  override the default behavior for a sub form now.

* Changed css in crud-table.pt for a table to "listing" so that tables
  now look like plone tables.

* Copy translation files to an english folder, so if your browser
  negociate to ``en,nl``, you will get english translations instead of
  dutch ones (like expected).
  [thefunny42]

* Send an event IAfterWidgetUpdateEvent after updating display widgets
  manually in a CRUD form.
  [thefunny42]

0.5.2 - 2008-08-28
------------------

* Add a namespace traversal adapter that allows traversal to widgets. This
  is useful for AJAX calls, for example.

0.5.1 - 2008-08-21
------------------

* Add batching to ``plone.z3cform.crud`` CrudForm.

* Look up the layout template as an IPageTemplate adapter. This means that
  it is possible for Plone to provide a "Ploneish" default template for forms
  that don't opt into this, without those forms having a direct Plone
  dependency.

* Default to the titleless form template, since the layout template will
  provide a title anyway.

* In ``plone.z3cform.layout``, allow labels to be defined per form
  instance, and not only per form class.

0.5.0 - 2008-07-30
------------------

* No longer depend on &lt;3.5 of zope.component.

0.4 - 2008-07-25
----------------

* Depend on zope.component&lt;3.5 to avoid ``TypeError("Missing
  'provides' attribute")`` error.

* Allow ICrudForm.add to raise ValidationError, which allows for
  displaying a user-friendly error message.

* Make the default layout template CMFDefault- compatible.

0.3 - 2008-07-24
----------------

* Moved Plone layout wrapper to ``plone.app.z3cform.layout``.  If you
  were using ``plone.z3cform.base.FormWrapper`` to get the Plone
  layout before, you'll have to use
  ``plone.app.z3cform.layout.FormWrapper`` instead now.  (Also, make
  sure you include plone.app.z3cform's ZCML in this case.)

* Move out Plone-specific subpackages to ``plone.app.z3cform``.  These
  are:

  - wysywig: Kupu/Plone integration

  - queryselect: use z3c.formwidget.query with Archetypes

  Clean up testing code and development ``buildout.cfg`` to not pull
  in Plone anymore.
  [nouri]

* Relicensed under the ZPL 2.1 and moved into the Zope repository.
  [nouri]

* Add German translation.
  [saily]

0.2 - 2008-06-20
----------------

* Fix usage of NumberDataConverter with zope.i18n &gt;= 3.4 as the
  previous test setup was partial and did not register all adapters
  from z3c.form (some of them depends on zope &gt;= 3.4)
  [gotcha, jfroche]

* More tests
  [gotcha, jfroche]

0.1 - 2008-05-21
----------------

* Provide and *register* default form and subform templates.  These
  allow forms to be used with the style provided in this package
  without having to declare ``form = ViewPageTemplateFile('form.pt')``.

  This does not hinder you from overriding with your own ``form``
  attribute like usual.  You can also still register a more
  specialized IPageTemplate for your form.

* Add custom FileUploadDataConverter that converts a Zope 2 FileUpload
  object to a Zope 3 one before handing it to the original
  implementation.  Also add support for different enctypes.
  [skatja, nouri]

* Added Archetypes reference selection widget (queryselect)
  [malthe]

* Moved generic Zope 2 compatibility code for z3c.form and a few
  goodies from Singing &amp; Dancing into this new package.
  [nouri]</description>
<homepage rdf:resource="http://pypi.python.org/pypi/plone.z3cform" />
<maintainer><foaf:Person><foaf:name>Daniel Nouri and contributors</foaf:name>
<foaf:mbox_sha1sum>686355885d055fc3fefd3de55fe087f4516a4631</foaf:mbox_sha1sum></foaf:Person></maintainer>
<release><Version><revision>0.5.6</revision></Version></release>
</Project></rdf:RDF>