Metadata-Version: 1.0
Name: plone.z3cform
Version: 0.7.7
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 enables the use of `z3c.form`_ in Zope 2.
        It depends only on Zope 2 and z3c.form.
        
        For Plone integration, there is also `plone.app.z3cform`_, which can be
        installed to make the default form templates more Ploneish. That package
        pulls in this one as a dependency.
        
        In addition to pure interoperability support, a few patterns which are useful
        in Zope 2 applications are implemented here.
        
        .. contents:: Contents
        
        Installation
        ============
        
        To use this package, simply install it as a dependency of the package where
        you are using forms, via the ``install_requires`` line in ``setup.py``. Then
        loads its configuration via ZCML::
        
            <include package="plone.z3cform" />
        
        Standalone forms
        ================
        
        If you are using Zope 2.12 or later, z3c.form forms will *almost* work
        out of the box. However, two obstacles remain:
        
        * The standard file upload data converter does not work with Zope 2, so
          fields (like ``zope.schema.Bytes``) using the file widget will not work
          correctly.
        * z3c.form expects request values to be decoded to unicode strings by the
          publisher, which does not happen in Zope 2.
        
        To address the first problem, this package provides an override for the
        standard data converter adapter (registered on the ``zope.schema.Bytes`` class
        directly, so as to override the default, which is registered for the less
        general ``IBytes`` interface). To address the second, it applies a monkey
        patch to the ``update()`` methods of ``BaseForm`` and ``GroupForm`` from
        ``z3c.form`` which performs the necessary decoding in a way that is consistent
        with the Zope 3-style publisher.
        
        Note: If you override ``update()`` in your own form you must either call the
        base class version or call the function ``plone.z3cform.z2.processInputs()``
        on the request *before* any values in the request are used. For example::
        
            from plone.z3cform.z2 import processInputs
            from z3c.form import form
            
            ...
            
            class MyForm(form.Form):
                
                ...
                
                def update(self):
                    processInputs(self.request)
                    ...
        
        Other than that, you can create a form using standard `z3c.form`_ conventions.
        For example::
        
            from zope.interface import Interface
            from zope import schema
            from z3c.form import form, button
            
            class IMyFormSchema(Interface):
                field1 = schema.TextLine(title=u"A field")
                field2 = schema.Int(title=u"Another field")
            
            class MyForm(form.Form):
                fields = field.Fields(IMyformSchema)
                
                @button.buttonAndHandler(u'Submit')
                def handleApply(self, action):
                    data, errors = self.extractData()
                    # do something
        
        You can register this as a view in ZCML using the standard ``<browser:page />``
        directive::
        
            <browser:page
                for="*"
                name="my-form"
                class=".forms.MyForm"
                permission="zope2.View"
                />
        
        A default template will be used to render the form. If you want to associate
        a custom template, you should do so by setting the ``template`` class variable
        instead of using the ``template`` attribute of the ZCML directive::
        
            from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
            
            class MyForm(form.Form):
                fields = field.Fields(IMyformSchema)
                template = ViewPageTemplateFile('mytemplate.pt')
                
                @button.buttonAndHandler(u'Submit')
                def handleApply(self, action):
                    data, errors = self.extractData()
                    # do something
        
        See below for more details about standard form macros.
        
        Note that to render any of the standard widgets, you will also need to make
        sure the request is marked with ``z3c.form.interfaces.IFormLayer``, as is
        the norm with z3c.form. If you install `plone.app.z3cform`_ in Plone, that
        is already done for you, but in other scenarios, you will need to do this
        in whichever way Zope browser layers are normally applied.
        
        Layout form wrapper
        ===================
        
        In versions of Zope prior to 2.12, z3c.form instances cannot be registered
        as views directly, because they do not support Zope 2 security (via the
        acquisition mechanism). Whilst it may be possible to support this via custom
        mix-ins, the preferred approach is to use a wrapper view, which separates the
        rendering of the form from the page layout.
        
        There are a few other reasons why you may want to use the wrapper view, even
        in later versions of Zope:
        
        * To support both an earlier version of Zope and Zope 2.12+
        * To re-use the same form in multiple views or viewlets
        * To use the ``IPageTemplate`` adapter lookup semantics from z3c.form to
          provide a different default or override template for the overall page
          layout, while retaining (or indeed customising independently) the default
          layout of the form.
        
        When using the wrapper view, you do *not* need to ensure your requests are
        marked with ``IFormLayer``, as it is applied automatically during the
        rendering of the wrapper view.
        
        The easiest way to create a wrapper view is to call the ``wrap_form()``
        function::
        
            from zope.interface import Interface
            from zope import schema
            from z3c.form import form, button
            
            from plone.z3cform import layout
            
            class IMyFormSchema(Interface):
                field1 = schema.TextLine(title=u"A field")
                field2 = schema.Int(title=u"Another field")
            
            class MyForm(form.Form):
                fields = field.Fields(IMyformSchema)
                
                @button.buttonAndHandler(u'Submit')
                def handleApply(self, action):
                    data, errors = self.extractData()
                    # do something
            
            MyFormView = layout.wrap_form(MyForm)
        
        You can now register the (generated) ``MyFormView`` class as a browser view::
        
            <browser:page
                for="*"
                name="my-form"
                class=".forms.MyFormView"
                permission="zope2.View"
                />
        
        If you want to have more control, you can define the wrapper class manually.
        You should derive from the default version to get the correct semantics. The
        following is equivalent to the ``wrap_form()`` call above::
        
            class MyFormView(layout.FormWrapper):
                form = MyForm
        
        You can of then add additional methods to the class, use a custom page
        template, and so on.
        
        The default ``FormWrapper`` class exposes a few methods and properties:
        
        * ``update()`` is called to prepare the request and then update the wrapped
          form.
        * ``render()`` is called to render the wrapper view. If a template has
          been set (normally via the ``template`` attribute of the
          ``<browser:page />`` directive), it will be rendered here. Otherwise,
          a default page template is found by adapting the view (``self``) and 
          the request to ``zope.pagetemplate.interfaces.IPageTemplate``, in the
          same way that ``z3c.form`` does for its views. A default template is
          supplied with this package (and customised in `plone.app.z3cform`_ to
          achieve a standard Plone look and feel).
        * ``form`` is a class variable referring to the form class, as set above.
        * ``form_instance`` is an instance variable set to the current form instance
          once the view has been initialised.
        
        When a form is rendered in a wrapper view, the form instance is temporarily
        marked with ``plone.z3cform.interfaces.IWrappedForm`` (unless the form is
        a subform), to allow custom adapter registrations. Specifically, this is used
        to ensure that a form rendered "standalone" gets a full-page template applied,
        while a form rendered in a wrapper is rendered using a template that renders
        the form elements only.
        
        Default templates and macros
        ============================
        
        Several standard templates are provided with this package. These are all
        registered as adapters from ``(view, request)`` to ``IPageTemplate``, as is
        the convention in z3c.form. It therefore follows that these defaults can be
        customised with an adapter override, e.g. for a specific browser layer. This
        is useful if you want to override the standard rendering of all forms. If you
        just want to provide a custom template for a particular form or wrapper view,
        you can specify a template directly on the form or view, as shown above.
        
        * ``templates/layout.pt`` is the default template for the layout wrapper view.
          It uses the CMFDefault ``main_template`` and fills the ``header`` slot.
        * ``templates/wrappedform.pt`` is the default template for wrapped forms.
          It renders the ``titlelessform`` macro from the ``@@ploneform-macros`` view.
        * ``templates/subform.pt`` is the default template for sub-forms.
          It uses the macros in ``@@ploneform-macros`` view to render a heading,
          top/bottom content (verbatim) and the fields and actions of the subform (but
          does not) render the ``<form />`` tag itself.
        * ``templates/form.pt`` is the default template for a standalone form. It uses
          the macro ``context/@@standard_macros/page`` (supplied by Five and normally
          delegating to CMF's ``main_template``) to render a form where the form label
          is the page heading.
        
        As hinted, this package also registers a view ``@@ploneform-macros``, which
        contains a set of macros that be used to construct forms with a standard
        layout, error message display, and so on. It contains the following macros:
        
        * ``form`` is a full page form, including the label (output as an ``<h3 />``),
          description, and all the elements of ``titlelessform``.  It defines two
          slots: ``title`` contains the label, and ``description`` contains the
          description.
        * ``titlelessform`` includes the form ``status`` at the top, the ``<form />``
          element, and the contents of the ``fields`` and ``actions`` macros. It also
          defines four slots: ``formtop`` is just inside the opening ``<form>`` tag;
          ``formbottom``` is just inside the closing ``</form>`` tag;
          ``fields`` contains the ``fields`` macro; and ``actions`` contains the
          ``actions`` macro.
        * ``fields`` iterates over all widgets in the form and renders each, using the
          contents of the ``field`` macro.  It also defines one slot, ``field`` which
          contains the ``field`` macro.
        * ``field`` renders a single field. It expects the variable ``widget`` to be
          defined in the TAL scope, referring to a z3c.form widget instance. It will
          output an error message if there is a field validation error, a label,
          a marker to say whether the field is required, the field description, and 
          the widget itself (normally just an ``<input />`` element).
        * ``actions`` renders all actions (buttons) on the form. This normally results
          in a row of ``<input type="submit" ... />`` elements.
        
        Thus, to use the ``titlelessform`` macro, you could add something like the
        following in a custom form template::
        
            <metal:use use-macro="context/@@ploneform-macros/titlelessform" />
        
        Note that all of the templates mentioned above are customised by
        `plone.app.z3cform`_ to use standard Plone markup (but still retain the same
        macros), so if you are using that package to configure this one, you should
        look for the Plone-specific versions there.
        
        Template factories
        ==================
        
        If you want to provide an ``IPageTemplate`` adapter to customise the default
        page template used for wrapper views, forms or sub-forms, this package
        provides helper classes to create an adapter factory for that purpose. You
        should use these instead of ``z3c.form.form.FormTemplateFactory`` and
        (possibly) ``z3c.form.widget.WidgetTemplateFactory`` to get page templates
        with Zope 2 semantics. These factories are also `Chameleon`_ aware, if you
        have `five.pt`_ installed.
        
        The most commonly used factory is
        ``plone.z3cform.templates.ZopeTwoFormTemplateFactory``, which can be used to
        render a wrapper view or a standalone form.
        
        To render a wrapped form, you can use
        ``plone.z3cform.templates.FormTemplateFactory``, which is closer to the
        default ``z3c.form`` version, but adds Chameleon-awareness.
        
        To render a widget, the default ``WidgetTemplateFactory`` from z3c.form should
        suffice, but if you need Zope 2 semantics for any reason, you can use
        ``plone.z3cform.templates.ZopeTwoWidgetTemplateFactory``.
        
        As an example, here are the default registrations from this package::
            
            import z3c.form.interfaces
            import plone.z3cform.interfaces
            
            from plone.z3cform.templates import ZopeTwoFormTemplateFactory
            from plone.z3cform.templates import FormTemplateFactory
            
            path = lambda p: os.path.join(os.path.dirname(plone.z3cform.__file__), 'templates', p)
            
            layout_factory = ZopeTwoFormTemplateFactory(path('layout.pt'),
                form=plone.z3cform.interfaces.IFormWrapper
            )
        
            wrapped_form_factory = FormTemplateFactory(path('wrappedform.pt'),
                    form=plone.z3cform.interfaces.IWrappedForm,
                )
        
            # Default templates for the standalone form use case
        
            standalone_form_factory = ZopeTwoFormTemplateFactory(path('form.pt'),
                    form=z3c.form.interfaces.IForm
                )
        
            subform_factory = FormTemplateFactory(path('subform.pt'),
                    form=z3c.form.interfaces.ISubForm
                )
        
        These are registered in ZCML like so::
        
          <!-- Form templates for wrapped layout use case -->
          <adapter factory=".templates.layout_factory" />
          <adapter factory=".templates.wrapped_form_factory" />
          
          <!-- Form templates for standalone form use case -->
          <adapter factory=".templates.standalone_form_factory" />
          <adapter factory=".templates.subform_factory" />
        
        The widget traverser
        ====================
        
        It is sometimes useful to be able to register a view on a *widget* and be
        able to traverse to that view, for example during a background AJAX request.
        As an example of widget doing this, see `plone.formwidget.autocomplete`_.
        
        This package provides a ``++widget++`` namespace traversal adapter which can
        be used for this purpose. It is looked up on either the form wrapper view,
        or the form itself (in the case of standalone) forms. Thus, if you have a
        form view called ``@@my-form``, with a field called ``myfield``, you could
        traverse to the widget for that view using::
        
            http://example.com/@@my-form/++widget++myfield
        
        The widget may be on the form itself, or in a group (fieldset). If it exists
        in multiple groups, the first one found will be used.
        
        The example above will yield widget, but it is probably not publishable.
        You would therefore commonly register a view on the widget itself and use
        that. In this case, ``self.context`` in the view is the widget instance. Such
        a view could be looked up with::
        
            http://example.com/@@my-form/++widget++myfield/@@some-view
        
        A caveat about security
        -----------------------
        
        In Zope 2.12 and later, the security machinery is aware of ``__parent__``
        pointers. Thus, traversal and authorisation on ``@@some-view`` in the example
        above will work just fine for a standard widget. In earlier versions of Zope,
        you will need to mix acquisition into your widget (which rules out using any
        of the standard ``z3c.form`` widgets). For example::
        
            from Acquisition import Explicit
            from z3c.form.widget import Widget
            
            class MyWidget(Widget, Explicit):
                ...
        
        Unfortunately, in Zope 2.12, this will cause some problems during traversal
        unless you also mix acquisition into the view you registered on the widget
        (``@@some-view`` above). Specifically, you will get an error as the publisher
        tries to wrap the view in the widget.
        
        To stay compatible with both Zope 2.12+ and earlier versions, you have two
        options:
        
        * Ensure that you mix acquisition into the view on the widget
        * Ensure that the widget inherits from ``Explicit``, but does *not* provide
          the ``IAcquirer`` interface. This tricks the publisiher into relying on
          ``__parent__`` pointers in Zope 2.12.
        
        To do the latter, you can use ``implementsOnly()``, e.g.::
        
            from zope.interface import implementsOnly
            from Acquisition import Explicit
            from z3c.form.widget import Widget
            
            ...
            
            class MyWidget(Widget, Explicit):
                implementsOnly(IMyWidget) # or just IWdget from z3c.form
                ...
        
        .. _z3c.form: http://pypi.python.org/pypi/z3c.form
        .. _plone.app.z3cform: http://pypi.python.org/pypi/plone.app.z3cform
        .. _CMF: http://www.zope.org/Products/CMF
        .. _Chameleon: http://pypi.python.org/pypi/Chameleon
        .. _five.pt: http://pypi.python.org/pypi/five.pt
        .. _plone.formwidget.autocomplete: http://pypi.python.org/pypi/plone.formwidget.autocomplete
        
        Fieldsets and form extenders
        ============================
        
        The ``plone.z3cform.fieldsets`` package provides support for z3c.form 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):
          ...     # avoid needing an acl_users for this test in Zope 2.10
          ...     __allow_access_to_unprotected_subobjects__ = 1
          ...     implements(ITest, IAttributeAnnotatable)
          ...     title = u""
          ...     def getPhysicalRoot(self): # needed for template to acquire REQUEST in Zope 2.10
          ...         return self
        
          >>> 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 directly on the form. This known as the default fieldset.
        
        This form should work as-is, i.e. we can update it. First we need to fake a
        request.
        
          >>> from z3c.form.testing import TestRequest
        
          >>> request = TestRequest()
          >>> request.other = {}
          >>> context = Test()
          >>> context.REQUEST = request
        
          >>> form = TestForm(context, request)
          >>> try: # Zope 2.10 templates need a proper acquisition chain
          ...     from Acquisition import ImplicitAcquisitionWrapper
          ...     form = ImplicitAcquisitionWrapper(form, context)
          ... except:
          ...     pass
          >>> 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")
          ...     qux = schema.TextLine(title=u"Qux")
          
        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""
          ...     qux = 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 group.
          ...         self.move('extra.baz', after='extra.bar')
          ...         
          ...         # Remove 'qux' and re-insert into 'Second' group,
          ...         # then move it before 'baz'
          ...         self.remove('qux', prefix='extra')
          ...         self.add(all_fields.select('qux', prefix='extra'), group='Second')
          ...         self.move('qux', before='baz', prefix='extra', relative_prefix='extra')
          
          >>> 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.qux', 'extra.baz']
        
        CRUD (Create, Read, Update and Delete) forms
        ============================================
        
        This module provides an abstract base class to create CRUD forms.
        By default, such forms provide a tabular view of multiple objects, whose
        attributes 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
        
        Batching
        --------
        
        The BatchNavigation adapter is used for rendering batch navigation in CRUD
        forms.
        
          >>> from z3c.batching.batch import Batch
          >>> from plone.z3cform.crud.crud import BatchNavigation
        
        Here's a little batch, set to three items per page and starting at the
        third item:
        
          >>> l = [10, 11, 12, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52]
          >>> batch = Batch(l, start=3, size=3)
          >>> list(batch)
          [20, 21, 22]
        
        We can create the BatchNavigation now.  We set the make_link attribute
        to be a function that takes an argument (the page number) and returns
        a link to it:
        
          >>> def make_link(page):
          ...     return u"linkto?page=%s" % page
        
          >>> from z3c.form.testing import TestRequest
          >>> view = BatchNavigation(batch, TestRequest())
          >>> view.make_link = make_link
        
        We monkey-patch the template to see what's being passed:
        
          >>> from pprint import pprint
          >>> def template(self, **kwargs):
          ...     print "Batch", kwargs['batch']
          ...     print "Links", [p['link'] for p in kwargs['pages']]
          ...     print "Labels", [p['label'] for p in kwargs['pages']]
          >>> BatchNavigation.template = template
          >>> view()
          Batch <Batch start=3, size=3>
          Links [u'linkto?page=0', u'linkto?page=0', None, u'linkto?page=2', u'linkto?page=3', u'linkto?page=4', u'linkto?page=2']
          Labels [u'Previous', u'1', u'2', u'3', u'4', u'5', u'Next']
        
        Rendering for the first and last page, we can see that "Previous" and
        "Next" links are ommitted accordingly:
        
          >>> batch = Batch(l, start=0, size=3)
          >>> view.context = batch
          >>> view()
          Batch <Batch start=0, size=3>
          Links [None, u'linkto?page=1', u'linkto?page=2', u'linkto?page=3', u'linkto?page=4', u'linkto?page=1']
          Labels [u'1', u'2', u'3', u'4', u'5', u'Next']
        
          >>> batch = Batch(l, start=12, size=3)
          >>> view.context = batch
          >>> view()
          Batch <Batch start=12, size=3>
          Links [u'linkto?page=3', u'linkto?page=0', u'linkto?page=1', u'linkto?page=2', u'linkto?page=3', None]
          Labels [u'Previous', u'1', u'2', u'3', u'4', u'5']
        
        Changelog
        =========
        
        0.7.7 - 2011-06-30
        ------------------
        
        * Avoid rendering a wrapped form if a redirect has already occurred after
          updating it.
          [davisagli]
        
        * Remove <a name=""/> elements from inside the CRUD table TBODY element
          they were otherwise unused (and illegal in that location of the HTML content
          model).
          [mj]
        
        0.7.6 - 2011-05-17
        ------------------
        
        * Add ability to find widgets with non-integer names in lists. This shouldn't
          generally be something that happens, and ideally should be removed if
          DataGridField looses it's 'AA' and 'TT' rows.
          [lentinj]
        
        0.7.5 - 2011-05-03
        ------------------
        
        * Fix traversal tests on Zope 2.10 to handle TraversalError instead of
          LocationError.
          [elro]
        
        * Fix traversal.py syntax to be python2.4 compatible.
        
        * Revert [120798] as it breaks on Zope2.10 / Plone 3.3. We can deal with Zope
          2.14 in 0.8.x.
          [elro]
        
        0.7.4 - 2011-05-03
        ------------------
        
        * Define 'hidden' within field macro.
          [elro]
        
        * Ignore "form.widgets." if ++widget++ path begins with it.
          [lentinj]
        
        * Rework traverser to handle lists and subforms
          [lentinj]
        
        * Only search a group's widgets if they exist. collective.z3cform.wizard doesn't
          create widgets for pages/groups other than the current one
          [lentinj, elro]
        
        * Deal with forward compatibility with Zope 2.14.
        
        * Adds Brazilian Portuguese translation.
          [davilima6]
        
        0.7.3 - 2011-03-02
        ------------------
        
        * Handle wrong fieldnames more cleanly in the ++widget++ traverser.
          [elro]
        
        0.7.2 - 2011-02-17
        ------------------
        
        * Make sure the CRUD add form doesn't use a standalone template.
          [davisagli]
        
        0.7.1 - 2011-01-18
        ---------------------
        
        * Add zope.app.testing to test dependencies so that it continues to work under
          Zope 2.13.
          [esteele]
        
        0.7.0 - 2010-08-04
        ------------------
        
        * Add a marker interface which can be used by widgets to defer any security
          checks they may be doing when they are set up during traversal with the
          ++widgets++ namespace
          [dukebody]
        
        * Fix re-ordering of fields not in the default fieldset. Thanks to Thomas
          Buchberger for the patch.
          [optilude]
        
        * Added Norwegian translation.
          [regebro]
        
        0.6.0 - 2010-04-20
        ------------------
        
        * In the CRUD table, fix odd/even labels, which were reversed.
          [limi]
        
        * Added slots to the ``titlelessform`` macro. See ``README.txt`` for details.
          [optilude, davisagli]
        
        * Remove the distinction between wrapped and unwrapped subforms. A subform is
          always wrapped by the form that contains it, and can use a Zope 3 page
          template.
          [davisagli]
        
        * Fixed tests in Plone 3.
          [davisagli]
        
        * Fixed tests in Plone 4
          [optilude]
        
        * Made it possible to distinguish wrapped and unwrapped forms via the 
          IWrappedForm marker interface.
          [optilude]
        
        * Made it possible to use z3c.form forms without a FormWrapper in Plone 4.
          [optilude]
        
        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
