[Checkins] SVN: z3c.form/trunk/ add new widgetmanager to mix fields and content providers in forms (merge from fieldsandcontentproviders branch)

Jean-Francois Roche jfroche at jfroche.be
Thu Jul 1 08:00:21 EDT 2010


Log message for revision 114046:
  add new widgetmanager to mix fields and content providers in forms (merge from fieldsandcontentproviders branch)

Changed:
  U   z3c.form/trunk/CHANGES.txt
  U   z3c.form/trunk/buildout.cfg
  U   z3c.form/trunk/setup.py
  U   z3c.form/trunk/src/z3c/form/README.txt
  U   z3c.form/trunk/src/z3c/form/configure.zcml
  A   z3c.form/trunk/src/z3c/form/contentprovider.py
  A   z3c.form/trunk/src/z3c/form/contentprovider.txt
  U   z3c.form/trunk/src/z3c/form/index.txt
  U   z3c.form/trunk/src/z3c/form/interfaces.py
  U   z3c.form/trunk/src/z3c/form/testing.py
  A   z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt
  U   z3c.form/trunk/src/z3c/form/tests/test_doc.py
  U   z3c.form/trunk/src/z3c/form/util.py

-=-
Modified: z3c.form/trunk/CHANGES.txt
===================================================================
--- z3c.form/trunk/CHANGES.txt	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/CHANGES.txt	2010-07-01 12:00:21 UTC (rev 114046)
@@ -5,6 +5,11 @@
 2.3.5 (unreleased)
 ------------------
 
+- Feature : mix fields and content providers in forms. This allow to enrich
+  the form by interlacing html snippets produced by content providers.
+  Adding html outside the widgets avoids the systematic need of 
+  subclassing or changing the full widget rendering.
+
 - Bug: Radio widget was not treating value as a list in hidden mode.
 
 

Modified: z3c.form/trunk/buildout.cfg
===================================================================
--- z3c.form/trunk/buildout.cfg	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/buildout.cfg	2010-07-01 12:00:21 UTC (rev 114046)
@@ -1,7 +1,7 @@
 [buildout]
 develop = . benchmark
 parts = test test-no-z3zpt checker coverage-test coverage-report docs i18n
-        benchmark python
+        benchmark python omelette
 
 [test-environment]
 CHAMELEON_DEBUG = False
@@ -77,3 +77,7 @@
 recipe = zc.recipe.egg
 eggs = z3c.form
 interpreter = python
+
+[omelette]
+recipe = collective.recipe.omelette
+eggs = ${test:eggs}

Modified: z3c.form/trunk/setup.py
===================================================================
--- z3c.form/trunk/setup.py	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/setup.py	2010-07-01 12:00:21 UTC (rev 114046)
@@ -19,11 +19,13 @@
 import xml.sax.saxutils
 from setuptools import setup, find_packages
 
+
 def read(*rnames):
     text = open(os.path.join(os.path.dirname(__file__), *rnames)).read()
     text = unicode(text, 'utf-8').encode('ascii', 'xmlcharrefreplace')
     return xml.sax.saxutils.escape(text)
 
