Metadata-Version: 1.0
Name: plone.z3cform
Version: 0.5.10
Summary: plone.z3cform is a library that allows use of z3c.form with Zope 2 and the CMF.
Home-page: http://pypi.python.org/pypi/plone.z3cform
Author: Daniel Nouri and contributors
Author-email: daniel.nouri@gmail.com
License: ZPL 2.1
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:
        
        >>> from zope import interface, schema
        >>> from z3c.form import form, field, button
        >>> from plone.z3cform.layout import wrap_form
        
        >>> class MySchema(interface.Interface):
        ...     age = schema.Int(title=u"Age")
        
        >>> 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
        
        >>> 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.
        
        >>> from plone.z3cform.crud import crud
        
        Setup
        -----
        
        >>> from plone.z3cform.tests import setup_defaults
        >>> setup_defaults()
        
        A simple form
        -------------
        
        First, let's define an interface and a class to play with:
        
        >>> from zope import interface, schema
        >>> class IPerson(interface.Interface) :
        ...     name = schema.TextLine()
        ...     age = schema.Int()
        
        >>> class Person(object):
        ...     interface.implements(IPerson)
        ...     def __init__(self, name=None, age=None):
        ...         self.name, self.age = name, age
        ...     def __repr__(self):
        ...         return "<Person with name=%r, age=%r>" % (self.name, self.age)
        
        For this test, we take the the name of our persons as keys in our
        storage:
        
        >>> storage = {'Peter': Person(u'Peter', 16),
        ...            'Martha': Person(u'Martha', 32)}
        
        Our simple form looks like this:
        
        >>> 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:
        
        >>> from z3c.form.testing import TestRequest
        >>> print MyForm(None, TestRequest())() \
        ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
        <div class="crud-form">...Martha...Peter...</div>
        
        Editing items with our form
        ---------------------------
        
        Before we start with editing objects, let's log all events that the
        form fires for us:
        
        >>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
        >>> from plone.z3cform.tests import create_eventlog
        >>> log = create_eventlog(IObjectModifiedEvent)
        
        >>> request = TestRequest()
        >>> request.form['crud-edit.Martha.widgets.select-empty-marker'] = u'1'
        >>> request.form['crud-edit.Peter.widgets.select-empty-marker'] = u'1'
        >>> request.form['crud-edit.Martha.widgets.name'] = u'Martha'
        >>> request.form['crud-edit.Martha.widgets.age'] = u'55'
        >>> request.form['crud-edit.Peter.widgets.name'] = u'Franz'
        >>> request.form['crud-edit.Peter.widgets.age'] = u'16'
        >>> request.form['crud-edit.form.buttons.edit'] = u'Apply changes'
        >>> html = MyForm(None, request)()
        >>> "Successfully updated" in html
        True
        
        Two modified events should have been fired:
        
        >>> event1, event2 = log.pop(), log.pop()
        >>> storage['Peter'] in (event1.object, event2.object)
        True
        >>> storage['Martha'] in (event1.object, event2.object)
        True
        >>> log
        []
        
        If we don't make any changes, we'll get a message that says so:
        
        >>> html = MyForm(None, request)()
        >>> "No changes made" in html
        True
        >>> 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?
        
        >>> storage['Peter']
        <Person with name=u'Franz', age=16>
        
        We can override the CrudForm's ``before_update`` method to perform a
        rename whenever the name of a person is changed:
        
        >>> 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:
        
        >>> request.form['crud-edit.Martha.widgets.name'] = u'Maria'
        >>> html = MyRenamingForm(None, request)()
        >>> "Successfully updated" in html
        True
        >>> log.pop().object == storage['Maria']
        True
        >>> log
        []
        >>> 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:
        
        >>> request.form['crud-edit.Maria.widgets.name'] = u'Maria'
        >>> request.form['crud-edit.Maria.widgets.age'] = u'55'
        >>> request.form['crud-edit.Maria.widgets.select'] = [u'selected']
        >>> html = MyRenamingForm(None, request)()
        >>> "No changes" in html
        True
        >>> log
        []
        
        And what if we do have changes *and* click the checkbox?
        
        >>> request.form['crud-edit.Maria.widgets.age'] = u'50'
        >>> html = MyRenamingForm(None, request)()
        >>> "Successfully updated" in html
        True
        >>> log.pop().object == storage['Maria']
        True
        >>> log
        []
        
        If we omit the name, we'll get an error:
        
        >>> request.form['crud-edit.Maria.widgets.name'] = u''
        >>> html = MyRenamingForm(None, request)()
        >>> "There were some errors" in html
        True
        >>> "Required input is missing" in html
        True
        
        We expect an error message in the title cell of Maria:
        
        >>> checkbox_pos = html.index('crud-edit.Maria.widgets.select-empty-marker')
        >>> "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:
        
        >>> request = TestRequest()
        >>> request.form['crud-edit.Peter.widgets.select'] = ['selected']
        >>> request.form['crud-edit.form.buttons.delete'] = u'Delete'
        >>> html = MyForm(None, request)()
        >>> "Successfully deleted items" in html
        True
        >>> 'Franz' in html
        False
        >>> storage
        {'Maria': <Person with name=u'Maria', age=50>}
        
        Add an item with our form
        -------------------------
        
        >>> from zope.lifecycleevent.interfaces import IObjectCreatedEvent
        >>> from plone.z3cform.tests import create_eventlog
        >>> log = create_eventlog(IObjectCreatedEvent)
        
        >>> request = TestRequest()
        >>> request.form['crud-add.form.widgets.name'] = u'Daniel'
        >>> request.form['crud-add.form.widgets.age'] = u'28'
        >>> request.form['crud-add.form.buttons.add'] = u'Add'
        >>> html = MyForm(None, request)()
        >>> "Item added successfully" in html
        True
        
        Added items should show up right away:
        
        >>> "Daniel" in html
        True
        
        >>> storage['Daniel']
        <Person with name=u'Daniel', age=28>
        >>> log.pop().object == storage['Daniel']
        True
        >>> log
        []
        
        What if we try to add "Daniel" twice?  Our current implementation of
        the add form will simply overwrite the data:
        
        >>> save_daniel = storage['Daniel']
        >>> html = MyForm(None, request)()
        >>> "Item added successfully" in html
        True
        >>> save_daniel is storage['Daniel']
        False
        >>> log.pop().object is storage['Daniel']
        True
        
        Let's implement a class that prevents this:
        
        >>> 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)
        
        >>> save_daniel = storage['Daniel']
        >>> html = MyCarefulForm(None, request)()
        >>> "Item added successfully" in html
        False
        >>> "There's already an item with the name 'Daniel'" in html
        True
        >>> save_daniel is storage['Daniel']
        True
        >>> 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:
        
        >>> from z3c.form import field
        >>> class MyAdvancedForm(MyForm):
        ...     update_schema = field.Fields(IPerson).select('age')
        ...     view_schema = field.Fields(IPerson).select('name')
        ...     add_schema = IPerson
        
        >>> print MyAdvancedForm(None, TestRequest())() \
        ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
        <div class="crud-form">...Daniel...Maria...</div>
        
        We can still edit the age of our Persons:
        
        >>> request = TestRequest()
        >>> request.form['crud-edit.Maria.widgets.age'] = u'40'
        >>> request.form['crud-edit.Daniel.widgets.age'] = u'35'
        >>> request.form['crud-edit.form.buttons.edit'] = u'Apply Changes'
        >>> html = MyAdvancedForm(None, request)()
        >>> "Successfully updated" in html
        True
        
        >>> storage['Maria'].age
        40
        >>> storage['Daniel'].age
        35
        
        We can still add a Person using both name and age:
        
        >>> request = TestRequest()
        >>> request.form['crud-add.form.widgets.name'] = u'Thomas'
        >>> request.form['crud-add.form.widgets.age'] = u'28'
        >>> request.form['crud-add.form.buttons.add'] = u'Add'
        >>> html = MyAdvancedForm(None, request)()
        >>> "Item added successfully" in html
        True
        >>> len(storage)
        3
        >>> storage['Thomas']
        <Person with name=u'Thomas', age=28>
        
        Our form can also contain links to our items:
        
        >>> class MyAdvancedLinkingForm(MyAdvancedForm):
        ...     def link(self, item, field):
        ...         if field == 'name':
        ...             return 'http://en.wikipedia.org/wiki/%s' % item.name
        
        >>> print MyAdvancedLinkingForm(None, TestRequest())() \
        ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
        <div class="crud-form">...
        ...<a href="http://en.wikipedia.org/wiki/Daniel"...
        ...<a href="http://en.wikipedia.org/wiki/Maria"...
        ...<a href="http://en.wikipedia.org/wiki/Thomas"...
        </div>
        
        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:
        
        >>> class MyAdvancedLinkingForm(MyAdvancedLinkingForm):
        ...     update_schema = IPerson
        ...     view_schema = field.Fields(IPerson).select('name')
        ...     add_schema = IPerson
        
        >>> print MyAdvancedLinkingForm(None, TestRequest())() \
        ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
        <div class="crud-form">...
        ...<a href="http://en.wikipedia.org/wiki/Thomas"...Thomas...</a>...
        </div>
        
        We can now change Thomas's name and see the change reflected in the
        Wikipedia link immediately:
        
        >>> request = TestRequest()
        >>> 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)
        >>> request.form['crud-edit.Thomas.widgets.name'] = u'Dracula'
        >>> request.form['crud-edit.form.buttons.edit'] = u'Apply Changes'
        
        >>> print MyAdvancedLinkingForm(None, request)() \
        ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
        <div class="crud-form">...
        ...<a href="http://en.wikipedia.org/wiki/Dracula"...Dracula...</a>...
        </div>
        >>> 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:
        
        >>> class OnlyEditForm(MyForm):
        ...     addform_factory = crud.NullForm
        >>> html = OnlyEditForm(None, TestRequest())()
        >>> 'Edit' in html, 'Add' in html
        (True, False)
        
        >>> class OnlyAddForm(MyForm):
        ...     editform_factory = crud.NullForm
        >>> html = OnlyAddForm(None, TestRequest())()
        >>> '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:
        
        >>> from pprint import pprint
        >>> from z3c.form import button
        
        >>> 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()
        
        >>> 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)
        
        >>> request = TestRequest()
        >>> html = MyCustomForm(None, TestRequest())()
        >>> "Delete" in html, "Apply changes" in html, "Capitalize" in html
        (False, False, True)
        >>> pprint(storage)
        {'Daniel': <Person with name=u'Daniel', age=35>,
        'Maria': <Person with name=u'Maria', age=40>,
        'Thomas': <Person with name=u'Thomas', age=28>}
        
        >>> request.form['crud-edit.Thomas.widgets.select'] = ['selected']
        >>> request.form['crud-edit.form.buttons.capitalize'] = u'Capitalize'
        >>> html = MyCustomForm(None, request)()
        >>> "Capitalized items" in html
        True
        >>> pprint(storage)
        {'Daniel': <Person with name=u'Daniel', age=35>,
        'Maria': <Person with name=u'Maria', age=40>,
        'Thomas': <Person with name=u'THOMAS', age=28>}
        
        We *cannot* use any of the other buttons:
        
        >>> del request.form['crud-edit.form.buttons.capitalize']
        >>> request.form['crud-edit.form.buttons.delete'] = u'Delete'
        >>> html = MyCustomForm(None, request)()
        >>> "Successfully deleted items" in html
        False
        >>> '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.
        
        >>> import zope.schema
        >>> 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
        
        >>> class MyCustomEditForm(MyEditForm):
        ...     editsubform_factory = MyCustomEditSubForm
        
        >>> class MyCustomFormWithCustomSubForm(MyCustomForm):
        ...     editform_factory = MyCustomEditForm
        
        >>> request = TestRequest()
        >>> html = MyCustomFormWithCustomSubForm(None, TestRequest())()
        
        Still uses same form as before
        >>> "Delete" in html, "Apply changes" in html, "Capitalize" in html
        (False, False, True)
        
        Just changes the widget used for selecting...
        >>> '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.
        
        >>> class MyBatchingForm(MyForm):
        ...     batch_size = 2
        >>> request = TestRequest()
        >>> html = MyBatchingForm(None, request)()
        >>> "Daniel" in html, "Maria" in html
        (True, True)
        >>> "THOMAS" in html
        False
        
        >>> request.form['crud-edit.form.page'] = '1'
        >>> html = MyBatchingForm(None, request)()
        >>> "Daniel" in html, "Maria" in html
        (False, False)
        >>> "THOMAS" in html
        True
        
        Let's change Thomas' age on the second page:
        
        >>> request.form['crud-edit.Thomas.widgets.name'] = u'Thomas'
        >>> request.form['crud-edit.Thomas.widgets.age'] = '911'
        >>> request.form['crud-edit.form.buttons.edit'] = u'Apply changes'
        >>> html = MyBatchingForm(None, request)()
        >>> "Successfully updated" in html
        True
        >>> "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.
        
        >>> from plone.z3cform.fieldsets import group, extensible
        
        To use this, you have to mix it into another form as the *first* base class:
        
        >>> from zope.annotation import IAttributeAnnotatable
        >>> from z3c.form import form, field, tests, group
        >>> from zope.interface import Interface, implements
        >>> from zope import schema
        
        >>> class ITest(Interface):
        ...     title = schema.TextLine(title=u"Title")
        
        >>> class Test(object):
        ...     implements(ITest, IAttributeAnnotatable)
        ...     title = u""
        
        >>> 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:
        
        >>> from z3c.form.testing import TestRequest
        
        >>> request = TestRequest()
        >>> context = Test()
        
        >>> form = TestForm(context, request)
        >>> form.update()
        >>> _ = 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.
        
        >>> from plone.z3cform.fieldsets.interfaces import IFormExtender
        >>> from zope.component import adapts, provideAdapter
        
        >>> 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.
        
        >>> from zope.annotation import factory
        >>> from zope.annotation.attribute import AttributeAnnotations
        >>> from persistent import Persistent
        >>> class ExtraBehavior(Persistent):
        ...     implements(IExtraBehavior)
        ...     adapts(Test)
        ...
        ...     foo = u""
        ...     bar = u""
        ...     baz = u""
        ...     fub = u""
        
        >>> ExtraBehavior = factory(ExtraBehavior)
        >>> provideAdapter(ExtraBehavior)
        >>> 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.
        
        >>> 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')
        
        
        >>> provideAdapter(factory=ExtraBehaviorExtender, name=u"test.extender")
        
        With this in place, let's update the form once again.
        
        >>> form = TestForm(context, request)
        >>> form.update()
        
        At this point, we should have a set of default fields that represent the
        ones set in the adapter.
        
        >>> form.fields.keys()
        ['extra.fub', 'title', 'extra.foo']
        
        And we should have one group created by the group factory:
        
        >>> form.groups # doctest: +ELLIPSIS
        (<plone.z3cform.fieldsets.group.Group object at ...>,)
        
        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.
        
        >>> isinstance(form.groups[0], group.Group)
        True
        
        This should have the group fields provided by the adapter as well.
        
        >>> form.groups[0].fields.keys()
        ['extra.bar', 'extra.baz']
        
        Changelog
        =========
        
        0.5.10 - 2010-02-01
        -------------------
        
        * A z3c.form.form.AddForm do a redirect in its render method.
        So we have to render the form to see if we have a redirection.
        In the case of redirection, we don't render the layout at all.
        This version remove the contents method on FormWrapper,
        it's now an attribute set during the FormWrapper.update.
        This change fixes status message not shown because it was consumed by
        the never shown rendered form.
        [vincentfretin]
        
        0.5.9 - 2010-01-08
        ------------------
        
        * Fix security problem with the ++widget++ namespace
        [optilude]
        
        0.5.8 - 2009-11-24
        ------------------
        
        * Don't do the rendering if there is a redirection, use the update/render
        pattern for that.
        See http://dev.plone.org/plone/ticket/10022 for an example how
        to adapt your code, in particular if you used FormWrapper with ViewletBase.
        [vincentfretin]
        
        0.5.7 - 2009-11-17
        ------------------
        
        * Fix silly doctests so that they don't break in Python 2.6 / Zope 2.12
        [optilude]
        
        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 <includeOverrides /> 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 > 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 <3.5 of zope.component.
        
        0.4 - 2008-07-25
        ----------------
        
        * Depend on zope.component<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 >= 3.4 as the
        previous test setup was partial and did not register all adapters
        from z3c.form (some of them depends on zope >= 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 & Dancing into this new package.
        [nouri]
        
        
Keywords: zope cmf form widget
Platform: UNKNOWN
Classifier: Framework :: Plone
Classifier: Framework :: Zope2
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