+
 chapters = '\n'.join(
     [read('src', 'z3c', 'form', name)
     for name in ('README.txt',
@@ -35,32 +37,33 @@
                  'zcml.txt',
                  'validator.txt',
                  'widget.txt',
+                 'contentprovider.txt',
                  'action.txt',
                  'value.txt',
                  'datamanager.txt',
                  'converter.txt',
                  'term.txt',
-                 'util.txt')])
+                 'util.txt',
+                 )])
 
 
-setup (
+setup(
     name='z3c.form',
-    version = '2.3.5dev',
-    author = "Stephan Richter, Roger Ineichen and the Zope Community",
-    author_email = "zope-dev at zope.org",
-    description = "An advanced form and widget framework for Zope 3",
+    version='2.3.5dev',
+    author="Stephan Richter, Roger Ineichen and the Zope Community",
+    author_email="zope-dev at zope.org",
+    description="An advanced form and widget framework for Zope 3",
     long_description=(
         read('README.txt')
         + '\n\n' +
         'Detailed Documentation\n'
         '**********************\n'
         + '\n' + chapters
-        + '\n\n' +
-        read('CHANGES.txt')
-        ),
-    license = "ZPL 2.1",
-    keywords = "zope3 form widget",
-    classifiers = [
+        + '\n\n'
+        + read('CHANGES.txt')),
+    license="ZPL 2.1",
+    keywords="zope3 form widget",
+    classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Web Environment',
         'Intended Audience :: Developers',
@@ -70,17 +73,17 @@
         'Operating System :: OS Independent',
         'Topic :: Internet :: WWW/HTTP',
         'Framework :: Zope3'],
-    url = 'http://pypi.python.org/pypi/z3c.form',
-    packages = find_packages('src'),
-    include_package_data = True,
-    package_dir = {'':'src'},
-    namespace_packages = ['z3c'],
-    extras_require = dict(
-        extra = [
+    url='http://pypi.python.org/pypi/z3c.form',
+    packages=find_packages('src'),
+    include_package_data=True,
+    package_dir={'': 'src'},
+    namespace_packages=['z3c'],
+    extras_require=dict(
+        extra=[
             'z3c.pt >= 1.0b4',
             'z3c.ptcompat',
         ],
-        test = [
+        test=[
             'lxml >= 2.1.1',
             'z3c.coverage',
             'z3c.template',
@@ -93,16 +96,16 @@
             'zope.app.testing',
             'zope.testing',
             ],
-        zope34 = [
+        zope34=[
             'zope.app.component',
             ],
-        latest = [
+        latest=[
             'zope.site',
             ],
-        adding = ['zope.app.container'],
-        docs = ['z3c.recipe.sphinxdoc'],
+        adding=['zope.app.container'],
+        docs=['z3c.recipe.sphinxdoc'],
         ),
-    install_requires = [
+    install_requires=[
         'setuptools',
         'zope.browser',
         'zope.component',
@@ -122,5 +125,5 @@
         #'zope.site' or 'zope.app.component',
         'zope.traversing',
         ],
-    zip_safe = False,
+    zip_safe=False,
     )

Modified: z3c.form/trunk/src/z3c/form/README.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/README.txt	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/README.txt	2010-07-01 12:00:21 UTC (rev 114046)
@@ -48,6 +48,11 @@
   Explains in detail the design goals surrounding widgets and widget managers
   and how they were realized with the implemented API.
 
+- ``contentprovider.txt`` [advanced users]
+
+  Explains how to mix content providers in forms to render more html around
+  widgets.
+
 - ``action.txt`` [advanced users]
 
   Explains in detail the design goals surrounding action managers and

Modified: z3c.form/trunk/src/z3c/form/configure.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/configure.zcml	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/configure.zcml	2010-07-01 12:00:21 UTC (rev 114046)
@@ -30,6 +30,10 @@
       factory=".field.FieldWidgets"
       />
 
+  <adapter
+      factory=".contentprovider.FieldWidgetsAndProviders"
+      />
+
   <!-- Data Converters -->
   <adapter
       factory=".converter.FieldDataConverter"

Copied: z3c.form/trunk/src/z3c/form/contentprovider.py (from rev 114045, z3c.form/branches/fieldsandcontentproviders/src/z3c/form/contentprovider.py)
===================================================================
--- z3c.form/trunk/src/z3c/form/contentprovider.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/contentprovider.py	2010-07-01 12:00:21 UTC (rev 114046)
@@ -0,0 +1,115 @@
+import zope.component
+import zope.interface
+import zope.location
+import zope.schema.interfaces
+from z3c.form.error import MultipleErrors
+from zope.contentprovider.interfaces import IContentProvider
+
+from z3c.form.field import FieldWidgets
+from z3c.form import interfaces
+from z3c.form.interfaces import IContentProviders
+
+
+class BaseProvider(object):
+    __slots__ = ('position')
+
+lookup_ = BaseProvider()
+
+
+class ContentProviders(dict):
+    zope.interface.implements(IContentProviders)
+
+    def __init__(self, names=None):
+        super(ContentProviders, self).__init__()
+        if names is not None:
+            for position, name in enumerate(names):
+                self[name] = lookup_
+
+    def __setitem__(self, key, value):
+        factory = ContentProviderFactory(factory=value, name=key)
+        super(ContentProviders, self).__setitem__(key, factory)
+
+
+class ContentProviderFactory(object):
+
+    def __init__(self, factory, name):
+        self.factory = factory
+        self.name = name
+        self.position = getattr(factory, 'position', None)
+
+    def __call__(self, manager):
+        if self.factory != lookup_:
+            contentProvider = self.factory(manager.content, manager.request, manager.form)
+        else:
+            contentProvider = zope.component.getMultiAdapter((manager.content, manager.request, manager.form),
+                                                             IContentProvider, self.name)
+        return contentProvider
+
+
+class FieldWidgetsAndProviders(FieldWidgets):
+    zope.component.adapts(
+        interfaces.IFieldsAndContentProvidersForm, interfaces.IFormLayer, zope.interface.Interface)
+    zope.interface.implementsOnly(interfaces.IWidgets)
+
+    def update(self):
+        super(FieldWidgetsAndProviders, self).update()
+        uniqueOrderedKeys = self._data_keys
+        for name in self.form.contentProviders:
+            factory = self.form.contentProviders[name]
+            if factory.position is None:
+                raise ValueError("Position of the following"
+                 " content provider should be an integer: '%s'." % name)
+            contentProvider = factory(self)
+            shortName = name
+            contentProvider.update()
+            uniqueOrderedKeys.insert(factory.position, shortName)
+            self._data_values.insert(factory.position, contentProvider)
+            self._data[shortName] = contentProvider
+            zope.location.locate(contentProvider, self, shortName)
+            # allways ensure that we add all keys and keep the order given from
+            # button items
+            self._data_keys = uniqueOrderedKeys
+
+    def extract(self):
+        """See interfaces.IWidgets"""
+        data = {}
+        errors = ()
+        for name, widget in self.items():
+            if IContentProvider.providedBy(widget):
+                continue
+            if widget.mode == interfaces.DISPLAY_MODE:
+                continue
+            value = widget.field.missing_value
+            try:
+                widget.setErrors = self.setErrors
+                raw = widget.extract()
+                if raw is not interfaces.NO_VALUE:
+                    value = interfaces.IDataConverter(widget).toFieldValue(raw)
+                zope.component.getMultiAdapter(
+                    (self.content,
+                     self.request,
+                     self.form,
+                     getattr(widget, 'field', None),
+                     widget),
+                    interfaces.IValidator).validate(value)
+            except (zope.interface.Invalid,
+                    ValueError, MultipleErrors), error:
+                view = zope.component.getMultiAdapter(
+                    (error, self.request, widget, widget.field,
+                     self.form, self.content), interfaces.IErrorViewSnippet)
+                view.update()
+                if self.setErrors:
+                    widget.error = view
+                errors += (view,)
+            else:
+                name = widget.__name__
+                data[name] = value
+        for error in self.validate(data):
+            view = zope.component.getMultiAdapter(
+                (error, self.request, None, None, self.form, self.content),
+                interfaces.IErrorViewSnippet)
+            view.update()
+            errors += (view,)
+        if self.setErrors:
+            self.errors = errors
+        return data, errors

Copied: z3c.form/trunk/src/z3c/form/contentprovider.txt (from rev 114045, z3c.form/branches/fieldsandcontentproviders/src/z3c/form/contentprovider.txt)
===================================================================
--- z3c.form/trunk/src/z3c/form/contentprovider.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/contentprovider.txt	2010-07-01 12:00:21 UTC (rev 114046)
@@ -0,0 +1,311 @@
+=================
+Content Providers
+=================
+
+We want to mix fields and content providers.
+
+This allow to enrich the form by interlacing html snippets produced by content
+providers.
+
+For instance, we might want to render the table of results in a search form.
+
+We might also need to render HTML close to a widget as a handle used when
+improving UI with Ajax.
+
+Adding HTML outside the widgets avoids the systematic need of 
+subclassing or changing the full widget rendering.
+
+Test setup
+----------
+Before we can use a widget manager, the ``IFieldWidget`` adapter
+has to be registered for the ``ITextLine`` field::
+
+  >>> import zope.component
+  >>> import zope.interface
+  >>> from z3c.form import interfaces, widget
+  >>> from z3c.form.browser import text
+  >>> from z3c.form.testing import TestRequest
+
+  >>> @zope.component.adapter(zope.schema.TextLine, TestRequest)
+  ... @zope.interface.implementer(interfaces.IFieldWidget)
+  ... def TextFieldWidget(field, request):
+  ...     return widget.FieldWidget(field, text.TextWidget(request))
+
+  >>> zope.component.provideAdapter(TextFieldWidget)
+
+  >>> from z3c.form import converter
+  >>> zope.component.provideAdapter(converter.FieldDataConverter)
+  >>> zope.component.provideAdapter(converter.FieldWidgetDataConverter)
+
+We define a simple test schema with fields::
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IPerson(zope.interface.Interface):
+  ...
+  ...     id = zope.schema.TextLine(
+  ...         title=u'ID',
+  ...         description=u"The person's ID.",
+  ...         required=True)
+  ...
+  ...     fullname = zope.schema.TextLine(
+  ...         title=u'FullName',
+  ...         description=u"The person's name.",
+  ...         required=True)
+  ...
+
+A class that implements the schema::
+
+  >>> class Person(object):
+  ...    id = 'james'
+  ...    fullname = 'James Bond'
+
+The usual request instance::
+
+  >>> request = TestRequest()
+
+We want to insert a content provider inbetween fields. 
+We define a test content provider that renders extra help text::
+
+  >>> from zope.contentprovider.provider import ContentProviderBase
+  >>> class ExtendedHelp(ContentProviderBase):
+  ...
+  ...   def update(self):
+  ...       self.person = self.context.id
+  ...
+  ...   def render(self):
+  ...       return '<div class="ex-help">Help about person %s</div>' % self.person
+
+Form definition
+--------------- 
+
+The meat of the tests begins here.
+
+We define a form as usual by inheriting from ``form.Form``::
+
+  >>> from z3c.form import field, form
+  >>> from zope.interface import implements
+
+To enable content providers, the form class must :
+
+  1. implement ``IFieldsAndContentProvidersForm``
+  2. have a ``contentProviders`` attribute that is 
+     an instance of the ``ContentProviders`` class.
+
+::
+
+  >>> from z3c.form.interfaces import IFieldsAndContentProvidersForm
+  >>> from z3c.form.contentprovider import ContentProviders
+
+Content provider assignment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Content providers classes (factories) can be assigned directly to the
+``ContentProviders`` container:: 
+
+  >>> class PersonForm(form.Form):
+  ...     implements(IFieldsAndContentProvidersForm)
+  ...     fields = field.Fields(IPerson)
+  ...     ignoreContext = True
+  ...     contentProviders = ContentProviders()
+  ...     contentProviders['longHelp'] = ExtendedHelp
+  ...     contentProviders['longHelp'].position = 1
+
+Let's instantiate content and form instances::
+
+  >>> person = Person()
+  >>> personForm = PersonForm(person, request)
+
+Once the widget manager has been updated, it holds the content provider::
+
+  >>> from z3c.form.contentprovider import FieldWidgetsAndProviders
+  >>> manager = FieldWidgetsAndProviders(personForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> widgets = manager._data
+  >>> ids = widgets.keys()
+  >>> ids.sort()
+  >>> ids
+  ['fullname', 'id', 'longHelp']
+  >>> widgets['longHelp']
+  <ExtendedHelp object at ...>
+  >>> widgets['id']
+  <TextWidget 'form.widgets.id'>
+  >>> widgets['fullname']
+  <TextWidget 'form.widgets.fullname'>
+  >>> manager.get('longHelp').render()
+  '<div class="ex-help">Help about person james</div>'
+
+Content provider lookup
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Forms can also refer by name to content providers.
+
+Let's register a content provider by name as usual::
+
+  >>> from zope.component import provideAdapter
+  >>> from zope.contentprovider.interfaces import IContentProvider
+  >>> from z3c.form.interfaces import IFormLayer
+  >>> provideAdapter(ExtendedHelp,
+  ...                (zope.interface.Interface,
+  ...                 IFormLayer,
+  ...                 zope.interface.Interface),
+  ...                provides=IContentProvider, name='longHelp')
+
+Let the form refer to it::
+
+  >>> class LookupPersonForm(form.Form):
+  ...     implements(IFieldsAndContentProvidersForm)
+  ...     prefix = 'form.'
+  ...     fields = field.Fields(IPerson)
+  ...     ignoreContext = True
+  ...     contentProviders = ContentProviders(['longHelp'])
+  ...     contentProviders['longHelp'].position = 2
+
+  >>> lookupForm = LookupPersonForm(person, request)
+
+After update, the widget manager refers to the content provider::
+
+  >>> from z3c.form.contentprovider import FieldWidgetsAndProviders
+  >>> manager = FieldWidgetsAndProviders(lookupForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> widgets = manager._data
+  >>> ids = widgets.keys()
+  >>> ids.sort()
+  >>> ids
+  ['fullname', 'id', 'longHelp']
+  >>> widgets['longHelp']
+  <ExtendedHelp object at ...>
+  >>> widgets['id']
+  <TextWidget 'form.widgets.id'>
+  >>> widgets['fullname']
+  <TextWidget 'form.widgets.fullname'>
+  >>> manager.get('longHelp').render()
+  '<div class="ex-help">Help about person james</div>'
+
+Providers position
+~~~~~~~~~~~~~~~~~~
+
+Until here, we have defined position for content providers without explaining
+how it is used.
+
+A position needs to be defined for each provider. Let's forget to define a
+position::
+  
+  >>> class UndefinedPositionForm(form.Form):
+  ...     implements(IFieldsAndContentProvidersForm)
+  ...     prefix = 'form.'
+  ...     fields = field.Fields(IPerson)
+  ...     ignoreContext = True
+  ...     contentProviders = ContentProviders(['longHelp'])
+
+  >>> form = UndefinedPositionForm(person, request)
+  >>> manager = FieldWidgetsAndProviders(form, request, person)
+  >>> manager.ignoreContext = True
+
+When updating the widget manager, we get an exception::
+
+  >>> manager.update()
+  Traceback (most recent call last):
+  ...
+  ValueError: Position of the following content provider should be an integer: 'longHelp'.
+
+Let's check positioning of content providers::
+   
+  >>> LookupPersonForm.contentProviders['longHelp'].position = 0
+  >>> manager = FieldWidgetsAndProviders(lookupForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.values()
+  [<ExtendedHelp object at ...>, <TextWidget 'form.widgets.id'>, <TextWidget 'form.widgets.fullname'>]
+
+  >>> LookupPersonForm.contentProviders['longHelp'].position = 1
+  >>> manager = FieldWidgetsAndProviders(lookupForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.values()
+  [<TextWidget 'form.widgets.id'>, <ExtendedHelp object at ...>, <TextWidget 'form.widgets.fullname'>]
+
+  >>> LookupPersonForm.contentProviders['longHelp'].position = 2
+  >>> manager = FieldWidgetsAndProviders(lookupForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.values()
+  [<TextWidget 'form.widgets.id'>, <TextWidget 'form.widgets.fullname'>, <ExtendedHelp object at ...>]
+
+Using value larger than sequence length implies end of sequence::
+
+  >>> LookupPersonForm.contentProviders['longHelp'].position = 3
+  >>> manager = FieldWidgetsAndProviders(lookupForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.values()
+  [<TextWidget 'form.widgets.id'>, <TextWidget 'form.widgets.fullname'>, <ExtendedHelp object at ...>]
+
+A negative value is interpreted same as ``insert`` method of Python lists::
+
+  >>> LookupPersonForm.contentProviders['longHelp'].position = -1
+  >>> manager = FieldWidgetsAndProviders(lookupForm, request, person)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.values()
+  [<TextWidget 'form.widgets.id'>, <ExtendedHelp object at ...>, <TextWidget 'form.widgets.fullname'>]
+
+Rendering the form
+------------------
+
+Once the form has been updated, it can be rendered.
+
+Since we have not assigned a template yet, we have to do it now.
+We have a small template as part of this example::
+  
+  >>> import os
+  >>> from z3c.form import ptcompat as viewpagetemplatefile
+  >>> from z3c.form import tests
+  >>> def personTemplate(form):
+  ...     form.template = viewpagetemplatefile.bind_template(
+  ...         viewpagetemplatefile.ViewPageTemplateFile(
+  ...             'simple_edit_with_providers.pt', 
+  ...             os.path.dirname(tests.__file__)), form)
+  >>> personTemplate(personForm)
+
+To enable form updating, all widget adapters must be registered::
+  
+  >>> from z3c.form.testing import setupFormDefaults
+  >>> setupFormDefaults()
+
+``FieldWidgetsAndProviders`` is registered as widget manager for 
+``IFieldsAndContentProvidersForm``::
+
+  >>> personForm.update()
+  >>> personForm.widgets
+  <z3c.form.contentprovider.FieldWidgetsAndProviders object at ...>
+
+Let's render the form::
+
+  >>> print personForm.render()
+  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+  <html xmlns="http://www.w3.org/1999/xhtml">
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-id">ID</label>
+          <input id="form-widgets-id" name="form.widgets.id"
+                 class="text-widget required textline-field"
+                 value="" type="text" />
+        </div>
+        <div class="row">
+          <div class="ex-help">Help about person james</div>
+        </div>
+        <div class="row">
+          <label for="form-widgets-fullname">FullName</label>
+          <input id="form-widgets-fullname"
+                 name="form.widgets.fullname"
+                 class="text-widget required textline-field"
+                 value="" type="text" />
+        </div>
+      </form>
+    </body>
+  </html>

Modified: z3c.form/trunk/src/z3c/form/index.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/index.txt	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/index.txt	2010-07-01 12:00:21 UTC (rev 114046)
@@ -15,6 +15,7 @@
    zcml
    validator
    widget
+   contentprovider
    action
    datamanager
    converter

Modified: z3c.form/trunk/src/z3c/form/interfaces.py
===================================================================
--- z3c.form/trunk/src/z3c/form/interfaces.py	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/interfaces.py	2010-07-01 12:00:21 UTC (rev 114046)
@@ -228,6 +228,11 @@
         specification.
         """
 
+class IContentProviders(IManager):
+    """
+    A content provider manager
+    """
+
 # ----[ Data Managers ]------------------------------------------------------
 
 class IDataManager(zope.interface.Interface):
@@ -1007,7 +1012,15 @@
                       'the form.'),
         schema=IFields)
 
+class IFieldsAndContentProvidersForm(IForm):
+    """A form that is based upon defined fields and content providers"""
 
+    contentProviders = zope.schema.Object(
+        title=_('Content providers'),
+        description=_('A manager describing the content providers to be used for '
+                      'the form.'),
+        schema=IContentProviders)
+
 class IButtonForm(IForm):
     """A form that is based upon defined buttons."""
 

Modified: z3c.form/trunk/src/z3c/form/testing.py
===================================================================
--- z3c.form/trunk/src/z3c/form/testing.py	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/testing.py	2010-07-01 12:00:21 UTC (rev 114046)
@@ -34,8 +34,8 @@
 
 from z3c.form import browser, button, converter, datamanager, error, field
 from z3c.form import form, interfaces, term, validator, widget
+from z3c.form import contentprovider
 from z3c.form.browser import radio, select, text, textarea
-from z3c.form.browser import file as fileWidget
 
 from z3c.form.ptcompat import AVAILABLE
 
@@ -205,6 +205,9 @@
     zope.component.provideAdapter(datamanager.AttributeField)
     # Adapter to use form.fields to generate widgets
     zope.component.provideAdapter(field.FieldWidgets)
+    # Adapter that uses form.fields to generate widgets
+    # AND interlace content providers
+    zope.component.provideAdapter(contentprovider.FieldWidgetsAndProviders)
     # Adapters to lookup the widget for a field
     # Text Field Widget
     zope.component.provideAdapter(

Copied: z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt (from rev 114045, z3c.form/branches/fieldsandcontentproviders/src/z3c/form/tests/simple_edit_with_providers.pt)
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt	2010-07-01 12:00:21 UTC (rev 114046)
@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <body>
+    <i tal:condition="view/status" tal:content="view/status" />
+    <ul tal:condition="view/widgets/errors">
+      <li tal:repeat="error view/widgets/errors">
+        <tal:block condition="error/widget">
+          <tal:block replace="error/widget/label" />:
+        </tal:block>
+        <tal:block replace="structure error/render" />
+      </li>
+    </ul>
+    <form action=".">
+      <tal:snippets repeat="snippet view/widgets/values">
+
+      <div class="row" tal:define="is_widget snippet/id | nothing">
+
+        <tal:widget condition="is_widget" 
+                    define="widget snippet">
+        <b tal:condition="widget/error"
+           tal:content="structure widget/error/render"
+        /><label tal:condition="widget/id"
+               for=""
+               tal:attributes="for widget/id"
+               tal:content="widget/label" />
+        <input type="text" tal:replace="structure widget/render" />
+        </tal:widget>
+
+        <tal:provider condition="not:is_widget"
+                      define="contentprovider snippet"
+                      replace="structure contentprovider/render" />
+                      
+      </div>
+
+      </tal:snippets>
+      <div class="action" tal:repeat="action view/actions/values">
+        <input type="submit" tal:replace="structure action/render" />
+      </div>
+    </form>
+  </body>
+</html>

Modified: z3c.form/trunk/src/z3c/form/tests/test_doc.py
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/test_doc.py	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/tests/test_doc.py	2010-07-01 12:00:21 UTC (rev 114046)
@@ -56,6 +56,12 @@
             checker=checker,
             ),
         doctest.DocFileSuite(
+            '../contentprovider.txt',
+            setUp=setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE,
+            checker=checker,
+            ),
+        doctest.DocFileSuite(
             '../value.txt',
             setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,

Modified: z3c.form/trunk/src/z3c/form/util.py
===================================================================
--- z3c.form/trunk/src/z3c/form/util.py	2010-07-01 10:47:15 UTC (rev 114045)
+++ z3c.form/trunk/src/z3c/form/util.py	2010-07-01 12:00:21 UTC (rev 114046)
@@ -149,7 +149,14 @@
             raise ValueError(value)
         self.data.append(value)
 
+    def insert(self, position, value):
+        if value in self.data:
+            raise ValueError(value)
+        self.data.insert(position, value)
 
+    #XXX TODO: Inherit from list
+
+
 class Manager(object):
     """Non-persistent IManager implementation."""
     zope.interface.implements(interfaces.IManager)
@@ -203,6 +210,10 @@
     def __contains__(self, name):
         return bool(self.get(name))
 
+    #XXX TODO:
+    # Add __setitem__ that will add key, value at the end of both lists as in PEP0372
+    # Add insertBefore(key)
+    #     insertAfter(key)
 
 class SelectionManager(Manager):
     """Non-persisents ISelectionManager implementation."""



More information about the checkins mailing list