[Checkins] SVN: z3c.form/ Initial import of new form and widget framework for Zope 3.

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu May 24 10:45:03 EDT 2007


Log message for revision 75940:
  Initial import of new form and widget framework for Zope 3.
  

Changed:
  A   z3c.form/
  A   z3c.form/branches/
  A   z3c.form/tags/
  A   z3c.form/trunk/
  A   z3c.form/trunk/bootstrap.py
  A   z3c.form/trunk/buildout.cfg
  A   z3c.form/trunk/setup.py
  A   z3c.form/trunk/src/
  A   z3c.form/trunk/src/z3c/
  A   z3c.form/trunk/src/z3c/__init__.py
  A   z3c.form/trunk/src/z3c/form/
  A   z3c.form/trunk/src/z3c/form/README.txt
  A   z3c.form/trunk/src/z3c/form/SETUP.cfg
  A   z3c.form/trunk/src/z3c/form/TODO.txt
  A   z3c.form/trunk/src/z3c/form/__init__.py
  A   z3c.form/trunk/src/z3c/form/action.py
  A   z3c.form/trunk/src/z3c/form/action.txt
  A   z3c.form/trunk/src/z3c/form/browser/
  A   z3c.form/trunk/src/z3c/form/browser/README.txt
  A   z3c.form/trunk/src/z3c/form/browser/__init__.py
  A   z3c.form/trunk/src/z3c/form/browser/checkbox.py
  A   z3c.form/trunk/src/z3c/form/browser/checkbox.txt
  A   z3c.form/trunk/src/z3c/form/browser/checkbox.zcml
  A   z3c.form/trunk/src/z3c/form/browser/checkbox_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/checkbox_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/configure.zcml
  A   z3c.form/trunk/src/z3c/form/browser/file.py
  A   z3c.form/trunk/src/z3c/form/browser/file.txt
  A   z3c.form/trunk/src/z3c/form/browser/file.zcml
  A   z3c.form/trunk/src/z3c/form/browser/file_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/file_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/orderedselect.py
  A   z3c.form/trunk/src/z3c/form/browser/orderedselect.txt
  A   z3c.form/trunk/src/z3c/form/browser/orderedselect.zcml
  A   z3c.form/trunk/src/z3c/form/browser/orderedselect_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/orderedselect_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/password.py
  A   z3c.form/trunk/src/z3c/form/browser/password.txt
  A   z3c.form/trunk/src/z3c/form/browser/password.zcml
  A   z3c.form/trunk/src/z3c/form/browser/password_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/password_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/radio.py
  A   z3c.form/trunk/src/z3c/form/browser/radio.txt
  A   z3c.form/trunk/src/z3c/form/browser/radio.zcml
  A   z3c.form/trunk/src/z3c/form/browser/radio_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/radio_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/select.py
  A   z3c.form/trunk/src/z3c/form/browser/select.txt
  A   z3c.form/trunk/src/z3c/form/browser/select.zcml
  A   z3c.form/trunk/src/z3c/form/browser/select_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/select_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/submit.py
  A   z3c.form/trunk/src/z3c/form/browser/submit.txt
  A   z3c.form/trunk/src/z3c/form/browser/submit.zcml
  A   z3c.form/trunk/src/z3c/form/browser/submit_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/submit_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/tests.py
  A   z3c.form/trunk/src/z3c/form/browser/text.py
  A   z3c.form/trunk/src/z3c/form/browser/text.txt
  A   z3c.form/trunk/src/z3c/form/browser/text.zcml
  A   z3c.form/trunk/src/z3c/form/browser/text_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/text_input.pt
  A   z3c.form/trunk/src/z3c/form/browser/textarea.py
  A   z3c.form/trunk/src/z3c/form/browser/textarea.zcml
  A   z3c.form/trunk/src/z3c/form/browser/textarea_display.pt
  A   z3c.form/trunk/src/z3c/form/browser/textarea_input.pt
  A   z3c.form/trunk/src/z3c/form/button.py
  A   z3c.form/trunk/src/z3c/form/button.txt
  A   z3c.form/trunk/src/z3c/form/configure.zcml
  A   z3c.form/trunk/src/z3c/form/converter.py
  A   z3c.form/trunk/src/z3c/form/converter.txt
  A   z3c.form/trunk/src/z3c/form/datamanager.py
  A   z3c.form/trunk/src/z3c/form/datamanager.txt
  A   z3c.form/trunk/src/z3c/form/error.pt
  A   z3c.form/trunk/src/z3c/form/error.py
  A   z3c.form/trunk/src/z3c/form/error.txt
  A   z3c.form/trunk/src/z3c/form/field.py
  A   z3c.form/trunk/src/z3c/form/field.txt
  A   z3c.form/trunk/src/z3c/form/form-graph.dot
  A   z3c.form/trunk/src/z3c/form/form-graph.png
  A   z3c.form/trunk/src/z3c/form/form-graph.ps
  A   z3c.form/trunk/src/z3c/form/form.py
  A   z3c.form/trunk/src/z3c/form/form.txt
  A   z3c.form/trunk/src/z3c/form/i18n.py
  A   z3c.form/trunk/src/z3c/form/interfaces.py
  A   z3c.form/trunk/src/z3c/form/meta.zcml
  A   z3c.form/trunk/src/z3c/form/subform.py
  A   z3c.form/trunk/src/z3c/form/subform.txt
  A   z3c.form/trunk/src/z3c/form/term.py
  A   z3c.form/trunk/src/z3c/form/term.txt
  A   z3c.form/trunk/src/z3c/form/testing.py
  A   z3c.form/trunk/src/z3c/form/tests/
  A   z3c.form/trunk/src/z3c/form/tests/__init__.py
  A   z3c.form/trunk/src/z3c/form/tests/custom_error.pt
  A   z3c.form/trunk/src/z3c/form/tests/simple_caredit.pt
  A   z3c.form/trunk/src/z3c/form/tests/simple_display.pt
  A   z3c.form/trunk/src/z3c/form/tests/simple_edit.pt
  A   z3c.form/trunk/src/z3c/form/tests/simple_owneredit.pt
  A   z3c.form/trunk/src/z3c/form/tests/simple_subedit.pt
  A   z3c.form/trunk/src/z3c/form/tests/test_doc.py
  A   z3c.form/trunk/src/z3c/form/util.py
  A   z3c.form/trunk/src/z3c/form/util.txt
  A   z3c.form/trunk/src/z3c/form/validator.py
  A   z3c.form/trunk/src/z3c/form/validator.txt
  A   z3c.form/trunk/src/z3c/form/value.py
  A   z3c.form/trunk/src/z3c/form/value.txt
  A   z3c.form/trunk/src/z3c/form/widget-graph.dot
  A   z3c.form/trunk/src/z3c/form/widget-graph.png
  A   z3c.form/trunk/src/z3c/form/widget-graph.ps
  A   z3c.form/trunk/src/z3c/form/widget.py
  A   z3c.form/trunk/src/z3c/form/widget.txt
  A   z3c.form/trunk/src/z3c/form/z3c.form-configure.zcml
  A   z3c.form/trunk/src/z3c/form/z3c.form-meta.zcml
  A   z3c.form/trunk/src/z3c/form/zcml.py
  A   z3c.form/trunk/src/z3c/form/zcml.txt

-=-
Added: z3c.form/trunk/bootstrap.py
===================================================================
--- z3c.form/trunk/bootstrap.py	                        (rev 0)
+++ z3c.form/trunk/bootstrap.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: z3c.form/trunk/bootstrap.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/buildout.cfg
===================================================================
--- z3c.form/trunk/buildout.cfg	                        (rev 0)
+++ z3c.form/trunk/buildout.cfg	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,12 @@
+[buildout]
+develop = .
+parts = test coverage
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.form [test]
+
+[coverage]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+

Added: z3c.form/trunk/setup.py
===================================================================
--- z3c.form/trunk/setup.py	                        (rev 0)
+++ z3c.form/trunk/setup.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup
+
+$Id$
+"""
+from setuptools import setup, find_packages
+
+setup (
+    name='z3c.form',
+    version='1.0c2',
+    author = "Stephan Richter, Roger Ineichen and the Zope Community",
+    author_email = "zope3-dev at zope.org",
+    description = "An advanced form and widget framework for Zope 3",
+    license = "ZPL 2.1",
+    keywords = "zope3 form widget",
+    classifiers = [
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+    url = 'http://svn.zope.org/z3c.form',
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c'],
+    extras_require = dict(
+        test = ['zope.testing', 'z3c.coverage', 'z3c.template'],
+        ),
+    install_requires = [
+        'setuptools',
+        'zope.app.pagetemplate',
+        'zope.app.testing',
+        'zope.component',
+        'zope.configuration',
+        'zope.event',
+        'zope.formlib',
+        'zope.i18n',
+        'zope.i18nmessageid',
+        'zope.interface',
+        'zope.lifecycleevent',
+        'zope.location',
+        'zope.pagetemplate',
+        'zope.publisher',
+        'zope.schema',
+        'zope.security',
+        ],
+    dependency_links = ['http://download.zope.org/distribution'],
+    zip_safe = False,
+    )


Property changes on: z3c.form/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/__init__.py
===================================================================
--- z3c.form/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/__init__.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,7 @@
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace('z3c')
+except ImportError:
+    pass
+


Property changes on: z3c.form/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/README.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/README.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/README.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,93 @@
+=================
+Forms and Widgets
+=================
+
+This package provides an implementation for HTML forms and widgets. The goal
+is to provide a simple API but with the ability to easily customize any data or
+steps. This document, provides the content of this package's documentation
+files. The documents are ordered in the way they should be read:
+
+- ``form.txt`` [must read]
+
+  Describes the setup and usage of forms in the most common usages. Some
+  details are provided to the structure of form components.
+
+- ``subform.txt`` [must read]
+
+  Introduces the complexities surrounding sub-forms and details two classes of
+  sub-forms, including code examples.
+
+- ``field.txt`` [must read]
+
+  Provides a comprehensive explanation of the field manager API and how it is
+  to be used.
+
+- ``button.txt`` [must read]
+
+  Provides a comprehensive explanation of the button manager API. It also
+  outlines how to create buttons within schemas and how buttons are converted
+  to actions.
+
+- ``zcml.txt`` [must read]
+
+  Explains the ZCML directives defines by this package, which are designed to
+  make it easier to register new templates without writing Python code.
+
+- ``validator.txt`` [advanced users]
+
+  Validators are used to validate converted form data. This document provides
+  a comprehensive overview of the API and how to use it effectively.
+
+- ``widget.txt`` [advanced users]
+
+  Explains in detail the design goals surrounding widgets and widget managers
+  and how they were realized with the implemented API.
+
+- ``action.txt`` [advanced users]
+
+  Explains in detail the design goals surrounding action managers and
+  actions. The execution of actions using action handlers is also covered. The
+  document demonstrates how actions can be created without the use of buttons.
+
+- ``value.txt`` [informative]
+
+  The concept of attribute value adapters is introduced and fully
+  explained. Some motivation for this new and powerful pattern is given as
+  well.
+
+- ``datamanager.txt`` [informative]
+
+  Data managers are resposnsible for accessing and writing the data. While
+  attribute access is the most common case, data managers can also manage
+  other data structures, such as dictionaries.
+
+- ``converter.txt`` [informative]
+
+  Data converters convert data between internal and widget values and vice
+  versa.
+
+- ``term.txt`` [informative]
+
+  Terms are wrappers around sources and vocabularies to provide a common
+  interface for choices in this package.
+
+- ``util.txt`` [informative]
+
+  The ``util`` module provides several helper functions and classes. The
+  components not tested otherwise are explained in this file.
+
+
+Browser Documentation
+---------------------
+
+There are several documentation files in the ``browser/`` sub-package. They
+mainly document the basic widgets provided by the package.
+
+- ``README.txt`` [advanced users]
+
+  This file contains a checklist, ensuring that all fields have a widget.
+
+- ``<fieldname>.txt``
+
+  Each field name documentation file comprehensively explains the widget and
+  how it is ensured to work properly.


Property changes on: z3c.form/trunk/src/z3c/form/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/SETUP.cfg
===================================================================
--- z3c.form/trunk/src/z3c/form/SETUP.cfg	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/SETUP.cfg	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+  z3c.form-*.zcml
+</data-files>

Added: z3c.form/trunk/src/z3c/form/TODO.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/TODO.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/TODO.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,29 @@
+====
+TODO
+====
+
+Framework
+---------
+
+
+Documentation
+-------------
+
+
+Samples
+-------
+
+Write some samples to show the power of the new widget and form framework.
+
+- Object name as an additional field in an add form
+
+
+Improvements
+------------
+
+Add explicit difference between error and confirmation message e.g.
+"There were some errors." and "No changes were applied."
+
+
+Notes from Roger
+----------------


Property changes on: z3c.form/trunk/src/z3c/form/TODO.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/__init__.py
===================================================================
--- z3c.form/trunk/src/z3c/form/__init__.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/__init__.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.form/trunk/src/z3c/form/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/action.py
===================================================================
--- z3c.form/trunk/src/z3c/form/action.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/action.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,90 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Form Framework Action Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.component
+
+from z3c.form import interfaces, util
+
+
+class Action(object):
+    """Action class."""
+
+    zope.interface.implements(interfaces.IAction)
+
+    __name__ = __parent__ = None
+
+    def __init__(self, request, title, name=None):
+        self.request = request
+        self.title = title
+        if name is None:
+            name = util.createId(title)
+        self.name = name
+
+    def isExecuted(self):
+        return self.name in self.request
+
+    def __repr__(self):
+        return '<%s %r %r>' % (self.__class__.__name__, self.name, self.title)
+
+
+class Actions(util.Manager):
+    """Action manager class."""
+    zope.interface.implementsOnly(interfaces.IActions)
+
+    __name__ = __parent__ = None
+
+    def __init__(self, form, request, content):
+        super(Actions, self).__init__()
+        self.form = form
+        self.request = request
+        self.content = content
+
+    @property
+    def executedActions(self):
+        return [action for action in self.values()
+                if action.isExecuted()]
+
+    def update(self):
+        """See z3c.form.interfaces.IActions."""
+        pass
+
+    def execute(self):
+        """See z3c.form.interfaces.IActions."""
+        adapter = None
+        for action in self.executedActions:
+            adapter = zope.component.queryMultiAdapter(
+                (self.form, self.request, self.content, action),
+                interface=interfaces.IActionHandler)
+            if adapter is not None:
+                return adapter()
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.__name__)
+
+
+class ActionHandlerBase(object):
+    """Action handler base adapter."""
+
+    zope.interface.implements(interfaces.IActionHandler)
+
+    def __init__(self, form, request, content, action):
+        self.form = form
+        self.request = request
+        self.content = content
+        self.action = action


Property changes on: z3c.form/trunk/src/z3c/form/action.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/action.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/action.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/action.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,187 @@
+===============
+Action Managers
+===============
+
+Action managers are components that manage all actions that can be taken
+within a view, usually a form. They are also responsible for executing actions
+when asked to do so.
+
+Creating an action manager
+--------------------------
+
+An action manager is a form-related adapter that has the following
+discriminator: form, request, and content. While there is a base
+implementation for an action manager, the ``action`` module does not provide a
+full implementation.
+
+So we first have to build a simple implementation based on the ``Actions``
+manager base class which allows us to add actions. Note that the following
+implementation is for deomnstration purposes. If you want to see a real action
+manager implementation, then have a look at ``ButtonActions``. Let's now
+implement our simple action manager:
+
+  >>> from z3c.form import action
+  >>> class SimpleActions(action.Actions):
+  ...     """Simple sample."""
+  ...
+  ...     def append(self, name, action):
+  ...         """See z3c.form.interfaces.IActions."""
+  ...         if not name in self:
+  ...             self._data_keys.append(name)
+  ...         self._data_values.append(action)
+  ...         self._data[name] = action
+
+Before we can initialise the action manager, we have to create instances for
+our three discriminators, just enough to get it working:
+
+  >>> import zope.interface
+  >>> from z3c.form import interfaces
+  >>> class Form(object):
+  ...     zope.interface.implements(interfaces.IForm)
+  >>> form = Form()
+
+  >>> class Content(object):
+  ...     zope.interface.implements(zope.interface.Interface)
+  >>> content = Content()
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+We are now ready to create the action manager, which is a simple
+triple-adapter:
+
+  >>> manager = SimpleActions(form, request, content)
+  >>> manager
+  <SimpleActions None>
+
+As we can see in the manager representation above, the name of the manager is
+``None``, since we have not specified one:
+
+  >>> manager.__name__ = 'example'
+  >>> manager
+  <SimpleActions 'example'>
+
+
+Managing and Accessing Actions
+------------------------------
+
+Initially there are no actions in the manager:
+
+  >>> manager.keys()
+  []
+
+Our simple implementation of has an additional ``append()`` method, which we
+will use to add actions:
+
+  >>> apply = action.Action(request, u'Apply')
+  >>> manager.append(apply.name, apply)
+
+The action is added immediately:
+
+  >>> manager.keys()
+  ['apply']
+
+However, you should not rely on it being added, and always update the manager
+once all actions were defined:
+
+  >>> manager.update()
+
+Note: If the title of the action is a more complex unicode string and no name
+is specified for the action, then a hexadecimal name is created from the
+title:
+
+  >>> action.Action(request, u'Apply Now!').name
+  '4170706c79204e6f7721'
+
+Since the action manager is an enumerable mapping, ...
+
+  >>> from zope.interface.common.mapping import IEnumerableMapping
+  >>> IEnumerableMapping.providedBy(manager)
+  True
+
+there are several API methods available:
+
+  >>> manager['apply']
+  <Action 'apply' u'Apply'>
+  >>> manager['foo']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'foo'
+
+  >>> manager.get('apply')
+  <Action 'apply' u'Apply'>
+  >>> manager.get('foo', 'default')
+  'default'
+
+  >>> 'apply' in manager
+  True
+  >>> 'foo' in manager
+  False
+
+  >>> manager.values()
+  [<Action 'apply' u'Apply'>]
+
+  >>> manager.items()
+  [('apply', <Action 'apply' u'Apply'>)]
+
+  >>> len(manager)
+  1
+
+
+Executing actions
+-----------------
+
+When an action is executed, an execution adapter is looked up. If there is no
+adapter, nothing happens. So let's create a request that submits the apply
+button:
+
+  >>> request = TestRequest(form={'apply': 'Apply'})
+  >>> manager = SimpleActions(form, request, content)
+
+We also want to have two buttons in this case, so that we can ensure that only
+one is executed:
+
+  >>> apply = action.Action(request, u'Apply')
+  >>> manager.append(apply.name, apply)
+
+  >>> cancel = action.Action(request, u'Cancel')
+  >>> manager.append(cancel.name, cancel)
+  >>> manager.update()
+
+Now that the manager is updated, we can ask it for the "executed" actions:
+
+  >>> manager.executedActions
+  [<Action 'apply' u'Apply'>]
+
+Executing the actions does nothing, because there are no handlers yet:
+
+  >>> manager.execute()
+
+
+Let's now register an action handler that listens to the "Apply" action. An
+action handler has four discriminators: form, request, content, and
+action. All those objects are available to the handler under those names. When
+using the base action handler from the ``action`` module, ``__call__()`` is
+the only method that needs to be implemented:
+
+  >>> from z3c.form import util
+
+  >>> class SimpleActionHandler(action.ActionHandlerBase):
+  ...     zope.component.adapts(
+  ...         None, TestRequest, None, util.getSpecification(apply))
+  ...     def __call__(self):
+  ...         print 'successfully applied'
+
+  >>> zope.component.provideAdapter(SimpleActionHandler)
+
+As you can see, we registered the action specifically for the apply
+action. Now, executing the actions calls this handler:
+
+  >>> manager.execute()
+  successfully applied
+
+Of course it only works for the "Apply" action and not ""Cancel":
+
+  >>> request = TestRequest(form={'cancel': 'Cancel'})
+  >>> manager.request = apply.request = cancel.request = request
+  >>> manager.execute()


Property changes on: z3c.form/trunk/src/z3c/form/action.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/README.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/README.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/README.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,568 @@
+======
+README
+======
+
+The z3c.form library provides a form framework and widgets. This document
+should make sure that we implement a widget for each field defined in
+zope.schema. Take a look at the different widget doctest for more information
+about the widgets.
+
+  >>> import zope.schema
+  >>> from z3c.form import browser
+
+Let's setup all required adapters using zcml. This makes sure we test the real
+configuration.
+
+  >>> from zope.configuration import xmlconfig
+  >>> import zope.component
+  >>> import z3c.form
+  >>> xmlconfig.XMLConfig('meta.zcml', zope.component)()
+  >>> xmlconfig.XMLConfig('meta.zcml', z3c.form)()
+  >>> xmlconfig.XMLConfig('configure.zcml', z3c.form)()
+
+also define a helper method for test the widgets:
+
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.testing import TestRequest
+  >>> def setupWidget(field):
+  ...     request = TestRequest()
+  ...     widget = zope.component.getMultiAdapter((field, request),
+  ...         interfaces.IFieldWidget)
+  ...     widget.id = u'foo'
+  ...     widget.name = u'bar'
+  ...     return widget
+
+
+ASCII
+-----
+
+  >>> field = zope.schema.ASCII(default='This is\n ASCII.')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <textarea id="foo" name="bar" class="textAreaWidget">This is
+   ASCII.</textarea>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  This is
+  ASCII.
+
+
+ASCIILine
+---------
+
+  >>> field = zope.schema.ASCIILine(default='An ASCII line.')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="An ASCII line." />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  An ASCII line.
+
+
+Bool
+----
+
+  >>> field = zope.schema.Bool(default=True)
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <span class="option">
+    <input type="radio" id="bar.0" name="bar:list"
+           class="radioWidget" value="yes" checked="checked" />
+   <span class="label">yes</span>
+  </span><span class="option">
+    <input type="radio" id="bar.1" name="bar:list"
+           class="radioWidget" value="no" />
+   <span class="label">no</span>
+  </span>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  yes
+
+For the boolean, the checkbox widget can be used as well:
+
+  >>> from z3c.form.browser import checkbox
+  >>> widget = checkbox.CheckBoxFieldWidget(field, TestRequest())
+  >>> widget.id = u'foo'
+  >>> widget.name = u'bar'
+  >>> widget.update()
+
+  >>> print widget.render()
+  <span class="option">
+    <input type="checkbox" id="bar.0" name="bar:list"
+           class="checkBoxWidget" value="yes"
+           checked="checked" />
+    <span class="label">yes</span>
+  </span><span class="option">
+    <input type="checkbox" id="bar.1" name="bar:list"
+           class="checkBoxWidget" value="no" />
+    <span class="label">no</span>
+  </span>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  yes
+
+Button
+------
+
+  >>> from z3c.form import button
+  >>> field = button.Button(title=u'Press me!')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="submit" id="foo" name="bar"
+         class="submitWidget" value="Press me!" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  <input type="submit" id="foo" name="bar"
+         class="submitWidget" value="Press me!" disabled="" />
+
+
+Bytes
+-----
+
+  >>> field = zope.schema.Bytes(default='\10\45\n\32')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="file" id="foo" name="bar" class="fileWidget" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> widget.render()
+  u'\x08%\n\x1a\n'
+
+
+BytesLine
+---------
+
+  >>> field = zope.schema.BytesLine(default='A Bytes line.')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="A Bytes line." />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  A Bytes line.
+
+
+Choice
+------
+
+  >>> from zope.schema import vocabulary
+  >>> terms = [vocabulary.SimpleTerm(*value) for value in
+  ...          [(True, 'yes', 'Yes'), (False, 'no', 'No')]]
+  >>> vocabulary = vocabulary.SimpleVocabulary(terms)
+  >>> field = zope.schema.Choice(default=True, vocabulary=vocabulary)
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <select id="foo" name="bar:list" class="selectWidget" 
+          size="1">
+    <option id="foo.0" value="yes" selected="selected">Yes</option>
+    <option id="foo.1" value="no">No</option>
+  </select>
+  <input name="bar-empty-marker" type="hidden" value="1" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  Yes
+
+
+Date
+----
+
+  >>> import datetime
+  >>> field = zope.schema.Date(default=datetime.date(2007, 4, 1))
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="2007-04-01" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  2007-04-01
+
+
+Datetime
+--------
+
+  >>> field = zope.schema.Datetime(default=datetime.datetime(2007, 4, 1, 12))
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="2007-04-01 12:00:00" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  2007-04-01 12:00:00
+
+
+Decimal
+-------
+
+  >>> import decimal
+  >>> field = zope.schema.Decimal(default=decimal.Decimal('12.87'))
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="12.87" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  12.87
+
+
+Dict
+----
+
+There is no default widget for this field, since the sematics are fairly
+complex.
+
+
+DottedName
+----------
+
+  >>> field = zope.schema.DottedName(default='z3c.form')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="z3c.form" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  z3c.form
+
+
+Float
+-----
+
+  >>> field = zope.schema.Float(default=12.8)
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="12.8" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  12.8
+
+
+FrozenSet
+---------
+
+  >>> field = zope.schema.FrozenSet(
+  ...     value_type=zope.schema.Choice(values=(1, 2, 3, 4)),
+  ...     default=frozenset([1, 3]) )
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <select id="foo" name="bar:list" class="selectWidget" 
+          multiple="multiple" size="5">
+    <option id="foo.0" value="1" selected="selected">1</option>
+    <option id="foo.1" value="2">2</option>
+    <option id="foo.2" value="3" selected="selected">3</option>
+    <option id="foo.3" value="4">4</option>
+  </select>
+  <input name="bar-empty-marker" type="hidden" value="1" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  1, 3
+
+
+Id
+--
+
+  >>> field = zope.schema.Id(default='z3c.form')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="z3c.form" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  z3c.form
+
+
+Int
+---
+
+  >>> field = zope.schema.Int(default=12)
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="12" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  12
+
+
+List
+----
+
+  >>> field = zope.schema.List(
+  ...     value_type=zope.schema.Choice(values=(1, 2, 3, 4)),
+  ...     default=[1, 3] )
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <script type="text/javascript">
+  ...
+  </script>
+  <BLANKLINE>
+  <table border="0" class="ordered-selection-field">
+    <tr>
+      <td>
+        <select id="bar.from" name="bar.from" size="5"
+                multiple="">
+          <option value="1">1</option>
+          <option value="2">2</option>
+          <option value="3">3</option>
+          <option value="4">4</option>
+        </select>
+      </td>
+      <td>
+        <button name="from2toButton" type="button"
+                value=" -&gt;"
+                onclick="javascript:from2to('bar')">&nbsp;-&gt;</button>
+        <br />
+        <button name="to2fromButton" type="button"
+                value="&lt;- "
+                onclick="javascript:to2from('bar')">&lt;-&nbsp;</button>
+      </td>
+      <td>
+        <select id="bar.to" name="bar.to" size="5" multiple="">
+          <option value="1">1</option>
+          <option value="3">3</option>
+        </select>
+        <input name="bar-empty-marker" type="hidden" />
+        <span id="bar.toDataContainer">
+          <script type="text/javascript">
+            copyDataForSubmit('bar');</script>
+        </span>
+      </td>
+      <td>
+        <button name="upButton" type="button" value="^"
+                onclick="javascript:moveUp('bar')">^</button>
+        <br />
+        <button name="downButton" type="button" value="v"
+                onclick="javascript:moveDown('bar')">v</button>
+      </td>
+    </tr>
+  </table>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  1, 3
+
+
+Object
+------
+
+By default, we are not going to provide widgets for an object, since we
+believe this is better done using sub-forms.
+
+
+Password
+--------
+
+  >>> field = zope.schema.Password(default=u'mypwd')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="password" id="foo" name="bar"
+         class="passwordWidget" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  mypwd
+
+
+Set
+---
+
+  >>> field = zope.schema.Set(
+  ...     value_type=zope.schema.Choice(values=(1, 2, 3, 4)),
+  ...     default=set([1, 3]) )
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <select id="foo" name="bar:list" class="selectWidget" 
+          multiple="multiple"  size="5">
+    <option id="foo.0" value="1" selected="selected">1</option>
+    <option id="foo.1" value="2">2</option>
+    <option id="foo.2" value="3" selected="selected">3</option>
+    <option id="foo.3" value="4">4</option>
+  </select>
+  <input name="bar-empty-marker" type="hidden" value="1" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  1, 3
+
+
+SourceText
+----------
+
+  >>> field = zope.schema.SourceText(default=u'<source />')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <textarea id="foo" name="bar" class="textAreaWidget">&lt;source /&gt;</textarea>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  &lt;source /&gt;
+
+
+Text
+----
+
+  >>> field = zope.schema.Text(default=u'Some\n Text.')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <textarea id="foo" name="bar" class="textAreaWidget">Some
+   Text.</textarea>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  Some
+  Text.
+
+
+TextLine
+--------
+
+  >>> field = zope.schema.TextLine(default=u'Some Text line.')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="Some Text line." />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  Some Text line.
+
+
+Time
+----
+
+  >>> field = zope.schema.Time(default=datetime.time(12, 0))
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="12:00:00" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  12:00:00
+
+
+Timedelta
+---------
+
+  >>> field = zope.schema.Timedelta(default=datetime.timedelta(days=3))
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="3 days, 0:00:00" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  3 days, 0:00:00
+
+
+Tuple
+-----
+
+  >>> field = zope.schema.Tuple(
+  ...     value_type=zope.schema.Choice(values=(1, 2, 3, 4)),
+  ...     default=(1, 3) )
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <script type="text/javascript">
+  ...
+  </script>
+  <BLANKLINE>
+  <table border="0" class="ordered-selection-field">
+    <tr>
+      <td>
+        <select id="bar.from" name="bar.from" size="5"
+                multiple="">
+          <option value="1">1</option>
+          <option value="2">2</option>
+          <option value="3">3</option>
+          <option value="4">4</option>
+        </select>
+      </td>
+      <td>
+        <button name="from2toButton" type="button"
+                value=" -&gt;"
+                onclick="javascript:from2to('bar')">&nbsp;-&gt;</button>
+        <br />
+        <button name="to2fromButton" type="button"
+                value="&lt;- "
+                onclick="javascript:to2from('bar')">&lt;-&nbsp;</button>
+      </td>
+      <td>
+        <select id="bar.to" name="bar.to" size="5" multiple="">
+          <option value="1">1</option>
+          <option value="3">3</option>
+        </select>
+        <input name="bar-empty-marker" type="hidden" />
+        <span id="bar.toDataContainer">
+          <script type="text/javascript">
+            copyDataForSubmit('bar');</script>
+        </span>
+      </td>
+      <td>
+        <button name="upButton" type="button" value="^"
+                onclick="javascript:moveUp('bar')">^</button>
+        <br />
+        <button name="downButton" type="button" value="v"
+                onclick="javascript:moveDown('bar')">v</button>
+      </td>
+    </tr>
+  </table>
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  1, 3
+
+
+URI
+---
+
+  >>> field = zope.schema.URI(default='http://zope.org')
+  >>> widget = setupWidget(field)
+  >>> widget.update()
+  >>> print widget.render()
+  <input type="text" id="foo" name="bar" class="textWidget"
+         value="http://zope.org" />
+
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  http://zope.org


Property changes on: z3c.form/trunk/src/z3c/form/browser/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/__init__.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/__init__.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/__init__.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.form/trunk/src/z3c/form/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/checkbox.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/checkbox.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/checkbox.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Text Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.component
+import zope.interface
+import zope.schema
+import zope.schema.interfaces
+from zope.i18n import translate
+
+from z3c.form import interfaces
+from z3c.form import widget
+
+
+class CheckBoxWidget(widget.SequenceWidget):
+    """Input type checkbox widget implementation."""
+
+    zope.interface.implementsOnly(interfaces.ICheckBoxWidget)
+
+    css = u'checkBoxWidget'
+    alt = None
+    readonly = None
+    accesskey = None
+    items = ()
+
+    def isChecked(self, term):
+        return term.token in self.value
+
+    def update(self):
+        """See z3c.form.interfaces.IWidget."""
+        super(CheckBoxWidget, self).update()
+        self.items = []
+        for count, term in enumerate(self.terms):
+            checked = self.isChecked(term)
+            id = '%s.%i' % (self.name, count)
+            label = term.token
+            if zope.schema.interfaces.ITitledTokenizedTerm.providedBy(term):
+                label = translate(term.title, context=self.request,
+                                  default=term.title)
+            self.items.append(
+                {'id':id, 'name':self.name + ':list', 'value':term.token,
+                 'label':label, 'checked':checked})
+
+
+ at zope.component.adapter(zope.schema.interfaces.IField, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def CheckBoxFieldWidget(field, request):
+    """IFieldWidget factory for CheckBoxWidget."""
+    return widget.FieldWidget(field, CheckBoxWidget(request))


Property changes on: z3c.form/trunk/src/z3c/form/browser/checkbox.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/checkbox.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/checkbox.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/checkbox.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,100 @@
+==============
+CheckBoxWidget
+==============
+
+Note: the checkbox widget isn't registered for a field by default. You can use
+the widgetFactory argument of a IField object if you construct fields or
+set the custom widget factory on selected fields later.
+
+The CheckBoxWidget renders a checkbox input type field e.g.
+<input type="checkbox" />
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from zope.app.form.interfaces import IInputWidget
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import checkbox
+
+The TextWidget is a widget:
+
+  >>> verifyClass(interfaces.IWidget, checkbox.CheckBoxWidget)
+  True
+
+The widget can render a input field only by adapting a request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> widget = checkbox.CheckBoxWidget(request)
+
+Set a name for the widget:
+
+  >>> widget.name = u'foo'
+
+Such a field provides IWidget:
+
+  >>> interfaces.IWidget.providedBy(widget)
+  True
+
+We also need to register the template for at least the widget and request:
+
+  >>> import os.path
+  >>> import zope.interface
+  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> import z3c.form.browser
+  >>> import z3c.form.widget
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'checkbox_input.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='input')
+
+If we render the widget we get an emtpy output:
+
+  >>> print widget.render()
+
+Let's provide some values for this widget. We can do this by defining a source
+providing ITerms. This source uses descriminators wich will fit for our setup.
+
+  >>> import zope.schema.interfaces
+  >>> from zope.schema.vocabulary import SimpleVocabulary
+  >>> import z3c.form.term
+  >>> class MyTerms(z3c.form.term.ChoiceTerms):
+  ...     def __init__(self, context, request, form, field, widget):
+  ...         self.terms = SimpleVocabulary.fromValues(['yes', 'no'])
+  >>> zope.component.provideAdapter(z3c.form.term.BoolTerms,
+  ...     adapts=(zope.interface.Interface,
+  ...             interfaces.IFormLayer, zope.interface.Interface,
+  ...             zope.interface.Interface, interfaces.ICheckBoxWidget))
+
+Now let's try if we get widget values:
+
+  >>> widget.update()
+  >>> print widget.render()
+  <span class="option">
+    <input type="checkbox" id="foo.0" name="foo:list"
+           class="checkBoxWidget" value="yes" />
+   <span class="label">yes</span>
+  </span><span class="option">
+    <input type="checkbox" id="foo.1" name="foo:list"
+           class="checkBoxWidget" value="no" />
+   <span class="label">no</span>
+  </span>
+
+
+If we set the value for the widget to ``yes``, we can se that the checkbox field
+get rendered with a checked flag:
+
+  >>> widget.value = 'yes'
+  >>> widget.update()
+  >>> print widget.render()
+  <span class="option">
+    <input type="checkbox" id="foo.0" name="foo:list"
+           class="checkBoxWidget" value="yes"
+           checked="checked" />
+   <span class="label">yes</span>
+  </span><span class="option">
+    <input type="checkbox" id="foo.1" name="foo:list"
+           class="checkBoxWidget" value="no" />
+   <span class="label">no</span>
+  </span>


Property changes on: z3c.form/trunk/src/z3c/form/browser/checkbox.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/checkbox.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/checkbox.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/checkbox.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,31 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <!-- Note: the CheckBoxFieldWidget isn't registered by default 
+       If you like to use checkboxes you need to use them as custom widget 
+       factories directly in a form.
+       
+       Because there is no need for a checkbox since we use select options
+       for all kind of Set and ordered list widgets where the order is relevant.
+       
+       Note: if you use List or Tuple of Choises, you get need a ordered widget
+       if order doesn't matter, you should use a Set of Choice and you will
+       get a multi select widget. -->
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.ICheckBoxWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="checkbox_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.ICheckBoxWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="checkbox_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/checkbox.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/checkbox_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/checkbox_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/checkbox_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,4 @@
+<tal:block tal:repeat="value view/displayValue"
+  ><tal:block replace="value"
+  /><tal:block condition="not:repeat/value/end">, </tal:block
+></tal:block>


Property changes on: z3c.form/trunk/src/z3c/form/browser/checkbox_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/checkbox_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/checkbox_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/checkbox_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,30 @@
+<span class="option"
+      tal:repeat="item view/items">
+  <input type="checkbox" id="" name="" class="" 
+         alt="" title="" tabindex="" disabled="" readonly="" accesskey="" value="" checked="checked"
+         tal:condition="item/checked"
+         tal:attributes="id item/id;
+                         name item/name;
+                         class view/css;
+                         alt view/alt;
+                         title view/title;
+                         tabindex view/tabindex;
+                         disabled view/disabled;
+                         readonly view/readonly;
+                         accesskey view/accesskey;
+                         value item/value" 
+  /><input type="checkbox" id="" name="" class="" 
+         alt="" title="" tabindex="" disabled="" readonly="" accesskey="" value=""
+         tal:condition="not:item/checked"
+         tal:attributes="id item/id;
+                         name item/name;
+                         class view/css;
+                         alt view/alt;
+                         title view/title;
+                         tabindex view/tabindex;
+                         disabled view/disabled;
+                         readonly view/readonly;
+                         accesskey view/accesskey;
+                         value item/value" />
+ <span class="label" tal:content="item/label">Label</span>
+</span>


Property changes on: z3c.form/trunk/src/z3c/form/browser/checkbox_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/configure.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/configure.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/configure.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,16 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <include file="checkbox.zcml" />
+  <include file="file.zcml" />
+  <include file="orderedselect.zcml" />
+  <include file="password.zcml" />
+  <include file="radio.zcml" />
+  <include file="select.zcml" />
+  <include file="submit.zcml" />
+  <include file="text.zcml" />
+  <include file="textarea.zcml" />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/file.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/file.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""T Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema.interfaces
+
+from z3c.form import interfaces, widget
+from z3c.form.browser import text
+
+
+class FileWidget(text.TextWidget):
+    """Input type text widget implementation."""
+    zope.interface.implementsOnly(interfaces.IFileWidget)
+
+    css = u'fileWidget'
+
+ at zope.component.adapter(zope.schema.interfaces.IBytes, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def FileFieldWidget(field, request):
+    """IFieldWidget factory for FileWidget."""
+    return widget.FieldWidget(field, FileWidget(request))


Property changes on: z3c.form/trunk/src/z3c/form/browser/file.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/file.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/file.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,65 @@
+===========
+File Widget
+===========
+
+The file widget allows you to upload a new file to the server. The "file" type
+of the "INPUT" element is described here:
+
+http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#edef-INPUT
+
+As for all widgets, the file widget must provide the new ``IWidget``
+interface:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import file
+
+  >>> verifyClass(interfaces.IWidget, file.FileWidget)
+  True
+
+The widget can be instantiated only using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+  >>> widget = file.FileWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+  >>> widget.id = u'widget.id'
+  >>> widget.name = u'widget.name'
+
+We also need to register the template for the widget:
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form.testing import getPath
+  >>> from z3c.form.widget import WidgetTemplateFactory
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('file_input.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.IFileWidget),
+  ...     IPageTemplate, name=interfaces.INPUT_MODE)
+
+If we render the widget we get a simple input element:
+
+  >>> print widget.render()
+  <input type="file" id="widget.id" name="widget.name"
+         class="fileWidget" />
+
+Let's now make sure that we can extract user entered data from a widget:
+
+  >>> import cStringIO
+  >>> myfile = cStringIO.StringIO('My file contents.')
+
+  >>> widget.request = TestRequest(form={'widget.name': myfile})
+  >>> widget.update()
+  >>> widget.extract()
+  <cStringIO.StringI object at ...>
+
+If nothing is found in the request, the default is returned:
+
+  >>> widget.request = TestRequest()
+  >>> widget.update()
+  >>> widget.extract()
+  <NOVALUE>


Property changes on: z3c.form/trunk/src/z3c/form/browser/file.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/file.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/file.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,26 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".file.FileFieldWidget"
+      for="zope.schema.interfaces.IBytes
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.IFileWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="file_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.IFileWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="file_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/file.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/file_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/file_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+<tal:block condition="view/value" content="view/value" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/file_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/file_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/file_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,13 @@
+<input type="file" id="" name="" class="" size="" alt="" title=""
+       tabindex="" disabled="" readonly="" accesskey="" maxlength=""
+       tal:attributes="id view/id;
+                       name view/name;
+					   class view/css;
+                       title view/title;
+                       alt view/alt;
+                       tabindex view/tabindex;
+                       disabled view/disabled;
+                       readonly view/readonly;
+                       accesskey view/accesskey;
+                       maxlength view/maxlength;
+                       size view/size" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/file_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/orderedselect.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/orderedselect.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/orderedselect.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Ordered-Selection Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema
+import zope.schema.interfaces
+from zope.i18n import translate
+
+from z3c.form import interfaces, widget
+
+
+class OrderedSelectWidget(widget.SequenceWidget):
+    """Ordered-Select widget implementation."""
+    zope.interface.implementsOnly(interfaces.IOrderedSelectWidget)
+
+    size = 5
+    multiple = True
+    items = ()
+    selectedItems = ()
+
+    def getItem(self, term, count=0):
+        id = '%s.%i' % (self.id, count)
+        content = term.token
+        if zope.schema.interfaces.ITitledTokenizedTerm.providedBy(term):
+            content = translate(
+                term.title, context=self.request, default=term.title)
+        return {'id':id, 'value':term.token, 'content':content}
+
+    def update(self):
+        """See z3c.form.interfaces.IWidget."""
+        super(OrderedSelectWidget, self).update()
+        self.items = [
+            self.getItem(term, count)
+            for count, term in enumerate(self.terms)]
+        self.selectedItems = [
+            self.getItem(self.terms.getTermByToken(token), count)
+            for count, token in enumerate(self.value)]
+
+
+ at zope.component.adapter(zope.schema.interfaces.ISequence, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def OrderedSelectFieldWidget(field, request):
+    """IFieldWidget factory for SelectWidget."""
+    return widget.FieldWidget(field, OrderedSelectWidget(request))
+
+ at zope.component.adapter(
+    zope.schema.interfaces.ISequence, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def SequenceSelectFieldWidget(field, request):
+    """IFieldWidget factory for SelectWidget."""
+    return zope.component.getMultiAdapter(
+        (field, field.value_type, request), interfaces.IFieldWidget)
+
+
+ at zope.component.adapter(zope.schema.interfaces.ISequence,
+    zope.schema.interfaces.IChoice, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def SequenceChoiceSelectFieldWidget(field, value_type, request):
+    """IFieldWidget factory for SelectWidget."""
+    return OrderedSelectFieldWidget(field, request)


Property changes on: z3c.form/trunk/src/z3c/form/browser/orderedselect.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/orderedselect.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/orderedselect.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/orderedselect.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,189 @@
+=====================
+Ordered-Select Widget
+=====================
+
+The ordered select widget allows you to select one or more values from a set
+of given options and sort those options. Unfortunately, HTML does not provide
+such a widget as part of its specification, so that the system has to use a
+combnation of "option" elements, buttons and Javascript.
+
+As for all widgets, the select widget must provide the new ``IWidget``
+interface:
+
+  >>> from zope.interface import verify
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import orderedselect
+
+  >>> verify.verifyClass(interfaces.IWidget, orderedselect.OrderedSelectWidget)
+  True
+
+The widget can be instantiated only using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+  >>> widget = orderedselect.OrderedSelectWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+  >>> widget.id = u'widget.id'
+  >>> widget.name = u'widget.name'
+
+We also need to register the template for at least the widget and request:
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form.testing import getPath
+  >>> from z3c.form.widget import WidgetTemplateFactory
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('orderedselect_input.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.IOrderedSelectWidget),
+  ...     IPageTemplate, name=interfaces.INPUT_MODE)
+
+If we render the widget we get an emtpy widget:
+
+  >>> print widget.render()
+  <script type="text/javascript">
+  ...
+  </script>
+  <table border="0" class="ordered-selection-field">
+    <tr>
+      <td>
+        <select id="widget.name.from" name="widget.name.from"
+                size="5" multiple="">
+        </select>
+      </td>
+      <td>
+        <button name="from2toButton" type="button"
+                value=" -&gt;"
+                onclick="javascript:from2to('widget.name')">&nbsp;-&gt;</button>
+        <br />
+        <button name="to2fromButton" type="button"
+                value="&lt;- "
+                onclick="javascript:to2from('widget.name')">&lt;-&nbsp;</button>
+      </td>
+      <td>
+        <select id="widget.name.to" name="widget.name.to"
+                size="5" multiple="">
+        </select>
+        <input name="widget.name-empty-marker" type="hidden" />
+        <span id="widget.name.toDataContainer">
+          <script type="text/javascript">
+            copyDataForSubmit('widget.name');</script>
+        </span>
+      </td>
+      <td>
+        <button name="upButton" type="button" value="^"
+                onclick="javascript:moveUp('widget.name')">^</button>
+        <br />
+        <button name="downButton" type="button" value="v"
+                onclick="javascript:moveDown('widget.name')">v</button>
+      </td>
+    </tr>
+  </table>
+
+Let's provide some values for this widget. We can do this by defining a source
+providing ``ITerms``. This source uses descriminators wich will fit our setup.
+
+  >>> import zope.schema.interfaces
+  >>> from zope.schema.vocabulary import SimpleVocabulary
+  >>> import z3c.form.term
+
+  >>> class SelectionTerms(z3c.form.term.Terms):
+  ...     def __init__(self, context, request, form, field, widget):
+  ...         self.terms = SimpleVocabulary([
+  ...              SimpleVocabulary.createTerm(1, 'a', u'A'),
+  ...              SimpleVocabulary.createTerm(2, 'b', u'B'),
+  ...              SimpleVocabulary.createTerm(3, 'c', u'C')
+  ...              ])
+
+  >>> zope.component.provideAdapter(SelectionTerms,
+  ...     (None, interfaces.IFormLayer, None, None,
+  ...      interfaces.IOrderedSelectWidget) )
+
+Now let's try if we get widget values:
+
+  >>> widget.update()
+  >>> print widget.render()
+  <script type="text/javascript">
+  ...
+  </script>
+  <table border="0" class="ordered-selection-field">
+    <tr>
+      <td>
+        <select id="widget.name.from" name="widget.name.from"
+                size="5" multiple="">
+          <option value="a">A</option>
+          <option value="b">B</option>
+          <option value="c">C</option>
+        </select>
+      </td>
+      ...
+    </tr>
+  </table>
+
+If we select item "b", then it should be selected:
+
+  >>> widget.value = ['b']
+  >>> widget.update()
+  >>> print widget.render()
+  <script type="text/javascript">
+  ...
+  </script>
+  <table border="0" class="ordered-selection-field">
+    <tr>
+      <td>
+        <select id="widget.name.from" name="widget.name.from"
+                size="5" multiple="">
+          <option value="a">A</option>
+          <option value="b">B</option>
+          <option value="c">C</option>
+        </select>
+      </td>
+      ...
+      <td>
+        <select id="widget.name.to" name="widget.name.to"
+                size="5" multiple="">
+          <option value="b">B</option>
+        </select>
+        <input name="widget.name-empty-marker" type="hidden" />
+        <span id="widget.name.toDataContainer">
+          <script type="text/javascript">
+            copyDataForSubmit('widget.name');</script>
+        </span>
+      </td>
+      ...
+    </tr>
+  </table>
+
+Let's now make sure that we can extract user entered data from a widget:
+
+  >>> widget.request = TestRequest(form={'widget.name': ['c']})
+  >>> widget.update()
+  >>> widget.extract()
+  ['c']
+
+Unfortunately, when nothing is selected, we do not get an empty list sent into
+the request, but simply no entry at all. For this we have the empty marker, so
+that:
+
+  >>> widget.request = TestRequest(form={'widget.name-empty-marker': '1'})
+  >>> widget.update()
+  >>> widget.extract()
+  []
+
+If nothing is found in the request, the default is returned:
+
+  >>> widget.request = TestRequest()
+  >>> widget.update()
+  >>> widget.extract()
+  <NOVALUE>
+
+Let's now make sure that a bogus value causes extract to return the default as
+described by the interface:
+
+  >>> widget.request = TestRequest(form={'widget.name': ['x']})
+  >>> widget.update()
+  >>> widget.extract()
+  <NOVALUE>


Property changes on: z3c.form/trunk/src/z3c/form/browser/orderedselect.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/orderedselect.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/orderedselect.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/orderedselect.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,28 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".orderedselect.SequenceSelectFieldWidget"
+      />
+
+  <adapter
+      factory=".orderedselect.SequenceChoiceSelectFieldWidget"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.IOrderedSelectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="orderedselect_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.IOrderedSelectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="orderedselect_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/orderedselect.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/orderedselect_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/orderedselect_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/orderedselect_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,4 @@
+<tal:block tal:repeat="value view/displayValue"
+  ><tal:block replace="value"
+  /><tal:block condition="not:repeat/value/end">, </tal:block
+></tal:block>


Property changes on: z3c.form/trunk/src/z3c/form/browser/orderedselect_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/orderedselect_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/orderedselect_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/orderedselect_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,195 @@
+<script type="text/javascript">
+
+function moveItems(from, to)
+  {
+  // shortcuts for selection fields
+  var src = document.getElementById(from);
+  var tgt = document.getElementById(to);
+
+  if (src.selectedIndex == -1) selectionError();
+  else
+    {
+    // iterate over all selected items
+    // --> attribute "selectedIndex" doesn't support multiple selection.
+    // Anyway, it works here, as a moved item isn't selected anymore,
+    // thus "selectedIndex" indicating the "next" selected item :)
+    while (src.selectedIndex > -1)
+      if (src.options[src.selectedIndex].selected)
+        {
+        // create a new virtal object with values of item to copy
+        temp = new Option(src.options[src.selectedIndex].text,
+                      src.options[src.selectedIndex].value);
+        // append virtual object to targe
+        tgt.options[tgt.length] = temp;
+        // want to select newly created item
+        temp.selected = true;
+        // delete moved item in source
+        src.options[src.selectedIndex] = null;
+      }
+    }
+  }
+
+// move item from "from" selection to "to" selection
+function from2to(name)
+  {
+  moveItems(name+".from", name+".to");
+  copyDataForSubmit(name);
+  }
+
+// move item from "to" selection back to "from" selection
+function to2from(name)
+  {
+  moveItems(name+".to", name+".from");
+  copyDataForSubmit(name);
+  }
+
+function swapFields(a, b)
+  {
+  // swap text
+  var temp = a.text;
+  a.text = b.text;
+  b.text = temp;
+  // swap value
+  temp = a.value;
+  a.value = b.value;
+  b.value = temp;
+  // swap selection
+  temp = a.selected;
+  a.selected = b.selected;
+  b.selected = temp;
+  }
+
+// move selected item in "to" selection one up
+function moveUp(name)
+  {
+  // shortcuts for selection field
+  var toSel = document.getElementById(name+".to");
+
+  if (toSel.selectedIndex == -1)
+      selectionError();
+  else if (toSel.options[0].selected)
+      alert("Cannot move further up!");
+  else for (var i = 0; i < toSel.length; i++)
+    if (toSel.options[i].selected)
+      {
+      swapFields(toSel.options[i-1], toSel.options[i]);
+      copyDataForSubmit(name);
+      }
+  }
+
+// move selected item in "to" selection one down
+function moveDown(name)
+  {
+    // shortcuts for selection field
+    var toSel = document.getElementById(name+".to");
+
+    if (toSel.selectedIndex == -1) {
+        selectionError();
+    } else if (toSel.options[toSel.length-1].selected) {
+        alert("Cannot move further down!");
+    } else {
+      for (var i = toSel.length-1; i >= 0; i--) {
+        if (toSel.options[i].selected) {
+          swapFields(toSel.options[i+1], toSel.options[i]);
+        }
+      }
+      copyDataForSubmit(name);
+    }
+  }
+
+// copy each item of "toSel" into one hidden input field
+function copyDataForSubmit(name)
+  {
+  // shortcuts for selection field and hidden data field
+  var toSel = document.getElementById(name+".to");
+  var toDataContainer = document.getElementById(name+".toDataContainer");
+
+  // delete all child nodes (--> complete content) of "toDataContainer" span
+  while (toDataContainer.hasChildNodes())
+      toDataContainer.removeChild(toDataContainer.firstChild);
+
+  // create new hidden input fields - one for each selection item of
+  // "to" selection
+  for (var i = 0; i < toSel.options.length; i++)
+    {
+    // create virtual node with suitable attributes
+    var newNode = document.createElement("input");
+    var newAttr = document.createAttribute("name");
+    newAttr.nodeValue = name;
+    newNode.setAttributeNode(newAttr);
+
+    newAttr = document.createAttribute("type");
+    newAttr.nodeValue = "hidden";
+    newNode.setAttributeNode(newAttr);
+
+    newAttr = document.createAttribute("value");
+    newAttr.nodeValue = toSel.options[i].value;
+    newNode.setAttributeNode(newAttr);
+
+    // actually append virtual node to DOM tree
+    toDataContainer.appendChild(newNode);
+    }
+  }
+
+// error message for missing selection
+function selectionError()
+  {alert("Must select something!")}
+
+</script>
+
+<table border="0" class="ordered-selection-field">
+  <tr>
+    <td>
+      <select id="from" name="from" size="5" multiple=""
+          tal:attributes="name string:${view/name}.from;
+                          id string:${view/name}.from">
+        <option tal:repeat="entry view/items"
+                tal:attributes="value entry/value"
+                tal:content="entry/content" i18n:translate=""/>
+      </select>
+    </td>
+    <td>
+      <button name="from2toButton" type="button" value=" -&gt;"
+          onclick="javascript:from2to()"
+          tal:attributes="onClick string:javascript:from2to('${view/name}')"
+          >&nbsp;-&gt;</button>
+      <br />
+      <button name="to2fromButton" type="button" value="&lt;- "
+          onclick="javascript:to2from()"
+          tal:attributes="onClick string:javascript:to2from('${view/name}')"
+          >&lt;-&nbsp;</button>
+    </td>
+    <td>
+      <select id="to" name="to" size="5" multiple=""
+          tal:attributes="name string:${view/name}.to;
+                          id string:${view/name}.to">
+        <option tal:repeat="entry view/selectedItems"
+                tal:attributes="value entry/value"
+                tal:content="entry/content" i18n:translate=""/>
+      </select>
+      <input name="foo-empty-marker" type="hidden"
+        tal:attributes="name string:${view/name}-empty-marker"/>
+      <span id="toDataContainer"
+            tal:attributes="id string:${view/name}.toDataContainer">
+        <script type="text/javascript" tal:content="string:
+          copyDataForSubmit('${view/name}');">
+          // initial copying of field "field.to" --> "field"
+          copyDataForSubmit("<i tal:replace="${view/name}"/>");
+        </script>
+      </span>
+    </td>
+    <td>
+      <button
+          name="upButton" type="button" value="^"
+          onclick="javascript:moveUp()"
+          tal:attributes="onClick string:javascript:moveUp('${view/name}')"
+          >^</button>
+      <br />
+      <button
+          name="downButton" type="button" value="v"
+          onclick="javascript:moveDown()"
+          tal:attributes="onClick string:javascript:moveDown('${view/name}')"
+          >v</button>
+    </td>
+  </tr>
+</table>


Property changes on: z3c.form/trunk/src/z3c/form/browser/orderedselect_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/password.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/password.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/password.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Password Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema.interfaces
+
+from z3c.form import interfaces, widget
+from z3c.form.browser import text
+
+class PasswordWidget(text.TextWidget):
+    """Input type password widget implementation."""
+    zope.interface.implementsOnly(interfaces.IPasswordWidget)
+
+    css = u'passwordWidget'
+
+
+ at zope.component.adapter(zope.schema.interfaces.IPassword, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def PasswordFieldWidget(field, request):
+    """IFieldWidget factory for TextWidget."""
+    return widget.FieldWidget(field, PasswordWidget(request))


Property changes on: z3c.form/trunk/src/z3c/form/browser/password.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/password.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/password.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/password.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,70 @@
+===============
+Password Widget
+===============
+
+The password widget allows you to upload a new password to the server. The
+"password" type of the "INPUT" element is described here:
+
+http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#edef-INPUT
+
+As for all widgets, the password widget must provide the new ``IWidget``
+interface:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import password
+
+  >>> verifyClass(interfaces.IWidget, password.PasswordWidget)
+  True
+
+The widget can be instantiated only using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+  >>> widget = password.PasswordWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+  >>> widget.id = u'widget.id'
+  >>> widget.name = u'widget.name'
+
+We also need to register the template for the widget:
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form.testing import getPath
+  >>> from z3c.form.widget import WidgetTemplateFactory
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('password_input.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.IPasswordWidget),
+  ...     IPageTemplate, name=interfaces.INPUT_MODE)
+
+If we render the widget we get a simple input element:
+
+  >>> print widget.render()
+  <input type="password" id="widget.id" name="widget.name"
+         class="passwordWidget" />
+
+Even when we set a value on the widget, it is not displayed for security
+reasons:
+
+  >>> widget.value = 'password'
+  >>> print widget.render()
+  <input type="password" id="widget.id" name="widget.name"
+         class="passwordWidget" />
+
+Let's now make sure that we can extract user entered data from a widget:
+
+  >>> widget.request = TestRequest(form={'widget.name': 'password'})
+  >>> widget.update()
+  >>> widget.extract()
+  'password'
+
+If nothing is found in the request, the default is returned:
+
+  >>> widget.request = TestRequest()
+  >>> widget.update()
+  >>> widget.extract()
+  <NOVALUE>


Property changes on: z3c.form/trunk/src/z3c/form/browser/password.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/password.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/password.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/password.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,26 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".password.PasswordFieldWidget"
+      for="zope.schema.interfaces.IPassword
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.IPasswordWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="password_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.IPasswordWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="password_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/password.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/password_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/password_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/password_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+<tal:block condition="view/value" content="view/value" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/password_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/password_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/password_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/password_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,13 @@
+<input type="password" id="" name="" class="" size="" alt="" title=""
+       tabindex="" disabled="" readonly="" accesskey="" maxlength=""
+       tal:attributes="id view/id;
+                       name view/name;
+					   class view/css;
+                       title view/title;
+                       alt view/alt;
+                       tabindex view/tabindex;
+                       disabled view/disabled;
+                       readonly view/readonly;
+                       accesskey view/accesskey;
+                       maxlength view/maxlength;
+                       size view/size" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/password_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/radio.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/radio.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/radio.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Text Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.component
+import zope.interface
+import zope.schema
+import zope.schema.interfaces
+from zope.i18n import translate
+
+from z3c.form import interfaces
+from z3c.form import widget
+
+
+class RadioWidget(widget.SequenceWidget):
+    """Input type radio widget implementation."""
+
+    zope.interface.implementsOnly(interfaces.IRadioWidget)
+
+    css = u'radioWidget'
+    alt = None
+    readonly = None
+    accesskey = None
+    items = ()
+
+    def isChecked(self, term):
+        return term.token in self.value
+
+    def update(self):
+        """See z3c.form.interfaces.IWidget."""
+        super(RadioWidget, self).update()
+        self.items = []
+        for count, term in enumerate(self.terms):
+            checked = self.isChecked(term)
+            id = '%s.%i' % (self.name, count)
+            label = term.token
+            if zope.schema.interfaces.ITitledTokenizedTerm.providedBy(term):
+                label = translate(term.title, context=self.request,
+                                  default=term.title)
+            self.items.append(
+                {'id':id, 'name':self.name + ':list', 'value':term.token,
+                 'label':label, 'checked':checked})
+
+
+ at zope.component.adapter(zope.schema.interfaces.IField, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def RadioFieldWidget(field, request):
+    """IFieldWidget factory for RadioWidget."""
+    return widget.FieldWidget(field, RadioWidget(request))


Property changes on: z3c.form/trunk/src/z3c/form/browser/radio.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/radio.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/radio.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/radio.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,93 @@
+===========
+RadioWidget
+===========
+
+The RadioWidget renders a radio input type field e.g. <input type="radio" />
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from zope.app.form.interfaces import IInputWidget
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import radio
+
+The TextWidget is a widget:
+
+ >>> verifyClass(interfaces.IWidget, radio.RadioWidget)
+  True
+
+The widget can render a input field only by adapting a request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> widget = radio.RadioWidget(request)
+
+Set a name for the widget:
+
+  >>> widget.name = u'foo'
+
+Such a field provides IWidget:
+
+ >>> interfaces.IWidget.providedBy(widget)
+  True
+
+We also need to register the template for at least the widget and request:
+
+  >>> import os.path
+  >>> import zope.interface
+  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> import z3c.form.browser
+  >>> import z3c.form.widget
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'radio_input.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='input')
+
+If we render the widget we get an emtpy output:
+
+  >>> print widget.render()
+
+Let's provide some values for this widget. We can do this by defining a source
+providing ITerms. This source uses descriminators wich will fit for our setup.
+
+  >>> import zope.schema.interfaces
+  >>> from zope.schema.vocabulary import SimpleVocabulary
+  >>> import z3c.form.term
+  >>> class MyTerms(z3c.form.term.ChoiceTerms):
+  ...     def __init__(self, context, request, form, field, widget):
+  ...         self.terms = SimpleVocabulary.fromValues(['yes', 'no'])
+  >>> zope.component.provideAdapter(z3c.form.term.BoolTerms,
+  ...     adapts=(zope.interface.Interface,
+  ...             interfaces.IFormLayer, zope.interface.Interface,
+  ...             zope.interface.Interface, interfaces.IRadioWidget))
+
+Now let's try if we get widget values:
+
+  >>> widget.update()
+  >>> print widget.render()
+  <span class="option">
+    <input type="radio" id="foo.0" name="foo:list"
+           class="radioWidget" value="yes" />
+   <span class="label">yes</span>
+  </span><span class="option">
+    <input type="radio" id="foo.1" name="foo:list"
+           class="radioWidget" value="no" />
+   <span class="label">no</span>
+  </span>
+
+If we set the value for the widget to ``yes``, we can se that the radio field
+get rendered with a checked flag:
+
+  >>> widget.value = 'yes'
+  >>> widget.update()
+  >>> print widget.render()
+  <span class="option">
+    <input type="radio" id="foo.0" name="foo:list"
+           class="radioWidget" value="yes" checked="checked" />
+   <span class="label">yes</span>
+  </span><span class="option">
+    <input type="radio" id="foo.1" name="foo:list"
+           class="radioWidget" value="no" />
+   <span class="label">no</span>
+  </span>


Property changes on: z3c.form/trunk/src/z3c/form/browser/radio.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/radio.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/radio.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/radio.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,26 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".radio.RadioFieldWidget"
+      for="zope.schema.interfaces.IBool
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.IRadioWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="radio_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.IRadioWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="radio_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/radio.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/radio_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/radio_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/radio_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,4 @@
+<tal:block tal:repeat="value view/displayValue"
+  ><tal:block replace="value"
+  /><tal:block condition="not:repeat/value/end">, </tal:block
+></tal:block>


Property changes on: z3c.form/trunk/src/z3c/form/browser/radio_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/radio_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/radio_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/radio_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,30 @@
+<span class="option"
+      tal:repeat="item view/items">
+  <input type="radio" id="" name="" class="" 
+         alt="" title="" tabindex="" disabled="" readonly="" accesskey="" value="" checked="checked"
+         tal:condition="item/checked"
+         tal:attributes="id item/id;
+                         name item/name;
+                         class view/css;
+                         alt view/alt;
+                         title view/title;
+                         tabindex view/tabindex;
+                         disabled view/disabled;
+                         readonly view/readonly;
+                         accesskey view/accesskey;
+                         value item/value" 
+  /><input type="radio" id="" name="" class="" 
+         alt="" title="" tabindex="" disabled="" readonly="" accesskey="" value=""
+         tal:condition="not:item/checked"
+         tal:attributes="id item/id;
+                         name item/name;
+                         class view/css;
+                         alt view/alt;
+                         title view/title;
+                         tabindex view/tabindex;
+                         disabled view/disabled;
+                         readonly view/readonly;
+                         accesskey view/accesskey;
+                         value item/value" />
+ <span class="label" tal:content="item/label">Label</span>
+</span>


Property changes on: z3c.form/trunk/src/z3c/form/browser/radio_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/select.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/select.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/select.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,92 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Text Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema
+import zope.schema.interfaces
+from zope.i18n import translate
+
+from z3c.form import interfaces, widget
+from z3c.form.i18n import MessageFactory as _
+
+
+class SelectWidget(widget.SequenceWidget):
+    """Select widget implementation."""
+
+    zope.interface.implementsOnly(interfaces.ISelectWidget)
+
+    css = u'selectWidget'
+    size = 1
+    multiple = None
+    items = ()
+
+    noValueMessage = _('no value')
+
+    def isSelected(self, term):
+        return term.token in self.value
+
+    def update(self):
+        """See z3c.form.interfaces.IWidget."""
+        super(SelectWidget, self).update()
+        self.items = []
+        if not self.required and self.multiple is None:
+            self.items.append({
+                'id': self.id + '.novalue',
+                'value': self.noValueToken,
+                'content': self.noValueMessage,
+                'selected': self.noValueToken in self.value
+                })
+        for count, term in enumerate(self.terms):
+            selected = self.isSelected(term)
+            id = '%s.%i' % (self.id, count)
+            content = term.token
+            if zope.schema.interfaces.ITitledTokenizedTerm.providedBy(term):
+                content = translate(
+                    term.title, context=self.request, default=term.title)
+            self.items.append(
+                {'id':id, 'value':term.token, 'content':content,
+                 'selected':selected})
+
+
+ at zope.component.adapter(zope.schema.interfaces.IChoice, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def SelectFieldWidget(field, request):
+    """IFieldWidget factory for SelectWidget."""
+    return widget.FieldWidget(field, SelectWidget(request))
+
+
+ at zope.component.adapter(
+    zope.schema.interfaces.IUnorderedCollection, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def CollectionSelectFieldWidget(field, request):
+    """IFieldWidget factory for SelectWidget."""
+    widget = zope.component.getMultiAdapter((field, field.value_type, request),
+        interfaces.IFieldWidget)
+    widget.size = 5
+    widget.multiple = 'multiple'
+    return widget
+
+
+ at zope.component.adapter(
+    zope.schema.interfaces.IUnorderedCollection,
+    zope.schema.interfaces.IChoice, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def CollectionChoiceSelectFieldWidget(field, value_type, request):
+    """IFieldWidget factory for SelectWidget."""
+    return SelectFieldWidget(field, request)


Property changes on: z3c.form/trunk/src/z3c/form/browser/select.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/select.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/select.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/select.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,130 @@
+=============
+Select Widget
+=============
+
+The select widget allows you to select one or more values from a set of given
+options. The "SELECT" and "OPTION" elements are described here:
+
+http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#edef-SELECT
+
+As for all widgets, the select widget must provide the new ``IWidget``
+interface:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import select
+
+  >>> verifyClass(interfaces.IWidget, select.SelectWidget)
+  True
+
+The widget can be instantiated only using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+  >>> widget = select.SelectWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+  >>> widget.id = u'widget.id'
+  >>> widget.name = u'widget.name'
+
+We also need to register the template for at least the widget and request:
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form.testing import getPath
+  >>> from z3c.form.widget import WidgetTemplateFactory
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('select_input.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.ISelectWidget),
+  ...     IPageTemplate, name=interfaces.INPUT_MODE)
+
+If we render the widget we get an emtpy widget:
+
+  >>> print widget.render()
+  <select id="widget.id" name="widget.name:list"
+          class="selectWidget" size="1">
+  </select>
+  <input name="widget.name-empty-marker" type="hidden"
+         value="1" />
+
+Let's provide some values for this widget. We can do this by defining a source
+providing ``ITerms``. This source uses descriminators wich will fit our setup.
+
+  >>> import zope.schema.interfaces
+  >>> from zope.schema.vocabulary import SimpleVocabulary
+  >>> import z3c.form.term
+
+  >>> class SelectionTerms(z3c.form.term.Terms):
+  ...     def __init__(self, context, request, form, field, widget):
+  ...         self.terms = SimpleVocabulary.fromValues(['a', 'b', 'c'])
+
+  >>> zope.component.provideAdapter(SelectionTerms,
+  ...     (None, interfaces.IFormLayer, None, None, interfaces.ISelectWidget) )
+
+Now let's try if we get widget values:
+
+  >>> widget.update()
+  >>> print widget.render()
+  <select id="widget.id" name="widget.name:list" 
+          class="selectWidget" size="1">
+  <option id="widget.id.novalue" value="--NOVALUE--">no value</option>
+  <option id="widget.id.0" value="a">a</option>
+  <option id="widget.id.1" value="b">b</option>
+  <option id="widget.id.2" value="c">c</option>
+  </select>
+  <input name="widget.name-empty-marker" type="hidden" value="1" />
+
+If we select item "b", then it should be selected:
+
+  >>> widget.value = ['b']
+  >>> widget.update()
+  >>> print widget.render()
+  <select id="widget.id" name="widget.name:list" 
+          class="selectWidget" size="1">
+  <option id="widget.id.novalue" value="--NOVALUE--">no value</option>
+  <option id="widget.id.0" value="a">a</option>
+  <option id="widget.id.1" value="b" selected="selected">b</option>
+  <option id="widget.id.2" value="c">c</option>
+  </select>
+  <input name="widget.name-empty-marker" type="hidden" value="1" />
+
+Let's now make sure that we can extract user entered data from a widget:
+
+  >>> widget.request = TestRequest(form={'widget.name': ['c']})
+  >>> widget.update()
+  >>> widget.extract()
+  ['c']
+
+When "no value" is selected, then no verification against the terms is done:
+
+  >>> widget.request = TestRequest(form={'widget.name': ['--NOVALUE--']})
+  >>> widget.update()
+  >>> widget.extract(default=1)
+  ['--NOVALUE--']
+
+Unfortunately, when nothing is selected, we do not get an empty list sent into
+the request, but simply no entry at all. For this we have the empty marker, so
+that:
+
+  >>> widget.request = TestRequest(form={'widget.name-empty-marker': '1'})
+  >>> widget.update()
+  >>> widget.extract()
+  []
+
+If nothing is found in the request, the default is returned:
+
+  >>> widget.request = TestRequest()
+  >>> widget.update()
+  >>> widget.extract(default=1)
+  1
+
+Let's now make sure that a bogus value causes extract to return the default as
+described by the interface:
+
+  >>> widget.request = TestRequest(form={'widget.name': ['x']})
+  >>> widget.update()
+  >>> widget.extract(default=1)
+  1


Property changes on: z3c.form/trunk/src/z3c/form/browser/select.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/select.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/select.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/select.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,32 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".select.SelectFieldWidget"
+      />
+
+  <adapter
+      factory=".select.CollectionSelectFieldWidget"
+      />
+
+  <adapter
+      factory=".select.CollectionChoiceSelectFieldWidget"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.ISelectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="select_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.ISelectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="select_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/select.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/select_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/select_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/select_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,4 @@
+<tal:block tal:repeat="value view/displayValue"
+  ><tal:block replace="value"
+  /><tal:block condition="not:repeat/value/end">, </tal:block
+></tal:block>


Property changes on: z3c.form/trunk/src/z3c/form/browser/select_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/select_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/select_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/select_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,23 @@
+<select id="" name="" class="" tabindex="" disabled="" multiple="" size=""
+        tal:attributes="id view/id;
+                        name string:${view/name}:list;
+                        class view/css;
+                        tabindex view/tabindex;
+                        disabled view/disabled;
+                        multiple view/multiple;
+                        size view/size">
+<tal:block repeat="item view/items"
+  ><option id="" value="" selected="selected"
+         tal:condition="item/selected"
+         tal:attributes="id item/id;
+                         value item/value"
+         tal:content="item/content">label</option
+  ><option id="" value=""
+         tal:condition="not:item/selected"
+         tal:attributes="id item/id;
+                         value item/value"
+         tal:content="item/content">label</option
+></tal:block>
+</select>
+<input name="field-empty-marker" type="hidden" value="1"
+       tal:attributes="name string:${view/name}-empty-marker" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/select_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/submit.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/submit.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/submit.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Submit Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+from z3c.form import interfaces, widget
+
+
+class SubmitWidget(widget.Widget):
+    """A submit button of a form."""
+    zope.interface.implementsOnly(interfaces.ISubmitWidget)
+
+    css = u'submitWidget'
+    accesskey = None
+
+
+ at zope.component.adapter(interfaces.IButton, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def SubmitFieldWidget(field, request):
+    submit = widget.FieldWidget(field, SubmitWidget(request))
+    submit.value = field.title
+    return submit


Property changes on: z3c.form/trunk/src/z3c/form/browser/submit.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/submit.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/submit.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/submit.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,70 @@
+=============
+Submit Widget
+=============
+
+The submit widget allows you to upload a new submit to the server. The
+"submit" type of the "INPUT" element is described here:
+
+http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#edef-INPUT
+
+As for all widgets, the submit widget must provide the new ``IWidget``
+interface:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import submit
+
+  >>> verifyClass(interfaces.IWidget, submit.SubmitWidget)
+  True
+
+The widget can be instantiated only using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+  >>> widget = submit.SubmitWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+  >>> widget.id = u'widget.id'
+  >>> widget.name = u'widget.name'
+
+We also need to register the template for the widget:
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form.testing import getPath
+  >>> from z3c.form.widget import WidgetTemplateFactory
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('submit_input.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.ISubmitWidget),
+  ...     IPageTemplate, name=interfaces.INPUT_MODE)
+
+If we render the widget we get a simple input element:
+
+  >>> print widget.render()
+  <input type="submit" id="widget.id" name="widget.name"
+         class="submitWidget" />
+
+Setting a value for the widget effectively changes the button label:
+
+  >>> widget.value = 'Submit'
+  >>> print widget.render()
+  <input type="submit" id="widget.id" name="widget.name"
+         class="submitWidget" value="Submit" />
+
+
+Let's now make sure that we can extract user entered data from a widget:
+
+  >>> widget.request = TestRequest(form={'widget.name': 'submit'})
+  >>> widget.update()
+  >>> widget.extract()
+  'submit'
+
+If nothing is found in the request, the default is returned:
+
+  >>> widget.request = TestRequest()
+  >>> widget.update()
+  >>> widget.extract()
+  <NOVALUE>


Property changes on: z3c.form/trunk/src/z3c/form/browser/submit.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/submit.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/submit.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/submit.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,24 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".submit.SubmitFieldWidget"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.ISubmitWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="submit_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.ISubmitWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="submit_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/submit.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/submit_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/submit_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/submit_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,5 @@
+<input type="submit" id="" name="" class="" value="" disabled=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       class view/css;
+                       value view/value" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/submit_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/submit_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/submit_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/submit_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,8 @@
+<input type="submit" id="" name="" class="" value=""
+       accesskey=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       class view/css;
+                       value view/value;
+                       accesskey view/accesskey;
+                       " />


Property changes on: z3c.form/trunk/src/z3c/form/browser/submit_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/tests.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/tests.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/tests.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,56 @@
+###############################################################################
+#
+# Copyright 2006 by refline (Schweiz) AG, CH-5630 Muri
+#
+###############################################################################
+
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import doctest
+import unittest
+from zope.testing.doctestunit import DocFileSuite
+
+from z3c.form import testing
+
+def test_suite():
+    return unittest.TestSuite((
+        DocFileSuite('README.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('checkbox.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('file.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('orderedselect.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('password.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('radio.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('select.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('submit.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('text.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        ))


Property changes on: z3c.form/trunk/src/z3c/form/browser/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/text.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/text.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/text.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,46 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Text Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema.interfaces
+
+from z3c.form import interfaces, widget
+
+
+class TextWidget(widget.Widget):
+    """Input type text widget implementation."""
+
+    zope.interface.implementsOnly(interfaces.ITextWidget)
+
+    css = u'textWidget'
+    size = None
+    value = u''
+
+    # optional html attributes
+    alt = None
+    readonly = None
+    maxlength = None
+    accesskey = None
+
+
+ at zope.component.adapter(zope.schema.interfaces.IField, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def TextFieldWidget(field, request):
+    """IFieldWidget factory for TextWidget."""
+    return widget.FieldWidget(field, TextWidget(request))


Property changes on: z3c.form/trunk/src/z3c/form/browser/text.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/text.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/text.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/text.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,46 @@
+==========
+TextWidget
+==========
+
+The widget can render a input field for a text line:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from zope.app.form.interfaces import IInputWidget
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import text
+
+The TextWidget is a widget:
+
+ >>> verifyClass(interfaces.IWidget, text.TextWidget)
+  True
+
+The widget can render a input field only by adapting a request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> widget = text.TextWidget(request)
+
+Such a field provides IWidget:
+
+ >>> interfaces.IWidget.providedBy(widget)
+  True
+
+We also need to register the template for at least the widget and request:
+
+  >>> import os.path
+  >>> import zope.interface
+  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> import z3c.form.browser
+  >>> import z3c.form.widget
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'text_input.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='input')
+
+If we render the widget we get the HTML:
+
+  >>> widget.render()
+  u'<input type="text" id="" name="" class="textWidget" value="" />\n'


Property changes on: z3c.form/trunk/src/z3c/form/browser/text.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/text.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/text.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/text.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,92 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IBytesLine
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IASCIILine
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.ITextLine
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IId
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IInt
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IFloat
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IDecimal
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IDate
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IDatetime
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.ITime
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.ITimedelta
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".text.TextFieldWidget"
+      for="zope.schema.interfaces.IURI
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.ITextWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="text_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.ITextWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="text_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/text.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/text_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/text_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/text_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+<tal:block condition="view/value" content="view/value" />
\ No newline at end of file


Property changes on: z3c.form/trunk/src/z3c/form/browser/text_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/text_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/text_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/text_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,14 @@
+<input type="text" id="" name="" class="" size="" alt="" title=""
+       tabindex="" disabled="" readonly="" accesskey="" maxlength="" value=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       class view/css;
+                       title view/title;
+                       alt view/alt;
+                       tabindex view/tabindex;
+                       disabled view/disabled;
+                       readonly view/readonly;
+                       accesskey view/accesskey;
+                       maxlength view/maxlength;
+                       value view/value;
+                       size view/size" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/text_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/textarea.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/textarea.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/textarea.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Text Area Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema.interfaces
+
+from z3c.form import interfaces, widget
+
+
+class TextAreaWidget(widget.Widget):
+    """Textarea widget implementation."""
+
+    zope.interface.implementsOnly(interfaces.ITextAreaWidget)
+
+    css = u'textAreaWidget'
+    cols = None
+    rows = None
+    value = u''
+
+    # optional html attributes
+    readonly = None
+    accesskey = None
+
+
+ at zope.component.adapter(zope.schema.interfaces.IField, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def TextAreaFieldWidget(field, request):
+    """IFieldWidget factory for TextWidget."""
+    return widget.FieldWidget(field, TextAreaWidget(request))


Property changes on: z3c.form/trunk/src/z3c/form/browser/textarea.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/browser/textarea.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/textarea.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/textarea.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,32 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <adapter
+      factory=".textarea.TextAreaFieldWidget"
+      for="zope.schema.interfaces.IASCII
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <adapter
+      factory=".textarea.TextAreaFieldWidget"
+      for="zope.schema.interfaces.IText
+           z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.ITextAreaWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="textarea_display.pt"
+      />
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.ITextAreaWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="textarea_input.pt"
+      />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/browser/textarea.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/textarea_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/textarea_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/textarea_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+<tal:block condition="view/value" content="view/value" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/textarea_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/browser/textarea_input.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/textarea_input.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/textarea_input.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,13 @@
+<textarea
+    id="" name="" class="" cols="" rows=""
+    tabindex="" disabled="" readonly="" accesskey=""
+    tal:attributes="id view/id;
+                    name view/name;
+                    class view/css;
+                    cols view/cols;
+                    rows view/rows;
+                    tabindex view/tabindex;
+                    disabled view/disabled;
+                    readonly view/readonly;
+                    accesskey view/accesskey;"
+    tal:content="view/value" />


Property changes on: z3c.form/trunk/src/z3c/form/browser/textarea_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/button.py
===================================================================
--- z3c.form/trunk/src/z3c/form/button.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/button.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,234 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Button and Button Manager implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import sys
+import zope.interface
+import zope.location
+import zope.schema
+
+from zope.interface import adapter
+from zope.schema.fieldproperty import FieldProperty
+from z3c.form import action, interfaces, util
+from z3c.form.browser import submit
+
+class Button(zope.schema.Field):
+    """A simple button in a form."""
+    zope.interface.implements(interfaces.IButton)
+
+    accessKey = FieldProperty(interfaces.IButton['accessKey'])
+
+    def __init__(self, *args, **kwargs):
+        # Provide some shortcut ways to specify the name
+        if args:
+            kwargs['__name__'] = args[0]
+            args = args[1:]
+        if 'name' in kwargs:
+            kwargs['__name__'] = kwargs['name']
+            del kwargs['name']
+        # Extract button-specific arguments
+        self.accessKey = kwargs.pop('accessKey', None)
+        self.condition = kwargs.pop('condition', None)
+        # Initialize the button
+        super(Button, self).__init__(*args, **kwargs)
+        # Make sure the button has a name by the time it is initialized.
+        if self.__name__ is '':
+            self.__name__ = util.createId(self.title)
+
+    def __repr__(self):
+        return '<%s %r %r>' %(
+            self.__class__.__name__, self.__name__, self.title)
+
+class Buttons(util.SelectionManager):
+    """Button manager."""
+    zope.interface.implements(interfaces.IButtons)
+
+    managerInterface = interfaces.IButtons
+    prefix = 'buttons'
+
+    def __init__(self, *args):
+        buttons = []
+        for arg in args:
+            if zope.interface.interfaces.IInterface.providedBy(arg):
+                for name, button in zope.schema.getFieldsInOrder(arg):
+                    if interfaces.IButton.providedBy(button):
+                        buttons.append((name, button))
+            elif self.managerInterface.providedBy(arg):
+                buttons += arg.items()
+            elif interfaces.IButton.providedBy(arg):
+                buttons.append((arg.__name__, arg))
+            else:
+                raise TypeError("Unrecognized argument type", arg)
+        keys = []
+        seq = []
+        byname = {}
+        for name, button in buttons:
+            keys.append(name)
+            seq.append(button)
+            byname[name] = button
+
+        self._data_keys = keys
+        self._data_values = seq
+        self._data = byname
+
+
+class Handlers(object):
+    """Action Handlers for a Button-based form."""
+    zope.interface.implements(interfaces.IButtonHandlers)
+
+    def __init__(self):
+        self._registry = adapter.AdapterRegistry()
+        self._handlers = ()
+
+    def addHandler(self, button, handler):
+        """See interfaces.IButtonHandlers"""
+        # Create a specification for the button
+        buttonSpec = util.getSpecification(button)
+        if isinstance(buttonSpec, util.classTypes):
+            buttonSpec = zope.interface.implementedBy(buttonSpec)
+        # Register the handler
+        self._registry.register(
+            (buttonSpec,), interfaces.IButtonHandler, '', handler)
+        self._handlers += ((button, handler),)
+
+    def getHandler(self, button):
+        """See interfaces.IButtonHandlers"""
+        buttonProvided = zope.interface.providedBy(button)
+        #if button.__class__.__name__ == 'SpecialButton':
+        #    import pdb; pdb.set_trace()
+        return self._registry.lookup1(buttonProvided, interfaces.IButtonHandler)
+
+    def copy(self):
+        """See interfaces.IButtonHandlers"""
+        handlers = Handlers()
+        for button, handler in self._handlers:
+            handlers.addHandler(button, handler)
+        return handlers
+
+    def __add__(self, other):
+        """See interfaces.IButtonHandlers"""
+        if not isinstance(other, Handlers):
+            raise NotImplementedError
+        handlers = self.copy()
+        for button, handler in other._handlers:
+            handlers.addHandler(button, handler)
+        return handlers
+
+    def __repr__(self):
+        return '<Handlers %r>' %[handler for button, handler in self._handlers]
+
+
+class Handler(object):
+    zope.interface.implements(interfaces.IButtonHandler)
+
+    def __init__(self, button, func):
+        self.button = button
+        self.func = func
+
+    def __call__(self, form, action):
+        return self.func(form, action)
+
+    def __repr__(self):
+        return '<%s for %r>' %(self.__class__.__name__, self.button)
+
+
+def handler(button):
+    """A decorator for defining a success handler."""
+    def createHandler(func):
+        handler = Handler(button, func)
+        frame = sys._getframe(1)
+        f_locals = frame.f_locals
+        handlers = f_locals.setdefault('handlers', Handlers())
+        handlers.addHandler(button, handler)
+        return handler
+    return createHandler
+
+
+def buttonAndHandler(title, **kwargs):
+    # Add the title to button constructor keyword arguments
+    kwargs['title'] = title
+    # Extract directly provided interfaces:
+    provides = kwargs.pop('provides', ())
+    # Create button and add it to the button manager
+    button = Button(**kwargs)
+    zope.interface.alsoProvides(button, provides)
+    frame = sys._getframe(1)
+    f_locals = frame.f_locals
+    buttons = f_locals.setdefault('buttons', Buttons())
+    f_locals['buttons'] += Buttons(button)
+    # Return the handler decorator
+    return handler(button)
+
+
+class ButtonAction(action.Action, submit.SubmitWidget, zope.location.Location):
+    zope.interface.implements(interfaces.IFieldWidget)
+
+    def __init__(self, request, field, name):
+        action.Action.__init__(self, request, field.title, name)
+        submit.SubmitWidget.__init__(self, request)
+        self.field = field
+
+    @property
+    def accesskey(self):
+        return self.field.accessKey
+
+    @property
+    def value(self):
+        return self.title
+
+    @property
+    def id(self):
+        return self.name
+
+
+class ButtonActions(action.Actions):
+    zope.component.adapts(
+        interfaces.IButtonForm,
+        zope.interface.Interface,
+        zope.interface.Interface)
+
+    def update(self):
+        """See z3c.form.interfaces.IActions."""
+        # Create a unique prefix
+        prefix = util.expandPrefix(self.form.prefix)
+        prefix += util.expandPrefix(self.form.buttons.prefix)
+        for name, button in self.form.buttons.items():
+            # Only create an action for the button, if the condition is
+            # fulfilled
+            if button.condition is not None and not button.condition(self.form):
+                continue
+            fullName = prefix + name
+            buttonAction = ButtonAction(self.request, button, fullName)
+            self._data_keys.append(name)
+            self._data_values.append(buttonAction)
+            self._data[name] = buttonAction
+            zope.location.locate(buttonAction, self, name)
+
+
+class ButtonActionHandler(action.ActionHandlerBase):
+    zope.component.adapts(
+        interfaces.IHandlerForm,
+        zope.interface.Interface,
+        zope.interface.Interface,
+        ButtonAction)
+
+    def __call__(self):
+        handler = self.form.handlers.getHandler(self.action.field)
+        # If no handler is found, then that's okay too.
+        if handler is None:
+            return
+        return handler(self.form, self.action)


Property changes on: z3c.form/trunk/src/z3c/form/button.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/button.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/button.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/button.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,459 @@
+=======
+Buttons
+=======
+
+Buttons are a method to declare actions for a form. Like fields describe
+widgets within a form, buttons describe actions. The symmetry goes even
+further; like fields, buttons are schema fields within schema. When the form
+is instantiated and updated, the buttons are converted to actions.
+
+  >>> from z3c.form import button
+
+
+Schema Defined Buttons
+----------------------
+
+Let's now create a schema that describes the buttons of a form. Having button
+schemas allows one to more easily reuse button declarations and to group them
+logically. ``Button`` objects are just a simple extension to ``Field``
+objects, so they behave identical within a schema:
+
+  >>> import zope.interface
+  >>> class IButtons(zope.interface.Interface):
+  ...     apply = button.Button(title=u'Apply')
+  ...     cancel = button.Button(title=u'Cancel')
+
+In reality, only the title and name is relevant. Let's now create a form that
+provides those buttons.
+
+  >>> from z3c.form import interfaces
+  >>> class Form(object):
+  ...     zope.interface.implements(
+  ...         interfaces.IButtonForm, interfaces.IHandlerForm)
+  ...     buttons = button.Buttons(IButtons)
+  ...     prefix = 'form'
+  ...
+  ...     @button.handler(IButtons['apply'])
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+  ...
+  ...     @button.handler(IButtons['cancel'])
+  ...     def cancel(self, action):
+  ...         self.request.response.redirect('index.html')
+
+Let' now create an action manager for the button manager in the form. To do
+that we first need a request and a form instance:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> form = Form()
+
+Action managers are instantiated using the form, request, and
+context/content. A special button-action-manager implementation is avaialble
+in the ``button`` package:
+
+  >>> actions = button.ButtonActions(form, request, None)
+  >>> actions.update()
+
+Once the action manager is updated, the buttons should be available as
+actions:
+
+  >>> actions.keys()
+  ['apply', 'cancel']
+  >>> actions['apply']
+  <ButtonAction 'form.buttons.apply' u'Apply'>
+
+Button actions are locations:
+
+  >>> apply = actions['apply']
+  >>> apply.__name__
+  'apply'
+  >>> apply.__parent__
+  <ButtonActions None>
+
+A button action is also a submit widget. The attributes translate as follows:
+
+  >>> interfaces.ISubmitWidget.providedBy(apply)
+  True
+
+  >>> apply.value == apply.title
+  True
+  >>> apply.id == apply.name
+  True
+
+Next we want to display our button actions. To be able to do this, we have to
+register a template for the submit widget:
+
+  >>> from z3c.form import testing, widget
+  >>> templatePath = testing.getPath('submit_input.pt')
+  >>> factory = widget.WidgetTemplateFactory(templatePath, 'text/html')
+
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, TestRequest, None, None,
+  ...      interfaces.ISubmitWidget),
+  ...     IPageTemplate, name='input')
+
+A widget template has many discriminators: context, request, view, field, and
+widget. We can now render each action:
+
+  >>> print actions['apply'].render()
+  <input type="submit" id="form.buttons.apply"
+         name="form.buttons.apply" class="submitWidget"
+         value="Apply" />
+
+So displaying is nice, but how do button handlers get executed? The action
+manager provides attributes and method to check whether actions were
+executed. Initially there are no executed actions:
+
+  >>> list(actions.executedActions)
+  []
+
+So in this case executing the actions does not do anything:
+
+  >>> actions.execute()
+
+But if the request contains the information that the button was pressed, the
+execution works:
+
+  >>> request = TestRequest(form={'form.buttons.apply': 'Apply'})
+
+  >>> actions = button.ButtonActions(form, request, None)
+  >>> actions.update()
+  >>> actions.execute()
+
+Aehm, something should have happened. But in order for the system to look at
+the handlers declared in the form, a special action handler has to be
+registered with the system:
+
+  >>> zope.component.provideAdapter(button.ButtonActionHandler)
+
+And voila, the execution works:
+
+  >>> actions.execute()
+  successfully applied
+
+Finally, if there is no handler for a button, then the button click is
+silently ignored:
+
+  >>> form.handlers = button.Handlers()
+  >>> actions.execute()
+
+While this might seem awkward at first, this is an intended feature. Sometimes
+there are several sub-forms that listen to a particular button and one form or
+another might simply not care about the button at all and not provide a
+handler.
+
+
+In-Form Button Declarations
+---------------------------
+
+Some readers might find it cumbersome to declare a full schema just to create
+some buttons. A faster method is to write simple arguments to the button
+manager:
+
+  >>> class Form(object):
+  ...     zope.interface.implements(
+  ...         interfaces.IButtonForm, interfaces.IHandlerForm)
+  ...     buttons = button.Buttons(
+  ...         button.Button('apply', title=u'Apply'))
+  ...     prefix = 'form.'
+  ...
+  ...     @button.handler(buttons['apply'])
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+
+The first argument of the ``Button`` class constructor is the name of the
+button. Optionally, this can also be one of the follwoing keyword arguments:
+
+  >>> button.Button(name='apply').__name__
+  'apply'
+  >>> button.Button(__name__='apply').__name__
+  'apply'
+
+If no name is specified, the name will be assigned to the button. When it can
+it uses the title, otherwise a hexadecimal string is created:
+
+  >>> button.Button(title=u'Apply').__name__
+  'apply'
+  >>> button.Button(title=u'Apply and more').__name__
+  '4170706c7920616e64206d6f7265'
+
+
+This declaration behaves identical to the one before:
+
+  >>> form = Form()
+  >>> request = TestRequest()
+
+  >>> actions = button.ButtonActions(form, request, None)
+  >>> actions.update()
+  >>> actions.execute()
+
+When sending in the right information, the actions are executed:
+
+  >>> request = TestRequest(form={'form.buttons.apply': 'Apply'})
+  >>> actions = button.ButtonActions(form, request, None)
+  >>> actions.update()
+  >>> actions.execute()
+  successfully applied
+
+An even simpler method -- resembling closest the API provided by formlib -- is
+to create the button and handler at the same time:
+
+  >>> class Form(object):
+  ...     zope.interface.implements(
+  ...         interfaces.IButtonForm, interfaces.IHandlerForm)
+  ...     prefix = 'form.'
+  ...
+  ...     @button.buttonAndHandler(u'Apply')
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+
+In this case the ``buttonAndHandler`` decorator creates a button and a handler
+for it. By default the name is computed from the title of the button, which is
+required. All (keyword) arguments are forwarded to the button
+constructor. Let's now render the form:
+
+  >>> request = TestRequest(form={'form.buttons.apply': 'Apply'})
+  >>> actions = button.ButtonActions(form, request, None)
+  >>> actions.update()
+  >>> actions.execute()
+  successfully applied
+
+If the title is a more complex string, then the name of the button becomes a
+hex-encoded string:
+
+  >>> class Form(object):
+  ...
+  ...     @button.buttonAndHandler(u'Apply and Next')
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+
+  >>> Form.buttons.keys()
+  ['4170706c7920616e64204e657874']
+
+Of course, you can use the ``__name__`` argument to specify a nem
+yourself. The decorator, however, also allows the keyword ``name``:
+
+  >>> class Form(object):
+  ...
+  ...     @button.buttonAndHandler(u'Apply and Next', name='applyNext')
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+
+  >>> Form.buttons.keys()
+  ['applyNext']
+
+This helper function also supports a keyword argument ``provides``, which
+allows the developer to specify a sequence of interfaces that the generated
+button should directly provide. Those provided interfaces can be used for a
+multitude of things, including handler discrimination and UI layout:
+
+  >>> class IMyButton(zope.interface.Interface):
+  ...    pass
+
+  >>> class Form(object):
+  ...
+  ...     @button.buttonAndHandler(u'Apply', provides=(IMyButton,))
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+
+  >>> IMyButton.providedBy(Form.buttons['apply'])
+  True
+
+
+Button Conditions
+-----------------
+
+Sometimes it is desirable to only show a button when a certain condition is
+fulfilled. The ``Button`` field supports conditions via a simple argument. The
+``condition`` argument must be a callable takening the form as argument and
+returns a truth-value. If the condition is not fulfilled, the button will not
+be converted to an action:
+
+  >>> class Form(object):
+  ...     prefix='form'
+  ...     showApply = True
+  ...
+  ...     @button.buttonAndHandler(
+  ...         u'Apply', condition=lambda form: form.showApply)
+  ...     def apply(self, action):
+  ...         print 'successfully applied'
+
+In this case a form variable specifies the availability. Initially the button
+is available as action:
+
+  >>> myform = Form()
+  >>> actions = button.ButtonActions(myform, TestRequest(), None)
+  >>> actions.update()
+  >>> actions.keys()
+  ['apply']
+
+If we set the show-apply attribute to false, the action will not be available.
+
+  >>> myform.showApply = False
+  >>> actions = button.ButtonActions(myform, TestRequest(), None)
+  >>> actions.update()
+  >>> actions.keys()
+  []
+
+This feature is very helpful in multi-forms and wizards.
+
+
+The Button Manager
+------------------
+
+The button manager contains several additional API methods that make the
+management of buttons easy.
+
+First, you are able to add button managers:
+
+  >>> bm1 = button.Buttons(IButtons)
+  >>> bm2 = button.Buttons(button.Button('help', title=u'Help'))
+
+  >>> bm1 + bm2
+  <z3c.form.button.Buttons object at ...>
+  >>> list(bm1 + bm2)
+  ['apply', 'cancel', 'help']
+
+The result of the addition is another button manager. Also note that the order
+of the buttons is preserved throughout the addition. Adding anything else is
+not well-defined:
+
+  >>> bm1 + 1
+  Traceback (most recent call last):
+  ...
+  TypeError: unsupported operand type(s) for +: 'Buttons' and 'int'
+
+Second, you can select the buttons in a particular order:
+
+  >>> bm = bm1 + bm2
+  >>> list(bm)
+  ['apply', 'cancel', 'help']
+
+  >>> list(bm.select('help', 'apply', 'cancel'))
+  ['help', 'apply', 'cancel']
+
+The ``select()`` method can also be used to eliminate another button:
+
+  >>> list(bm.select('help', 'apply'))
+  ['help', 'apply']
+
+Of course, in the example above we elimintated one and reoganized the buttons.
+
+Third, you can omit one or more buttons:
+
+  >>> list(bm.omit('cancel'))
+  ['apply', 'help']
+
+Finally, while the constructor is very flexible, you cannot just pass in
+anything:
+
+  >>> button.Buttons(1, 2)
+  Traceback (most recent call last):
+  ...
+  TypeError: ('Unrecognized argument type', 1)
+
+When creating a new form derived from another, you often want to keep existing
+buttons and add new ones. In order no to change the super-form class, you need
+to copy the button manager:
+
+  >>> bm.keys()
+  ['apply', 'cancel', 'help']
+  >>> bm.copy().keys()
+  ['apply', 'cancel', 'help']
+
+
+The Handlers Object
+-------------------
+
+All handlers of a form are collected in the ``handlers`` attribute, which is a
+``Handlers`` instance:
+
+  >>> isinstance(form.handlers, button.Handlers)
+  True
+  >>> form.handlers
+  <Handlers [<Handler for <Button 'apply' u'Apply'>>]>
+
+Internally the object uses an adapter registry to manage the handlers for
+buttons. If a handler is registered for a button, it simply behaves as an
+instance-adapter.
+
+The object itself is pretty simple. You can get a handler as follows:
+
+  >>> apply = form.buttons['apply']
+  >>> form.handlers.getHandler(apply)
+  <Handler for <Button 'apply' u'Apply'>>
+
+But you can also register handlers for groups of buttons, either by interface
+or class:
+
+  >>> class SpecialButton(button.Button):
+  ...     pass
+
+  >>> def handleSpecialButton(form, action):
+  ...     return 'Special button action'
+
+  >>> form.handlers.addHandler(
+  ...     SpecialButton, button.Handler(SpecialButton, handleSpecialButton))
+
+  >>> form.handlers
+  <Handlers
+      [<Handler for <Button 'apply' u'Apply'>>,
+       <Handler for <class 'SpecialButton'>>]>
+
+Now all special buttons should use that handler:
+
+  >>> button1 = SpecialButton(title=u'Button 1')
+  >>> button2 = SpecialButton(title=u'Button 2')
+
+  >>> form.handlers.getHandler(button1)(form, None)
+  'Special button action'
+  >>> form.handlers.getHandler(button2)(form, None)
+  'Special button action'
+
+However, registering a more specific handler for button 1 will override the
+general handler:
+
+  >>> def handleButton1(form, action):
+  ...     return 'Button 1 action'
+
+  >>> form.handlers.addHandler(
+  ...     button1, button.Handler(button1, handleButton1))
+
+  >>> form.handlers.getHandler(button1)(form, None)
+  'Button 1 action'
+  >>> form.handlers.getHandler(button2)(form, None)
+  'Special button action'
+
+You can also add handlers objects:
+
+  >>> handlers2 = button.Handlers()
+
+  >>> button3 = SpecialButton(title=u'Button 3')
+  >>> handlers2.addHandler(
+  ...     button3, button.Handler(button3, None))
+
+  >>> form.handlers + handlers2
+  <Handlers
+      [<Handler for <Button 'apply' u'Apply'>>,
+       <Handler for <class 'SpecialButton'>>,
+       <Handler for <SpecialButton '427574746f6e2031' u'Button 1'>>,
+       <Handler for <SpecialButton '427574746f6e2033' u'Button 3'>>]>
+
+However, adding other components is not supported:
+
+  >>> form.handlers + 1
+  Traceback (most recent call last):
+  ...
+  NotImplementedError
+
+The handlers also provide a method to copy the handlers to a new instance:
+
+  >>> copy = form.handlers.copy()
+  >>> isinstance(copy, button.Handlers)
+  True
+  >>> copy is form.handlers
+  False
+
+This is commonly needed when one wants to extend the handlers of a super-form.


Property changes on: z3c.form/trunk/src/z3c/form/button.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/configure.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/configure.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/configure.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,173 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="z3c.form">
+
+  <!-- default z3c.form layer -->
+  <interface
+      interface="z3c.form.interfaces.IFormLayer"
+      type="zope.publisher.interfaces.browser.IBrowserSkinType"
+      />
+
+  <!-- Validators -->
+  <adapter
+      factory=".validator.SimpleFieldValidator"
+      />
+  <adapter
+      factory=".validator.InvariantsValidator"
+      />
+
+  <!-- Data Managers -->
+  <adapter
+      factory=".datamanager.AttributeField"
+      />
+  <adapter
+      factory=".datamanager.DictionaryField"
+      />
+
+  <!-- Widget Manager -->
+  <adapter
+      factory=".field.FieldWidgets"
+      />
+
+  <!-- Data Converters -->
+  <adapter
+      factory=".converter.FieldDataConverter"
+      />
+  <adapter
+      factory=".converter.DateDataConverter"
+      />
+  <adapter
+      factory=".converter.TimeDataConverter"
+      />
+  <adapter
+      factory=".converter.DatetimeDataConverter"
+      />
+  <adapter
+      factory=".converter.TimedeltaDataConverter"
+      />
+  <adapter
+      factory=".converter.SequenceDataConverter"
+      />
+  <adapter
+      factory=".converter.CollectionSequenceDataConverter"
+      />
+  <adapter
+      factory=".converter.FieldWidgetDataConverter"
+      />
+
+  <!-- ITerms -->
+  <adapter
+      factory=".term.ChoiceTerms"
+      />
+  <adapter
+      factory=".term.CollectionTerms"
+      />
+  <adapter
+      factory=".term.BoolTerms"
+      />
+
+  <!-- Action Managers and Handlers -->
+  <adapter
+      factory=".button.ButtonActions"
+      />
+  <adapter
+      factory=".button.ButtonActionHandler"
+      />
+
+  <!-- Error Views -->
+  <adapter
+      factory=".error.ErrorViewSnippet"
+      trusted="True"
+      permission="zope.Public"
+      />
+  <adapter
+      factory=".error.ValueErrorViewSnippet"
+      trusted="True"
+      permission="zope.Public"
+      />
+  <adapter
+      factory=".error.StandardErrorViewTemplate"
+      />
+
+  <!-- APIDoc documentation -->
+  <configure
+      xmlns:zcml="http://namespaces.zope.org/zcml"
+      xmlns:apidoc="http://namespaces.zope.org/apidoc"
+      zcml:condition="have apidoc">
+
+    <apidoc:bookchapter
+        id="z3c-form"
+        title="z3c.form - Widgets and Forms"
+        doc_path="README.txt"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-form"
+        title="Forms"
+        doc_path="form.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-subform"
+        title="Sub-Forms"
+        doc_path="subform.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-field"
+        title="Fields"
+        doc_path="field.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-button"
+        title="Buttons"
+        doc_path="button.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-validator"
+        title="Validators"
+        doc_path="validator.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-zcml"
+        title="ZCML Directives"
+        doc_path="zcml.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-widget"
+        title="Widgets"
+        doc_path="widget.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-action"
+        title="Actions"
+        doc_path="action.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-value"
+        title="Attribute Values"
+        doc_path="value.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-datamanager"
+        title="Data Managers"
+        doc_path="datamanager.txt"
+        parent="z3c-form"
+        />
+    <apidoc:bookchapter
+        id="z3c-form-converter"
+        title="Data Converters"
+        doc_path="converter.txt"
+        parent="z3c-form"
+        />
+  </configure>
+
+  <include package=".browser" />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/converter.py
===================================================================
--- z3c.form/trunk/src/z3c/form/converter.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/converter.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,160 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Data Converters
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import datetime
+import zope.interface
+import zope.component
+import zope.schema
+
+from z3c.form import interfaces
+
+
+class FieldDataConverter(object):
+    """A data converter using the field's ``fromUnicode()`` method."""
+    zope.component.adapts(
+        zope.schema.interfaces.IFromUnicode, interfaces.IWidget)
+    zope.interface.implements(interfaces.IDataConverter)
+
+    def __init__(self, field, widget):
+        self.field = field
+        self.widget = widget
+
+    def toWidgetValue(self, value):
+        """See interfaces.IDataConverter"""
+        if value is self.field.missing_value:
+            return u''
+        return unicode(value)
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        return self.field.fromUnicode(value)
+
+    def __repr__(self):
+        return '<DataConverter from %s to %s>' %(
+            self.field.__class__.__name__, self.widget.__class__.__name__)
+
+
+ at zope.component.adapter(interfaces.IFieldWidget)
+ at zope.interface.implementer(interfaces.IDataConverter)
+def FieldWidgetDataConverter(widget):
+    """Provide a data converter based on a field widget."""
+    return zope.component.queryMultiAdapter(
+        (widget.field, widget), interfaces.IDataConverter)
+
+
+class DateDataConverter(FieldDataConverter):
+    """A special data converter for dates."""
+    zope.component.adapts(
+        zope.schema.interfaces.IDate, interfaces.IWidget)
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        return datetime.date(*[int(part) for part in value.split('-')])
+
+
+class TimeDataConverter(FieldDataConverter):
+    """A special data converter for times."""
+    zope.component.adapts(
+        zope.schema.interfaces.ITime, interfaces.IWidget)
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        return datetime.time(*[int(part) for part in value.split(':')])
+
+
+class DatetimeDataConverter(FieldDataConverter):
+    """A special data converter for datetimes."""
+    zope.component.adapts(
+        zope.schema.interfaces.IDatetime, interfaces.IWidget)
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        dateString, timeString = value.split(' ')
+        dt = [int(part) for part in dateString.split('-')]
+        dt += [int(part) for part in timeString.split(':')]
+        return datetime.datetime(*dt)
+
+
+class TimedeltaDataConverter(FieldDataConverter):
+    """A special data converter for timedeltas."""
+    zope.component.adapts(
+        zope.schema.interfaces.ITimedelta, interfaces.IWidget)
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        daysString, crap, timeString = value.split(' ')
+        days = int(daysString)
+        seconds = [int(part)*60**(2-n)
+                   for n, part in enumerate(timeString.split(':'))]
+        return datetime.timedelta(days, sum(seconds))
+
+
+class SequenceDataConverter(FieldDataConverter):
+    """Basic data converter for ISequenceWidget."""
+
+    zope.component.adapts(
+        zope.schema.interfaces.IField, interfaces.ISequenceWidget)
+
+    def toWidgetValue(self, value):
+        """Convert from Python bool to HTML representation."""
+        widget = self.widget
+        # if the value is the missing value, then the widget's "noValueToken"
+        # is returned
+        if value is self.field.missing_value:
+            return [widget.noValueToken]
+        # Look up the term in the terms
+        terms = zope.component.getMultiAdapter(
+            (widget.context, widget.request, widget.form, self.field, widget),
+             interfaces.ITerms)
+        return [terms.getTerm(value).token]
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        widget = self.widget
+        if value[0] == widget.noValueToken:
+            return self.field.missing_value
+        terms = zope.component.getMultiAdapter(
+            (widget.context, widget.request, widget.form, self.field, widget),
+             interfaces.ITerms)
+        return terms.getValue(value[0])
+
+
+class CollectionSequenceDataConverter(FieldDataConverter):
+    """A special converter between collections and sequence widgets."""
+
+    zope.component.adapts(
+        zope.schema.interfaces.ICollection, interfaces.ISequenceWidget)
+
+    def toWidgetValue(self, value):
+        """Convert from Python bool to HTML representation."""
+        widget = self.widget
+        terms = zope.component.getMultiAdapter(
+            (widget.context, widget.request, widget.form, self.field, widget),
+             interfaces.ITerms)
+        return [terms.getTerm(entry).token for entry in value]
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        widget = self.widget
+        terms = zope.component.getMultiAdapter(
+            (widget.context, widget.request, widget.form, self.field, widget),
+             interfaces.ITerms)
+        collectionType = self.field._type
+        if isinstance(collectionType, tuple):
+            collectionType = collectionType[-1]
+        return collectionType([terms.getValue(token) for token in value])


Property changes on: z3c.form/trunk/src/z3c/form/converter.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/converter.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/converter.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/converter.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,299 @@
+==============
+Data Converter
+==============
+
+The data converter is the component that converts an internal data value as
+described by a field to an external value as required by a widget and vice
+versa. The goal of the converter is to avoid field and widget proliferation
+solely to handle different types of values. The purpose of fields is to
+describe internal data types and structures and that of widgets to provide one
+particular mean of input.
+
+The only two discriminators for the converter are the field and the widget.
+
+Let's look at the ``Int`` field to ``TextWidget`` converter as an example:
+
+  >>> import zope.schema
+  >>> age = zope.schema.Int(
+  ...     __name__='age',
+  ...     title=u'Age',
+  ...     min=0)
+
+  >>> from z3c.form.testing import TestRequest
+  >>> from z3c.form import widget
+  >>> text = widget.Widget(TestRequest())
+
+  >>> from z3c.form import converter
+  >>> conv = converter.FieldDataConverter(age, text)
+
+The converter can now convert any integer to a the value the test widget deals
+with, which is an ASCII string:
+
+  >>> conv.toWidgetValue(34)
+  u'34'
+
+When the missing value is passed in, an empty string should be returned:
+
+  >>> conv.toWidgetValue(age.missing_value)
+  u''
+
+Of course, values can also be converted from a widget value to field value:
+
+  >>> conv.toFieldValue('34')
+  34
+
+Of course, trying to convert a non-integer string representation fails in a
+conversion error:
+
+  >>> conv.toFieldValue('3.4')
+  Traceback (most recent call last):
+  ...
+  ValueError: invalid literal for int(): 3.4
+
+Also, the conversion to the field value also validates the data; in this case
+negative values are not allowed:
+
+  >>> conv.toFieldValue('-34')
+  Traceback (most recent call last):
+  ...
+  TooSmall: (-34, 0)
+
+That's pretty much the entire API. When dealing with converters within the
+component architecture, everything is a little bit simpler. So let's register
+the converter:
+
+  >>> import zope.component
+  >>> zope.component.provideAdapter(converter.FieldDataConverter)
+
+Once we ensure that our widget is a text widget, we can lookup the adapter:
+
+  >>> import zope.interface
+  >>> from z3c.form import interfaces
+  >>> zope.interface.alsoProvides(text, interfaces.ITextWidget)
+
+  >>> zope.component.getMultiAdapter((age, text), interfaces.IDataConverter)
+  <DataConverter from Int to Widget>
+
+For field-widgets there is a helper adapter that makes the lookup even
+simpler:
+
+  >>> zope.component.provideAdapter(converter.FieldWidgetDataConverter)
+
+After converting our simple widget to a field widget,
+
+  >>> fieldtext = widget.FieldWidget(age, text)
+
+we can now lookup the data converter adapter just by the field widget itself:
+
+  >>> interfaces.IDataConverter(fieldtext)
+  <DataConverter from Int to Widget>
+
+
+Date Data Converter
+-------------------
+
+Since the ``Date`` field does not provide ``IFromUnicode``, we have to provide
+a custom data converter. This default one is not very sophisticated and is
+inteded for use with the text widget:
+
+  >>> date = zope.schema.Date()
+
+  >>> ddc = converter.DateDataConverter(date, text)
+  >>> ddc
+  <DataConverter from Date to Widget>
+
+Dates are simply converted to ISO format:
+
+  >>> import datetime
+  >>> bday = datetime.date(1980, 1, 25)
+
+  >>> ddc.toWidgetValue(bday)
+  u'1980-01-25'
+
+The converter only knows how to convert this particular format back to a
+datetime value:
+
+  >>> ddc.toFieldValue(u'1980-01-25')
+  datetime.date(1980, 1, 25)
+
+
+Time Data Converter
+-------------------
+
+Since the ``Time`` field does not provide ``IFromUnicode``, we have to provide
+a custom data converter. This default one is not very sophisticated and is
+inteded for use with the text widget:
+
+  >>> time = zope.schema.Time()
+
+  >>> tdc = converter.TimeDataConverter(time, text)
+  >>> tdc
+  <DataConverter from Time to Widget>
+
+Dates are simply converted to ISO format:
+
+  >>> noon = datetime.time(12, 0, 0)
+
+  >>> tdc.toWidgetValue(noon)
+  u'12:00:00'
+
+The converter only knows how to convert this particular format back to a
+datetime value:
+
+  >>> tdc.toFieldValue(u'12:00:00')
+  datetime.time(12, 0)
+
+
+Datetime Data Converter
+-----------------------
+
+Since the ``Datetime`` field does not provide ``IFromUnicode``, we have to
+provide a custom data converter. This default one is not very sophisticated
+and is inteded for use with the text widget:
+
+  >>> dtField = zope.schema.Datetime()
+
+  >>> dtdc = converter.DatetimeDataConverter(dtField, text)
+  >>> dtdc
+  <DataConverter from Datetime to Widget>
+
+Dates are simply converted to ISO format:
+
+  >>> bdayNoon = datetime.datetime(1980, 1, 25, 12, 0, 0)
+
+  >>> dtdc.toWidgetValue(bdayNoon)
+  u'1980-01-25 12:00:00'
+
+The converter only knows how to convert this particular format back to a
+datetime value:
+
+  >>> dtdc.toFieldValue(u'1980-01-25 12:00:00')
+  datetime.datetime(1980, 1, 25, 12, 0)
+
+
+Timedelta Data Converter
+------------------------
+
+Since the ``Timedelta`` field does not provide ``IFromUnicode``, we have to
+provide a custom data converter. This default one is not very sophisticated
+and is inteded for use with the text widget:
+
+  >>> timedelta = zope.schema.Timedelta()
+
+  >>> tddc = converter.TimedeltaDataConverter(timedelta, text)
+  >>> tddc
+  <DataConverter from Timedelta to Widget>
+
+Dates are simply converted to ISO format:
+
+  >>> allOnes = datetime.timedelta(1, 3600+60+1)
+
+  >>> tddc.toWidgetValue(allOnes)
+  u'1 day, 1:01:01'
+
+The converter only knows how to convert this particular format back to a
+datetime value:
+
+  >>> tddc.toFieldValue(u'1 day, 1:01:01')
+  datetime.timedelta(1, 3661)
+
+
+Sequence Data Converter
+-----------------------
+
+For widgets and fields that work with choices of a sequence, a special data
+converter is required that works with terms. A prime example is a choice
+field. Before we can use the converter, we have to register the terms adapter:
+
+  >>> from z3c.form import term
+  >>> zope.component.provideAdapter(term.ChoiceTerms)
+
+Let's now create a choice field and a widget:
+
+  >>> from zope.schema.vocabulary import SimpleVocabulary
+
+  >>> gender = zope.schema.Choice(
+  ...     vocabulary = SimpleVocabulary([
+  ...              SimpleVocabulary.createTerm(0, 'm', u'male'),
+  ...              SimpleVocabulary.createTerm(1, 'f', u'female'),
+  ...              ]) )
+
+  >>> from z3c.form import widget
+  >>> seqWidget = widget.SequenceWidget(TestRequest())
+
+We now use the field and widget to instantiate the converter:
+
+  >>> sdv = converter.SequenceDataConverter(gender, seqWidget)
+
+We can now convert a real value to a widget value, which will be the term's
+token:
+
+  >>> sdv.toWidgetValue(0)
+  ['m']
+
+The result is always a sequence, since sequence widgets only deal collections
+of values. Of course, we can convert the widget value back to an internal
+value:
+
+  >>> sdv.toFieldValue(['m'])
+  0
+
+Sometimes a field is not required. In those cases, the value can be the
+missing value of the field:
+
+  >>> gender.missing_value = 'missing'
+
+  >>> sdv.toWidgetValue(gender.missing_value)
+  ['--NOVALUE--']
+
+Similarly, if "no value" has been specified in the widget, the missing value
+of the field is returned:
+
+  >>> sdv.toFieldValue([u'--NOVALUE--'])
+  'missing'
+
+Collection Sequence Data Converter
+----------------------------------
+
+For widgets and fields that work with a sequence of choices, another data
+converter is required that works with terms. A prime example is a list
+field. Before we can use the converter, we have to register the terms adapter:
+
+  >>> from z3c.form import term
+  >>> zope.component.provideAdapter(term.CollectionTerms)
+
+Let's now create a set field and a widget:
+
+  >>> genders = zope.schema.List(value_type=gender)
+  >>> seqWidget = widget.SequenceWidget(TestRequest())
+
+We now use the field and widget to instantiate the converter:
+
+  >>> csdv = converter.CollectionSequenceDataConverter(genders, seqWidget)
+
+We can now convert a real value to a widget value, which will be the term's
+token:
+
+  >>> csdv.toWidgetValue([0])
+  ['m']
+
+The result is always a sequence, since sequence widgets only deal collections
+of values. Of course, we can convert the widget value back to an internal
+value:
+
+  >>> csdv.toFieldValue(['m'])
+  [0]
+
+For some field, like the ``Set``, the collection type is a tuple. Sigh. In
+these cases we use the last entry in the tuple as the type to use:
+
+  >>> genders = zope.schema.Set(value_type=gender)
+  >>> seqWidget = widget.SequenceWidget(TestRequest())
+
+  >>> csdv = converter.CollectionSequenceDataConverter(genders, seqWidget)
+
+  >>> csdv.toWidgetValue(set([0]))
+  ['m']
+
+  >>> csdv.toFieldValue(['m'])
+  set([0])


Property changes on: z3c.form/trunk/src/z3c/form/converter.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/datamanager.py
===================================================================
--- z3c.form/trunk/src/z3c/form/datamanager.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/datamanager.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,101 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Widget Framework Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.schema
+from zope.security.checker import canAccess
+from zope.security.checker import canWrite
+
+from z3c.form import interfaces
+
+
+class DataManager(object):
+    """Data manager base class."""
+    zope.interface.implements(interfaces.IDataManager)
+
+
+class AttributeField(DataManager):
+    """Attribute field."""
+    zope.component.adapts(
+        zope.interface.Interface, zope.schema.interfaces.IField)
+
+    def __init__(self, context, field):
+        self.context = context
+        self.field = field
+
+    def get(self, default=interfaces.NOVALUE):
+        """See z3c.form.interfaces.IDataManager"""
+        # get the right adapter or context
+        context = self.context
+        if self.field.interface is not None:
+            context = self.field.interface(context)
+        return getattr(context, self.field.__name__, default)
+
+    def set(self, value):
+        """See z3c.form.interfaces.IDataManager"""
+        if self.field.readonly:
+            raise TypeError("Can't set values on read-only fields "
+                            "(name=%s, class=%s.%s)"
+                            % (self.field.__name__,
+                               self.context.__class__.__module__,
+                               self.context.__class__.__name__))
+        # get the right adapter or context
+        context = self.field.interface(self.context)
+        setattr(context, self.field.__name__, value)
+
+    def canAccess(self):
+        """See z3c.form.interfaces.IDataManager"""
+        return canAccess(self.context, self.field.__name__)
+
+    def canWrite(self):
+        """See z3c.form.interfaces.IDataManager"""
+        return canWrite(self.context, self.field.__name__)
+
+
+class DictionaryField(DataManager):
+    """Dictionary field."""
+    zope.component.adapts(
+        dict, zope.schema.interfaces.IField)
+
+    def __init__(self, data, field):
+        if not isinstance(data, dict):
+            raise ValueError("Data are not a dictionary: %s" %type(data))
+        self.data = data
+        self.field = field
+
+    def get(self, default=interfaces.NOVALUE):
+        """See z3c.form.interfaces.IDataManager"""
+        return self.data.get(self.field.__name__, default)
+
+    def set(self, value):
+        """See z3c.form.interfaces.IDataManager"""
+        if self.field.readonly:
+            raise TypeError("Can't set values on read-only fields name=%s"
+                            % self.field.__name__)
+        self.data[self.field.__name__] = value
+
+    def canAccess(self):
+        """See z3c.form.interfaces.IDataManager"""
+        return True
+
+    def canWrite(self):
+        """See z3c.form.interfaces.IDataManager"""
+        return True
+


Property changes on: z3c.form/trunk/src/z3c/form/datamanager.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/datamanager.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/datamanager.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/datamanager.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,257 @@
+=============
+Data Managers
+=============
+
+For the longest time the way widgets retrieved and stored their values on the
+actual content/model was done by binding the field to a context and then
+setting and getting the attribute from it. This has several distinct design
+shortcomings:
+
+1. The field has too much responsibility by knowing about its implementations.
+
+2. There is no way of redefining the method used to store and access data
+   other than rewriting fields.
+
+3. Finding the right content/model to modify is an implicit policy: Find an
+   adapter for the field's schema and then set the value there.
+
+While implementing dome real-world projects, we noticed that this approach is
+too limiting and we often could not use the form framework when we wanted or
+had to jump through many hoops to make it work for us. For example, if we want
+to display a form to collect data that does not correspond to a set of content
+components, we was forced to not only write a schema for the form but also
+implement that schema as a class. but all we wanted was a dictionary. For
+edit-form like tasks we often also had an initial dictionary, which we just
+wanted modified.
+
+Data managers abstract the getting and setting of the data. A data manager is
+responsible for setting one piece of data in a particular context.
+
+  >>> from z3c.form import datamanager
+
+
+Attribute Field Manager
+-----------------------
+
+The most common case, of course, is the management of class attributes through
+fields. In this case, the data manager needs to know about the context and the
+field it is managing the data for.
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IPerson(zope.interface.Interface):
+  ...     name = zope.schema.TextLine(
+  ...         title=u'Name',
+  ...         default=u'<no name>')
+
+  >>> class Person(object):
+  ...     zope.interface.implements(IPerson)
+  ...     name = u''
+  ...     def __init__(self, name):
+  ...         self.name = name
+
+  >>> stephan = Person(u'Stephan Richter')
+
+We can now instantiate the data manager for Stephan's name:
+
+  >>> nameDm = datamanager.AttributeField(stephan, IPerson['name'])
+
+The data manager consists of a few simple methods to accomplish its
+purpose. Getting the value is done using the ``get()`` method:
+
+  >>> nameDm.get()
+  u'Stephan Richter'
+
+The value can be set using ``set()``:
+
+  >>> nameDm.set(u'Stephan "Caveman" Richter')
+
+  >>> nameDm.get()
+  u'Stephan "Caveman" Richter'
+  >>> stephan.name
+  u'Stephan "Caveman" Richter'
+
+A final feature that is supported by the data manager is the check whether a
+value can be accessed and written. To demonstrate the feature, we first have
+to provide security declarations for our person:
+
+  >>> from zope.security.management import endInteraction
+  >>> from zope.security.management import newInteraction
+  >>> from zope.security.management import setSecurityPolicy
+  >>> import z3c.form.testing
+  >>> endInteraction()
+  >>> newPolicy = z3c.form.testing.SimpleSecurityPolicy()
+  >>> newPolicy.allowedPermissions = ('View', 'Edit')
+  >>> oldpolicy = setSecurityPolicy(newPolicy)
+  >>> newInteraction()
+
+  >>> from zope.security.checker import Checker
+  >>> from zope.security.checker import defineChecker
+  >>> personChecker = Checker({'name':'View', 'name':'Edit'})
+  >>> defineChecker(Person, personChecker)
+
+We now need to wrap stephan into a proxy:
+
+  >>> protectedStephan = zope.security.checker.ProxyFactory(stephan)
+
+Since we are not logged in as anyone, we cannot acces or write the value:
+
+  >>> nameDm = datamanager.AttributeField(protectedStephan, IPerson['name'])
+
+  >>> nameDm.canAccess()
+  False
+  >>> nameDm.canWrite()
+  False
+
+Clearly, this also means that ``get()`` and ``set()`` are also shut off:
+
+  >>> nameDm.get()
+  Traceback (most recent call last):
+  ...
+  Unauthorized: (<Person object at ...>, 'name', 'Edit')
+
+  >>> nameDm.set(u'Stephan')
+  Traceback (most recent call last):
+  ...
+  ForbiddenAttribute: ('name', <Person object at ...>)
+
+Now we have to setup the security system and "log in" as a user:
+
+  >>> newPolicy.allowedPermissions = ('View', 'Edit')
+  >>> newPolicy.loggedIn = True
+
+The created principal, with which we are logged in now, can only access the
+attribute:
+
+  >>> nameDm.canAccess()
+  True
+  >>> nameDm.canWrite()
+  False
+
+Thus only the ``get()`` method is allowed:
+
+  >>> nameDm.get()
+  u'Stephan "Caveman" Richter'
+
+  >>> nameDm.set(u'Stephan')
+  Traceback (most recent call last):
+  ...
+  ForbiddenAttribute: ('name', <Person object at ...>)
+
+If field's schema is not directly provided by the context, the datamanager
+will attempt to find an adapter. Let's give the person an address for example:
+
+  >>> class IAddress(zope.interface.Interface):
+  ...     city = zope.schema.TextLine(title=u'City')
+
+  >>> class Address(object):
+  ...     zope.component.adapts(IPerson)
+  ...     zope.interface.implements(IAddress)
+  ...     def __init__(self, person):
+  ...         self.person = person
+  ...     @apply
+  ...     def city():
+  ...         def get(self):
+  ...             return getattr(self.person, '_city', None)
+  ...         def set(self, value):
+  ...             self.person._city = value
+  ...         return property(get, set)
+
+  >>> zope.component.provideAdapter(Address)
+
+Now we can create a data manager for the city attribute:
+
+  >>> cityDm = datamanager.AttributeField(stephan, IAddress['city'])
+
+Initially there is no value, but of course we can create one:
+
+  >>> cityDm.get()
+
+  >>> cityDm.set(u'Maynard')
+  >>> cityDm.get()
+  u'Maynard'
+
+The value can be accessed through the adapter itself as well:
+
+  >>> IAddress(stephan).city
+  u'Maynard'
+
+While we think that implicitly looking up an adapter is not the cleanest
+solution, it allows us to mimic the behavior of ``zope.formlib``. We think
+that we will eventually provide alternative ways to accomplish the same in a
+more explicit way.
+
+Finally, if we try to set a value that is read-only, a type error is raised:
+
+  >>> readOnlyName = zope.schema.TextLine(
+  ...     __name__='name',
+  ...     readonly=True)
+
+  >>> nameDm = datamanager.AttributeField(stephan, readOnlyName)
+  >>> nameDm.set(u'Stephan')
+  Traceback (most recent call last):
+  ...
+  TypeError: Can't set values on read-only fields
+             (name=name, class=__builtin__.Person)
+
+
+Dictionary Field Manager
+------------------------
+
+Another implementation of the data manager interface is provided by the
+dictionary field manager, which does not expect an instance with attributes as
+its context, but a dictionary. It still uses a field to determine the key to
+modify.
+
+  >>> personDict = {}
+  >>> nameDm = datamanager.DictionaryField(personDict, IPerson['name'])
+
+The datamanager can really only deal with dictionaries and no other types:
+
+  >>> datamanager.DictionaryField([], IPerson['name'])
+  Traceback (most recent call last):
+  ...
+  ValueError: Data are not a dictionary: <type 'list'>
+
+Let's now access the name:
+
+  >>> nameDm.get()
+  <NOVALUE>
+
+Initially we get the default value (as specified in the field), since the
+person dictionariy has no entry. If no default value has been specified in the
+field, the missing value is returned.
+
+Now we set a value and it should be available:
+
+  >>> nameDm.set(u'Roger Ineichen')
+
+  >>> nameDm.get()
+  u'Roger Ineichen'
+  >>> personDict
+  {'name': u'Roger Ineichen'}
+
+Since this dictionary is not security proxied, any field can be accessed and
+written to:
+
+  >>> nameDm.canAccess()
+  True
+  >>> nameDm.canWrite()
+  True
+
+As with the attribute data manager, readonly fields cannot be set:
+
+  >>> nameDm = datamanager.DictionaryField(personDict, readOnlyName)
+  >>> nameDm.set(u'Stephan')
+  Traceback (most recent call last):
+  ...
+  TypeError: Can't set values on read-only fields name=name
+
+
+Cleanup
+-------
+
+We clean up the changes we made in these examples:
+
+  >>> endInteraction()
+  >>> ignore = setSecurityPolicy(oldpolicy)


Property changes on: z3c.form/trunk/src/z3c/form/datamanager.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/error.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/error.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/error.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+<div class="error" tal:content="view/message">Error</div>


Property changes on: z3c.form/trunk/src/z3c/form/error.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/error.py
===================================================================
--- z3c.form/trunk/src/z3c/form/error.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/error.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,113 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Error Views Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import os
+import zope.component
+import zope.interface
+import zope.schema
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.pagetemplate.interfaces import IPageTemplate
+
+import z3c.form
+from z3c.form import interfaces, util, value
+from z3c.form.i18n import MessageFactory as _
+
+ErrorViewMessage = value.StaticValueCreator(
+    discriminators = ('error', 'request', 'widget', 'field', 'form', 'content')
+    )
+
+
+def ErrorViewDiscriminators(
+    errorView,
+    error=None, request=None, widget=None, field=None, form=None, content=None):
+    zope.component.adapter(
+        util.getSpecification(error),
+        util.getSpecification(request),
+        util.getSpecification(widget),
+        util.getSpecification(field),
+        util.getSpecification(form),
+        util.getSpecification(content))(errorView)
+
+
+class ErrorViewSnippet(object):
+    """Error view snippet."""
+    zope.component.adapts(
+        zope.schema.ValidationError, None, None, None, None, None)
+    zope.interface.implements(interfaces.IErrorViewSnippet)
+
+    def __init__(self, error, request, widget, field, form, content):
+        self.error = self.context = error
+        self.request = request
+        self.widget = widget
+        self.field = field
+        self.form = form
+        self.content = content
+
+    def update(self):
+        value = zope.component.queryMultiAdapter(
+            (self.context, self.request, self.widget,
+             self.field, self.form, self),
+            interfaces.IValue, name='message')
+        if value is not None:
+            self.message = value.get()
+        else:
+            self.message = self.error.doc()
+
+    def render(self):
+        template = zope.component.getMultiAdapter(
+            (self, self.request), IPageTemplate)
+        return template(self)
+
+    def __repr__(self):
+        return '<%s for %s>' %(
+            self.__class__.__name__, self.error.__class__.__name__)
+
+
+class ValueErrorViewSnippet(ErrorViewSnippet):
+    """An error view for ValueError."""
+    zope.component.adapts(
+        ValueError, None, None, None, None, None)
+
+    message = _('The system could not process the given value.')
+
+    def update(self):
+        value = zope.component.queryMultiAdapter(
+            (self.context, self.request, self.widget,
+             self.field, self.form, self),
+            interfaces.IValue, name='message')
+        if value is not None:
+            self.message = value.get()
+
+
+class ErrorViewTemplateFactory(object):
+    """Error view template factory."""
+
+    template = None
+
+    def __init__(self, filename, contentType='text/html'):
+        self.template = ViewPageTemplateFile(filename, content_type=contentType)
+
+    def __call__(self, errorView, request):
+        return self.template
+
+# Create the standard error view template
+StandardErrorViewTemplate = ErrorViewTemplateFactory(
+    os.path.join(os.path.dirname(z3c.form.__file__), 'error.pt'), 'text/html')
+zope.component.adapter(
+    interfaces.IErrorViewSnippet, None)(StandardErrorViewTemplate)
+zope.interface.implementer(IPageTemplate)(StandardErrorViewTemplate)


Property changes on: z3c.form/trunk/src/z3c/form/error.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/error.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/error.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/error.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,168 @@
+===========
+Error Views
+===========
+
+Error views are looked up every time a validation error occurs during data
+extraction and/or validation. Unfortunately, it was often hard to adjust error
+messages based on specific situations. The error view implementation in this
+package is designed to provide several high-level features that make
+customizing error messages easier.
+
+  >>> from z3c.form import error
+
+
+Creating and Displaying the Default Error View
+----------------------------------------------
+
+Let's create an error view message for the ``TooSmall`` validation error:
+
+  >>> from zope.schema.interfaces import TooSmall, TooBig
+  >>> from z3c.form.testing import TestRequest
+
+  >>> view = error.ErrorViewSnippet(
+  ...     TooSmall(), TestRequest(), None, None, None, None)
+  >>> view
+  <ErrorViewSnippet for TooSmall>
+
+The discriminators for an error view are as follows: error, request, widget,
+field, form, and content. After updating the view, a test message is available:
+
+  >>> view.update()
+  >>> view.message
+  u'Value is too small'
+
+And after registering a template for the error view, we can also render the
+view:
+
+  >>> import os
+  >>> filename = os.path.join(os.path.dirname(error.__file__), 'error.pt')
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form import interfaces
+
+  >>> zope.component.provideAdapter(
+  ...     error.ErrorViewTemplateFactory(filename, 'text/html'),
+  ...     (interfaces.IErrorViewSnippet, None), IPageTemplate)
+
+  >>> print view.render()
+  <div class="error">Value is too small</div>
+
+
+Customizing Error Messages
+--------------------------
+
+As you can imagine, writing new error views for every scenario can be very
+tedious, especially if you only want to provide a specific error *message*. So
+let's create somewhat more interesting setup:
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IPerson(zope.interface.Interface):
+  ...     name = zope.schema.TextLine(title=u'Name')
+  ...     age = zope.schema.Int(
+  ...         title=u'Age',
+  ...         min=0)
+
+You must agree, that the follwoing message is pretty dull when entering a
+negative age:
+
+  >>> print view.render()
+  <div class="error">Value is too small</div>
+
+So let's register a better message for this particular situation:
+
+  >>> NegativeAgeMessage = error.ErrorViewMessage(
+  ...     u'A negative age is not sensible.',
+  ...     error=TooSmall, field=IPerson['age'])
+
+  >>> zope.component.provideAdapter(NegativeAgeMessage, name='message')
+
+The created object is a common attribute value for the error view message. The
+discriminators are the same as for the error view itself. Now we create an
+error view message for ``TooSmall`` of the age field:
+
+  >>> view = error.ErrorViewSnippet(
+  ...     TooSmall(), TestRequest(), None, IPerson['age'], None, None)
+
+  >>> view.update()
+  >>> print view.render()
+  <div class="error">A negative age is not sensible.</div>
+
+Much better, isn't it?
+
+
+Registering Custom Error Views
+------------------------------
+
+Even though message attribute values will solve most of our customization
+needs, sometimes one wishes to register a custom view to have more complex
+views. In this example we wish to register a custom error message:
+
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import tests
+
+  >>> class NegativeAgeView(error.ErrorViewSnippet):
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'custom_error.pt', os.path.dirname(tests.__file__))
+
+We now need to assert the special discriminators specific to this view:
+
+  >>> error.ErrorViewDiscriminators(
+  ...     NegativeAgeView, error=TooSmall, field=IPerson['age'])
+
+After registering the new and default error view, ...
+
+  >>> zope.component.provideAdapter(NegativeAgeView)
+  >>> zope.component.provideAdapter(error.ErrorViewSnippet)
+
+we can now make use of it, but only for this particular field and error:
+
+  >>> zope.component.getMultiAdapter(
+  ...     (TooSmall(), TestRequest(), None, IPerson['age'], None, None),
+  ...     interfaces.IErrorViewSnippet)
+  <NegativeAgeView for TooSmall>
+
+Other combinations will return the default screen instead:
+
+  >>> zope.component.getMultiAdapter(
+  ...     (TooBig(), TestRequest(), None, IPerson['age'], None, None),
+  ...     interfaces.IErrorViewSnippet)
+  <ErrorViewSnippet for TooBig>
+
+  >>> zope.component.getMultiAdapter(
+  ...     (TooSmall(), TestRequest(), None, IPerson['name'], None, None),
+  ...     interfaces.IErrorViewSnippet)
+  <ErrorViewSnippet for TooSmall>
+
+
+Value Error View Snippets
+-------------------------
+
+In the previous examples we have always worked with the view of the validation
+error. Since data managers can also return value errors, there is also an
+error view for them:
+
+  >>> valueError = ValueError(2)
+  >>> errorView = error.ValueErrorViewSnippet(
+  ...     valueError, TestRequest(), None, None, None, None)
+
+It uses the same template:
+
+  >>> errorView.update()
+  >>> print errorView.render()
+  <div class="error">The system could not process the given value.</div>
+
+Unfortunately, we cannot make use of the original string representation of the
+value error, since it cannot be localized well enough. Thus we provide our own
+message. Of course, the message can be overridden:
+
+  >>> CustomMessage = error.ErrorViewMessage(
+  ...     u'The entered value is not valid.', error=ValueError)
+  >>> zope.component.provideAdapter(CustomMessage, name='message')
+
+Let's now render the snippet again:
+
+  >>> errorView.update()
+  >>> print errorView.render()
+  <div class="error">The entered value is not valid.</div>


Property changes on: z3c.form/trunk/src/z3c/form/error.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/field.py
===================================================================
--- z3c.form/trunk/src/z3c/form/field.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/field.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,244 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Field Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.location
+import zope.schema.interfaces
+
+from z3c.form import interfaces, util
+
+
+def _initkw(keepReadOnly=(), omitReadOnly=False, **defaults):
+    return keepReadOnly, omitReadOnly, defaults
+
+
+class WidgetFactories(dict):
+
+    def __init__(self):
+        super(WidgetFactories, self).__init__()
+        self.default = None
+
+    def __getitem__(self, key):
+        if key not in self and self.default:
+            return self.default
+        return super(WidgetFactories, self).__getitem__(key)
+
+    def get(self, key, default=None):
+        if key not in self and self.default:
+            return self.default
+        return super(WidgetFactories, self).get(key, default)
+
+
+class WidgetFactoryProperty(object):
+
+    def __get__(self, inst, klass):
+        if not hasattr(inst, '_widgetFactories'):
+            inst._widgetFactories = WidgetFactories()
+        return inst._widgetFactories
+
+    def __set__(self, inst, value):
+        if not hasattr(inst, '_widgetFactories'):
+            inst._widgetFactories = WidgetFactories()
+        inst._widgetFactories.default = value
+
+
+class Field(object):
+    """Field implementation."""
+    zope.interface.implements(interfaces.IField)
+
+    widgetFactory = WidgetFactoryProperty()
+
+    def __init__(self, field, name=None, prefix='', mode=None, interface=None):
+        self.field = field
+        if name is None:
+            name = field.__name__
+        assert name
+        self.__name__ = util.expandPrefix(prefix) + name
+        self.prefix = prefix
+        self.mode = mode
+        if interface is None:
+            interface = field.interface
+        self.interface = interface
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.__name__)
+
+
+class Fields(util.SelectionManager):
+    """Field manager."""
+    zope.interface.implements(interfaces.IFields)
+    managerInterface = interfaces.IFields
+
+    def __init__(self, *args, **kw):
+        keepReadOnly, omitReadOnly, defaults = _initkw(**kw)
+
+        fields = []
+        for arg in args:
+            if isinstance(arg, zope.interface.interface.InterfaceClass):
+                for name, field in zope.schema.getFieldsInOrder(arg):
+                    fields.append((name, field, arg))
+
+            elif zope.schema.interfaces.IField.providedBy(arg):
+                name = arg.__name__
+                if not name:
+                        raise ValueError("Field has no name")
+                fields.append((name, arg, arg.interface))
+
+            elif self.managerInterface.providedBy(arg):
+                for form_field in arg.values():
+                    fields.append(
+                        (form_field.__name__, form_field, form_field.interface))
+
+            elif isinstance(arg, Field):
+                fields.append((arg.__name__, arg, arg.interface))
+
+            else:
+                raise TypeError("Unrecognized argument type", arg)
+
+        self._data_keys = []
+        self._data_values = []
+        self._data = {}
+        for name, field, iface in fields:
+            if isinstance(field, Field):
+                form_field = field
+            else:
+                if field.readonly:
+                    if omitReadOnly and (name not in keepReadOnly):
+                        continue
+                customDefaults = defaults.copy()
+                if iface is not None:
+                    customDefaults['interface'] = iface
+                form_field = Field(field, **customDefaults)
+                name = form_field.__name__
+
+            if name in self._data:
+                raise ValueError("Duplicate name", name)
+
+            self._data_values.append(form_field)
+            self._data_keys.append(name)
+            self._data[name] = form_field
+
+
+class FieldWidgets(util.Manager):
+    """Widget manager for IFieldWidget."""
+
+    zope.component.adapts(
+        interfaces.IFieldsForm, interfaces.IFormLayer, zope.interface.Interface)
+    zope.interface.implementsOnly(interfaces.IWidgets)
+
+    prefix = 'widgets.'
+    mode = interfaces.INPUT_MODE
+    errors = ()
+    ignoreContext = False
+    ignoreRequest = False
+
+    def __init__(self, form, request, content):
+        super(FieldWidgets, self).__init__()
+        self.form = form
+        self.request = request
+        self.content = content
+
+    def validate(self, data):
+        fields = self.form.fields.values()
+
+        # Step 1: Collect the data for the various schemas
+        schemaData = {}
+        for field in fields:
+            schema = field.interface
+            if schema is None:
+                continue
+
+            fieldData = schemaData.setdefault(schema, {})
+            if field.__name__ in data:
+                fieldData[field.field.__name__] = data[field.__name__]
+
+        # Step 2: Validate the individual schemas and collect errors
+        errors = ()
+        for schema, fieldData in schemaData.items():
+            validator = zope.component.getMultiAdapter(
+                (self.content, self.request, self.form, schema, self),
+                interfaces.IManagerValidator)
+            errors += validator.validate(fieldData)
+
+        return errors
+
+    def update(self):
+        """See interfaces.IWidgets"""
+        # Create a unique prefix
+        prefix = util.expandPrefix(self.form.prefix)
+        prefix += util.expandPrefix(self.prefix)
+        # Walk through each field, making a widget out of it
+        for field in self.form.fields.values():
+            # Step 1: Get the widget for the given field.
+            factory = field.widgetFactory.get(self.mode)
+            if factory is not None:
+                widget = factory(field.field, self.request)
+            else:
+                widget = zope.component.getMultiAdapter(
+                    (field.field, self.request), interfaces.IFieldWidget)
+            # Step 2: Set the prefix for the widget
+            shortName = field.__name__
+            widget.name = prefix + shortName
+            widget.id = prefix + shortName
+            # Step 3: Set the context
+            widget.context = self.content
+            zope.interface.alsoProvides(widget, interfaces.IContextAware)
+            # Step 4: Set the form
+            widget.form = self.form
+            zope.interface.alsoProvides(widget, interfaces.IFormAware)
+            # Step 5: Set some variables
+            widget.ignoreContext = self.ignoreContext
+            widget.ignoreRequest = self.ignoreRequest
+            # Step 6: Set the mode of the widget
+            widget.mode = self.mode
+            # Step 7: Update the widget
+            widget.update()
+            # Step 8: Add the widget to the manager
+            self._data_keys.append(shortName)
+            self._data_values.append(widget)
+            self._data[shortName] = widget
+            zope.location.locate(widget, self, shortName)
+
+    def extract(self):
+        """See interfaces.IWidgets"""
+        data = {}
+        self.errors = ()
+        for name, widget in self.items():
+            raw = widget.extract(widget.field.missing_value)
+            try:
+                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.schema.ValidationError, ValueError), error:
+                view = zope.component.getMultiAdapter(
+                    (error, self.request, widget, widget.field,
+                     self.form, self.content), interfaces.IErrorViewSnippet)
+                view.update()
+                widget.error = view
+                self.errors += (view,)
+            else:
+                name = widget.__name__
+                data[name] = value
+        self.errors += self.validate(data)
+        return data, self.errors


Property changes on: z3c.form/trunk/src/z3c/form/field.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/field.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/field.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/field.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,593 @@
+==============
+Field Managers
+==============
+
+One of the features in ``zope.formlib`` that works really well is the syntax
+used to define the contents of the form. The formlib uses form fields, to
+describe how the form should be put together. Since we liked this way of
+working, this package offers this feature as well in a very similar way.
+
+A field manager organizes all fields to be displayed within a form. Each field
+is associated with additional meta-data. The simplest way to create a field
+manager is to specify the schema from which to extract all fields.
+
+Thus, the first step is to create a schema:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IPerson(zope.interface.Interface):
+  ...      id = zope.schema.Int(
+  ...          title=u'Id',
+  ...          readonly=True)
+  ...
+  ...      name = zope.schema.TextLine(
+  ...          title=u'Name')
+  ...
+  ...      country = zope.schema.Choice(
+  ...          title=u'Country',
+  ...          values=(u'Germany', u'Switzerland', u'USA'),
+  ...          required=False)
+
+We can now create the field manager:
+
+  >>> from z3c.form import field
+  >>> manager = field.Fields(IPerson)
+
+Like all managers in this package, the enumerable mapping API is provided:
+
+  >>> manager['id']
+  <Field 'id'>
+  >>> manager['unknown']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'unknown'
+
+  >>> manager.get('id')
+  <Field 'id'>
+  >>> manager.get('unknown', 'default')
+  'default'
+
+  >>> 'id' in manager
+  True
+  >>> 'unknown' in manager
+  False
+
+  >>> manager.keys()
+  ['id', 'name', 'country']
+
+  >>> [key for key in manager]
+  ['id', 'name', 'country']
+
+  >>> manager.values()
+  [<Field 'id'>, <Field 'name'>, <Field 'country'>]
+
+  >>> manager.items()
+  [('id', <Field 'id'>),
+   ('name', <Field 'name'>),
+   ('country', <Field 'country'>)]
+
+  >>> len(manager)
+  3
+
+You can also select the fields that you would like to have:
+
+  >>> manager = manager.select('name', 'country')
+  >>> manager.keys()
+  ['name', 'country']
+
+Changing the order is simply a matter of changing the selction order:
+
+  >>> manager = manager.select('country', 'name')
+  >>> manager.keys()
+  ['country', 'name']
+
+Sometimes it is easier to simply omit a set of fields instead of selecting all
+the ones you want:
+
+  >>> manager = field.Fields(IPerson)
+  >>> manager = manager.omit('id')
+  >>> manager.keys()
+  ['name', 'country']
+
+You can also add two field managers together:
+
+  >>> manager2 = field.Fields(IPerson).select('id')
+  >>> (manager + manager2).keys()
+  ['name', 'country', 'id']
+
+But adding anything else to a field manager is not well defined:
+
+  >>> manager + 1
+  Traceback (most recent call last):
+  ...
+  TypeError: unsupported operand type(s) for +: 'Fields' and 'int'
+
+You can also not make any additions that would cause a name conflict:
+
+  >>> manager + manager
+  Traceback (most recent call last):
+  ...
+  ValueError: ('Duplicate name', 'name')
+
+When creating a new form derived from another, you often want to keep existing
+fields and add new ones. In order no to change the super-form class, you need
+to copy the field manager:
+
+  >>> manager.keys()
+  ['name', 'country']
+  >>> manager.copy().keys()
+  ['name', 'country']
+
+
+More on the Constructor
+-----------------------
+
+But the constructor does not only accept schemas to be passed in. One can also
+just pass in schema fields:
+
+  >>> field.Fields(IPerson['name']).keys()
+  ['name']
+
+However, the schema field has to have a name:
+
+  >>> email = zope.schema.TextLine(title=u'E-Mail')
+  >>> field.Fields(email)
+  Traceback (most recent call last):
+  ...
+  ValueError: Field has no name
+
+Adding a name helps:
+
+  >>> email.__name__ = 'email'
+  >>> field.Fields(email).keys()
+  ['email']
+
+Or you can just pass in other field managers, which is the feature the add
+mechanism uses:
+
+  >>> field.Fields(manager).keys()
+  ['name', 'country']
+
+Last, but not least, the constructor also accepts form fields, which is used
+by ``select()`` and ``omit()``:
+
+  >>> field.Fields(manager['name'], manager2['id']).keys()
+  ['name', 'id']
+
+If the constructor does not recognize any of the types above, it raises a
+``TypeError`` exception:
+
+  >>> field.Fields(object())
+  Traceback (most recent call last):
+  ...
+  TypeError: ('Unrecognized argument type', <object object at ...>)
+
+Additionally, you can specify several keyword arguments in the field manager
+constructor that are used to setup the fields:
+
+* ``omitReadOnly``
+
+  When set to ``True`` all read-only fields are omitted.
+
+    >>> field.Fields(IPerson, omitReadOnly=True).keys()
+    ['name', 'country']
+
+* ``keepReadOnly``
+
+  Sometimes you want to keep a particular read-only field around, even though
+  in general you want to omit them. In this case you can specify the fields to
+  keep:
+
+    >>> field.Fields(
+    ...     IPerson, omitReadOnly=True, keepReadOnly=('id',)).keys()
+    ['id', 'name', 'country']
+
+* ``prefix``
+
+  Sets the prefix of the fields. This argument is passed on to each field.
+
+    >>> manager = field.Fields(IPerson, prefix='myform.')
+    >>> manager['myform.name']
+    <Field 'myform.name'>
+
+
+* ``interface``
+
+  Usually the interface is inferred from the field itself. The interface is
+  used to determine whether an adapter must be looked up for a given
+  context.
+
+  But sometimes fields are generated in isolation to an interface or the
+  interface of the field is not the one you want. In this case you can specify
+  the interface:
+
+    >>> class IMyPerson(IPerson):
+    ...     pass
+
+    >>> manager = field.Fields(email, interface=IMyPerson)
+    >>> manager['email'].interface
+    <InterfaceClass __builtin__.IMyPerson>
+
+* ``mode``
+
+  The mode in which the widget will be rendered. By default there are two
+  available, "input" and "display". When mode is not specified, "input" is
+  chosen.
+
+    >>> from z3c.form import interfaces
+    >>> manager = field.Fields(IPerson, mode=interfaces.DISPLAY_MODE)
+    >>> manager['country'].mode
+    'display'
+
+
+Fields Widget Manager
+---------------------
+
+When a form (or any other widget-using view) is updated, one of the tasks is
+to create the widgets. Traditionally, generating the widgets involved looking
+at the form fields (or similar) of a form and generating the widgets using the
+information of those specifications. This solution is good for the common
+(about 85%) use cases, since it makes writing new forms very simple and allows
+a lot of control at a class-definition level.
+
+It has, however, its limitations. It does not, for example, allow for
+customization without rewriting a form. This can range from omitting fields on
+a particular form to generically adding a new widget to the form, such as an
+"object name" button on add forms. This package solves this issue by providing
+a widget manager, which is responsible providing the widgets for a particular
+view.
+
+The default widget manager for forms is able to look at a form's field
+definitions and create widgets for them. Thus, let's create a schema first:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class LastNameTooShort(zope.schema.interfaces.ValidationError):
+  ...     """The last name is too short."""
+
+  >>> class IPerson(zope.interface.Interface):
+  ...     lastName = zope.schema.TextLine(
+  ...         title=u'Last Name',
+  ...         description=u"The person's last name.",
+  ...         default=u'',
+  ...         required=True)
+  ...
+  ...     firstName = zope.schema.TextLine(
+  ...         title=u'First Name',
+  ...         description=u"The person's first name.",
+  ...         default=u'-- unknown --',
+  ...         required=False)
+  ...
+  ...     @zope.interface.invariant
+  ...     def twiceAsLong(person):
+  ...         if len(person.lastName) >= 2 * len(person.firstName):
+  ...             raise LastNameTooShort()
+
+Next we need a form that specifies the fields to be added:
+
+  >>> from z3c.form import field
+
+  >>> class AddPerson(object):
+  ...     prefix = 'form.'
+  ...     fields = field.Fields(IPerson)
+  >>> addPerson = AddPerson()
+
+For more details on how to define fields within a form, see ``field.txt``. We
+can now create the fields widget manager. It's discriminators are the form for
+which the widgets are created, the request, and the context that is being
+manipulated. Since we are developing an add-form the context is ``None``.
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> context = object()
+
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.ignoreContext = True
+
+The main resposibility of the manager is to provide the ``IEnumerableMapping``
+interface and an ``update()`` method. Initially the mapping, going from widget
+id to widget value, is empty:
+
+  >>> from zope.interface.common.mapping import IEnumerableMapping
+  >>> IEnumerableMapping.providedBy(manager)
+  True
+
+  >>> manager.keys()
+  []
+
+Only by "updating" the manager, the widgets will become available. But before
+we can use the update method we have to register the ``IFieldWidget`` adapter
+for the ``ITextLine`` field:
+
+  >>> from z3c.form import interfaces, widget
+
+  >>> @zope.component.adapter(zope.schema.TextLine, TestRequest)
+  ... @zope.interface.implementer(interfaces.IFieldWidget)
+  ... def TextFieldWidget(field, request):
+  ...     return widget.FieldWidget(field, widget.Widget(request))
+
+  >>> zope.component.provideAdapter(TextFieldWidget)
+
+  >>> from z3c.form import converter
+  >>> zope.component.provideAdapter(converter.FieldDataConverter)
+  >>> zope.component.provideAdapter(converter.FieldWidgetDataConverter)
+
+  >>> manager.update()
+
+Other than usual mappings in Python, the widget manager's widgets are always
+in a particular order:
+
+  >>> manager.keys()
+  ['lastName', 'firstName']
+
+Let's make sure that all enumerable mapping functions work correctly:
+
+  >>> manager['lastName']
+  <Widget 'form.widgets.lastName'>
+
+  >>> manager['unknown']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'unknown'
+
+  >>> manager.get('lastName')
+  <Widget 'form.widgets.lastName'>
+
+  >>> manager.get('unknown', 'default')
+  'default'
+
+  >>> 'lastName' in manager
+  True
+  >>> 'unknown' in manager
+  False
+
+  >>> [key for key in manager]
+  ['lastName', 'firstName']
+
+  >>> manager.values()
+  [<Widget 'form.widgets.lastName'>,
+   <Widget 'form.widgets.firstName'>]
+
+  >>> manager.items()
+  [('lastName', <Widget 'form.widgets.lastName'>),
+   ('firstName', <Widget 'form.widgets.firstName'>)]
+
+  >>> len(manager)
+  2
+
+When a widget is added to the widget manager, it is located:
+
+  >>> lname = manager['lastName']
+
+  >>> lname.__name__
+  'lastName'
+  >>> lname.__parent__
+  <z3c.form.field.FieldWidgets object at ...>
+
+All widgets created by this widget manager are context aware:
+
+  >>> interfaces.IContextAware.providedBy(lname)
+  True
+  >>> lname.context is context
+  True
+
+All widgets will also assume the mode of the manager:
+
+   >>> manager['lastName'].mode
+   'input'
+
+  >>> manager.mode = interfaces.DISPLAY_MODE
+  >>> manager.update()
+
+  >>> manager['lastName'].mode
+  'display'
+
+Besides managing widgets, the widget manager also controls the process of
+extracting and validating extracted data. Let's start with the validation
+first, which only validates the data as a whole, assuming each individual
+value being already validated.
+
+Before we can use the method, we have to register a "manager validator":
+
+  >>> from z3c.form import validator
+  >>> zope.component.provideAdapter(validator.InvariantsValidator)
+
+  >>> manager.validate(
+  ...     {'firstName': u'Stephan', 'lastName': u'Richter'})
+  ()
+
+The result of this method is a tuple of errors that occured during the
+validation. An empty tuple means the validation succeeded. Let's now make the
+validation fail:
+
+  >>> errors = manager.validate(
+  ...     {'firstName': u'Stephan', 'lastName': u'Richter-Richter'})
+
+  >>> [error.doc() for error in errors]
+  ['The last name is too short.']
+
+A special case occurs when the schema fields are not associated with an
+interface:
+
+  >>> name = zope.schema.TextLine(__name__='name')
+
+  >>> class PersonForm(object):
+  ...     prefix = 'form.'
+  ...     fields = field.Fields(name)
+  >>> personForm = PersonForm()
+
+  >>> manager = field.FieldWidgets(personForm, request, context)
+
+In this case, the widget manager's ``validate()`` method should simply ignore
+the field and not try to look up any invariants:
+
+  >>> manager.validate({'name': u'Stephan'})
+  ()
+
+Let's now have a look at the widget manager's ``extract()``, which returns a
+data dictionary and the collection of errors. Before we can validate, we have
+to register a validator for the widget:
+
+  >>> zope.component.provideAdapter(validator.SimpleFieldValidator)
+
+When all goes well, the data dictionary is complete and the error collection
+empty:
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.lastName': u'Richter'})
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.extract()
+  ({'lastName': u'Richter', 'firstName': u'Stephan'}, ())
+
+Since all errors are immediately converted to error view snippets, we have to
+provide the adapter from a validation error to an error view snippet first:
+
+  >>> from z3c.form import error
+  >>> zope.component.provideAdapter(error.ErrorViewSnippet)
+
+Let's now cause a widget-level error by not submitting the required last
+name:
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan'})
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> manager.extract()
+  ({'firstName': u'Stephan'}, (<ErrorViewSnippet for RequiredMissing>,))
+
+Finally, let's ensure that invariant failures are also caught:
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.lastName': u'Richter-Richter'})
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.ignoreContext = True
+  >>> manager.update()
+  >>> data, errors = manager.extract()
+  >>> errors[0].doc()
+  'The last name is too short.'
+
+And that's all.
+
+
+Fields -- Custom Widget Factories
+---------------------------------
+
+It is possible to declare custom widgets for fields within the field's
+declaration.
+
+Let's have a look at the default form first. Initially, the standard
+registered widgets are used:
+
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.update()
+
+  >>> manager['firstName']
+  <Widget 'form.widgets.firstName'>
+
+Now we would like to have our own custom input widget:
+
+  >>> class CustomInputWidget(widget.Widget):
+  ...     pass
+
+  >>> def CustomInputWidgetFactory(field, request):
+  ...     return widget.FieldWidget(field, CustomInputWidget(request))
+
+It can be simply assigned as follows:
+
+  >>> addPerson.fields['firstName'].widgetFactory = CustomInputWidgetFactory
+
+Now this widget should be used instead of the registered default one:
+
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.update()
+  >>> manager['firstName']
+  <CustomInputWidget 'form.widgets.firstName'>
+
+In the background the widget factory assignment really just registered the
+default factory for in the ``WidgetFactories`` object, which manages the
+custom widgets for all modes. Now all modes show this input widget:
+
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.mode = interfaces.DISPLAY_MODE
+  >>> manager.update()
+  >>> manager['firstName']
+  <CustomInputWidget 'form.widgets.firstName'>
+
+However, we can also register a specific widget for the display mode:
+
+  >>> class CustomDisplayWidget(widget.Widget):
+  ...     pass
+
+  >>> def CustomDisplayWidgetFactory(field, request):
+  ...     return widget.FieldWidget(field, CustomDisplayWidget(request))
+
+  >>> addPerson.fields['firstName']\
+  ...     .widgetFactory[interfaces.DISPLAY_MODE] = CustomDisplayWidgetFactory
+
+Now the display mode should produce the custom display widget, ...
+
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.mode = interfaces.DISPLAY_MODE
+  >>> manager.update()
+  >>> manager['firstName']
+  <CustomDisplayWidget 'form.widgets.firstName'>
+
+... while the input mode still shows the default custom input widget:
+
+  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager.mode = interfaces.INPUT_MODE
+  >>> manager.update()
+  >>> manager['firstName']
+  <CustomInputWidget 'form.widgets.firstName'>
+
+The widgets factories component,
+
+  >>> factories = addPerson.fields['firstName'].widgetFactory
+  >>> factories
+  {'display': <function CustomDisplayWidgetFactory at ...>}
+
+is pretty much a standard dictionary that also manages a default value:
+
+  >>> factories.default
+  <function CustomInputWidgetFactory at ...>
+
+When getting a value for a key, if the key is not found, the default is
+returned:
+
+  >>> factories.keys()
+  ['display']
+
+  >>> factories[interfaces.DISPLAY_MODE]
+  <function CustomDisplayWidgetFactory at ...>
+  >>> factories[interfaces.INPUT_MODE]
+  <function CustomInputWidgetFactory at ...>
+
+  >>> factories.get(interfaces.DISPLAY_MODE)
+  <function CustomDisplayWidgetFactory at ...>
+  >>> factories.get(interfaces.INPUT_MODE)
+  <function CustomInputWidgetFactory at ...>
+
+If no default is specified,
+
+  >>> factories.default = None
+
+then the dictionary behaves as usual:
+
+  >>> factories[interfaces.DISPLAY_MODE]
+  <function CustomDisplayWidgetFactory at ...>
+  >>> factories[interfaces.INPUT_MODE]
+  Traceback (most recent call last):
+  ...
+  KeyError: 'input'
+
+  >>> factories.get(interfaces.DISPLAY_MODE)
+  <function CustomDisplayWidgetFactory at ...>
+  >>> factories.get(interfaces.INPUT_MODE)
+


Property changes on: z3c.form/trunk/src/z3c/form/field.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/form-graph.dot
===================================================================
--- z3c.form/trunk/src/z3c/form/form-graph.dot	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/form-graph.dot	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,47 @@
+digraph FormComponents {
+  nodesep=0.5;
+  ranksep=0.9;
+  Fields -> Widgets [
+      color="red", label="creates", fontcolor="red"];
+  Fields -> Field [
+      color="blue", label="contains", fontcolor="blue"];
+  Widgets -> Widget [
+      color="blue", label="contains", fontcolor="blue"];
+  Buttons -> Button [
+      color="blue", label="contains", fontcolor="blue"];
+  Actions -> Action [
+      color="blue", label="contains", fontcolor="blue"];
+  Handlers -> Handler [
+      color="blue", label="contains", fontcolor="blue"];
+
+  Form -> Fields [
+      label="fields", fontname="Courier", fontsize="11"];
+  Form -> Widgets [
+      label="widgets", fontname="Courier", fontsize="11"];
+  Form -> Buttons [
+      label="buttons", fontname="Courier", fontsize="11"];
+  Form -> Actions [
+      label="actions", fontname="Courier", fontsize="11"];
+  Form -> Handlers [
+      label="handlers", fontname="Courier", fontsize="11"];
+
+  Buttons -> Actions [
+      color="red", label="creates", fontcolor="red"];
+  Actions -> Handler [
+      color="red", label="uses", fontcolor="red"];
+
+  EditForm -> Form [arrowhead="onormal"];
+  AddForm -> Form [arrowhead="onormal"];
+
+  // Reverse the order, so that layout is still sane
+  Button -> Action [
+      label="field", fontname="Courier", fontsize="11",
+      arrowhead="none", arrowtail="normal"];
+  Field -> Widget [
+      label="field\n[Field.field]", fontname="Courier", fontsize="11",
+      arrowhead="none", arrowtail="normal"];
+
+  {rank=same; Fields; Widgets; Buttons; Actions; Handlers;}
+  {rank=same; Field; Widget; Action; Button; Handler;}
+
+}

Added: z3c.form/trunk/src/z3c/form/form-graph.png
===================================================================
(Binary files differ)


Property changes on: z3c.form/trunk/src/z3c/form/form-graph.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: z3c.form/trunk/src/z3c/form/form-graph.ps
===================================================================
--- z3c.form/trunk/src/z3c/form/form-graph.ps	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/form-graph.ps	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,814 @@
+%!PS-Adobe-2.0
+%%Creator: dot version 2.8 (Sun Aug 13 01:37:46 UTC 2006)
+%%For: (srichter) Stephan Richter
+%%Title: FormComponents
+%%Pages: (atend)
+%%BoundingBox: 36 36 690 418
+%%EndComments
+save
+%%BeginProlog
+/DotDict 200 dict def
+DotDict begin
+
+/setupLatin1 {
+mark
+/EncodingVector 256 array def
+ EncodingVector 0
+
+ISOLatin1Encoding 0 255 getinterval putinterval
+EncodingVector 45 /hyphen put
+
+% Set up ISO Latin 1 character encoding
+/starnetISO {
+        dup dup findfont dup length dict begin
+        { 1 index /FID ne { def }{ pop pop } ifelse
+        } forall
+        /Encoding EncodingVector def
+        currentdict end definefont
+} def
+/Times-Roman starnetISO def
+/Times-Italic starnetISO def
+/Times-Bold starnetISO def
+/Times-BoldItalic starnetISO def
+/Helvetica starnetISO def
+/Helvetica-Oblique starnetISO def
+/Helvetica-Bold starnetISO def
+/Helvetica-BoldOblique starnetISO def
+/Courier starnetISO def
+/Courier-Oblique starnetISO def
+/Courier-Bold starnetISO def
+/Courier-BoldOblique starnetISO def
+cleartomark
+} bind def
+
+%%BeginResource: procset graphviz 0 0
+/coord-font-family /Times-Roman def
+/default-font-family /Times-Roman def
+/coordfont coord-font-family findfont 8 scalefont def
+
+/InvScaleFactor 1.0 def
+/set_scale {
+	dup 1 exch div /InvScaleFactor exch def
+	dup scale
+} bind def
+
+% styles
+/solid { [] 0 setdash } bind def
+/dashed { [9 InvScaleFactor mul dup ] 0 setdash } bind def
+/dotted { [1 InvScaleFactor mul 6 InvScaleFactor mul] 0 setdash } bind def
+/invis {/fill {newpath} def /stroke {newpath} def /show {pop newpath} def} bind def
+/bold { 2 setlinewidth } bind def
+/filled { } bind def
+/unfilled { } bind def
+/rounded { } bind def
+/diagonals { } bind def
+
+% hooks for setting color 
+/nodecolor { sethsbcolor } bind def
+/edgecolor { sethsbcolor } bind def
+/graphcolor { sethsbcolor } bind def
+/nopcolor {pop pop pop} bind def
+
+/beginpage {	% i j npages
+	/npages exch def
+	/j exch def
+	/i exch def
+	/str 10 string def
+	npages 1 gt {
+		gsave
+			coordfont setfont
+			0 0 moveto
+			(\() show i str cvs show (,) show j str cvs show (\)) show
+		grestore
+	} if
+} bind def
+
+/set_font {
+	findfont exch
+	scalefont setfont
+} def
+
+% draw aligned label in bounding box aligned to current point
+/alignedtext {			% width adj text
+	/text exch def
+	/adj exch def
+	/width exch def
+	gsave
+		width 0 gt {
+			text stringwidth pop adj mul 0 rmoveto
+		} if
+		[] 0 setdash
+		text show
+	grestore
+} def
+
+/boxprim {				% xcorner ycorner xsize ysize
+		4 2 roll
+		moveto
+		2 copy
+		exch 0 rlineto
+		0 exch rlineto
+		pop neg 0 rlineto
+		closepath
+} bind def
+
+/ellipse_path {
+	/ry exch def
+	/rx exch def
+	/y exch def
+	/x exch def
+	matrix currentmatrix
+	newpath
+	x y translate
+	rx ry scale
+	0 0 1 0 360 arc
+	setmatrix
+} bind def
+
+/endpage { showpage } bind def
+/showpage { } def
+
+/layercolorseq
+	[	% layer color sequence - darkest to lightest
+		[0 0 0]
+		[.2 .8 .8]
+		[.4 .8 .8]
+		[.6 .8 .8]
+		[.8 .8 .8]
+	]
+def
+
+/layerlen layercolorseq length def
+
+/setlayer {/maxlayer exch def /curlayer exch def
+	layercolorseq curlayer 1 sub layerlen mod get
+	aload pop sethsbcolor
+	/nodecolor {nopcolor} def
+	/edgecolor {nopcolor} def
+	/graphcolor {nopcolor} def
+} bind def
+
+/onlayer { curlayer ne {invis} if } def
+
+/onlayers {
+	/myupper exch def
+	/mylower exch def
+	curlayer mylower lt
+	curlayer myupper gt
+	or
+	{invis} if
+} def
+
+/curlayer 0 def
+
+%%EndResource
+%%EndProlog
+%%BeginSetup
+14 default-font-family set_font
+1 setmiterlimit
+% /arrowlength 10 def
+% /arrowwidth 5 def
+
+% make sure pdfmark is harmless for PS-interpreters other than Distiller
+/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse
+% make '<<' and '>>' safe on PS Level 1 devices
+/languagelevel where {pop languagelevel}{1} ifelse
+2 lt {
+    userdict (<<) cvn ([) cvn load put
+    userdict (>>) cvn ([) cvn load put
+} if
+
+%%EndSetup
+%%Page: 1 1
+%%PageBoundingBox: 36 36 690 418
+%%PageOrientation: Portrait
+gsave
+36 36 654 382 boxprim clip newpath
+36 36 translate
+0 0 1 beginpage
+1.0000 set_scale
+4 4 translate 0 rotate
+0.000 0.000 1.000 graphcolor
+0.000 0.000 1.000 graphcolor
+newpath -6 -6 moveto
+-6 380 lineto
+652 380 lineto
+652 -6 lineto
+closepath
+fill
+0.000 0.000 1.000 graphcolor
+newpath -6 -6 moveto
+-6 380 lineto
+652 380 lineto
+652 -6 lineto
+closepath
+stroke
+0.000 0.000 0.000 graphcolor
+14.00 /Times-Roman set_font
+% Fields
+gsave 10 dict begin
+49 136 30 18 ellipse_path
+stroke
+gsave 10 dict begin
+32 131 moveto
+(Fields)
+[7.44 3.84 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Widgets
+gsave 10 dict begin
+210 136 36 18 ellipse_path
+stroke
+gsave 10 dict begin
+186 131 moveto
+(Widgets)
+[12.96 3.84 6.96 6.72 6 3.84 5.52]
+xshow
+end grestore
+end grestore
+% Fields->Widgets
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 79 136 moveto
+103 136 136 136 163 136 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 163 140 moveto
+173 136 lineto
+163 133 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 163 140 moveto
+173 136 lineto
+163 133 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+109 139 moveto
+(creates)
+[6.24 4.8 6.24 6.24 3.84 6.24 5.52]
+xshow
+end grestore
+end grestore
+% Field
+gsave 10 dict begin
+27 18 27 18 ellipse_path
+stroke
+gsave 10 dict begin
+12 13 moveto
+(Field)
+[7.44 3.84 6.24 3.84 6.96]
+xshow
+end grestore
+end grestore
+% Fields->Field
+gsave 10 dict begin
+0.667 1.000 1.000 edgecolor
+newpath 46 118 moveto
+43 99 37 68 32 46 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.667 1.000 1.000 edgecolor
+newpath 35 45 moveto
+30 36 lineto
+29 46 lineto
+closepath
+fill
+0.667 1.000 1.000 edgecolor
+newpath 35 45 moveto
+30 36 lineto
+29 46 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+40 72 moveto
+(contains)
+[6.24 6.96 6.96 4.08 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Widget
+gsave 10 dict begin
+211 18 34 18 ellipse_path
+stroke
+gsave 10 dict begin
+190 13 moveto
+(Widget)
+[12.96 3.84 6.96 6.72 6 3.84]
+xshow
+end grestore
+end grestore
+% Widgets->Widget
+gsave 10 dict begin
+0.667 1.000 1.000 edgecolor
+newpath 210 118 moveto
+210 99 210 68 211 46 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.667 1.000 1.000 edgecolor
+newpath 215 46 moveto
+211 36 lineto
+208 46 lineto
+closepath
+fill
+0.667 1.000 1.000 edgecolor
+newpath 215 46 moveto
+211 36 lineto
+208 46 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+211 72 moveto
+(contains)
+[6.24 6.96 6.96 4.08 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Field->Widget
+newpath 65 18 moveto
+98 18 146 18 177 18 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 65 15 moveto
+55 18 lineto
+65 22 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 65 15 moveto
+55 18 lineto
+65 22 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+102 33 moveto
+(field)
+[6.72 6.72 6.72 6.72 6.72]
+xshow
+75 20 moveto
+([Field.field])
+[6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% Buttons
+gsave 10 dict begin
+317 136 35 18 ellipse_path
+stroke
+gsave 10 dict begin
+294 131 moveto
+(Buttons)
+[9.36 6.96 3.84 3.84 6.96 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Button
+gsave 10 dict begin
+314 18 33 18 ellipse_path
+stroke
+gsave 10 dict begin
+294 13 moveto
+(Button)
+[9.36 6.96 3.84 3.84 6.96 6.96]
+xshow
+end grestore
+end grestore
+% Buttons->Button
+gsave 10 dict begin
+0.667 1.000 1.000 edgecolor
+newpath 317 118 moveto
+316 99 315 68 315 46 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.667 1.000 1.000 edgecolor
+newpath 319 46 moveto
+315 36 lineto
+312 46 lineto
+closepath
+fill
+0.667 1.000 1.000 edgecolor
+newpath 319 46 moveto
+315 36 lineto
+312 46 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+317 72 moveto
+(contains)
+[6.24 6.96 6.96 4.08 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Actions
+gsave 10 dict begin
+463 136 35 18 ellipse_path
+stroke
+gsave 10 dict begin
+440 131 moveto
+(Actions)
+[9.6 6.24 3.84 3.84 6.96 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Buttons->Actions
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 353 136 moveto
+373 136 397 136 417 136 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 417 140 moveto
+427 136 lineto
+417 133 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 417 140 moveto
+427 136 lineto
+417 133 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+370 139 moveto
+(creates)
+[6.24 4.8 6.24 6.24 3.84 6.24 5.52]
+xshow
+end grestore
+end grestore
+% Action
+gsave 10 dict begin
+451 18 32 18 ellipse_path
+stroke
+gsave 10 dict begin
+431 13 moveto
+(Action)
+[9.6 6.24 3.84 3.84 6.96 6.96]
+xshow
+end grestore
+end grestore
+% Button->Action
+newpath 357 18 moveto
+377 18 400 18 418 18 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 357 15 moveto
+347 18 lineto
+357 22 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 357 15 moveto
+347 18 lineto
+357 22 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+365 20 moveto
+(field)
+[6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% Actions->Action
+gsave 10 dict begin
+0.667 1.000 1.000 edgecolor
+newpath 453 119 moveto
+448 109 443 97 440 85 curveto
+438 72 439 58 442 46 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.667 1.000 1.000 edgecolor
+newpath 445 47 moveto
+445 36 lineto
+439 45 lineto
+closepath
+fill
+0.667 1.000 1.000 edgecolor
+newpath 445 47 moveto
+445 36 lineto
+439 45 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+443 72 moveto
+(contains)
+[6.24 6.96 6.96 4.08 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Handler
+gsave 10 dict begin
+577 18 36 18 ellipse_path
+stroke
+gsave 10 dict begin
+553 13 moveto
+(Handler)
+[10.08 6.24 6.96 6.96 3.84 6.24 4.8]
+xshow
+end grestore
+end grestore
+% Actions->Handler
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 479 120 moveto
+499 99 532 65 554 41 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 556 44 moveto
+561 34 lineto
+551 39 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 556 44 moveto
+561 34 lineto
+551 39 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+528 72 moveto
+(uses)
+[6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% Handlers
+gsave 10 dict begin
+591 136 39 18 ellipse_path
+stroke
+gsave 10 dict begin
+565 131 moveto
+(Handlers)
+[10.08 6.24 6.96 6.96 3.84 6.24 4.8 5.52]
+xshow
+end grestore
+end grestore
+% Handlers->Handler
+gsave 10 dict begin
+0.667 1.000 1.000 edgecolor
+newpath 589 118 moveto
+586 99 583 68 580 46 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.667 1.000 1.000 edgecolor
+newpath 583 46 moveto
+579 36 lineto
+577 46 lineto
+closepath
+fill
+0.667 1.000 1.000 edgecolor
+newpath 583 46 moveto
+579 36 lineto
+577 46 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+586 72 moveto
+(contains)
+[6.24 6.96 6.96 4.08 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% Form
+gsave 10 dict begin
+317 252 28 18 ellipse_path
+stroke
+gsave 10 dict begin
+301 247 moveto
+(Form)
+[7.44 6.96 5.04 10.8]
+xshow
+end grestore
+end grestore
+% Form->Fields
+newpath 294 242 moveto
+246 221 138 175 83 151 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 85 148 moveto
+74 147 lineto
+82 154 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 85 148 moveto
+74 147 lineto
+82 154 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+200 190 moveto
+(fields)
+[6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% Form->Widgets
+newpath 300 238 moveto
+288 228 273 214 260 201 curveto
+249 189 237 174 228 161 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 231 159 moveto
+222 153 lineto
+225 163 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 231 159 moveto
+222 153 lineto
+225 163 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+263 190 moveto
+(widgets)
+[6.72 6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% Form->Buttons
+newpath 317 234 moveto
+317 215 317 186 317 164 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 321 164 moveto
+317 154 lineto
+314 164 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 321 164 moveto
+317 154 lineto
+314 164 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+317 190 moveto
+(buttons)
+[6.72 6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% Form->Actions
+newpath 335 238 moveto
+361 218 406 181 436 157 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 438 160 moveto
+444 151 lineto
+434 154 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 438 160 moveto
+444 151 lineto
+434 154 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+395 190 moveto
+(actions)
+[6.72 6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% Form->Handlers
+newpath 342 243 moveto
+369 233 414 216 452 201 curveto
+488 186 527 167 555 153 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 556 156 moveto
+564 149 lineto
+553 150 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 556 156 moveto
+564 149 lineto
+553 150 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+481 190 moveto
+(handlers)
+[6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% EditForm
+gsave 10 dict begin
+259 356 40 18 ellipse_path
+stroke
+gsave 10 dict begin
+232 351 moveto
+(EditForm)
+[8.64 6.96 3.84 3.84 7.44 6.96 5.04 10.8]
+xshow
+end grestore
+end grestore
+% EditForm->Form
+newpath 269 338 moveto
+278 322 292 297 302 278 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+newpath 305 279 moveto
+307 269 lineto
+299 276 lineto
+closepath
+stroke
+end grestore
+% AddForm
+gsave 10 dict begin
+375 356 40 18 ellipse_path
+stroke
+gsave 10 dict begin
+347 351 moveto
+(AddForm)
+[9.6 6.96 6.96 7.44 6.96 5.04 10.8]
+xshow
+end grestore
+end grestore
+% AddForm->Form
+newpath 365 338 moveto
+356 322 342 297 332 278 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+newpath 335 276 moveto
+327 269 lineto
+329 279 lineto
+closepath
+stroke
+end grestore
+endpage
+showpage
+grestore
+%%PageTrailer
+%%EndPage: 1
+%%Trailer
+%%Pages: 1
+end
+restore
+%%EOF

Added: z3c.form/trunk/src/z3c/form/form.py
===================================================================
--- z3c.form/trunk/src/z3c/form/form.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/form.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,224 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Form Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import sys
+import zope.interface
+import zope.component
+import zope.event
+import zope.lifecycleevent
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.publisher import browser
+from zope.formlib import form
+from zope.pagetemplate.interfaces import IPageTemplate
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.form import button, field, interfaces, util
+from z3c.form.i18n import MessageFactory as _
+
+
+def applyChanges(form, content, data):
+    changed = False
+    for name, field in form.fields.items():
+        dm = zope.component.getMultiAdapter(
+            (content, field.field), interfaces.IDataManager)
+        oldValue = dm.get()
+        # Only update the data, if it is different
+        if dm.get() != data[name]:
+            dm.set(data[name])
+            changed = True
+    return changed
+
+def extends(*args, **kwargs):
+    frame = sys._getframe(1)
+    f_locals = frame.f_locals
+    if not kwargs.get('ignoreFields', False):
+        f_locals['fields'] = field.Fields()
+        for arg in args:
+            f_locals['fields'] += getattr(arg, 'fields', field.Fields())
+    if not kwargs.get('ignoreHandlers', False):
+        f_locals['buttons'] = button.Buttons()
+        for arg in args:
+            f_locals['buttons'] += getattr(arg, 'buttons', button.Buttons())
+    if not kwargs.get('ignoreHandlers', False):
+        f_locals['handlers'] = button.Handlers()
+        for arg in args:
+            f_locals['handlers'] += getattr(arg, 'handlers', button.Handlers())
+
+
+class BaseForm(browser.BrowserPage):
+    """A base form."""
+    zope.interface.implements(interfaces.IForm,
+                              interfaces.IFieldsForm)
+
+    fields = field.Fields()
+
+    prefix = 'form.'
+    status = ''
+    template = None
+
+    def getContent(self):
+        return self.context
+
+    def updateWidgets(self):
+        self.widgets = zope.component.getMultiAdapter(
+            (self, self.request, self.getContent()), interfaces.IWidgets)
+        self.widgets.update()
+
+    def update(self):
+        self.updateWidgets()
+
+    def render(self):
+        # render content template
+        if self.template is None:
+            template = zope.component.getMultiAdapter((self, self.request),
+                IPageTemplate)
+            return template(self)
+        return self.template()
+
+
+class DisplayForm(BaseForm):
+
+    def updateWidgets(self):
+        self.widgets = zope.component.getMultiAdapter(
+            (self, self.request, self.getContent()), interfaces.IWidgets)
+        self.widgets.mode = interfaces.DISPLAY_MODE
+        self.widgets.ignoreRequest = True
+        self.widgets.update()
+
+
+class Form(BaseForm):
+    """The Form."""
+    zope.interface.implements(
+        interfaces.IInputForm, interfaces.IButtonForm, interfaces.IHandlerForm)
+
+    buttons = button.Buttons()
+
+    method = FieldProperty(interfaces.IInputForm['method'])
+    enctype = FieldProperty(interfaces.IInputForm['enctype'])
+    acceptCharset = FieldProperty(interfaces.IInputForm['acceptCharset'])
+    accept = FieldProperty(interfaces.IInputForm['accept'])
+
+    @property
+    def action(self):
+        """See interfaces.IInputForm"""
+        return self.request.getURL()
+
+    @property
+    def name(self):
+        """See interfaces.IInputForm"""
+        return self.prefix.strip('.')
+
+    id = name
+
+    def updateActions(self):
+        self.actions = zope.component.getMultiAdapter(
+            (self, self.request, self.getContent()), interfaces.IActions)
+        self.actions.update()
+
+    def update(self):
+        super(Form, self).update()
+        self.updateActions()
+        self.actions.execute()
+
+    def __call__(self):
+        self.update()
+        return self.render()
+
+
+class AddForm(Form):
+    """A field and button based add form."""
+    zope.interface.implements(interfaces.IAddForm)
+
+    _finishedAdd = False
+    formErrorsMessage = _('There were some errors.')
+
+    @button.buttonAndHandler(_('Add'), name='add')
+    def handleAdd(self, action):
+        data, errors = self.widgets.extract()
+        if errors:
+            self.status = self.formErrorsMessage
+            return
+        obj = self.create(data)
+        zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+        self.add(obj)
+        self._finishedAdd = True
+
+    def updateWidgets(self):
+        self.widgets = zope.component.getMultiAdapter(
+            (self, self.request, self.context), interfaces.IWidgets)
+        self.widgets.ignoreContext = True
+        self.widgets.update()
+
+    def create(self, data):
+        raise NotImplementedError
+
+    def add(self, object):
+        raise NotImplementedError
+
+    def nextURL(self):
+        raise NotImplementedError
+
+    def render(self):
+        if self._finishedAdd:
+            self.request.response.redirect(self.nextURL())
+            return ""
+        return super(AddForm, self).render()
+
+
+class EditForm(Form):
+    """A simple edit form with an apply button."""
+    zope.interface.implements(interfaces.IEditForm)
+
+    formErrorsMessage = _('There were some errors.')
+    successMessage = _('Data successfully updated.')
+    noChangesMessage = _('No changes were applied.')
+
+    def updateWidgets(self):
+        self.widgets = zope.component.getMultiAdapter(
+            (self, self.request, self.getContent()), interfaces.IWidgets)
+        self.widgets.update()
+
+    @button.buttonAndHandler(_('Apply'), name='apply')
+    def handleApply(self, action):
+        data, errors = self.widgets.extract()
+        if errors:
+            self.status = self.formErrorsMessage
+            return
+        content = self.getContent()
+        changed = applyChanges(self, content, data)
+        if changed:
+            zope.event.notify(
+                zope.lifecycleevent.ObjectModifiedEvent(content))
+            self.status = self.successMessage
+        else:
+            self.status = self.noChangesMessage
+
+
+class FormTemplateFactory(object):
+    """Form template factory."""
+
+    def __init__(self, filename, contentType='text/html', form=None,
+        request=None):
+        self.template = ViewPageTemplateFile(filename, content_type=contentType)
+        zope.component.adapter(
+            util.getSpecification(form),
+            util.getSpecification(request))(self)
+        zope.interface.implementer(IPageTemplate)(self)
+
+    def __call__(self, form, request):
+        return self.template


Property changes on: z3c.form/trunk/src/z3c/form/form.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/form.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/form.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/form.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,1088 @@
+=====
+Forms
+=====
+
+The purpose of this package, of course, is the development forms in a simple
+as possible way and still providing all the hooks to do customization at any
+level as required by our real-world use cases. Thus, once the system is setup
+with all its default registrations, it should be trivial to develop a new
+form.
+
+The strategy of this document is to provide the most common, and thus
+simplest, case first and then demonstrate the available customization
+options. In order to not overwhelm you with our set of well-chosen defaults,
+all the default component registrations have been made prior to doing those
+examples:
+
+  >>> from z3c.form import testing
+  >>> testing.setupFormDefaults()
+
+Before we can start writing forms, we must have the content to work with:
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IPerson(zope.interface.Interface):
+  ...
+  ...     name = zope.schema.TextLine(
+  ...         title=u'Name',
+  ...         required=True)
+  ...
+  ...     gender = zope.schema.Choice(
+  ...         title=u'Gender',
+  ...         values=('male', 'female'),
+  ...         required=False)
+  ...
+  ...     age = zope.schema.Int(
+  ...         title=u'Age',
+  ...         description=u"The person's age.",
+  ...         min=0,
+  ...         default=20,
+  ...         required=False)
+
+  >>> from zope.schema.fieldproperty import FieldProperty
+  >>> class Person(object):
+  ...     zope.interface.implements(IPerson)
+  ...     name = FieldProperty(IPerson['name'])
+  ...     gender = FieldProperty(IPerson['gender'])
+  ...     age = FieldProperty(IPerson['age'])
+  ...
+  ...     def __init__(self, name, gender=None, age=None):
+  ...         self.name = name
+  ...         if gender:
+  ...             self.gender = gender
+  ...         if age:
+  ...             self.age = age
+  ...
+  ...     def __repr__(self):
+  ...         return '<%s %r>' %(self.__class__.__name__, self.name)
+
+Okay, that should suffice for now.
+
+What's next? Well, first things first. Let's create an add form for the
+person. Since practice showed that the ``IAdding`` interface is overkill for
+most projects, the default add form of ``z3c.form`` requires you to define the
+creation and adding mechanism.
+
+__Note__:
+
+  If it is not done, ``NotImplementedError``'s are raised:
+
+    >>> from z3c.form.testing import TestRequest
+    >>> from z3c.form import form, field
+
+    >>> abstract = form.AddForm(None, TestRequest())
+
+    >>> abstract.create({})
+    Traceback (most recent call last):
+    ...
+    NotImplementedError
+
+    >>> abstract.add(1)
+    Traceback (most recent call last):
+    ...
+    NotImplementedError
+
+    >>> abstract.nextURL()
+    Traceback (most recent call last):
+    ...
+    NotImplementedError
+
+
+Thus let's now create a wroking add form:
+
+  >>> class PersonAddForm(form.AddForm):
+  ...
+  ...     fields = field.Fields(IPerson)
+  ...
+  ...     def create(self, data):
+  ...         return Person(**data)
+  ...
+  ...     def add(self, object):
+  ...         self.context[object.name] = object
+  ...
+  ...     def nextURL(self):
+  ...         return 'index.html'
+
+This is as simple as it gets. We explicitely define the pieces that are
+custom to every situation and let the default setup of the framework do the
+rest. Yes, this looks extremely similar to ``zope.formlib`` and it is
+intentional so, because we really like the simplicity of ``zope.formlib``'s
+way of dealing with the common use cases.
+
+During the test setup we also brought up a root folder already, so let's try
+to add a new person object there. For this add form, of course, the context is
+now the root folder:
+
+  >>> request = TestRequest()
+  >>> add = PersonAddForm(root, request)
+
+Since forms are not necessarily pages -- in fact often they are not -- they
+must not have a ``__call__`` method that does all the processing and rendering
+at once. Instead, this form embraces the update/render pattern. Thus, we first
+call the ``update()`` method.
+
+  >>> add.update()
+
+Actually a lot of things happen during this stage. Let me step through it one
+by one pointing out the effects.
+
+
+Find a widget manager and update it
+-----------------------------------
+
+The default widget manager knows to look for the ``fields`` attribute in the
+form, since it implements ``IFieldsForm``:
+
+  >>> from z3c.form import interfaces
+  >>> interfaces.IFieldsForm.providedBy(add)
+  True
+
+The widget manager is then stored in the ``widgets`` attribute as promised by
+the ``IForm`` interface:
+
+  >>> add.widgets
+  <z3c.form.field.FieldWidgets object at ...>
+
+The widget manager will have three widgets, one for each field:
+
+  >>> add.widgets.keys()
+  ['name', 'gender', 'age']
+
+When the widget manager updates itself, several sub-tasks are processed. The
+manager goes through each field, trying to create a fully representative
+widget for the field.
+
+Field Availability
+~~~~~~~~~~~~~~~~~~
+
+Just because a field is requested in the field manager, does not mean that a
+widget has to be created for the field. There are cases when a field
+declaration might be ignored. The following reasons come to mind:
+
+* No widget is created, if the data is not accessible in the content.
+* A custom widget manager has been registered to particularly ignore a field.
+
+In this simple case, all fields will be converted to widgets.
+
+Widget Creation
+~~~~~~~~~~~~~~~
+
+So first it instantiates the widget with the field. During the process,
+several pieces of information are transferred from the field to the widget:
+
+  >>> age = add.widgets['age']
+
+  # field.title -> age.label
+  >>> age.label
+  u'Age'
+
+  # field.required -> age.required
+  >>> age.required
+  False
+
+All these values can be overridden at later stages of the updating
+process.
+
+Widget Value
+~~~~~~~~~~~~
+
+The next step is to determine the the value that should be displayed by the
+widget. There are three places where the value could come from:
+
+* The field's default value.
+* The request, in case a form has not been submitted or an error occurred.
+* The context of the form, which represents the displayed content.
+
+Since we are currently building an add form, the only the first two places are
+effective. And since we do not have anything in the request, the value should
+be the field's default value or be empty.
+
+  >>> age.value
+  u'20'
+
+Since the widget only deals with output-ready values, the system also converts
+the value using a data converter.
+
+Widget Mode
+~~~~~~~~~~~
+
+Now the widget manager looks at the field to determine the widget mode -- in
+other words whether the widget is a display or edit widget. In this case all
+fields are input fields:
+
+  >>> age.mode
+  'input'
+
+Deciding which mode to use, however, might not be a trivial operation. It
+might depend on several factors:
+
+* The permission to the content's data value
+* The manual ``mode`` flag in the field
+* The ``read_only`` flag in the schema field
+
+
+Widget Attribute Values
+~~~~~~~~~~~~~~~~~~~~~~~
+
+As mentioned before, several widget attributes are optionally overridden when
+the widget updates itself:
+
+* label
+* hint
+* required
+* mode
+
+Since we have no customization components registered, all of those fields will
+remain as set before.
+
+
+Find an action manager, update and execute it
+---------------------------------------------
+
+After all widgets have been setup correctly, the actions are setup. By
+default, the form machinery uses the button declaration on the form to create
+its actions. For the add form, an add button is defined by default, so that we
+did not need to create our own. Thus, there should be one action:
+
+  >>> len(add.actions)
+  1
+
+The add button is an action and a widget at the same time:
+
+  >>> addAction = add.actions['add']
+  >>> addAction.title
+  u'Add'
+  >>> addAction.value
+  u'Add'
+
+After everything is setup, all pressed buttons are executed. Once a submitted
+action is detected, a special action handler adapter is used to determine the
+actions to take. Since the add button has not been pressed yet, no action
+occurred.
+
+
+Rendering the form
+------------------
+
+Once the update is complete we can render the form. Since we have not
+specified a template yet, we have to do this now. We have prepared a small and
+very simple template as part of this test:
+
+  >>> import os
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import tests
+  >>> def addTemplate(form):
+  ...     form.template = viewpagetemplatefile.BoundPageTemplate(
+  ...         viewpagetemplatefile.ViewPageTemplateFile(
+  ...             'simple_edit.pt', os.path.dirname(tests.__file__)), form)
+  >>> addTemplate(add)
+
+Let's now render the page:
+
+  >>> print add.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form.widgets.name">Name</label>
+          <input type="text" id="form.widgets.name" name="form.widgets.name"
+                 class="textWidget" value="" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.gender">Gender</label>
+          <select id="form.widgets.gender" name="form.widgets.gender:list"
+                  class="selectWidget" size="1">
+            <option id="form.widgets.gender.novalue"
+                    value="--NOVALUE--">no value</option>
+            <option id="form.widgets.gender.0" value="male">male</option>
+            <option id="form.widgets.gender.1" value="female">female</option>
+          </select>
+          <input name="form.widgets.gender-empty-marker" type="hidden"
+                 value="1" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.age">Age</label>
+          <input type="text" id="form.widgets.age" name="form.widgets.age"
+                 class="textWidget" value="20" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form.buttons.add" name="form.buttons.add"
+                 class="submitWidget" value="Add" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+
+Submitting an add form successfully
+-----------------------------------
+
+Let's now fill the request with all the right values so that upon submitting
+the form with the "Add" button, the person should be added to the root folder:
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.name': u'Stephan Richter',
+  ...     'form.widgets.gender': ['male'],
+  ...     'form.widgets.age': u'20',
+  ...     'form.buttons.add': u'Add'}
+  ...     )
+
+  >>> add = PersonAddForm(root, request)
+  >>> add.update()
+
+  >>> sorted(root)
+  [u'Stephan Richter']
+  >>> stephan = root[u'Stephan Richter']
+  >>> stephan.name
+  u'Stephan Richter'
+  >>> stephan.gender
+  'male'
+  >>> stephan.age
+  20
+
+
+Submitting an add form with invalid data
+----------------------------------------
+
+Next we try to submit the add form with the required name missing. Thus, the
+add form should not complete with the addition, but return with the add form
+pointing out the error.
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.gender': ['male'],
+  ...     'form.widgets.age': u'23',
+  ...     'form.buttons.add': u'Add'}
+  ...     )
+
+  >>> add = PersonAddForm(root, request)
+  >>> add.update()
+
+The widget manager and the widget causing the error should have an error
+message:
+
+  >>> [error for error in add.widgets.errors]
+  [<ErrorViewSnippet for RequiredMissing>]
+
+  >>> add.widgets['name'].error
+  <ErrorViewSnippet for RequiredMissing>
+
+Let's now render the form:
+
+  >>> from z3c.form import testing
+  >>> add.template = viewpagetemplatefile.BoundPageTemplate(
+  ...     viewpagetemplatefile.ViewPageTemplateFile(
+  ...         testing.getPath('../tests/simple_edit.pt'), ''), add)
+  >>> print add.render()
+  <html>
+    <body>
+      <i>There were some errors.</i>
+      <ul>
+        <li>
+          Name: <div class="error">Required input is missing.</div>
+        </li>
+      </ul>
+      <form action=".">
+        <div class="row">
+          <b><div class="error">Required input is missing.</div>
+          </b><label for="form.widgets.name">Name</label>
+          <input type="text" id="form.widgets.name" name="form.widgets.name"
+                 class="textWidget" value="" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.gender">Gender</label>
+          <select id="form.widgets.gender" name="form.widgets.gender:list"
+                  class="selectWidget" size="1">
+            <option id="form.widgets.gender.novalue"
+                    value="--NOVALUE--">no value</option>
+            <option id="form.widgets.gender.0" value="male"
+                    selected="selected">male</option>
+            <option id="form.widgets.gender.1" value="female">female</option>
+          </select>
+          <input name="form.widgets.gender-empty-marker" type="hidden"
+                 value="1" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.age">Age</label>
+          <input type="text" id="form.widgets.age" name="form.widgets.age"
+                 class="textWidget" value="23" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form.buttons.add" name="form.buttons.add"
+                 class="submitWidget" value="Add" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+Note that the values of the field are now extracted from the request.
+
+Let's now also provide a negative age, which is not possible either:
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.gender': ['male'],
+  ...     'form.widgets.age': u'-5',
+  ...     'form.buttons.add': u'Add'}
+  ...     )
+
+  >>> add = PersonAddForm(root, request)
+  >>> add.update()
+
+  >>> [(view.widget.label, view) for view in add.widgets.errors]
+  [(u'Name', <ErrorViewSnippet for RequiredMissing>),
+   (u'Age', <ErrorViewSnippet for TooSmall>)]
+
+But the errror message for a negative age is too generic:
+
+  >>> print add.widgets['age'].error.render()
+  <div class="error">Value is too small</div>
+
+It would be better to say that negative values are disallowed. So let's
+register a new error view snippet for the ``TooSmall`` error:
+
+  >>> from z3c.form import error
+
+  >>> class TooSmallView(error.ErrorViewSnippet):
+  ...     zope.component.adapts(
+  ...         zope.schema.interfaces.TooSmall, None, None, None, None, None)
+  ...
+  ...     def update(self):
+  ...         super(TooSmallView, self).update()
+  ...         if self.field.min == 0:
+  ...             self.message = u'The value cannot be a negative number.'
+
+  >>> zope.component.provideAdapter(TooSmallView)
+
+  >>> add = PersonAddForm(root, request)
+  >>> add.update()
+  >>> print add.widgets['age'].error.render()
+  <div class="error">The value cannot be a negative number.</div>
+
+
+Additional Form Attributes and API
+----------------------------------
+
+Since we are talking about HTML forms here, add and edit forms support all
+relevant FORM element attributes as attributes on the class.
+
+  >>> add.method
+  'post'
+  >>> add.enctype
+  'multipart/form-data'
+  >>> add.acceptCharset
+  >>> add.accept
+
+The ``action`` attribute is computed. By default it is the current URL:
+
+  >>> add.action
+  'http://127.0.0.1'
+
+The name is also computed. By default it takes the prefix and removes any
+trailing ".".
+
+  >>> add.name
+  'form'
+
+The template can then use those attributes, if it likes to. Since we are
+talking about templates, in this example we set the template manual, but if no
+template is specified, the system tries to find an adapter. Initially, there
+is no adapter, so rendering the form fails:
+
+  >>> add.template = None
+  >>> add.render()
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: ((...), <InterfaceClass ...IPageTemplate>, u'')
+
+The form module provides a simple component to create template factories:
+
+  >>> factory = form.FormTemplateFactory(
+  ...     testing.getPath('../tests/simple_edit.pt'), form=PersonAddForm)
+  >>> zope.component.provideAdapter(factory)
+
+Now the factory will be used to provide a template:
+
+  >>> print add.render()
+  <html>
+  ...
+  </html>
+
+Since a form can also be used as a page itself, it is callable:
+
+  >>> print add()
+  <html>
+  ...
+  </html>
+
+
+Changing Widget Attribute Values
+--------------------------------
+
+It frequently happens that a customer comes along and wants to slightly or
+totally change some of the text shown in forms or make optional fields
+required. Until now this always meant to implement a custom schema for this
+client. With the z3c.form framework all attributes -- for which it is sensible
+to replace a value without touching the code -- are customizable via an
+attribute value adapter.
+
+So let's change the label of the name widget from "Name" to "Full Name":
+
+  >>> from z3c.form import widget
+  >>> NameLabel = widget.StaticWidgetAttribute(
+  ...     u'Full Name', field=IPerson['name'])
+  >>> zope.component.provideAdapter(NameLabel, name='label')
+
+When the form renders, the label should be changed:
+
+  >>> add = PersonAddForm(root, TestRequest())
+  >>> addTemplate(add)
+  >>> add.update()
+  >>> print add.render()
+  <html>
+  ...
+  <div class="row">
+    <label for="form.widgets.name">Full Name</label>
+    <input type="text" id="form.widgets.name" name="form.widgets.name"
+           class="textWidget" value="" />
+  </div>
+  ...
+
+
+Adding a "Cancel" button
+------------------------
+
+Let's say a client requests that all add forms should have a "Cancel"
+button. When the button is pressed, the user is forwarded to the next URL of
+the add form. As always, the goal is to not touch the core implementation of
+the code, but make those changes externally.
+
+Adding a button/action is a little bit more involved than changing a value,
+because you have to isnert the additional action and customize the action
+handler. Based on your needs of flexibility, multiple approaches could be
+chosen. Here we demonstrate the simplest one.
+
+The first step is to create a custom action manager that always inserts a
+cancel action:
+
+  >>> from z3c.form import button
+  >>> class AddActions(button.ButtonActions):
+  ...     zope.component.adapts(
+  ...         interfaces.IAddForm,
+  ...         zope.interface.Interface,
+  ...         zope.interface.Interface)
+  ...
+  ...     def update(self):
+  ...         self.form.buttons = button.Buttons(
+  ...             self.form.buttons,
+  ...             button.Button('cancel', u'Cancel'))
+  ...         super(AddActions, self).update()
+
+After registering the new action manager,
+
+  >>> zope.component.provideAdapter(AddActions)
+
+the add form should display a cancel button:
+
+  >>> add.update()
+  >>> print add.render()
+  <html>
+  ...
+  <div class="action">
+    <input type="submit" id="form.buttons.add" name="form.buttons.add"
+           class="submitWidget" value="Add" />
+  </div>
+  <div class="action">
+    <input type="submit" id="form.buttons.cancel" name="form.buttons.cancel"
+           class="submitWidget" value="Cancel" />
+  </div>
+  ...
+
+But showing the button does not mean it does anything. So we also need a
+custom action handler to handle the cancel action:
+
+  >>> class AddActionHandler(button.ButtonActionHandler):
+  ...     zope.component.adapts(
+  ...         interfaces.IAddForm,
+  ...         zope.interface.Interface,
+  ...         zope.interface.Interface,
+  ...         button.ButtonAction)
+  ...
+  ...     def __call__(self):
+  ...         if self.action.name == 'form.buttons.cancel':
+  ...            self.form._finishedAdd = True
+  ...            return
+  ...         super(AddActionHandler, self).__call__()
+
+After registering the action handler,
+
+  >>> zope.component.provideAdapter(AddActionHandler)
+
+we can press the cancel button and we will be forwarded:
+
+  >>> request = TestRequest(form={'form.buttons.cancel': u'Cancel'})
+
+  >>> add = PersonAddForm(root, request)
+  >>> addTemplate(add)
+  >>> add.update()
+  >>> add.render()
+  ''
+
+  >>> request.response.getStatus()
+  302
+  >>> request.response.getHeader('Location')
+  'index.html'
+
+Eventually, we might have action managers and handlers that are much more
+powerful and some of the manual labor in this example would become
+unnecessary.
+
+
+Creating an Edit Form
+---------------------
+
+Now that we have exhaustively covered the customization possibilities of add
+forms, let's create an edit form. Edit forms are even simpler than add forms,
+since all actions are completely automatic:
+
+  >>> class PersonEditForm(form.EditForm):
+  ...
+  ...     fields = field.Fields(IPerson)
+
+We can use the created person from the successful addition above.
+
+  >>> edit = PersonEditForm(root[u'Stephan Richter'], TestRequest())
+
+After adding a template, we can look at the form:
+
+  >>> addTemplate(edit)
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form.widgets.name">Full Name</label>
+          <input type="text" id="form.widgets.name" name="form.widgets.name"
+                 class="textWidget" value="Stephan Richter" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.gender">Gender</label>
+          <select id="form.widgets.gender" name="form.widgets.gender:list"
+                  class="selectWidget" size="1">
+            <option id="form.widgets.gender.novalue"
+                    value="--NOVALUE--">no value</option>
+            <option id="form.widgets.gender.0" value="male"
+                    selected="selected">male</option>
+            <option id="form.widgets.gender.1" value="female">female</option>
+          </select>
+          <input name="form.widgets.gender-empty-marker" type="hidden"
+                 value="1" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.age">Age</label>
+          <input type="text" id="form.widgets.age" name="form.widgets.age"
+                 class="textWidget" value="20" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form.buttons.apply" name="form.buttons.apply"
+                 class="submitWidget" value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+As you can see, the data is being pulled in from the context for the edit
+form. Next we will look at the behavior when submitting the form.
+
+
+Failure Upon Submission of Edit Form
+------------------------------------
+
+Let's now submit the form having some invalid data.
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.name': u'Claudia Richter',
+  ...     'form.widgets.gender': ['female'],
+  ...     'form.widgets.age': u'-1',
+  ...     'form.buttons.apply': u'Apply'}
+  ...     )
+
+  >>> edit = PersonEditForm(root[u'Stephan Richter'], request)
+  >>> addTemplate(edit)
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+    <body>
+      <i>There were some errors.</i>
+      <ul>
+        <li>
+          Age: <div class="error">The value cannot be a negative number.</div>
+        </li>
+      </ul>
+      <form action=".">
+        <div class="row">
+          <label for="form.widgets.name">Full Name</label>
+          <input type="text" id="form.widgets.name" name="form.widgets.name"
+                 class="textWidget" value="Claudia Richter" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.gender">Gender</label>
+          <select id="form.widgets.gender" name="form.widgets.gender:list"
+                  class="selectWidget" size="1">
+            <option id="form.widgets.gender.novalue"
+                    value="--NOVALUE--">no value</option>
+            <option id="form.widgets.gender.0" value="male">male</option>
+            <option id="form.widgets.gender.1" value="female"
+                    selected="selected">female</option>
+          </select>
+          <input name="form.widgets.gender-empty-marker" type="hidden"
+                 value="1" />
+        </div>
+        <div class="row">
+          <b><div class="error">The value cannot be a negative number.</div>
+          </b><label for="form.widgets.age">Age</label>
+          <input type="text" id="form.widgets.age" name="form.widgets.age"
+                 class="textWidget" value="-1" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form.buttons.apply" name="form.buttons.apply"
+                 class="submitWidget" value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+
+Successfully Editing Content
+----------------------------
+
+Let's now resubmit the form with valid data, so the data should be updated.
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.name': u'Claudia Richter',
+  ...     'form.widgets.gender': ['female'],
+  ...     'form.widgets.age': u'27',
+  ...     'form.buttons.apply': u'Apply'}
+  ...     )
+
+  >>> edit = PersonEditForm(root[u'Stephan Richter'], request)
+  >>> addTemplate(edit)
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+  ...
+  <i>Data successfully updated.</i>
+  ...
+
+  >>> stephan = root[u'Stephan Richter']
+  >>> stephan.name
+  u'Claudia Richter'
+  >>> stephan.gender
+  'female'
+  >>> stephan.age
+  27
+
+
+Successful Action with No Changes
+---------------------------------
+
+When submitting the form without any changes, the form will tell you so.
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.name': u'Claudia Richter',
+  ...     'form.widgets.gender': ['female'],
+  ...     'form.widgets.age': u'27',
+  ...     'form.buttons.apply': u'Apply'}
+  ...     )
+
+  >>> edit = PersonEditForm(root[u'Stephan Richter'], request)
+  >>> addTemplate(edit)
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+  ...
+  <i>No changes were applied.</i>
+  ...
+
+
+Changing Status Messages
+------------------------
+
+Depending on the project, it is often desirable to change the status messages
+to fit the application. In ``zope.formlib`` this was hard to do, since the
+messages were burried within fairly complex methods that one did not want to
+touch. In this package all those messages are exposed as form attributes.
+
+There are three messages for the edit form:
+
+* ``formErrorsMessage`` -- Indicates that there was an error occurred while
+    applying the changes. This message is also available for the add form.
+
+* ``successMessage`` -- The form data was successfully applied.
+
+* ``noChangesMessage`` -- No changes were found in the form data.
+
+Let's now change the ``noChangesMessage``:
+
+  >>> edit.noChangesMessage = u'No changes were detected in the form data.'
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+  ...
+  <i>No changes were detected in the form data.</i>
+  ...
+
+When even more flexibility is required within a project, one could also
+implement these messages as properties looking up an attribute value. However,
+we have found this to be a rare case.
+
+
+Creating Edit Forms for Dictionaries
+------------------------------------
+
+Sometimes it is not desirable to edit a class instance that implements the
+fields, but other types of object. A good example is the need to modify a
+simple dictionaries, where the field names are the keys. To do that, a special
+data manager for dictionaries is available:
+
+  >>> from z3c.form import datamanager
+  >>> zope.component.provideAdapter(datamanager.DictionaryField)
+
+The only step the developer has to complete is to re-implement the form's
+``getContent()`` method to return the dictionary:
+
+  >>> personDict = {'name': u'Roger Ineichen'}
+  >>> class PersonDictEditForm(PersonEditForm):
+  ...     def getContent(self):
+  ...         return personDict
+
+We can now use the form as usual:
+
+  >>> edit = PersonDictEditForm(None, TestRequest())
+  >>> addTemplate(edit)
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form.widgets.name">Full Name</label>
+          <input type="text" id="form.widgets.name"
+         name="form.widgets.name" class="textWidget"
+         value="Roger Ineichen" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.gender">Gender</label>
+          <select id="form.widgets.gender" name="form.widgets.gender:list" 
+                  class="selectWidget" size="1">
+            <option id="form.widgets.gender.novalue"
+                    value="--NOVALUE--">no value</option>
+            <option id="form.widgets.gender.0" value="male">male</option>
+            <option id="form.widgets.gender.1" value="female">female</option>
+          </select>
+          <input name="form.widgets.gender-empty-marker" type="hidden"
+                 value="1" />
+        </div>
+        <div class="row">
+          <label for="form.widgets.age">Age</label>
+          <input type="text" id="form.widgets.age"
+                 name="form.widgets.age" class="textWidget" value="20" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form.buttons.apply"
+                 name="form.buttons.apply" class="submitWidget"
+                 value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+Note that the name displayed in the form is identical to the one in the
+dictionary. Let's now submit a form to ensure that the data is also written to
+the dictionary:
+
+  >>> request = TestRequest(form={
+  ...     'form.widgets.name': u'Jesse Ineichen',
+  ...     'form.widgets.gender': ['male'],
+  ...     'form.widgets.age': u'5',
+  ...     'form.buttons.apply': u'Apply'}
+  ...     )
+  >>> edit = PersonDictEditForm(None, request)
+  >>> edit.update()
+
+  >>> from zope.testing.doctestunit import pprint
+  >>> pprint(personDict)
+  {'age': 5,
+   'gender': 'male',
+   'name': u'Jesse Ineichen'}
+
+
+Creating a Display Form
+-----------------------
+
+Creating a display form is simple; just instantiate, update and render it:
+
+  >>> class PersonDisplayForm(form.DisplayForm):
+  ...     fields = field.Fields(IPerson)
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_display.pt', os.path.dirname(tests.__file__))
+
+  >>> display = PersonDisplayForm(stephan, TestRequest())
+  >>> display.update()
+  >>> print display.render()
+  <html>
+    <body>
+      <div class="row">
+        Claudia Richter
+      </div>
+      <div class="row">
+        female
+      </div>
+      <div class="row">
+        27
+      </div>
+    </body>
+  </html>
+
+
+Extending Forms
+---------------
+
+One very common use case is to extend forms. For example, you would like to
+use the edit form and its defined "Apply" button, but add another button
+yourself. Unfortunately, just inheriting the form is not enough, because the
+new button and handler declarations will override the inherited ones. Let me
+demonstrate the problem:
+
+  >>> class BaseForm(form.Form):
+  ...     fields = field.Fields(IPerson).select('name')
+  ...
+  ...     @button.buttonAndHandler(u'Apply')
+  ...     def handleApply(self, form):
+  ...         print 'success'
+
+  >>> BaseForm.fields.keys()
+  ['name']
+  >>> BaseForm.buttons.keys()
+  ['apply']
+  >>> BaseForm.handlers
+  <Handlers [<Handler for <Button 'apply' u'Apply'>>]>
+
+Let's now derive a form from the base form:
+
+  >>> class DerivedForm(BaseForm):
+  ...     fields = field.Fields(IPerson).select('gender')
+  ...
+  ...     @button.buttonAndHandler(u'Cancel')
+  ...     def handleCancel(self, form):
+  ...         print 'cancel'
+
+  >>> DerivedForm.fields.keys()
+  ['gender']
+  >>> DerivedForm.buttons.keys()
+  ['cancel']
+  >>> DerivedForm.handlers
+  <Handlers [<Handler for <Button 'cancel' u'Cancel'>>]>
+
+The obvious method to "inherit" the base form's information os to copy it
+over:
+
+  >>> class DerivedForm(BaseForm):
+  ...     fields = BaseForm.fields.copy()
+  ...     buttons = BaseForm.buttons.copy()
+  ...     handlers = BaseForm.handlers.copy()
+  ...
+  ...     fields += field.Fields(IPerson).select('gender')
+  ...
+  ...     @button.buttonAndHandler(u'Cancel')
+  ...     def handleCancel(self, form):
+  ...         print 'cancel'
+
+  >>> DerivedForm.fields.keys()
+  ['name', 'gender']
+  >>> DerivedForm.buttons.keys()
+  ['apply', 'cancel']
+  >>> DerivedForm.handlers
+  <Handlers
+      [<Handler for <Button 'apply' u'Apply'>>,
+       <Handler for <Button 'cancel' u'Cancel'>>]>
+
+But this is pretty clumsy. Instead, the ``form`` module provides a helper
+method that will do the extending for you:
+
+  >>> class DerivedForm(BaseForm):
+  ...     form.extends(BaseForm)
+  ...
+  ...     fields += field.Fields(IPerson).select('gender')
+  ...
+  ...     @button.buttonAndHandler(u'Cancel')
+  ...     def handleCancel(self, form):
+  ...         print 'cancel'
+
+  >>> DerivedForm.fields.keys()
+  ['name', 'gender']
+  >>> DerivedForm.buttons.keys()
+  ['apply', 'cancel']
+  >>> DerivedForm.handlers
+  <Handlers
+      [<Handler for <Button 'apply' u'Apply'>>,
+       <Handler for <Button 'cancel' u'Cancel'>>]>
+
+If you, for example do not want to extend the buttons and handlers, you can
+turn that off:
+
+  >>> class DerivedForm(BaseForm):
+  ...     form.extends(BaseForm, ignoreButtons=True, ignoreHandlers=True)
+  ...
+  ...     fields += field.Fields(IPerson).select('gender')
+  ...
+  ...     @button.buttonAndHandler(u'Cancel')
+  ...     def handleCancel(self, form):
+  ...         print 'cancel'
+
+  >>> DerivedForm.fields.keys()
+  ['name', 'gender']
+  >>> DerivedForm.buttons.keys()
+  ['cancel']
+  >>> DerivedForm.handlers
+  <Handlers [<Handler for <Button 'cancel' u'Cancel'>>]>
+
+
+Custom widget factories
+-----------------------
+
+Another important part of a form is, that we can use custom widgets. We can do
+this in a form by defining a widget factory for a field. We can get the field
+by get them from the fields collection e.g. fields['foo']. Which means, we can
+define new widget factories by defining fields['foo'].widgetFactory = MyWidget.
+Let's show a sample and define a custom widget:
+
+  >>> from z3c.form.browser import text
+  >>> class MyWidget(text.TextWidget):
+  ...     """My new widget."""
+  ...     css = u'MyCSS'
+
+No we can define a field widget factory:
+
+  >>> def MyFieldWidget(field, request):
+  ...     """IFieldWidget factory for MyWidget."""
+  ...     return widget.FieldWidget(field, MyWidget(request))
+
+We register the MyWidget in a form like:
+
+  >>> class MyEditForm(form.EditForm):
+  ...
+  ...     fields = field.Fields(IPerson)
+  ...     fields['name'].widgetFactory = MyFieldWidget
+
+We can see, that the custom widget get used in the rendered form:
+
+  >>> myEdit = MyEditForm(root[u'Stephan Richter'], TestRequest())
+  >>> addTemplate(myEdit)
+  >>> myEdit.update()
+  >>> print myEdit.render()
+  ...
+  <html...
+  <input type="text" id="form.widgets.name"
+         name="form.widgets.name" class="MyCSS"
+         value="Claudia Richter" />
+  ...


Property changes on: z3c.form/trunk/src/z3c/form/form.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/i18n.py
===================================================================
--- z3c.form/trunk/src/z3c/form/i18n.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/i18n.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""I18n message factory z3c.form
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.i18nmessageid
+
+MessageFactory = zope.i18nmessageid.MessageFactory('z3c.form')


Property changes on: z3c.form/trunk/src/z3c/form/i18n.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/interfaces.py
===================================================================
--- z3c.form/trunk/src/z3c/form/interfaces.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/interfaces.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,652 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Form and Widget Framework Interfaces
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+from zope.interface.common import mapping
+from zope.location.interfaces import ILocation
+from zope.publisher.interfaces.browser import IBrowserRequest
+
+from z3c.form.i18n import MessageFactory as _
+
+
+INPUT_MODE = 'input'
+DISPLAY_MODE = 'display'
+
+class NOVALUE(object):
+    def __repr__(self):
+        return '<NOVALUE>'
+NOVALUE = NOVALUE()
+
+# ----[ Layer Declaration ]--------------------------------------------------
+
+class IFormLayer(IBrowserRequest):
+    """A layer that contains all registrations of this package.
+
+    It is intended that someone can just use this layer as a base layer when
+    using this package.
+    """
+
+
+# ----[ Generic Manager Interfaces ]-----------------------------------------
+
+class IManager(mapping.IEnumerableMapping):
+    """A manager of some kind of items.
+
+    *Important*: While managers are mappings, the order of the items is
+     assumed to be important! Effectively a manager is an ordered mapping.
+
+    In general, managers do not have to support a manipulation
+    API. Oftentimes, managers are populated during initialization or while
+    updating.
+    """
+
+class ISelectionManager(IManager):
+    """Managers that support item selection and management.
+
+    This manager allows one to more carefully specify the contained items.
+
+    *Important*: The API is chosen in a way, that the manager is still
+    immutable. All methods in this interface must return *new* instances of
+    the manager.
+    """
+
+    def __add__(other):
+        """Used for merge two managers."""
+
+    def select(*names):
+        """Return a modified instance with an ordered subset of items."""
+
+    def omit(*names):
+        """Return a modified instance omitting given items."""
+
+    def copy():
+        """Copy all items to a new instance and return it."""
+
+
+# ----[ Validators ]---------------------------------------------------------
+
+class IValidator(zope.interface.Interface):
+    """A validator for a particular value."""
+
+    def validate(self, value):
+        """Validate the value.
+
+        If successful, return ``None``. Otherwise raise an ``Invalid`` error.
+        """
+
+class IManagerValidator(zope.interface.Interface):
+    """A validator that validates a set of data."""
+
+    def validate(self, data):
+        """Validate a dictionary of data.
+
+        This method is only responsible of validating relationships between
+        the values in the data. It can be assumed that all values have been
+        validated in isolation before.
+
+        The return value of this method is a tuple of errors that occurred
+        during the validation process.
+        """
+
+    def validateObject(self, obj):
+        """Validate an object.
+
+        The same semantics as in ``validate()`` apply, except that the values
+        are retrieved from the object and not the data dictionary.
+        """
+
+
+# ----[ Errors ]--------------------------------------------------------------
+
+class IErrorViewSnippet(zope.interface.Interface):
+    """A view providing a view for an error"""
+
+    error = zope.schema.Field(
+        title=_('Error'),
+        description=_('Error the view is for.'),
+        required=True)
+
+    def update(self):
+        """Update view."""
+
+    def render(self):
+        """Render view."""
+
+
+# ----[ Fields ]--------------------------------------------------------------
+
+class IField(zope.interface.Interface):
+    """Field wrapping a schema field used in the form."""
+
+    # TODO: define this fields
+    __name__ = zope.schema.TextLine(
+        title=_('Title'),
+        description=_('The name of the field within the form.'),
+        required=True)
+
+    field = zope.schema.Field(
+        title=_('Schema Field'),
+        description=_('The schema field that is to be rendered.'),
+        required=True)
+
+    prefix = zope.schema.Field()
+
+    mode = zope.schema.Field()
+
+    interface = zope.schema.Field()
+
+    dataProvider = zope.schema.Field()
+
+    widgetFactory = zope.schema.Field(
+        title=_('Widget Factory'),
+        description=_('The widget factory.'),
+        required=False,
+        default=None,
+        missing_value=None)
+
+
+class IFields(ISelectionManager):
+    """IField manager."""
+
+
+# ----[ Data Managers ]------------------------------------------------------
+
+class IDataManager(zope.interface.Interface):
+    """Data manager."""
+
+    def get(default=NOVALUE):
+        """Get the value.
+
+        If no value can be found, return the default value.
+        """
+
+    def set(value):
+        """Set the value"""
+
+    def canAccess():
+        """Can the value be accessed."""
+
+    def canWrite():
+        """Can the data manager write a value."""
+
+
+# ----[ Data Converters ]----------------------------------------------------
+
+class IDataConverter(zope.interface.Interface):
+    """A data converter from field to widget values and vice versa."""
+
+    def toWidgetValue(self, value):
+        """Convert the field value to a widget output value.
+
+        If conversion fails or is not possible, a ``ValueError`` *must* be
+        raised. However, this method should effectively never fail, because
+        incoming value is well-defined.
+        """
+
+    def toFieldValue(self, value):
+        """Convert an input value to a field/system internal value.
+
+        This methods *must* also validate the converted value against the
+        field.
+
+        If the conversion fails, a ``ValueError`` *must* be raised. If
+        the validation fails, a ``ValidationError`` *must* be raised.
+        """
+
+
+# value interfaces
+class IValue(zope.interface.Interface):
+    """A value."""
+
+    def get():
+        """Returns the value."""
+
+
+# term interfaces
+class ITerms(zope.interface.Interface):
+    """"""
+
+    context = zope.schema.Field()
+    request = zope.schema.Field()
+    form = zope.schema.Field()
+    field = zope.schema.Field()
+    widget = zope.schema.Field()
+
+    def getTerm(value):
+        """Return an ITitledTokenizedTerm object for the given value
+
+        LookupError is raised if the value isn't in the source
+        """
+
+    def getTermByToken(token):
+        """Return an ITokenizedTerm for the passed-in token.
+
+        If `token` is not represented in the vocabulary, `LookupError`
+        is raised.
+        """
+
+    def getValue(token):
+        """Return a value for a given identifier token
+
+        LookupError is raised if there isn't a value in the source.
+        """
+
+
+# ----[ Widgets ]------------------------------------------------------------
+
+class IWidget(ILocation):
+    """A widget within a form"""
+
+    template = zope.interface.Attribute('''The widget template''')
+
+    mode = zope.schema.BytesLine(
+        title=_('Mode'),
+        description=_('A widget mode.'),
+        required=True,
+        default=DISPLAY_MODE)
+
+    label = zope.schema.TextLine(
+        title=_('Label'),
+        description=_('''
+        The widget label.
+
+        Label may be translated for the request.
+
+        The attribute may be implemented as either a read-write or read-only
+        property, depending on the requirements for a specific implementation.
+        '''),
+        required=True)
+
+    required = zope.schema.Bool(
+        title=_('Required'),
+        description=_('If true the widget should be displayed as required '
+                      'input.'),
+        required=True)
+
+    error = zope.schema.Field(
+        title=_('Error'),
+        description=_('If an error occurred during any step, the error view '
+                      'stored here.'),
+        required=False)
+
+    value = zope.schema.Field(
+        title=_('Value'),
+        description=_('The value that the widget represents.'),
+        required=False)
+
+    ignoreRequest = zope.schema.Bool(
+        title=_('Ignore Request'),
+        description=_('A flag, when set, forces the widget not to look at '
+                      'the request for a value.'),
+        default=False,
+        required=False)
+
+    def extract(default=NOVALUE):
+        """Extract the string value(s) of the widget from the form.
+
+        The return value may be any Python construct, but is typically a
+        simple string, sequence of strings or a dictionary.
+
+        The value *must not* be converted into a native format.
+
+        If an error occurs during the extraction, the default value should be
+        returned. Since this should never happen, if the widget is properly
+        designed and used, it is okay to not raise an error here, since we do
+        not want to crash the system during an inproper request.
+
+        If there is no value to extract, the default is to be returned.
+        """
+
+    def update():
+        """Setup all of the widget information used for displaying."""
+
+    def render():
+        """Return the widget's text representation."""
+
+
+class ISequenceWidget(IWidget):
+    """Sequence widget."""
+
+    noValueToken = zope.schema.ASCIILine(
+        title=_('NOVALUE Token'),
+        description=_('The token to be used, if no value has been selected.'))
+
+    terms = zope.schema.Object(
+        title=_('Terms'),
+        description=_('A component that provides the options for selection.'),
+        schema=ITerms)
+
+class ISelectWidget(ISequenceWidget):
+    """Select widget with ITerms option."""
+
+class IOrderedSelectWidget(ISequenceWidget):
+    """Ordered Select widget with ITerms option."""
+
+class ICheckBoxWidget(ISequenceWidget):
+    """Checbox widget."""
+
+class IRadioWidget(ISequenceWidget):
+    """Radio widget."""
+
+class ISubmitWidget(IWidget):
+    """Submit widget."""
+
+class ITextAreaWidget(IWidget):
+    """Text widget."""
+
+class ITextWidget(IWidget):
+    """Text widget."""
+
+class IFileWidget(ITextWidget):
+    """File widget."""
+
+class IPasswordWidget(ITextWidget):
+    """Password widget."""
+
+
+class IWidgets(IManager):
+    """A widget manager"""
+
+    prefix = zope.schema.BytesLine(
+        title=_('Prefix'),
+        description=_('The prefix of the widgets.'),
+        default='widgets.',
+        required=True)
+
+    mode = zope.schema.BytesLine(
+        title=_('Prefix'),
+        description=_('The prefix of the widgets.'),
+        default=INPUT_MODE,
+        required=True)
+
+    errors = zope.schema.Field(
+        title=_('Errors'),
+        description=_('The collection of errors that occured during '
+                      'validation.'),
+        default=(),
+        required=True)
+
+    ignoreContext = zope.schema.Bool(
+        title=_('Ignore Context'),
+        description=_('If set the context is ignored to retrieve a value.'),
+        default=False,
+        required=False)
+
+    ignoreRequest = zope.schema.Bool(
+        title=_('Ignore Request'),
+        description=_('If set the request is ignored to retrieve a value.'),
+        default=False,
+        required=False)
+
+
+    def update():
+        """Setup widgets."""
+
+    def extract():
+        """Extract the values from the widgets and validate them.
+        """
+
+
+# ----[ Actions ]------------------------------------------------------------
+
+class IAction(zope.interface.Interface):
+    """Action"""
+
+    __name__ = zope.schema.TextLine(
+        title=_('Name'),
+        description=_('The object name.'),
+        required=False,
+        default=None)
+
+    title = zope.schema.TextLine(
+        title=_('Title'),
+        description=_('The action title.'),
+        required=True)
+
+    def isExecuted():
+        """Determine whether the action has been executed."""
+
+
+class IActionHandler(zope.interface.Interface):
+    """Action handler."""
+
+
+class IActions(IManager):
+    """A action manager"""
+
+    executedActions = zope.interface.Attribute(
+        '''An iterable of all executed actions (usually just one).''')
+
+    def update():
+        """Setup actions."""
+
+    def execute():
+        """Exceute actions."""
+
+
+class IButton(zope.schema.interfaces.IField):
+    """A button in a form."""
+
+    accessKey = zope.schema.TextLine(
+        title=_('Access Key'),
+        description=_('The key when pressed causes the button to be pressed.'),
+        min_length=1,
+        max_length=1,
+        required=False)
+
+
+class IButtons(ISelectionManager):
+    """Button manager."""
+
+
+class IButtonHandlers(zope.interface.Interface):
+    """A collection of handlers for buttons."""
+
+    def addHandler(button, handler):
+        """Add a new handler for a button."""
+
+    def getHandler(button):
+        """Get the handler for the button."""
+
+    def copy():
+        """Copy this object and return the copy."""
+
+    def __add__(other):
+        """Add another handlers object.
+
+        During the process a copy of the current handlers object should be
+        created and the other one is added to the copy. The return value is
+        the copy.
+        """
+
+
+class IButtonHandler(zope.interface.Interface):
+    """A handler managed by the button handlers."""
+
+    def __call__(self, form, action):
+        """Execute the handler."""
+
+
+# ----[ Forms ]--------------------------------------------------------------
+
+class IHandlerForm(zope.interface.Interface):
+    """A form that stores the handlers locally."""
+
+    handlers = zope.schema.Object(
+        title=_('Handlers'),
+        description=_('A list of action handlers defined on the form.'),
+        schema=IButtonHandlers,
+        required=True)
+
+
+class IContextAware(zope.interface.Interface):
+    """Offers a context attribute.
+
+    For advanced uses, the widget will make decisions based on the context
+    it is rendered in.
+    """
+
+    context = zope.schema.Field(
+        title=_('Context'),
+        description=_('The context in which the widget is displayed.'),
+        required=True)
+
+    ignoreContext = zope.schema.Bool(
+        title=_('Ignore Context'),
+        description=_('A flag, when set, forces the widget not to look at '
+                      'the context for a value.'),
+        default=False,
+        required=False)
+
+
+class IFormAware(zope.interface.Interface):
+    """Offers a form attribute.
+
+    For advanced uses the widget will make decisions based on the form
+    it is rendered in.
+    """
+
+    form = zope.schema.Field()
+
+
+class IFieldWidget(zope.interface.Interface):
+    """Offers a field attribute.
+
+    For advanced uses the widget will make decisions based on the field
+    it is rendered for.
+    """
+
+    field = zope.schema.Field(
+        title=_('Field'),
+        description=_('The schema field which the widget is representing.'),
+        required=True)
+
+
+class IForm(zope.interface.Interface):
+    """Form"""
+
+    widgets = zope.schema.Object(
+        title=_('Widgets'),
+        description=_('A widget manager containing the widgets to be used in '
+                      'the form.'),
+        schema=IWidgets)
+
+    prefix = zope.schema.BytesLine(
+        title=_('Prefix'),
+        description=_('The prefix of the form used to uniquely identify it.'),
+        default='form.')
+
+    status = zope.schema.Text(
+        title=_('Status'),
+        description=_('The status message of the form.'),
+        default=None,
+        required=False)
+
+
+class ISubForm(IForm):
+    """A subform."""
+
+
+class IInputForm(zope.interface.Interface):
+    """A form that is meant to process the input of the form controls."""
+
+    action = zope.schema.URI(
+        title=_('Action'),
+        description=_('The action defines the URI to which the form data is '
+                      'sent.'),
+        required=True)
+
+    name = zope.schema.TextLine(
+        title=_('Name'),
+        description=_('The name of the form used to identify it.'),
+        required=False)
+
+    id = zope.schema.TextLine(
+        title=_('Id'),
+        description=_('The id of the form used to identify it.'),
+        required=False)
+
+    method = zope.schema.Choice(
+        title=_('Method'),
+        description=_('The HTTP method used to submit the form.'),
+        values=('get', 'post'),
+        default='post',
+        required=False)
+
+    enctype = zope.schema.ASCIILine(
+        title=_('Encoding Type'),
+        description=_('The data encoding used to submit the data safely.'),
+        default='multipart/form-data',
+        required=False)
+
+    acceptCharset = zope.schema.ASCIILine(
+        title=_('Accepted Character Sets'),
+        description=_('This is a list of character sets the server accepts. '
+                      'By default this is unknwon.'),
+        required=False)
+
+    accept = zope.schema.ASCIILine(
+        title=_('Accepted Content Types'),
+        description=_('This is a list of content types the server can '
+                      'safely handle.'),
+        required=False)
+
+
+class IAddForm(IForm):
+    """A form to create and add a new component."""
+
+    def create(self, data):
+        """Create the new object using the given data.
+
+        Returns the newly created object.
+        """
+
+    def add(self, object):
+        """Add the object somewhere."""
+
+
+class IEditForm(IForm):
+    """A form to edit data of a component."""
+
+    def getContent():
+        """Return the content object to be modified."""
+
+    def applyChanges(data):
+        """Apply the changes to the content component."""
+
+
+class IFieldsForm(IForm):
+    """A form that is based upon defined fields."""
+
+    fields = zope.schema.Object(
+        title=_('Fields'),
+        description=_('A field manager describing the fields to be used for '
+                      'the form.'),
+        schema=IFields)
+
+
+class IButtonForm(IForm):
+    """A form that is based upon defined buttons."""
+
+    buttons = zope.schema.Object(
+        title=_('Buttons'),
+        description=_('A button manager describing the buttons to be used for '
+                      'the form.'),
+        schema=IButtons)


Property changes on: z3c.form/trunk/src/z3c/form/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/meta.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/meta.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/meta.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,16 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/z3c">
+
+    <meta:directive
+        name="widgetTemplate"
+        schema=".zcml.IWidgetTemplateDirective"
+        handler=".zcml.widgetTemplateDirective"
+        />
+
+  </meta:directives>
+
+</configure>
+


Property changes on: z3c.form/trunk/src/z3c/form/meta.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/subform.py
===================================================================
--- z3c.form/trunk/src/z3c/form/subform.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/subform.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Logical Sub-Form Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+
+from z3c.form import action, button, form, interfaces
+from z3c.form.i18n import MessageFactory as _
+
+
+class EditSubForm(form.BaseForm):
+    zope.interface.implements(
+        interfaces.ISubForm, interfaces.IHandlerForm)
+
+    formErrorsMessage = _('There were some errors.')
+    successMessage = _('Data successfully updated.')
+    noChangesMessage = _('No changes were applied.')
+
+    def __init__(self, context, request, parentForm):
+        self.context = context
+        self.request = request
+        self.parentForm = self.__parent__ = parentForm
+
+    @button.handler(form.EditForm.buttons['apply'])
+    def handleApply(self, action):
+        data, errors = self.widgets.extract()
+        if errors:
+            form.status = self.formErrorsMessage
+            return
+        content = self.getContent()
+        changed = form.applyChanges(self, content, data)
+        if changed:
+            zope.event.notify(
+                zope.lifecycleevent.ObjectModifiedEvent(content))
+            self.status = self.successMessage
+        else:
+            self.status = self.noChangesMessage
+
+
+    def update(self):
+        super(EditSubForm, self).update()
+        for action in self.parentForm.actions.executedActions:
+            adapter = zope.component.queryMultiAdapter(
+                (self, self.request, self.getContent(), action),
+                interface=interfaces.IActionHandler)
+            if adapter:
+                adapter()


Property changes on: z3c.form/trunk/src/z3c/form/subform.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/subform.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/subform.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/subform.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,486 @@
+=========
+Sub-Forms
+=========
+
+Traditionally, the Zope community talks about sub-forms in a generic manner
+without defining their purpose, restrictions and assumptions. When we
+initially talked about sub-forms for this package, we quickly noticed that
+there are several classes of sub-forms with different goals.
+
+Of course, we need to setup our defaults for this demonstration as well:
+
+  >>> from z3c.form import testing
+  >>> testing.setupFormDefaults()
+
+
+Class I: The form within the form
+---------------------------------
+
+This class of sub-forms provides a complete form within a form, including its
+own actions. When an action of the sub-form is submitted, the parent form
+usually does not interact with that action at all. The same is true for the
+reverse; when an action of the parent form is submitted, the sub-form does not
+react.
+
+A classic example for this type of sub-form is uploading an image. The subform
+allows uploading a file and once the file is uploaded the image is shown as
+well as a "Delete"/"Clear" button. The sub-form will store the image in the
+session and when the main form is submitted it looks in the session for the
+image.
+
+This scenario was well supported in ``zope.formlib`` and also does not require
+special support in ``z3c.form``. Let me show you, how this can be done.
+
+In this example, we would like to describe a car and its owner:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IOwner(zope.interface.Interface):
+  ...     name = zope.schema.TextLine(title=u'Name')
+  ...     license = zope.schema.TextLine(title=u'License')
+
+  >>> class ICar(zope.interface.Interface):
+  ...     model = zope.schema.TextLine(title=u'Model')
+  ...     make = zope.schema.TextLine(title=u'Make')
+  ...     owner = zope.schema.Object(title=u'Owner', schema=IOwner)
+
+Let's now implement the two interfaces and create instances, so that we can
+create edit forms for it:
+
+  >>> class Owner(object):
+  ...     zope.interface.implements(IOwner)
+  ...     def __init__(self, name, license):
+  ...         self.name = name
+  ...         self.license = license
+
+  >>> class Car(object):
+  ...     zope.interface.implements(ICar)
+  ...     def __init__(self, model, make, owner):
+  ...         self.model = model
+  ...         self.make = make
+  ...         self.owner = owner
+
+  >>> me = Owner(u'Stephan Richter', u'MA-1231FW97')
+  >>> mycar = Car(u'Nissan', u'Sentra', me)
+
+We define the owner sub-form as we would any other form. The only difference
+is the template, which should not render a form-tag:
+
+  >>> import os
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import form, field, tests
+
+  >>> templatePath = os.path.dirname(tests.__file__)
+
+  >>> class OwnerForm(form.EditForm):
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_owneredit.pt', templatePath)
+  ...     fields = field.Fields(IOwner)
+  ...     prefix = 'owner'
+
+Next we define the car form, which has the owner form as a sub-form. The car
+form also needs a special template, since it needs to render the sub-form at
+some point. For the simplicity of this example, I have duplicated a lot of
+template code here, but you can use your favorite template techniques, such as
+METAL macros, viewlets, or pagelets to make better reuse of some code.
+
+  >>> class CarForm(form.EditForm):
+  ...     fields = field.Fields(ICar).select('model', 'make')
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_caredit.pt', templatePath)
+  ...     prefix = 'car'
+  ...     def update(self):
+  ...         self.owner = OwnerForm(self.context.owner, self.request)
+  ...         self.owner.update()
+  ...         super(CarForm, self).update()
+
+Let's now instantiate the form and render it:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+  >>> print carForm.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="car.widgets.model">Model</label>
+          <input type="text" id="car.widgets.model"
+                 name="car.widgets.model" class="textWidget" value="Nissan" />
+        </div>
+        <div class="row">
+          <label for="car.widgets.make">Make</label>
+          <input type="text" id="car.widgets.make"
+                 name="car.widgets.make" class="textWidget" value="Sentra" />
+        </div>
+        <fieldset>
+          <legend>Owner</legend>
+          <div class="row">
+            <label for="owner.widgets.name">Name</label>
+            <input type="text" id="owner.widgets.name"
+                   name="owner.widgets.name" class="textWidget"
+                   value="Stephan Richter" />
+          </div>
+          <div class="row">
+            <label for="owner.widgets.license">License</label>
+            <input type="text" id="owner.widgets.license"
+                   name="owner.widgets.license" class="textWidget"
+                   value="MA-1231FW97" />
+          </div>
+          <div class="action">
+            <input type="submit" id="owner.buttons.apply"
+                   name="owner.buttons.apply" class="submitWidget"
+                   value="Apply" />
+          </div>
+        </fieldset>
+        <div class="action">
+          <input type="submit" id="car.buttons.apply"
+                 name="car.buttons.apply" class="submitWidget"
+                 value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+I can now submit the owner form, which should not submit any car changes I
+might have made in the form:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'BMW',
+  ...     'car.widgets.make': u'325',
+  ...     'owner.widgets.name': u'Stephan Richter',
+  ...     'owner.widgets.license': u'MA-97097A87',
+  ...     'owner.buttons.apply': u'Apply'
+  ...     })
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+
+  >>> mycar.model
+  u'Nissan'
+  >>> mycar.make
+  u'Sentra'
+
+  >>> me.name
+  u'Stephan Richter'
+  >>> me.license
+  u'MA-97097A87'
+
+Also, the form should say that the data of the owner has changed:
+
+  >>> print carForm.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="car.widgets.model">Model</label>
+          <input type="text" id="car.widgets.model"
+                 name="car.widgets.model" class="textWidget"
+                 value="BMW" />
+        </div>
+        <div class="row">
+          <label for="car.widgets.make">Make</label>
+          <input type="text" id="car.widgets.make"
+                 name="car.widgets.make" class="textWidget"
+                 value="325" />
+        </div>
+        <fieldset>
+          <legend>Owner</legend>
+          <i>Data successfully updated.</i>
+          <div class="row">
+            <label for="owner.widgets.name">Name</label>
+            <input type="text" id="owner.widgets.name"
+                   name="owner.widgets.name" class="textWidget"
+                   value="Stephan Richter" />
+          </div>
+          <div class="row">
+            <label for="owner.widgets.license">License</label>
+            <input type="text" id="owner.widgets.license"
+                   name="owner.widgets.license" class="textWidget"
+                   value="MA-97097A87" />
+          </div>
+          <div class="action">
+            <input type="submit" id="owner.buttons.apply"
+                   name="owner.buttons.apply" class="submitWidget"
+                   value="Apply" />
+          </div>
+        </fieldset>
+        <div class="action">
+          <input type="submit" id="car.buttons.apply"
+                 name="car.buttons.apply" class="submitWidget"
+                 value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+The same is true the other way around as well. Submitting the overall form
+does not submit the owner form:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'BMW',
+  ...     'car.widgets.make': u'325',
+  ...     'car.buttons.apply': u'Apply',
+  ...     'owner.widgets.name': u'Claudia Richter',
+  ...     'owner.widgets.license': u'MA-123403S2',
+  ...     })
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+
+  >>> mycar.model
+  u'BMW'
+  >>> mycar.make
+  u'325'
+
+  >>> me.name
+  u'Stephan Richter'
+  >>> me.license
+  u'MA-97097A87'
+
+
+Class II: The logical unit
+--------------------------
+
+In this class of sub-forms, a sub-form is often just a collection of widgets
+without any actions. Instead, the sub-form must be able to react to the
+actions of the parent form. A good example of those types of sub-forms is
+actually the example I chose above.
+
+So let's redevelop our example above in a way that the owner sub-form is just
+a logical unit that shares the action with its parent form. Initially, the
+example does not look very different, except that we use ``EditSubForm`` as a
+base class:
+
+  >>> from z3c.form import subform
+
+  >>> class OwnerForm(subform.EditSubForm):
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_subedit.pt', templatePath)
+  ...     fields = field.Fields(IOwner)
+  ...     prefix = 'owner'
+
+The main form also is pretty much the same, except that a subform takes three
+constructor arguments, the last one being the parent form:
+
+  >>> class CarForm(form.EditForm):
+  ...     fields = field.Fields(ICar).select('model', 'make')
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_caredit.pt', templatePath)
+  ...     prefix = 'car'
+  ...
+  ...     def update(self):
+  ...         super(CarForm, self).update()
+  ...         self.owner = OwnerForm(self.context.owner, self.request, self)
+  ...         self.owner.update()
+
+Rendering the form works as before:
+
+  >>> request = TestRequest()
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+  >>> print carForm.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="car.widgets.model">Model</label>
+          <input type="text" id="car.widgets.model" name="car.widgets.model"
+                 class="textWidget" value="BMW" />
+        </div>
+        <div class="row">
+          <label for="car.widgets.make">Make</label>
+          <input type="text" id="car.widgets.make" name="car.widgets.make"
+                 class="textWidget" value="325" />
+        </div>
+        <fieldset>
+          <legend>Owner</legend>
+          <div class="row">
+            <label for="owner.widgets.name">Name</label>
+            <input type="text" id="owner.widgets.name" name="owner.widgets.name"
+                   class="textWidget" value="Stephan Richter" />
+          </div>
+          <div class="row">
+            <label for="owner.widgets.license">License</label>
+            <input type="text" id="owner.widgets.license"
+                   name="owner.widgets.license" class="textWidget" 
+                   value="MA-97097A87" />
+          </div>
+        </fieldset>
+        <div class="action">
+          <input type="submit" id="car.buttons.apply"
+                 name="car.buttons.apply" class="submitWidget"
+                 value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+The itnersting part of this setup is that the "Apply" button calls the action
+handlers for both, the main and the sub-form:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'Ford',
+  ...     'car.widgets.make': u'F150',
+  ...     'car.buttons.apply': u'Apply',
+  ...     'owner.widgets.name': u'Claudia Richter',
+  ...     'owner.widgets.license': u'MA-991723FDG',
+  ...     })
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+
+  >>> mycar.model
+  u'Ford'
+  >>> mycar.make
+  u'F150'
+  >>> me.name
+  u'Claudia Richter'
+  >>> me.license
+  u'MA-991723FDG'
+
+Let's now have a look at cases where an error happens. If an error occurs in
+the parent form, the sub-form is still submitted:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'Volvo\n',
+  ...     'car.widgets.make': u'450',
+  ...     'car.buttons.apply': u'Apply',
+  ...     'owner.widgets.name': u'Stephan Richter',
+  ...     'owner.widgets.license': u'MA-991723FDG',
+  ...     })
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+
+  >>> mycar.model
+  u'Ford'
+  >>> mycar.make
+  u'F150'
+  >>> me.name
+  u'Stephan Richter'
+  >>> me.license
+  u'MA-991723FDG'
+
+Let's look at the rendered form:
+
+  >>> print carForm.render()
+  <html>
+    <body>
+      <i>There were some errors.</i>
+      <ul>
+        <li>
+          Model: <div class="error">Constraint not satisfied</div>
+        </li>
+      </ul>
+      <form action=".">
+        <div class="row">
+          <b><div class="error">Constraint not satisfied</div>
+          </b><label for="car.widgets.model">Model</label>
+          <input type="text" id="car.widgets.model"
+                 name="car.widgets.model" class="textWidget" value="Volvo " />
+        </div>
+        <div class="row">
+          <label for="car.widgets.make">Make</label>
+          <input type="text" id="car.widgets.make"
+         name="car.widgets.make" class="textWidget" value="450" />
+        </div>
+        <fieldset>
+          <legend>Owner</legend>
+          <i>Data successfully updated.</i>
+          <div class="row">
+            <label for="owner.widgets.name">Name</label>
+            <input type="text" id="owner.widgets.name"
+                 name="owner.widgets.name" class="textWidget"
+                 value="Stephan Richter" />
+          </div>
+          <div class="row">
+            <label for="owner.widgets.license">License</label>
+            <input type="text" id="owner.widgets.license"
+                 name="owner.widgets.license" class="textWidget"
+                 value="MA-991723FDG" />
+          </div>
+        </fieldset>
+        <div class="action">
+          <input type="submit" id="car.buttons.apply"
+                 name="car.buttons.apply" class="submitWidget"
+                 value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+Now, we know, we know. This might not be the behavior that *you* want. But
+remember how we started this document. We started with the recognition that
+there are many classes and policies surrounding subforms. So while this
+package provides some sensible default behavior, it is not intended to be
+comprehensive.
+
+Let's now create an error in the sub-form, ensuring that an error message
+occurs:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'Volvo',
+  ...     'car.widgets.make': u'450',
+  ...     'car.buttons.apply': u'Apply',
+  ...     'owner.widgets.name': u'Claudia\n Richter',
+  ...     'owner.widgets.license': u'MA-991723F12',
+  ...     })
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+
+  >>> mycar.model
+  u'Volvo'
+  >>> mycar.make
+  u'450'
+  >>> me.name
+  u'Stephan Richter'
+  >>> me.license
+  u'MA-991723FDG'
+
+  >>> print carForm.render()
+  <html>
+    ...
+    <fieldset>
+      <legend>Owner</legend>
+      <ul>
+        <li>
+          Name: <div class="error">Constraint not satisfied</div>
+        </li>
+      </ul>
+      ...
+    </fieldset>
+    ...
+  </html>
+
+If the data did not change, it is also locally reported:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'Ford',
+  ...     'car.widgets.make': u'F150',
+  ...     'car.buttons.apply': u'Apply',
+  ...     'owner.widgets.name': u'Stephan Richter',
+  ...     'owner.widgets.license': u'MA-991723FDG',
+  ...     })
+
+  >>> carForm = CarForm(mycar, request)
+  >>> carForm.update()
+  >>> print carForm.render()
+  <html>
+    ...
+    <fieldset>
+      <legend>Owner</legend>
+      <i>No changes were applied.</i>
+      ...
+    </fieldset>
+    ...
+  </html>
+
+Final Note: With ``zope.formlib`` and ``zope.app.form`` people usually wrote
+complex object widgets to handle objects within forms. We never considered
+this a good way of programming, since one looses control over the layout too
+easily.


Property changes on: z3c.form/trunk/src/z3c/form/subform.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/term.py
===================================================================
--- z3c.form/trunk/src/z3c/form/term.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/term.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Terms Implementation
+
+$Id$
+"""
+import zope.schema
+from zope.schema import vocabulary
+
+from z3c.form import interfaces
+from z3c.form.i18n import MessageFactory as _
+
+
+class Terms(object):
+    """Base implementationf or custom ITerms."""
+
+    zope.interface.implements(interfaces.ITerms)
+
+    def getTerm(self, value):
+        return self.terms.getTerm(value)
+
+    def getTermByToken(self, token):
+        return self.terms.getTermByToken(token)
+
+    def getValue(self, token):
+        return self.terms.getTermByToken(token).value
+
+    def __iter__(self):
+        return iter(self.terms)
+
+    def __len__(self):
+        return self.terms.__len__()
+
+    def __contains__(self, value):
+        return self.terms.__contains__(value)
+
+
+class ChoiceTerms(Terms):
+    """ITerms adapter for zope.schema.IChoice based implementations."""
+
+    zope.component.adapts(
+        zope.interface.Interface,
+        interfaces.IFormLayer,
+        zope.interface.Interface,
+        zope.schema.interfaces.IChoice,
+        interfaces.IWidget)
+
+    def __init__(self, context, request, form, field, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.field = field
+        self.widget = widget
+        self.terms = field.vocabulary
+
+
+class BoolTerms(Terms):
+    """Default yes and no terms are used by default for IBool fields."""
+
+    zope.component.adapts(
+        zope.interface.Interface,
+        interfaces.IFormLayer,
+        zope.interface.Interface,
+        zope.schema.interfaces.IBool,
+        interfaces.IWidget)
+
+    def __init__(self, context, request, form, field, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.field = field
+        self.widget = widget
+        terms = [vocabulary.SimpleTerm(value, title, title=title)
+                 for title, value in [(_('yes'), True), (_('no'), False)]]
+        self.terms = vocabulary.SimpleVocabulary(terms)
+
+
+class CollectionTerms(Terms):
+    """ITerms adapter for zope.schema.ICollection based implementations."""
+
+    zope.component.adapts(
+        zope.interface.Interface,
+        interfaces.IFormLayer,
+        zope.interface.Interface,
+        zope.schema.interfaces.ICollection,
+        interfaces.IWidget)
+
+    def __init__(self, context, request, form, field, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.field = field
+        self.widget = widget
+        self.terms = field.value_type.vocabulary


Property changes on: z3c.form/trunk/src/z3c/form/term.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/term.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/term.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/term.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,103 @@
+=====
+Terms
+=====
+
+Terms are used to provide choices for sequence widgets or any other construct
+needing them. Since Zope 3 already has sources and vocabularies, the base
+terms class simply builds on them.
+
+Thus, let's create a vocabulary first:
+
+  >>> from zope.schema import vocabulary
+  >>> ratings = vocabulary.SimpleVocabulary([
+  ...     vocabulary.SimpleVocabulary.createTerm(0, '0', u'bad'),
+  ...     vocabulary.SimpleVocabulary.createTerm(1, '1', u'okay'),
+  ...     vocabulary.SimpleVocabulary.createTerm(2, '2', u'good')
+  ...     ])
+
+Now we can create the terms object:
+
+  >>> from z3c.form import term
+  >>> terms = term.Terms()
+  >>> terms.terms = ratings
+
+Getting a term from a given value is simple:
+
+  >>> terms.getTerm(0).title
+  u'bad'
+  >>> terms.getTerm(3)
+  Traceback (most recent call last):
+  ...
+  LookupError: 3
+
+When converting values from their Web representation back to the internal
+representation, we have to be able to look up a term by its token:
+
+  >>> terms.getTermByToken('0').title
+  u'bad'
+  >>> terms.getTerm('3')
+  Traceback (most recent call last):
+  ...
+  LookupError: 3
+
+However, often we just want the value so asking for the value that is
+represented by a token saves usually one line of code:
+
+  >>> terms.getValue('0')
+  0
+  >>> terms.getValue('3')
+  Traceback (most recent call last):
+  ...
+  LookupError: 3
+
+You can also iterate through all terms:
+
+  >>> [entry.title for entry in terms]
+  [u'bad', u'okay', u'good']
+
+Or ask how many terms you have in the first place:
+
+  >>> len(terms)
+  3
+
+Finally the API allows you to check whether a particular value is available in
+the terms:
+
+  >>> 0 in terms
+  True
+  >>> 3 in terms
+  False
+
+Now, there are several terms implementations that were designed for particular
+fields. Within the framework, terms are used as adapters with the follwoing
+discriminators: context, request, form, field, and widget.
+
+The first terms implementation is for ``Choice`` fields:
+
+  >>> import zope.schema
+
+  >>> ratingField = zope.schema.Choice(
+  ...     title=u'Rating',
+  ...     vocabulary=ratings)
+
+  >>> terms = term.ChoiceTerms(None, None, None, ratingField, None)
+  >>> [entry.title for entry in terms]
+  [u'bad', u'okay', u'good']
+
+A similar terms implementation exists for a ``Bool`` field:
+
+  >>> truthField = zope.schema.Bool()
+
+  >>> terms = term.BoolTerms(None, None, None, truthField, None)
+  >>> [entry.title for entry in terms]
+  [u'yes', u'no']
+
+Finally, there is a terms adapter for all collections:
+
+  >>> ratingsField = zope.schema.List(
+  ...     title=u'Ratings',
+  ...     value_type=ratingField)
+
+  >>> terms = term.CollectionTerms(None, None, None, ratingsField, None)
+  >>> [entry.title for entry in terms]
+  [u'bad', u'okay', u'good']


Property changes on: z3c.form/trunk/src/z3c/form/term.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/testing.py
===================================================================
--- z3c.form/trunk/src/z3c/form/testing.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/testing.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,144 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Common z3c.form test setups
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+import os
+import zope.component
+import zope.interface
+import zope.schema
+from zope.pagetemplate.interfaces import IPageTemplate
+from zope.publisher.browser import TestRequest
+from zope.security.interfaces import IInteraction
+from zope.security.interfaces import ISecurityPolicy
+from zope.security import checker
+from zope.app.testing import setup
+
+from z3c.form import browser, button, converter, datamanager, error, field
+from z3c.form import interfaces, term, validator, widget
+from z3c.form.browser import radio, select, text
+
+
+class TestRequest(TestRequest):
+    zope.interface.implements(interfaces.IFormLayer)
+
+class SimpleSecurityPolicy(object):
+    """Allow all access."""
+    zope.interface.implements(IInteraction)
+    zope.interface.classProvides(ISecurityPolicy)
+
+    loggedIn = False
+    allowedPermissions = ()
+
+    def __init__(self, loggedIn=False, allowedPermissions=()):
+        self.loggedIn = loggedIn
+        self.allowedPermissions = allowedPermissions
+
+    def __call__(self, *participations):
+        self.participations = []
+        return self
+
+    def add(self, participation):
+        pass
+
+    def remove(self, participation):
+        pass
+
+    def checkPermission(self, permission, object):
+        if permission is checker.CheckerPublic:
+            return True
+        if self.loggedIn:
+            if permission in self.allowedPermissions:
+                return True
+        return False
+
+
+def getPath(filename):
+    return os.path.join(os.path.dirname(browser.__file__), filename)
+
+def setUp(test):
+    test.globs = {'root': setup.placefulSetUp(True)}
+
+
+def setupFormDefaults():
+    # Validator adapters
+    zope.component.provideAdapter(validator.SimpleFieldValidator)
+    zope.component.provideAdapter(validator.InvariantsValidator)
+    # Data manager adapter to get and set values to content
+    zope.component.provideAdapter(datamanager.AttributeField)
+    # Adapter to use form.fields to generate widgets
+    zope.component.provideAdapter(field.FieldWidgets)
+    # Adapters to lookup the widget for a field
+    # Text Field Widget
+    zope.component.provideAdapter(
+        text.TextFieldWidget,
+        adapts=(zope.schema.interfaces.ITextLine, interfaces.IFormLayer))
+    zope.component.provideAdapter(
+        text.TextFieldWidget,
+        adapts=(zope.schema.interfaces.IInt, interfaces.IFormLayer))
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('text_input.pt'), 'text/html'),
+        (None, None, None, None, interfaces.ITextWidget),
+        IPageTemplate, name=interfaces.INPUT_MODE)
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('text_display.pt'), 'text/html'),
+        (None, None, None, None, interfaces.ITextWidget),
+        IPageTemplate, name=interfaces.DISPLAY_MODE)
+    # Radio Field Widget
+    zope.component.provideAdapter(radio.RadioFieldWidget)
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('radio_input.pt'), 'text/html'),
+        (None, None, None, None, interfaces.IRadioWidget),
+        IPageTemplate, name=interfaces.INPUT_MODE)
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('radio_display.pt'), 'text/html'),
+        (None, None, None, None, interfaces.IRadioWidget),
+        IPageTemplate, name=interfaces.DISPLAY_MODE)
+    # Select Field Widget
+    zope.component.provideAdapter(select.SelectFieldWidget)
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('select_input.pt'), 'text/html'),
+        (None, None, None, None, interfaces.ISelectWidget),
+        IPageTemplate, name=interfaces.INPUT_MODE)
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('select_display.pt'), 'text/html'),
+        (None, None, None, None, interfaces.ISelectWidget),
+        IPageTemplate, name=interfaces.DISPLAY_MODE)
+    # Submit Field Widget
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('submit_input.pt'), 'text/html'),
+        (None, None, None, None, interfaces.ISubmitWidget),
+        IPageTemplate, name=interfaces.INPUT_MODE)
+    # selectwidget helper adapters
+    zope.component.provideAdapter(select.CollectionSelectFieldWidget)
+    zope.component.provideAdapter(select.CollectionChoiceSelectFieldWidget)
+    # Adapter to  convert between field/internal and widget values
+    zope.component.provideAdapter(converter.FieldDataConverter)
+    zope.component.provideAdapter(converter.SequenceDataConverter)
+    zope.component.provideAdapter(converter.FieldWidgetDataConverter)
+    # Adapter for providing terms to radio list and other widgets
+    zope.component.provideAdapter(term.ChoiceTerms)
+    zope.component.provideAdapter(term.BoolTerms)
+    # Adapter to use form.buttons to generate actions
+    zope.component.provideAdapter(button.ButtonActions)
+    # Adapter to use form.handlers to generate handle actions
+    zope.component.provideAdapter(button.ButtonActionHandler)
+    # Error View(s)
+    zope.component.provideAdapter(error.ErrorViewSnippet)
+    zope.component.provideAdapter(error.StandardErrorViewTemplate)
+
+def tearDown(test):
+    setup.placefulTearDown()


Property changes on: z3c.form/trunk/src/z3c/form/testing.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/tests/__init__.py
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/__init__.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/__init__.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+# Make a package.


Property changes on: z3c.form/trunk/src/z3c/form/tests/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/tests/custom_error.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/custom_error.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/custom_error.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,4 @@
+<div class="error">
+  <img src="alert.png" alt="Alert" />
+  <span tal:content="view/message">Error</span>
+</div>


Property changes on: z3c.form/trunk/src/z3c/form/tests/custom_error.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/tests/simple_caredit.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_caredit.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_caredit.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,28 @@
+<html>
+  <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 replace="error/widget/label"
+        />: <tal:block replace="structure error/render" />
+      </li>
+    </ul>
+    <form action=".">
+      <div class="row" tal:repeat="widget view/widgets/values">
+        <b tal:condition="widget/error"
+           tal:content="structure widget/error/render"
+        /><label for=""
+               tal:attributes="for widget/name"
+               tal:content="widget/label" />
+        <input type="text" tal:replace="structure widget/render"
+      /></div>
+      <fieldset>
+        <legend>Owner</legend>
+        <div tal:replace="structure view/owner/render" />
+      </fieldset>
+      <div class="action" tal:repeat="action view/actions/values">
+        <input type="submit" tal:replace="structure action/render"
+      /></div>
+    </form>
+  </body>
+</html>


Property changes on: z3c.form/trunk/src/z3c/form/tests/simple_caredit.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/tests/simple_display.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_display.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_display.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,6 @@
+<html>
+  <body>
+    <div class="row" tal:repeat="widget view/widgets/values">
+      <span tal:replace="structure widget/render" /></div>
+  </body>
+</html>


Property changes on: z3c.form/trunk/src/z3c/form/tests/simple_display.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/tests/simple_edit.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_edit.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_edit.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,24 @@
+<html>
+  <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 replace="error/widget/label"
+        />: <tal:block replace="structure error/render" />
+      </li>
+    </ul>
+    <form action=".">
+      <div class="row" tal:repeat="widget view/widgets/values">
+        <b tal:condition="widget/error"
+           tal:content="structure widget/error/render"
+        /><label for=""
+               tal:attributes="for widget/name"
+               tal:content="widget/label" />
+        <input type="text" tal:replace="structure widget/render"
+      /></div>
+      <div class="action" tal:repeat="action view/actions/values">
+        <input type="submit" tal:replace="structure action/render"
+      /></div>
+    </form>
+  </body>
+</html>


Property changes on: z3c.form/trunk/src/z3c/form/tests/simple_edit.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/tests/simple_owneredit.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_owneredit.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_owneredit.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,18 @@
+<i tal:condition="view/status" tal:content="view/status" />
+<ul tal:condition="view/widgets/errors">
+  <li tal:repeat="error view/widgets/errors">
+    <tal:block replace="error/widget/label"
+    />: <tal:block replace="structure error/render" />
+  </li>
+</ul>
+<div class="row" tal:repeat="widget view/widgets/values">
+  <b tal:condition="widget/error"
+     tal:content="structure widget/error/render"
+  /><label for=""
+         tal:attributes="for widget/name"
+         tal:content="widget/label" />
+  <input type="text" tal:replace="structure widget/render"
+/></div>
+<div class="action" tal:repeat="action view/actions/values">
+  <input type="submit" tal:replace="structure action/render"
+/></div>


Property changes on: z3c.form/trunk/src/z3c/form/tests/simple_owneredit.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/tests/simple_subedit.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_subedit.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_subedit.pt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,15 @@
+<i tal:condition="view/status" tal:content="view/status" />
+<ul tal:condition="view/widgets/errors">
+  <li tal:repeat="error view/widgets/errors">
+    <tal:block replace="error/widget/label"
+    />: <tal:block replace="structure error/render" />
+  </li>
+</ul>
+<div class="row" tal:repeat="widget view/widgets/values">
+  <b tal:condition="widget/error"
+     tal:content="structure widget/error/render"
+  /><label for=""
+         tal:attributes="for widget/name"
+         tal:content="widget/label" />
+  <input type="text" tal:replace="structure widget/render"
+/></div>


Property changes on: z3c.form/trunk/src/z3c/form/tests/simple_subedit.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/tests/test_doc.py
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/test_doc.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/test_doc.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""z3c.form Test Module
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import unittest
+from zope.testing import doctest
+from zope.app.testing import placelesssetup
+
+from z3c.form import testing
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite(
+            '../action.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../datamanager.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../field.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../value.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../validator.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../term.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../error.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../widget.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../button.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../zcml.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../converter.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../form.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../subform.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite(
+            '../util.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        ))


Property changes on: z3c.form/trunk/src/z3c/form/tests/test_doc.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/util.py
===================================================================
--- z3c.form/trunk/src/z3c/form/util.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/util.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,137 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Utilities helpful to the package.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import re
+import types
+import zope.interface
+
+from z3c.form import interfaces
+
+
+_identifier = re.compile('[A-Za-z][a-zA-Z0-9_]*$')
+
+def createId(name):
+    if _identifier.match(name):
+        return str(name).lower()
+    return name.encode('hex')
+
+
+classTypes = type, types.ClassType
+
+def getSpecification(spec, force=False):
+    """Get the specification of the given object.
+
+    If the given object is already a specification acceptable to the component
+    architecture, it is simply returned. This is true for classes
+    and specification objects (which includes interfaces).
+
+    In case of instances, an interface is generated on the fly and tagged onto
+    the object. Then the interface is returned as the specification.
+    """
+    # If the specification is an instance, then we do some magic.
+    if (force or
+        (spec is not None and
+         not zope.interface.interfaces.ISpecification.providedBy(spec)
+         and not isinstance(spec, classTypes)) ):
+        # Step 1: Create an interface
+        iface = zope.interface.interface.InterfaceClass(
+            'IGeneratedForObject_%i' %hash(spec))
+        # Step 2: Directly-provide the interface on the specification
+        zope.interface.alsoProvides(spec, iface)
+        # Step 3: Make the new interface the specification for this instance
+        spec = iface
+    return spec
+
+
+def expandPrefix(prefix):
+    """Expand prefix string by adding a trailing period if needed.
+
+    expandPrefix(p) should be used instead of p+'.' in most contexts.
+    """
+    if prefix and not prefix.endswith('.'):
+        return prefix + '.'
+    return prefix
+
+
+def getWidgetById(form, id):
+    """Get a widget by it's rendered DOM element id."""
+    prefix = form.prefix + form.widgets.prefix
+    if not id.startswith(prefix):
+        raise ValueError("Id %r must start with prefix %r" %(id, prefix))
+    shortName = id[len(prefix):]
+    return form.widgets.get(shortName, None)
+
+
+class Manager(object):
+    """Non-persistent IManager implementation."""
+    zope.interface.implements(interfaces.IManager)
+
+    def __init__(self, *args, **kw):
+        self._data_keys = []
+        self._data_values = []
+        self._data = {}
+
+    def __len__(self):
+        return len(self._data_values)
+
+    def __iter__(self):
+        return iter(self._data_keys)
+
+    def __getitem__(self, name):
+        return self._data[name]
+
+    def get(self, name, default=None):
+        return self._data.get(name, default)
+
+    def keys(self):
+        return self._data_keys
+
+    def values(self):
+        return self._data_values
+
+    def items(self):
+        return [(i, self._data[i]) for i in self._data_keys]
+
+    def __contains__(self, name):
+        return bool(self.get(name))
+
+
+class SelectionManager(Manager):
+    """Non-persisents ISelectionManager implementation."""
+    zope.interface.implements(interfaces.ISelectionManager)
+
+    managerInterface = None
+
+    def __add__(self, other):
+        if not self.managerInterface.providedBy(other):
+            return NotImplemented
+        return self.__class__(self, other)
+
+    def select(self, *names):
+        """See interfaces.ISelectionManager"""
+        return self.__class__(*[self[name] for name in names])
+
+    def omit(self, *names):
+        """See interfaces.ISelectionManager"""
+        return self.__class__(
+            *[item for item in self.values()
+              if item.__name__ not in names])
+
+    def copy(self):
+        """See interfaces.ISelectionManager"""
+        return self.__class__(*self.values())


Property changes on: z3c.form/trunk/src/z3c/form/util.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/util.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/util.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/util.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,48 @@
+=============================
+Utility Functions and Classes
+=============================
+
+This file documents the utility functiona and classes that are otherwise not
+tested.
+
+  >>> from z3c.form import util
+
+
+``getWidgetById(form, id)`` Function
+------------------------------------
+
+Given a form and a widget id, this function extracts the widget for you. First
+we need to create a properly developed form:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IPerson(zope.interface.Interface):
+  ...     name = zope.schema.TextLine(title=u'Name')
+
+  >>> from z3c.form import form, field
+  >>> class AddPerson(form.AddForm):
+  ...     fields = field.Fields(IPerson)
+
+  >>> from z3c.form import testing
+  >>> testing.setupFormDefaults()
+
+  >>> addPerson = AddPerson(None, testing.TestRequest())
+  >>> addPerson.update()
+
+We can now ask for the widget:
+
+  >>> util.getWidgetById(addPerson, 'form.widgets.name')
+  <TextWidget 'form.widgets.name'>
+
+The widget id can be split into a prefix and a widget name. The id must always
+start with the correct prefix, otherwise a value error is raised:
+
+  >>> util.getWidgetById(addPerson, 'myform.widgets.name')
+  Traceback (most recent call last):
+  ...
+  ValueError: Id 'myform.widgets.name' must start with prefix 'form.widgets.'
+
+If the widget is not found but the prefix is correct, ``None`` is returned:
+
+  >>> util.getWidgetById(addPerson, 'form.widgets.myname')


Property changes on: z3c.form/trunk/src/z3c/form/util.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/validator.py
===================================================================
--- z3c.form/trunk/src/z3c/form/validator.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/validator.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,149 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Validator Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema
+
+from z3c.form import interfaces, util
+
+
+class SimpleFieldValidator(object):
+    """Simple Field Validator"""
+    zope.interface.implements(interfaces.IValidator)
+    zope.component.adapts(
+        zope.interface.Interface,
+        zope.interface.Interface,
+        zope.interface.Interface,
+        zope.schema.interfaces.IField,
+        zope.interface.Interface)
+
+    def __init__(self, context, request, view, field, widget):
+        self.context = context
+        self.request = request
+        self.view = view
+        self.field = field
+        self.widget = widget
+
+    def validate(self, value):
+        """See interfaces.IValidator"""
+        field = self.field
+        if self.context is not None:
+            field = field.bind(self.context)
+        return field.validate(value)
+
+    def __repr__(self):
+        return "<%s for %s['%s']>" %(
+            self.__class__.__name__,
+            self.field.interface.getName(),
+            self.field.__name__)
+
+
+def WidgetValidatorDiscriminators(
+    validator, context=None, request=None, view=None, field=None, widget=None):
+    zope.component.adapter(
+        util.getSpecification(context),
+        util.getSpecification(request),
+        util.getSpecification(view),
+        util.getSpecification(field),
+        util.getSpecification(widget))(validator)
+
+
+class NoInputData(zope.interface.Invalid):
+    """There was no input data because:
+
+    - It wasn't asked for
+
+    - It wasn't entered by the user
+
+    - It was entered by the user, but the value entered was invalid
+
+    This exception is part of the internal implementation of checkInvariants.
+
+    """
+
+class Data(object):
+
+    def __init__(self, schema, data):
+        self._Data_data___ = data
+        self._Data_schema___ = schema
+
+    def __getattr__(self, name):
+        schema = self._Data_schema___
+        data = self._Data_data___
+        try:
+            field = schema[name]
+        except KeyError:
+            raise AttributeError(name)
+        # If the found field is a method, then raise an error.
+        if zope.interface.interfaces.IMethod.providedBy(field):
+            raise RuntimeError("Data value is not a schema field", name)
+        # Try to get the value for the field
+        value = data.get(name, data)
+        if value is data:
+            raise NoInputData(name)
+        # Optimization: Once we now we have a good value, set it as an
+        # attribute for faster access.
+        setattr(self, name, value)
+        return value
+
+
+class InvariantsValidator(object):
+    """Simple Field Validator"""
+    zope.interface.implements(interfaces.IManagerValidator)
+    zope.component.adapts(
+        zope.interface.Interface,
+        zope.interface.Interface,
+        zope.interface.Interface,
+        zope.interface.interfaces.IInterface,
+        zope.interface.Interface)
+
+    def __init__(self, context, request, view, schema, manager):
+        self.context = context
+        self.request = request
+        self.view = view
+        self.schema = schema
+        self.manager = manager
+
+    def validate(self, data):
+        """See interfaces.IManagerValidator"""
+        return self.validateObject(Data(self.schema, data))
+
+    def validateObject(self, object):
+        errors = []
+        try:
+            self.schema.validateInvariants(object, errors)
+        except zope.interface.Invalid:
+            pass # Just collect the errors
+
+        return tuple([error for error in errors
+                      if not isinstance(error, NoInputData)])
+
+    def __repr__(self):
+        return '<%s for %s>' %(self.__class__.__name__, self.schema.getName())
+
+
+def WidgetsValidatorDiscriminators(
+    validator,
+    context=None, request=None, view=None, schema=None, manager=None):
+    zope.component.adapter(
+        util.getSpecification(context),
+        util.getSpecification(request),
+        util.getSpecification(view),
+        util.getSpecification(schema),
+        util.getSpecification(manager))(validator)


Property changes on: z3c.form/trunk/src/z3c/form/validator.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/validator.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/validator.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/validator.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,315 @@
+==========
+Validators
+==========
+
+Validators are components that validate submitted data. This is certainly not
+a new concept, but in the previous form frameworks validation was hidden in
+many places:
+
+* Field/Widget Validation
+
+  The schema field consists of a ``validate()`` method. Validation is
+  automatically invoked when converting a unicode string to a field value
+  using ``fromUnicode()``. This makes it very hard to customize the field
+  validation. No hooks were provided to exert dditional restriction at the
+  presentation level.
+
+* Schema/Form Validation
+
+  This type of validation was not supported at all initially. ``zope.formlib``
+  fixed this problem by validating against schema invariants. While this was a
+  first good step, it still made it hard to customize validators, since it
+  required touching the base implementations of the forms.
+
+* Action Validation
+
+  ``zope.formlib`` supports the notion of action validatos. Actions have a
+  success and failure handler. If the validation succeeds, the success handler
+  is called, otherwise the failure handler is chosen. We believe that this
+  design was ill-conceived, especially the default, which required the data to
+  completely validate in order for the action to successful. There are many
+  actions that do not even care about the data in the form, such as "Help",
+  "Cancel" and "Reset" buttons. Thus validation should be part of the data
+  retrieval process and not the action.
+
+For me, the primary goals of the validator framework are as follows:
+
+* Assert additional restrictions on the data at the presentation
+  level.
+
+  There are several use cases for this. Sometimes clients desire additional
+  restrictions on data for their particular version of the software. It is not
+  always desireable to adjust the model for this client, since the framework
+  knows how to handle the less restrictive case anyways. In another case,
+  additional restrictions might be applied to a particular form due to limited
+  restrictions.
+
+* Make validation pluggable.
+
+  Like most other components of this package, it should be possible to control
+  the validation adapters at a fine grained level.
+
+  * Widgets: context, request, view, field[1], widget
+
+  * Widget Managers: context, request, view, schema[2], manager
+
+  [1].. This is optional, since widgets must not necessarily have fields.
+  [2].. This is optional, since widget managers must not necessarily have
+        manage field widgets and thus know about schemas.
+
+* Provide good defaults that behave sensibly.
+
+  God defaults are, like in anywhere in this pacakge, very important. We have
+  chosen to implement the ``zope.formlib`` behavior as the default, since it
+  worked very well -- with exception of action validation, of course.
+
+For this package, we have decided to support validators at the widget and
+widget manager level. By default the framework only supports field widgets,
+since the validation of field-absent widgets is generally not
+well-defined. Thus, we first need to create a schema.
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IPerson(zope.interface.Interface):
+  ...     login = zope.schema.TextLine(
+  ...         title=u'Login',
+  ...         min_length=1,
+  ...         max_length=10)
+  ...
+  ...     email = zope.schema.TextLine(
+  ...         title=u'E-mail')
+  ...
+  ...     @zope.interface.invariant
+  ...     def isLoginPartOfEmail(person):
+  ...         if not person.email.startswith(person.login):
+  ...             raise zope.interface.Invalid("The login not part of email.")
+
+
+Widget Validators
+-----------------
+
+Widget validators only validate the data of one particular widget. The
+validated value is always assumed to be an internal value and not a widget
+value.
+
+By default, the system uses the simple field validator, which simply uses the
+``validate()`` method of the field. For instantiation, all validators have the
+following signature for its discriminators: context, request, view, field, and
+widget
+
+  >>> from z3c.form import validator
+  >>> simple = validator.SimpleFieldValidator(
+  ...     None, None, None, IPerson['login'], None)
+
+A validator has a single method ``validate()``. When the validation is
+successful, ``None`` is returned:
+
+  >>> simple.validate(u'srichter')
+
+A validation error is raised, when the validation fails:
+
+  >>> simple.validate(u'StephanCaveman3')
+  Traceback (most recent call last):
+  ...
+  TooLong: (u'StephanCaveman3', 10)
+
+Let's now create a validator that also requires at least 1 numerical character
+in the login name:
+
+  >>> import re
+
+  >>> class LoginValidator(validator.SimpleFieldValidator):
+  ...
+  ...     def validate(self, value):
+  ...         super(LoginValidator, self).validate(value)
+  ...         if re.search('[0-9]', value) is None:
+  ...             raise zope.interface.Invalid('No numerical character found.')
+
+Let's now try our new validator:
+
+  >>> login = LoginValidator(None, None, None, IPerson['login'], None)
+
+  >>> login.validate(u'srichter1')
+
+  >>> login.validate(u'srichter')
+  Traceback (most recent call last):
+  ...
+  Invalid: No numerical character found.
+
+We can now register the validator with the component architecture, ...
+
+  >>> import zope.component
+  >>> zope.component.provideAdapter(LoginValidator)
+
+and look up the adapter using the usual way:
+
+  >>> from z3c.form import interfaces
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (None, None, None, IPerson['login'], None),
+  ...     interfaces.IValidator)
+  <LoginValidator for IPerson['login']>
+
+Unfortunately, the adapter is now registered for all fields, so that the
+E-mail field also has this restriction (which is okay in this case, but not
+generally):
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (None, None, None, IPerson['email'], None),
+  ...     interfaces.IValidator)
+  <LoginValidator for IPerson['email']>
+
+The validator module provides a helper function to set the discriminators for
+a validator, which can include instances:
+
+  >>> validator.WidgetValidatorDiscriminators(
+  ...     LoginValidator, field=IPerson['login'])
+
+Let's now clean up the component architecture and register the login validator
+again:
+
+  >>> from zope.testing import cleanup
+  >>> cleanup.cleanUp()
+
+  >>> zope.component.provideAdapter(LoginValidator)
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (None, None, None, IPerson['login'], None),
+  ...     interfaces.IValidator)
+  <LoginValidator for IPerson['login']>
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (None, None, None, IPerson['email'], None),
+  ...     interfaces.IValidator)
+
+
+Widget Manager Validators
+-------------------------
+
+The widget manager validator, while similar in spirit, works somewhat
+different. The discriminators of the widget manager validator are: context,
+request, view, schema, and manager.
+
+A simple default implementation is provided that checks the invariants of the
+schemas:
+
+  >>> invariants = validator.InvariantsValidator(
+  ...     None, None, None, IPerson, None)
+
+Widget manager validators have the option to validate a data dictionary,
+
+  >>> invariants.validate(
+  ...     {'login': u'srichter', 'email': u'srichter at foo.com'})
+  ()
+
+or an object implementing the schema:
+
+  >>> class Person(object):
+  ...     login = u'srichter'
+  ...     email = u'srichter at foo.com'
+  >>> stephan = Person()
+
+  >>> invariants.validateObject(stephan)
+  ()
+
+Since multiple errors can occur during the validation process, all errors are
+collected in a tuple, which is returned. If the tuple is empty, the validation
+was successful. Let's now generate a failure:
+
+  >>> errors = invariants.validate(
+  ...     {'login': u'srichter', 'email': u'strichter at foo.com'})
+
+  >>> errors
+  (<zope.interface.exceptions.Invalid instance at ...>,)
+
+  >>> str(errors[0])
+  'The login not part of email.'
+
+Let's now have a look at writing a custom validator. In this case, we want to
+ensure that the E-mail address is at most twice as long as the login:
+
+  >>> class CustomValidator(validator.InvariantsValidator):
+  ...     def validateObject(self, obj):
+  ...         errors = super(CustomValidator, self).validateObject(obj)
+  ...         if len(obj.email) > 2 * len(obj.login):
+  ...             errors += (zope.interface.Invalid('Email too long.'),)
+  ...         return errors
+
+Since the ``validate()`` method of ``InvatiantsValidator`` simply uses
+``validateObject()`` it is enough to only override ``validateObject()``. Now
+we can use the validator:
+
+  >>> custom = CustomValidator(
+  ...     None, None, None, IPerson, None)
+
+  >>> custom.validate(
+  ...     {'login': u'srichter', 'email': u'srichter at foo.com'})
+  ()
+  >>> custom.validate(
+  ...     {'login': u'srichter', 'email': u'srichter at foobar.com'})
+  (<zope.interface.exceptions.Invalid instance at ...>,)
+
+To register the custom validator only for this schema, we hace to use the
+discriminator generator again.
+
+  >>> from z3c.form import util
+  >>> validator.WidgetsValidatorDiscriminators(
+  ...     CustomValidator, schema=util.getSpecification(IPerson, force=True))
+
+Note: Of course we could have used the ``zope.component.adapts()`` function
+      from within the class, but I think it is too tediuos, since you have to
+      specify all discriminators and not only the specific ones you are
+      interested in.
+
+After registering the validator,
+
+  >>> zope.component.provideAdapter(CustomValidator)
+
+it becomes the validator for this schema:
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (None, None, None, IPerson, None), interfaces.IManagerValidator)
+  <CustomValidator for IPerson>
+
+  >>> class ICar(zope.interface.Interface):
+  ...     pass
+  >>> zope.component.queryMultiAdapter(
+  ...     (None, None, None, ICar, None), interfaces.IManagerValidator)
+
+
+The Data Wrapper
+----------------
+
+The ``Data`` class provides a wrapper to present a dictionary as a class
+instance. This is used to check for invariants, which always expect an
+object. While the common use cases of the data wrapper are well tested in the
+code above, there are some corner cases that need to be addressed.
+
+So let's start by creating a data object:
+
+  >>> data = validator.Data(IPerson, {'login': 'srichter', 'other': 1})
+
+When we try to access a name that is not in the schema, we get an attribute
+error:
+
+  >>> data.address
+  Traceback (most recent call last):
+  ...
+  AttributeError: address
+
+  >>> data.other
+  Traceback (most recent call last):
+  ...
+  AttributeError: other
+
+If the field found is a method, then a runtime error is raised:
+
+  >>> class IExtendedPerson(IPerson):
+  ...     def compute():
+  ...         """Compute something."""
+
+  >>> data = validator.Data(IExtendedPerson, {'compute': 1})
+  >>> data.compute
+  Traceback (most recent call last):
+  ...
+  RuntimeError: ('Data value is not a schema field', 'compute')


Property changes on: z3c.form/trunk/src/z3c/form/validator.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/value.py
===================================================================
--- z3c.form/trunk/src/z3c/form/value.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/value.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,106 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Attribute Value Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.component
+
+from z3c.form import interfaces, util
+
+class StaticValue(object):
+    """Static value adapter."""
+
+    zope.interface.implements(interfaces.IValue)
+
+    def __init__(self, value):
+        self.value = value
+
+    def get(self):
+        return self.value
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.value)
+
+
+class ComputedValue(object):
+    """Static value adapter."""
+
+    zope.interface.implements(interfaces.IValue)
+
+    def __init__(self, func):
+        self.func = func
+
+    def get(self):
+        return self.func(self)
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.get())
+
+
+class ValueFactory(object):
+    """Computed value factory."""
+
+    def __init__(self, value, valueClass, discriminators):
+        self.value = value
+        self.valueClass = valueClass
+        self.discriminators = discriminators
+
+    def __call__(self, *args):
+        sv = self.valueClass(self.value)
+        for name, value in zip(self.discriminators, args):
+            setattr(sv, name, value)
+        return sv
+
+
+class ValueCreator(object):
+    """Base class for value creator"""
+
+    valueClass = StaticValue
+
+    def __init__(self, discriminators):
+        self.discriminators = discriminators
+
+    def __call__(self, value, **kws):
+        # Step 1: Check that the keyword argument names match the
+        #         discriminators
+        if set(kws).difference(set(self.discriminators)):
+            raise ValueError(
+                'One or more keyword arguments did not match the '
+                'discriminators.')
+        # Step 2: Create an attribute value factory
+        factory = ValueFactory(value, self.valueClass, self.discriminators)
+        # Step 3: Build the adaptation signature
+        signature = []
+        for disc in self.discriminators:
+            spec = util.getSpecification(kws.get(disc))
+            signature.append(spec)
+        # Step 4: Assert the adaptation signature onto the factory
+        zope.component.adapter(*signature)(factory)
+        zope.interface.implementer(interfaces.IValue)(factory)
+        return factory
+
+
+class StaticValueCreator(ValueCreator):
+    """Creates static value."""
+
+    valueClass = StaticValue
+
+
+class ComputedValueCreator(ValueCreator):
+    """Creates computed value."""
+
+    valueClass = ComputedValue


Property changes on: z3c.form/trunk/src/z3c/form/value.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/value.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/value.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/value.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,215 @@
+========================
+Attribute Value Adapters
+========================
+
+In advanced, highly customized projects it is often the case that a property
+wants to be overridden for a particular customer in a particular case. A prime
+example is the label of a widget. Until this implementation of a form
+framework was written, widgets only could get their label from the field they
+were representing. Thus, wanting to change the label of a widget meant
+implementing a custom schema and re-registering the form in question for the
+custom schema. It is needless to say that this was very annoying.
+
+For this form framework, we are providing multiple levels of customization. 
+The user has the choice to change the value of an attribute through attribute 
+assignment or adapter lookup. The chronological order of an attribute value 
+assignment is as follows:
+
+1. During initialization or right thereafter, the attribute value can be set
+   by direct attribute assignment, i.e. ``obj.attr = value``
+
+2. While updating the object, an adapter is looked up for the attribute. If an
+   adapter is found, the attribute value will be overridden. Of course, if the
+   object does not have an ``update()`` method, one can choose another
+   location to do the adapter lookup.
+
+3. After updating, the developer again has the choice to override the attribute
+   allowing granularity above and beyond the adapter.
+
+The purpose of this module is to implement the availability of an attribute
+value using an adapter.
+
+  >>> from z3c.form import value
+
+The module provides helper functions and classes, to create those adapters
+with as little code as possible.
+
+
+Static Value Adapter
+--------------------
+
+To demonstrate the static value adapter, let's go back to our widget label
+example. Let's create a couple of simple widgets and forms first:
+
+  >>> class TextWidget(object):
+  ...    label = u'Text'
+  >>> tw = TextWidget()
+
+  >>> class CheckboxWidget(object):
+  ...    label = u'Checkbox'
+  >>> cbw = CheckboxWidget()
+
+  >>> class Form1(object):
+  ...    pass
+  >>> form1 = Form1()
+
+  >>> class Form2(object):
+  ...    pass
+  >>> form2 = Form2()
+
+We can now create a generic widget property adapter:
+
+  >>> WidgetAttribute = value.StaticValueCreator(
+  ...     discriminators = ('widget', 'view')
+  ...     )
+
+Creating the widget attribute object, using the helper function above, allows
+us to define the discriminators (or the granulatrity) that can be used to
+control a widget attribute by an adapter. In our case this is the widget
+itself and the form/view in which the widget is displayed. In other words, it
+will be possible to register a widget attribute value specifically for a
+particular widget, a particular form, or a combination thereof.
+
+Let's now create a label attribute adapter for the text widget, since our
+customer does not like the default label:
+
+  >>> TextLabel = WidgetAttribute(u'My Text', widget=TextWidget)
+
+The first argument of any static attribute value is the value itself, in our
+case the string "My Text". The following keyword arguments are the
+discriminators specified in the property factory. Since we only specify the
+widget, the label will be available to all widgets. But first we have to
+register the adapter:
+
+  >>> import zope.component
+  >>> zope.component.provideAdapter(TextLabel, name='label')
+
+The name of the adapter is the attribute name of the widget. Let's now see how
+we can get the label:
+
+  >>> from z3c.form import interfaces
+  >>> staticValue = zope.component.getMultiAdapter(
+  ...     (tw, form1), interfaces.IValue, name='label')
+  >>> staticValue
+  <StaticValue u'My Text'>
+
+The resulting value object has one public method ``get()``, which returns the
+actual value:
+
+  >>> staticValue.get()
+  u'My Text'
+
+As we said before, the value should be available to all forms, ...
+
+  >>> zope.component.getMultiAdapter(
+  ...     (tw, form2), interfaces.IValue, name='label')
+  <StaticValue u'My Text'>
+
+... but only to the ``TextWidget``:
+
+  >>> zope.component.getMultiAdapter(
+  ...     (cbw, form2), interfaces.IValue, name='label')
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: ((<CheckboxWidget...>, <Form2...>),
+                          <InterfaceClass ...IValue>, 'label')
+
+By the way, the attribute adapter factory notices, if you specify a
+discriminator that was not specified:
+
+  >>> WidgetAttribute(u'My Text', form=Form2)
+  Traceback (most recent call last):
+  ...
+  ValueError: One or more keyword arguments did not match the discriminators.
+
+  >>> WidgetAttribute.discriminators
+  ('widget', 'view')
+
+
+Computed Value Adapter
+----------------------
+
+A second implementation of the value adapter in the evaluated value, where one
+can specify a function that computes the value to be returned. The only
+argument to the function is the value adapter instance itself, which then
+contains all the discriminators as specified when creating the generic widget
+attribute factory. Let's take the same use case as before, but generating the
+value as follows:
+
+  >>> def getLabelValue(adapter):
+  ...     return adapter.widget.label + ' (1)'
+
+Now we create the value adapter for it:
+
+  >>> WidgetAttribute = value.ComputedValueCreator(
+  ...     discriminators = ('widget', 'view')
+  ...     )
+
+  >>> TextLabel = WidgetAttribute(getLabelValue, widget=TextWidget)
+
+After registering the adapter, ...
+
+  >>> zope.component.provideAdapter(TextLabel, name='label')
+
+we now get the answers:
+
+  >>> from z3c.form import interfaces
+  >>> zope.component.getMultiAdapter(
+  ...     (tw, form1), interfaces.IValue, name='label')
+  <ComputedValue u'Text (1)'>
+
+
+__Note__: The two implementations of the attribute value adapters are not
+          meant to be canonical features that must always be used. The API is
+          kept simple to allow you to quickly implement your own value
+          adapter.
+
+
+Automatic Interface Assignment
+------------------------------
+
+Oftentimes it is desirable to register an attribute value adapter for an
+instance. A good example is a field, so let's create a small schema:
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IPerson(zope.interface.Interface):
+  ...     firstName = zope.schema.TextLine(title=u'First Name')
+  ...     lastName = zope.schema.TextLine(title=u'Last Name')
+
+The customer now requires that the title -- which is the basis of the widget
+label for field widgets -- of the last name should be "Surname". Until now the
+option was to write a new schema changing the title. With this attribute value
+module, as introduced thus far, we would need to provide a special interface
+for the last name field, since registering a label adapter for all text fields
+would also change the first name.
+
+Before demonstrating the solution to this problem, let's first create a field
+attribute value:
+
+  >>> FieldAttribute = value.StaticValueCreator(
+  ...     discriminators = ('field',)
+  ...     )
+
+We can now create the last name title, changing only the title of the
+``lastName`` field. Instead of passing in an interface of class as the field
+discriminator, we pass in the field instance:
+
+  >>> LastNameTitle = FieldAttribute(u'Surname', field=IPerson['lastName'])
+
+The attribute value factory will automatically detect instances, create an
+interface on the fly, directly provide it on the field and makes it the
+discriminator interface for the adapter registratioon.
+
+So after registering the adapter, ...
+
+  >>> zope.component.provideAdapter(LastNameTitle, name='title')
+
+the adapter is only available to the last name field and not the first name:
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (IPerson['lastName'],), interfaces.IValue, name='title')
+  <StaticValue u'Surname'>
+
+  >>> zope.component.queryMultiAdapter(
+  ...     (IPerson['firstName'],), interfaces.IValue, name='title')


Property changes on: z3c.form/trunk/src/z3c/form/value.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/widget-graph.dot
===================================================================
--- z3c.form/trunk/src/z3c/form/widget-graph.dot	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/widget-graph.dot	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,40 @@
+digraph WidgetComponents {
+  Widgets -> FieldWidget [
+      color="blue", label="contains", fontcolor="blue"];
+  Widgets -> ErrorViewSnippet [
+      label="errors\n[list of]", fontname="Courier", fontsize="11"];
+  Widgets -> ManagerValidator [
+      color="red", label="uses", fontcolor="red"];
+  ManagerValidator -> ValidationError [
+      color="red", label="causes", fontcolor="red"];
+  FieldWidget -> SchemaField [
+      label="field", fontname="Courier", fontsize="11"];
+  FieldWidget -> DataConverter [
+      color="red", label="uses", fontcolor="red"];
+  DataConverter -> ValidationError [
+      color="red", label="causes", fontcolor="red"];
+  FieldWidget -> DataManager [
+      color="red", label="uses", fontcolor="red"];
+  FieldWidget -> Validator [
+      color="red", label="uses", fontcolor="red"];
+  Validator -> ValidationError [
+      color="red", label="causes", fontcolor="red"];
+  ErrorViewSnippet -> ValidationError [
+      label="error", fontname="Courier", fontsize="11"];
+  FieldWidget -> ErrorViewSnippet [
+      label="error", fontname="Courier", fontsize="11"];
+
+  FieldWidget -> Widget [
+      arrowhead="onormal", fontname="Courier", fontsize="11"];
+
+  SequenceWidget -> FieldWidget [
+      arrowhead="onormal", fontname="Courier", fontsize="11"];
+  SequenceWidget -> Terms [
+      label="terms", fontname="Courier", fontsize="11"];
+
+  SchemaField [
+      label="zope.schema\nField"];
+
+  {rank=same; ManagerValidator; Widgets;}
+  {rank=same; Widget; SequenceWidget; FieldWidget;}
+}

Added: z3c.form/trunk/src/z3c/form/widget-graph.png
===================================================================
(Binary files differ)


Property changes on: z3c.form/trunk/src/z3c/form/widget-graph.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: z3c.form/trunk/src/z3c/form/widget-graph.ps
===================================================================
--- z3c.form/trunk/src/z3c/form/widget-graph.ps	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/widget-graph.ps	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,757 @@
+%!PS-Adobe-2.0
+%%Creator: dot version 2.8 (Sun Aug 13 01:37:46 UTC 2006)
+%%For: (srichter) Stephan Richter
+%%Title: WidgetComponents
+%%Pages: (atend)
+%%BoundingBox: 36 36 838 366
+%%EndComments
+save
+%%BeginProlog
+/DotDict 200 dict def
+DotDict begin
+
+/setupLatin1 {
+mark
+/EncodingVector 256 array def
+ EncodingVector 0
+
+ISOLatin1Encoding 0 255 getinterval putinterval
+EncodingVector 45 /hyphen put
+
+% Set up ISO Latin 1 character encoding
+/starnetISO {
+        dup dup findfont dup length dict begin
+        { 1 index /FID ne { def }{ pop pop } ifelse
+        } forall
+        /Encoding EncodingVector def
+        currentdict end definefont
+} def
+/Times-Roman starnetISO def
+/Times-Italic starnetISO def
+/Times-Bold starnetISO def
+/Times-BoldItalic starnetISO def
+/Helvetica starnetISO def
+/Helvetica-Oblique starnetISO def
+/Helvetica-Bold starnetISO def
+/Helvetica-BoldOblique starnetISO def
+/Courier starnetISO def
+/Courier-Oblique starnetISO def
+/Courier-Bold starnetISO def
+/Courier-BoldOblique starnetISO def
+cleartomark
+} bind def
+
+%%BeginResource: procset graphviz 0 0
+/coord-font-family /Times-Roman def
+/default-font-family /Times-Roman def
+/coordfont coord-font-family findfont 8 scalefont def
+
+/InvScaleFactor 1.0 def
+/set_scale {
+	dup 1 exch div /InvScaleFactor exch def
+	dup scale
+} bind def
+
+% styles
+/solid { [] 0 setdash } bind def
+/dashed { [9 InvScaleFactor mul dup ] 0 setdash } bind def
+/dotted { [1 InvScaleFactor mul 6 InvScaleFactor mul] 0 setdash } bind def
+/invis {/fill {newpath} def /stroke {newpath} def /show {pop newpath} def} bind def
+/bold { 2 setlinewidth } bind def
+/filled { } bind def
+/unfilled { } bind def
+/rounded { } bind def
+/diagonals { } bind def
+
+% hooks for setting color 
+/nodecolor { sethsbcolor } bind def
+/edgecolor { sethsbcolor } bind def
+/graphcolor { sethsbcolor } bind def
+/nopcolor {pop pop pop} bind def
+
+/beginpage {	% i j npages
+	/npages exch def
+	/j exch def
+	/i exch def
+	/str 10 string def
+	npages 1 gt {
+		gsave
+			coordfont setfont
+			0 0 moveto
+			(\() show i str cvs show (,) show j str cvs show (\)) show
+		grestore
+	} if
+} bind def
+
+/set_font {
+	findfont exch
+	scalefont setfont
+} def
+
+% draw aligned label in bounding box aligned to current point
+/alignedtext {			% width adj text
+	/text exch def
+	/adj exch def
+	/width exch def
+	gsave
+		width 0 gt {
+			text stringwidth pop adj mul 0 rmoveto
+		} if
+		[] 0 setdash
+		text show
+	grestore
+} def
+
+/boxprim {				% xcorner ycorner xsize ysize
+		4 2 roll
+		moveto
+		2 copy
+		exch 0 rlineto
+		0 exch rlineto
+		pop neg 0 rlineto
+		closepath
+} bind def
+
+/ellipse_path {
+	/ry exch def
+	/rx exch def
+	/y exch def
+	/x exch def
+	matrix currentmatrix
+	newpath
+	x y translate
+	rx ry scale
+	0 0 1 0 360 arc
+	setmatrix
+} bind def
+
+/endpage { showpage } bind def
+/showpage { } def
+
+/layercolorseq
+	[	% layer color sequence - darkest to lightest
+		[0 0 0]
+		[.2 .8 .8]
+		[.4 .8 .8]
+		[.6 .8 .8]
+		[.8 .8 .8]
+	]
+def
+
+/layerlen layercolorseq length def
+
+/setlayer {/maxlayer exch def /curlayer exch def
+	layercolorseq curlayer 1 sub layerlen mod get
+	aload pop sethsbcolor
+	/nodecolor {nopcolor} def
+	/edgecolor {nopcolor} def
+	/graphcolor {nopcolor} def
+} bind def
+
+/onlayer { curlayer ne {invis} if } def
+
+/onlayers {
+	/myupper exch def
+	/mylower exch def
+	curlayer mylower lt
+	curlayer myupper gt
+	or
+	{invis} if
+} def
+
+/curlayer 0 def
+
+%%EndResource
+%%EndProlog
+%%BeginSetup
+14 default-font-family set_font
+1 setmiterlimit
+% /arrowlength 10 def
+% /arrowwidth 5 def
+
+% make sure pdfmark is harmless for PS-interpreters other than Distiller
+/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse
+% make '<<' and '>>' safe on PS Level 1 devices
+/languagelevel where {pop languagelevel}{1} ifelse
+2 lt {
+    userdict (<<) cvn ([) cvn load put
+    userdict (>>) cvn ([) cvn load put
+} if
+
+%%EndSetup
+%%Page: 1 1
+%%PageBoundingBox: 36 36 838 366
+%%PageOrientation: Portrait
+gsave
+36 36 802 330 boxprim clip newpath
+36 36 translate
+0 0 1 beginpage
+1.0000 set_scale
+4 4 translate 0 rotate
+0.000 0.000 1.000 graphcolor
+0.000 0.000 1.000 graphcolor
+newpath -6 -6 moveto
+-6 328 lineto
+800 328 lineto
+800 -6 lineto
+closepath
+fill
+0.000 0.000 1.000 graphcolor
+newpath -6 -6 moveto
+-6 328 lineto
+800 328 lineto
+800 -6 lineto
+closepath
+stroke
+0.000 0.000 0.000 graphcolor
+14.00 /Times-Roman set_font
+% Widgets
+gsave 10 dict begin
+442 304 36 18 ellipse_path
+stroke
+gsave 10 dict begin
+418 299 moveto
+(Widgets)
+[12.96 3.84 6.96 6.72 6 3.84 5.52]
+xshow
+end grestore
+end grestore
+% FieldWidget
+gsave 10 dict begin
+317 216 47 18 ellipse_path
+stroke
+gsave 10 dict begin
+282 211 moveto
+(FieldWidget)
+[7.44 3.84 6.24 3.84 6.96 12.96 3.84 6.96 6.72 6 3.84]
+xshow
+end grestore
+end grestore
+% Widgets->FieldWidget
+gsave 10 dict begin
+0.667 1.000 1.000 edgecolor
+newpath 421 289 moveto
+401 275 371 254 348 238 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.667 1.000 1.000 edgecolor
+newpath 350 235 moveto
+340 232 lineto
+346 241 lineto
+closepath
+fill
+0.667 1.000 1.000 edgecolor
+newpath 350 235 moveto
+340 232 lineto
+346 241 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+392 255 moveto
+(contains)
+[6.24 6.96 6.96 4.08 6.24 3.84 6.96 5.52]
+xshow
+end grestore
+end grestore
+% ErrorViewSnippet
+gsave 10 dict begin
+532 117 64 18 ellipse_path
+stroke
+gsave 10 dict begin
+480 112 moveto
+(ErrorViewSnippet)
+[8.64 5.28 4.8 6.96 4.8 9.84 3.84 5.76 10.08 7.68 6.96 3.84 6.96 6.96 6 3.84]
+xshow
+end grestore
+end grestore
+% Widgets->ErrorViewSnippet
+newpath 451 286 moveto
+458 272 468 252 477 234 curveto
+492 203 508 168 520 144 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 523 146 moveto
+524 135 lineto
+517 143 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 523 146 moveto
+524 135 lineto
+517 143 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+504 218 moveto
+(errors)
+[6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+495 205 moveto
+([list of])
+[6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% ManagerValidator
+gsave 10 dict begin
+730 304 64 18 ellipse_path
+stroke
+gsave 10 dict begin
+678 299 moveto
+(ManagerValidator)
+[12.48 6.24 6.96 6.24 6.72 6.24 4.8 8.88 6.24 3.84 3.84 6.96 6.24 3.84 6.96 4.8]
+xshow
+end grestore
+end grestore
+% Widgets->ManagerValidator
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 479 304 moveto
+523 304 599 304 655 304 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 655 308 moveto
+665 304 lineto
+655 301 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 655 308 moveto
+665 304 lineto
+655 301 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+573 307 moveto
+(uses)
+[6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% FieldWidget->ErrorViewSnippet
+newpath 347 202 moveto
+384 185 447 156 490 136 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 491 139 moveto
+499 132 lineto
+488 133 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 491 139 moveto
+499 132 lineto
+488 133 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+425 168 moveto
+(error)
+[6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% SchemaField
+gsave 10 dict begin
+276 117 51 28 ellipse_path
+stroke
+gsave 10 dict begin
+240 120 moveto
+(zope.schema)
+[6.24 6.96 6.96 6.24 3.6 5.52 6 6.96 6.24 10.8 6.24]
+xshow
+261 104 moveto
+(Field)
+[7.44 3.84 6.24 3.84 6.96]
+xshow
+end grestore
+end grestore
+% FieldWidget->SchemaField
+newpath 309 198 moveto
+304 186 298 169 292 154 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 295 153 moveto
+288 145 lineto
+289 156 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 295 153 moveto
+288 145 lineto
+289 156 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+303 168 moveto
+(field)
+[6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% DataConverter
+gsave 10 dict begin
+55 117 55 18 ellipse_path
+stroke
+gsave 10 dict begin
+13 112 moveto
+(DataConverter)
+[10.08 6.24 4.08 6.24 9.36 6.96 6.48 6.48 6.24 5.04 3.84 6.24 4.8]
+xshow
+end grestore
+end grestore
+% FieldWidget->DataConverter
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 281 204 moveto
+241 191 175 169 119 146 curveto
+112 143 104 139 96 136 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 98 133 moveto
+87 132 lineto
+95 139 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 98 133 moveto
+87 132 lineto
+95 139 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+212 167 moveto
+(uses)
+[6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% DataManager
+gsave 10 dict begin
+398 117 52 18 ellipse_path
+stroke
+gsave 10 dict begin
+359 112 moveto
+(DataManager)
+[10.08 6.24 4.08 6.24 12.48 6.24 6.96 6.24 6.72 6.24 4.8]
+xshow
+end grestore
+end grestore
+% FieldWidget->DataManager
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 331 199 moveto
+344 184 363 161 377 143 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 380 145 moveto
+383 135 lineto
+374 141 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 380 145 moveto
+383 135 lineto
+374 141 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+358 167 moveto
+(uses)
+[6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% Validator
+gsave 10 dict begin
+167 117 39 18 ellipse_path
+stroke
+gsave 10 dict begin
+140 112 moveto
+(Validator)
+[8.88 6.24 3.84 3.84 6.96 6.24 3.84 6.96 4.8]
+xshow
+end grestore
+end grestore
+% FieldWidget->Validator
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 293 200 moveto
+268 183 227 156 199 138 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 200 135 moveto
+190 132 lineto
+196 140 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 200 135 moveto
+190 132 lineto
+196 140 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+265 167 moveto
+(uses)
+[6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% Widget
+gsave 10 dict begin
+434 216 34 18 ellipse_path
+stroke
+gsave 10 dict begin
+413 211 moveto
+(Widget)
+[12.96 3.84 6.96 6.72 6 3.84]
+xshow
+end grestore
+end grestore
+% FieldWidget->Widget
+newpath 364 216 moveto
+373 216 381 216 390 216 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+newpath 390 220 moveto
+400 216 lineto
+390 213 lineto
+closepath
+stroke
+end grestore
+% ValidationError
+gsave 10 dict begin
+311 18 58 18 ellipse_path
+stroke
+gsave 10 dict begin
+266 13 moveto
+(ValidationError)
+[8.88 6.24 3.84 3.84 6.96 6.24 3.84 3.84 6.96 6.96 8.64 5.28 4.8 6.96 4.8]
+xshow
+end grestore
+end grestore
+% ErrorViewSnippet->ValidationError
+newpath 498 102 moveto
+459 84 396 56 353 37 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 355 34 moveto
+344 33 lineto
+352 40 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 355 34 moveto
+344 33 lineto
+352 40 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+429 58 moveto
+(error)
+[6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+% ManagerValidator->ValidationError
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 732 286 moveto
+736 244 740 141 685 88 curveto
+642 47 473 28 378 22 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 378 19 moveto
+368 21 lineto
+378 25 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 378 19 moveto
+368 21 lineto
+378 25 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+727 167 moveto
+(causes)
+[6.24 6.24 6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% DataConverter->ValidationError
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 87 102 moveto
+97 98 109 92 119 88 curveto
+153 72 161 67 195 54 curveto
+216 46 238 39 257 33 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 258 36 moveto
+267 30 lineto
+256 30 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 258 36 moveto
+267 30 lineto
+256 30 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+195 57 moveto
+(causes)
+[6.24 6.24 6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% Validator->ValidationError
+gsave 10 dict begin
+0.000 1.000 1.000 edgecolor
+newpath 189 102 moveto
+212 86 251 59 279 40 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 1.000 1.000 edgecolor
+newpath 281 43 moveto
+287 34 lineto
+277 37 lineto
+closepath
+fill
+0.000 1.000 1.000 edgecolor
+newpath 281 43 moveto
+287 34 lineto
+277 37 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+257 57 moveto
+(causes)
+[6.24 6.24 6.96 5.52 6.24 5.52]
+xshow
+end grestore
+end grestore
+% SequenceWidget
+gsave 10 dict begin
+642 216 60 18 ellipse_path
+stroke
+gsave 10 dict begin
+595 211 moveto
+(SequenceWidget)
+[7.68 6.24 6.72 6.96 6.24 6.96 6.24 6.24 12.96 3.84 6.96 6.72 6 3.84]
+xshow
+end grestore
+end grestore
+% SequenceWidget->FieldWidget
+newpath 605 230 moveto
+591 235 575 240 559 243 curveto
+490 254 470 254 400 243 curveto
+387 241 373 236 360 232 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+newpath 361 229 moveto
+350 229 lineto
+359 235 lineto
+closepath
+stroke
+end grestore
+% Terms
+gsave 10 dict begin
+645 117 31 18 ellipse_path
+stroke
+gsave 10 dict begin
+627 112 moveto
+(Terms)
+[7.44 6.24 5.04 10.8 5.52]
+xshow
+end grestore
+end grestore
+% SequenceWidget->Terms
+newpath 643 198 moveto
+643 183 644 162 644 145 curveto
+stroke
+gsave 10 dict begin
+solid
+1 setlinewidth
+0.000 0.000 0.000 edgecolor
+newpath 648 145 moveto
+644 135 lineto
+641 145 lineto
+closepath
+fill
+0.000 0.000 0.000 edgecolor
+newpath 648 145 moveto
+644 135 lineto
+641 145 lineto
+closepath
+stroke
+end grestore
+gsave 10 dict begin
+11.00 /Courier set_font
+644 168 moveto
+(terms)
+[6.72 6.72 6.72 6.72 6.72]
+xshow
+end grestore
+endpage
+showpage
+grestore
+%%PageTrailer
+%%EndPage: 1
+%%Trailer
+%%Pages: 1
+end
+restore
+%%EOF

Added: z3c.form/trunk/src/z3c/form/widget.py
===================================================================
--- z3c.form/trunk/src/z3c/form/widget.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/widget.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,221 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Widget Framework Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.location
+import zope.schema.interfaces
+from zope.pagetemplate.interfaces import IPageTemplate
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.i18n import translate
+
+from z3c.form import interfaces, util, value
+from z3c.form.i18n import MessageFactory as _
+
+PLACEHOLDER = object()
+
+StaticWidgetAttribute = value.StaticValueCreator(
+    discriminators = ('context', 'request', 'view', 'field', 'widget')
+    )
+ComputedWidgetAttribute = value.ComputedValueCreator(
+    discriminators = ('context', 'request', 'view', 'field', 'widget')
+    )
+
+
+class Widget(zope.location.Location):
+    """Widget base class."""
+
+    zope.interface.implements(interfaces.IWidget)
+
+    # widget specific attributes
+    label = u''
+    mode = interfaces.INPUT_MODE
+    required = False
+    ignoreRequest = False
+    ignoreContext = False
+    error = None
+    template = None
+    value = None
+
+    # html element attributes
+    id = u''
+    name = u''
+    title = None
+    css = None
+    tabindex = None
+    disabled = None
+
+    # this is only for a simpler handling, note that we offer interfaces
+    # for the following attributes, See IContextAware, IFormAware, IFieldWidget
+    context = None
+    form = None
+    field = None
+
+    def __init__(self, request):
+        self.request = request
+
+    def update(self):
+        """See z3c.form.interfaces.IWidget."""
+        # Step 1: Determine the value.
+        value = interfaces.NOVALUE
+        lookForDefault = False
+        # Step 1.1: If possible, get a value from the request
+        if not self.ignoreRequest:
+            widget_value = self.extract()
+            if widget_value is not interfaces.NOVALUE:
+                # Once we found the value in the request, it takes precendence
+                # over everything and nothing else has to be done.
+                self.value = widget_value
+                value = PLACEHOLDER
+        # Step 1.2: If we have a widget with a field and we have no value yet,
+        #           we have some more possible locations to get the value
+        if (interfaces.IFieldWidget.providedBy(self) and
+            value is interfaces.NOVALUE and
+            value is not PLACEHOLDER):
+            # Step 1.2.1: If the widget knows about its context and the
+            #              context is to be used to extract a value, get
+            #              it now via a data manager.
+            if (interfaces.IContextAware.providedBy(self) and
+                not self.ignoreContext):
+                value = zope.component.getMultiAdapter(
+                    (self.context, self.field), interfaces.IDataManager).get()
+            # Step 1.2.2: If we still do not have a value, we can always use
+            #             the default value of the field, id set
+            # NOTE: It should check field.default is not missing_value, but
+            # that requires fixing zope.schema first
+            if ((value is self.field.missing_value or
+                 value is interfaces.NOVALUE) and
+                self.field.default is not None):
+                value = self.field.default
+                lookForDefault = True
+        # Step 1.3: If we still have not found a value, then we try to get it
+        #           from an attribute value
+        if value is interfaces.NOVALUE or lookForDefault:
+            adapter = zope.component.queryMultiAdapter(
+                (self.context, self.request, self.form, self.field, self),
+                interfaces.IValue, name='default')
+            if adapter:
+                value = adapter.get()
+        # Step 1.4: Convert the value to one that the widget can understand
+        if value not in (interfaces.NOVALUE, PLACEHOLDER):
+            converter = interfaces.IDataConverter(self)
+            self.value = converter.toWidgetValue(value)
+        # Step 2: Update selected attributes
+        for attrName in ('label', 'required'):
+            value = zope.component.queryMultiAdapter(
+                (self.context, self.request, self.form, self.field, self),
+                interfaces.IValue, name=attrName)
+            if value is not None:
+                setattr(self, attrName, value.get())
+
+    def render(self):
+        """See z3c.form.interfaces.IWidget."""
+        template = self.template
+        if template is None:
+            template = zope.component.getMultiAdapter(
+                (self.context, self.request, self.form, self.field, self),
+                IPageTemplate, name=self.mode)
+        return template(self)
+
+    def extract(self, default=interfaces.NOVALUE):
+        """See z3c.form.interfaces.IWidget."""
+        return self.request.get(self.name, default)
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.name)
+
+
+class SequenceWidget(Widget):
+    """Sequence widget."""
+
+    zope.interface.implements(interfaces.ISequenceWidget)
+
+    value = ()
+    terms = None
+
+    noValueToken = '--NOVALUE--'
+
+    @property
+    def displayValue(self):
+        value = []
+        for token in self.value:
+            term = self.terms.getTermByToken(token)
+            if zope.schema.interfaces.ITitledTokenizedTerm.providedBy(term):
+                value.append(translate(
+                    term.title, context=self.request, default=term.title))
+            else:
+                value.append(term.token)
+        return value
+
+    def update(self):
+        """See z3c.form.interfaces.IWidget."""
+        # Create terms first, since we need them for the generic update.
+        if self.terms is None:
+            self.terms = zope.component.getMultiAdapter(
+                (self.context, self.request, self.form, self.field, self),
+                interfaces.ITerms)
+        super(SequenceWidget, self).update()
+
+    def extract(self, default=interfaces.NOVALUE):
+        """See z3c.form.interfaces.IWidget."""
+        if (self.name not in self.request and
+            self.name+'-empty-marker' in self.request):
+            return []
+        value = self.request.get(self.name, default)
+        if value != default:
+            for token in value:
+                if token == self.noValueToken:
+                    continue
+                try:
+                    self.terms.getTermByToken(token)
+                except LookupError:
+                    return default
+        return value
+
+
+def FieldWidget(field, widget):
+    """Set the field for the widget."""
+    widget.field = field
+    if not interfaces.IFieldWidget.providedBy(widget):
+        zope.interface.alsoProvides(widget, interfaces.IFieldWidget)
+    # Initial values are set. They can be overridden while updating the widget
+    # itself later on.
+    widget.name = widget.id = field.__name__
+    widget.label = field.title
+    widget.required = field.required
+    return widget
+
+
+class WidgetTemplateFactory(object):
+    """Widget template factory."""
+
+    def __init__(self, filename, contentType='text/html',
+                 context=None, request=None, view=None,
+                 field=None, widget=None):
+        self.template = ViewPageTemplateFile(filename, content_type=contentType)
+        zope.component.adapter(
+            util.getSpecification(context),
+            util.getSpecification(request),
+            util.getSpecification(view),
+            util.getSpecification(field),
+            util.getSpecification(widget))(self)
+        zope.interface.implementer(IPageTemplate)(self)
+
+    def __call__(self, context, request, view, field, widget):
+        return self.template


Property changes on: z3c.form/trunk/src/z3c/form/widget.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/widget.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/widget.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/widget.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,420 @@
+=======
+Widgets
+=======
+
+Widgets are small UI components that accept and process the textual user
+input. The only responsibility of a widget is to represent a value to the
+user, allow it to be modified and then return a new value. Good examples of
+widgets include the Qt widgets and HTML widgets. The widget is not responsible
+for converting its value to the desired internal value or validate the
+incoming data. These resposnibilities are passed data converters and
+validators, respecitively.
+
+There are several problems that can be identified in the original Zope 3 widget
+implementation located at ``zope.app.form``.
+
+(1) Field Dependence -- Widgets are always views of fields. While this might
+    be a correct choice for a high-level API, it is fundamentally wrong. It
+    disallows us to use widgets without defining fields. This also couples
+    certain pieces of information too tightly to the field, especially, value
+    retrieval from and storage to the context, validation and raw data
+    conversion.
+
+(2) Form Dependence -- While widgets do not have to be located within a form,
+    they are usually tightly coupled to it. It is very difficult to use
+    widgets outside the context of a form.
+
+(3) Traversability -- Widgets cannot be traversed, which means that they
+    cannot interact easily using Javascript. This is not a fundamental
+    problem, but simply a lack of the current design to recognize that small
+    UI components must also be traversable and thus have a URI.
+
+(4) Customizability -- A consequence of issue (1) is that widgets are not
+    customizable enough. Implementing real-world projects has shown that
+    widgets often want a very fine-grained ability to customize values. A
+    prime example is the label. Because the label of a widget is retrieved
+    from the field title, it is impossible to provide an alternative label for
+    a widget. While the label could be changed from the form, this would
+    require rewriting the entire form to change a label. Instead, we often
+    endde up writing cusom schemas.
+
+(5) Flexbility -- Oftentimes it is desired to have one widget, but multiple
+    styles of representation. For example, in one scenario the widget uses a
+    plain HTML widget and in another a fancy JavaScript widget is used. The
+    current implementation makes it very hard to provide alternative styles
+    for a widget.
+
+
+Creating and Using Simple Widgets
+---------------------------------
+
+When using the widget API by itself, the simplest way to use it is to just
+instantiate it using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> from z3c.form import widget
+  >>> request = TestRequest()
+  >>> age = widget.Widget(request)
+
+In this case we instantiated a generic widget. A full set of simple
+browser-based widgets can be found in the ``browser/`` package. Since no
+helper components are around to fill the attributes of the widget, we have to
+do it by hand:
+
+  >>> age.name = 'age'
+  >>> age.label = u'Age'
+  >>> age.value = '39'
+
+The most important attributes are the "name" and the "value". The name is used
+to identify the widget within the form. The value is either the value to be
+manipulated or the default value. The value must be provided in the form the
+widget needs it. It is the responsibility of a data converter to convert
+between the widget value and the desired internal value.
+
+Before we can render the widget, we have to register a template for the
+widget. The first step is to define the template:
+
+  >>> import tempfile
+  >>> textWidgetTemplate = tempfile.mktemp('text.pt')
+  >>> open(textWidgetTemplate, 'w').write('''\
+  ... <input type="text" name="" value=""
+  ...        tal:attributes="name view/name; value view/value;" />\
+  ... ''')
+
+Next, we have to create a template factory for the widget:
+
+  >>> from z3c.form.widget import WidgetTemplateFactory
+  >>> factory = WidgetTemplateFactory(
+  ...     textWidgetTemplate, widget=widget.Widget)
+
+The first argument, which is also required, is the path to the template
+file. An optional ``content_type`` keyword argument allows the developer to
+specify the output content type, usually "text/html". Then there are five
+keyword arguments that specify the discriminators of the template:
+
+* ``context`` -- This is the context in which the widget is displayed. In a
+  simple widget like the one we have now, the context is ``None``.
+
+* ``request`` -- This discriminator allows you to specify the type of request
+  for which the widget will be available. In our case this would be a browser
+  request. Note that browser requests can be further broken into layer, so you
+  could also specify a layer interface here.
+
+* ``view`` -- This is the view from which the widget is used. The simple
+  widget at hand, does not have a view associated with it though.
+
+* ``field`` -- This is the field for which the widget provides a
+  representation. Again, this simple widget does not use a field, so it is
+  ``None``.
+
+* ``widget`` -- This is the widget itself. With this discriminator you can
+  specify for which type of widget you are providing a template.
+
+We can now register the template factory. The name of the factory is the mode
+of the widget. By default, there are two widget modes: "input" and
+"display". However, since the mode is just a string, one can develop other
+kinds of modes as needed for a project. The default mode is "input":
+
+  >>> from z3c.form import interfaces
+  >>> age.mode is interfaces.INPUT_MODE
+  True
+
+  >>> import zope.component
+  >>> zope.component.provideAdapter(factory, name=interfaces.INPUT_MODE)
+
+Once everything is set up, the widget is updated and then rendered:
+
+  >>> age.update()
+  >>> print age.render()
+  <input type="text" name="age" value="39" />
+
+If a value is found in the request, it takes precedence, since the user
+entered the value:
+
+  >>> age.request = TestRequest(form={'age': '25'})
+  >>> age.update()
+  >>> print age.render()
+  <input type="text" name="age" value="25" />
+
+However, there is an option to turn off all request data:
+
+  >>> age.value = '39'
+  >>> age.ignoreRequest = True
+  >>> age.update()
+  >>> print age.render()
+  <input type="text" name="age" value="39" />
+
+
+Creating and Using Field Widgets
+--------------------------------
+
+An extended form of the widget allows fields to control several of the
+widget's properties. Let's create a field first:
+
+  >>> ageField = zope.schema.Int(
+  ...     __name__ = u'age',
+  ...     title = u'Age',
+  ...     min = 0,
+  ...     max = 130)
+
+We can now use our simple widget and create a field widget from it:
+
+  >>> ageWidget = widget.FieldWidget(ageField, age)
+
+Such a widget provides ``IFieldWidget``:
+
+  >>> interfaces.IFieldWidget.providedBy(ageWidget)
+  True
+
+Of course, this is more commonly done using an adapter. Commonly those
+adapters look like this:
+
+  >>> @zope.component.adapter(zope.schema.Int, TestRequest)
+  ... @zope.interface.implementer(interfaces.IFieldWidget)
+  ... def IntWidget(field, request):
+  ...     return widget.FieldWidget(field, widget.Widget(request))
+
+  >>> zope.component.provideAdapter(IntWidget)
+  >>> ageWidget = zope.component.getMultiAdapter((ageField, request),
+  ...     interfaces.IFieldWidget)
+
+Now we just have to update and render the widget:
+
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" />
+
+There is no initial value for the widget, since there is no value in the
+request and the field does not provide a default. Let's now give our field a
+default value and see what happens:
+
+  >>> ageField.default = 30
+  >>> ageWidget.update()
+  Traceback (most recent call last):
+  ...
+  TypeError: ('Could not adapt', <Widget u'age'>,
+              <InterfaceClass z3c.form.interfaces.IDataConverter>)
+
+In order for the widget to be able to take the field's default value and use
+it to provide an initial value the widget, we need to provide a data converter
+that defines how to convert from the field value to the widget value.
+
+  >>> from z3c.form import converter
+  >>> zope.component.provideAdapter(converter.FieldWidgetDataConverter)
+  >>> zope.component.provideAdapter(converter.FieldDataConverter)
+
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" value="30" />
+
+Again, the request value is honored above everything else:
+
+  >>> ageWidget.request = TestRequest(form={'age': '25'})
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" value="25" />
+
+
+Creating and Using Context Widgets
+----------------------------------
+
+When widgets represent an attribute value of an object, then this object must
+be set as the context of the widget:
+
+  >>> class Person(object):
+  ...     age = 45
+
+  >>> ageWidget.context = Person()
+  >>> zope.interface.alsoProvides(ageWidget, interfaces.IContextAware)
+
+The result is that the context value takes over precendence over the default
+value:
+
+  >>> ageWidget.request = TestRequest()
+  >>> ageWidget.update()
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: ((...), <InterfaceClass ...IDataManager>, u'')
+
+This call fails because the widget does not know how to extract the value from
+the context. Registering a data manager for the widget does the trick:
+
+  >>> from z3c.form import datamanager
+  >>> zope.component.provideAdapter(datamanager.AttributeField)
+
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" value="45" />
+
+The context can be explicitely ignored, making the widget display the default
+value again:
+
+  >>> ageWidget.ignoreContext = True
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" value="30" />
+
+Again, the request value is honored above everything else:
+
+  >>> ageWidget.request = TestRequest(form={'age': '25'})
+  >>> ageWidget.ignoreContext = False
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" value="25" />
+
+
+Dynamically Changing Attribute Values
+-------------------------------------
+
+Once widgets are used within a framework, it is very tedious to write Python
+code to adjust certain attributes, even though hooks exist. The easiest way to
+change those attribute values is actually to provide an adapter that provides
+the custom value.
+
+We can create a custom label for the age widget:
+
+  >>> AgeLabel = widget.StaticWidgetAttribute(
+  ...     u'Current Age',
+  ...     context=None, request=None, view=None, field=ageField, widget=None)
+
+Clearly, this code deos not require us to touch the orginal form and widget
+code, given that we have enough control over the selection. In the example
+above, all the selection discriminators are listed for demonstration
+purposes. Of course, the label in this case can be created as follows:
+
+  >>> AgeLabel = widget.StaticWidgetAttribute(u'Current Age', field=ageField)
+
+Much better, isn't it? Initially the label is the title of the field:
+
+  >>> ageWidget.label
+  u'Age'
+
+Let's now simply register the label as a named adapter; the name is the name
+of the attribute to change:
+
+  >>> zope.component.provideAdapter(AgeLabel, name='label')
+
+Asking the widget for the label now will return the newly registered label:
+
+  >>> ageWidget.update()
+  >>> ageWidget.label
+  u'Current Age'
+
+Of course, simply setting the label or changing the label extraction via a
+sub-class are other options you might want to consider. Furthermore, you
+could also create a computed attribute value or implement your own component.
+
+Overriding any other attribtue, such as ``required``, is done in the same
+way. If any widget provides new attributes, they are also overridable this
+way. For example, the selection widget defines a label for the option that no
+value was selected. We often want to override this, because the German
+translation sucks or the wording is often too generic.
+
+Overriding the default value is somewhat special due to the complexity of
+obtaining the value. So let's register one now:
+
+  >>> AgeDefault = widget.StaticWidgetAttribute(50, field=ageField)
+  >>> zope.component.provideAdapter(AgeDefault, name="default")
+
+Let's now instantiate, update and render the widget to see the default value:
+
+  >>> ageWidget = zope.component.getMultiAdapter((ageField, request),
+  ...     interfaces.IFieldWidget)
+  >>> ageWidget.update()
+  >>> print ageWidget.render()
+  <input type="text" name="age" value="50" />
+
+
+Sequence Widget
+---------------
+
+A common use case in user interfaces is to ask the user to select one or more
+items from a set of options/choices. The ``widget`` module provides a basic
+widget implementation to support this use case.
+
+The options available for selections are known as terms. Initially, there are
+no terms:
+
+  >>> request = TestRequest()
+  >>> seqWidget = widget.SequenceWidget(request)
+  >>> seqWidget.name = 'seq'
+
+  >>> seqWidget.terms is None
+  True
+
+There are two ways terms can be added, either manually or via an
+adapter. Those term objects must provide ``ITerms``. There is no simple
+default implementation, so we have to provide one ourselves:
+
+  >>> from zope.schema import vocabulary
+  >>> class Terms(vocabulary.SimpleVocabulary):
+  ...     zope.interface.implements(interfaces.ITerms)
+  ...     def getValue(self, token):
+  ...         return self.getTermByToken(token).value
+
+  >>> terms = Terms(
+  ...   [Terms.createTerm(1, 'v1', u'Value 1'),
+  ...    Terms.createTerm(2, 'v2', u'Value 2'),
+  ...    Terms.createTerm(3, 'v3', u'Value 3')])
+  >>> seqWidget.terms = terms
+
+Once the ``terms`` attribute is set, updating the widgets does not change the
+terms:
+
+  >>> seqWidget.update()
+  >>> [term.value for term in seqWidget.terms]
+  [1, 2, 3]
+
+The value of a sequence widget is a tuple/list of term tokens. When extracting
+values from the request, the values must be valid tokens, otherwise the
+default value is returned:
+
+  >>> seqWidget.request = TestRequest(form={'seq': ['v1']})
+  >>> seqWidget.extract()
+  ['v1']
+
+  >>> seqWidget.request = TestRequest(form={'seq': ['v4']})
+  >>> seqWidget.extract()
+  <NOVALUE>
+
+  >>> seqWidget.request = TestRequest(form={'seq-empty-marker': '1'})
+  >>> seqWidget.extract()
+  []
+
+Since the value of the widget is a tuple of tokens, when displaying the
+values, they have to be converted to the title of the term:
+
+  >>> seqWidget.value = ('v1', 'v2')
+  >>> seqWidget.displayValue
+  [u'Value 1', u'Value 2']
+
+To demonstrate how the terms is automatically chosen by a widget, we should
+instantiate a field widget. Let's do this with a choice field:
+
+  >>> seqField = zope.schema.Choice(
+  ...     title=u'Sequence Field',
+  ...     vocabulary=terms)
+
+Let's now create the field widget:
+
+  >>> seqWidget = widget.FieldWidget(seqField, widget.SequenceWidget(request))
+  >>> seqWidget.terms
+
+The terms should be available as soon as the widget is updated:
+
+  >>> seqWidget.update()
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: ((...), <InterfaceClass ...ITerms>, u'')
+
+This failed, because we did not register an adapter for the terms yet. After
+the adapter is registered, everything should work as expected:
+
+  >>> from z3c.form import term
+  >>> zope.component.provideAdapter(term.ChoiceTerms)
+
+  >>> seqWidget.update()
+  >>> seqWidget.terms
+  <z3c.form.term.ChoiceTerms object at ...>
+
+So that's it. Everything else is the same from then on.


Property changes on: z3c.form/trunk/src/z3c/form/widget.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/z3c.form-configure.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/z3c.form-configure.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/z3c.form-configure.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1 @@
+<include package="z3c.form" />


Property changes on: z3c.form/trunk/src/z3c/form/z3c.form-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/z3c.form-meta.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/z3c.form-meta.zcml	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/z3c.form-meta.zcml	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,6 @@
+<configure
+    xmlns:zcml="http://namespaces.zope.org/zcml">
+
+  <include package="z3c.form" file="meta.zcml" />
+
+</configure>


Property changes on: z3c.form/trunk/src/z3c/form/z3c.form-meta.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.form/trunk/src/z3c/form/zcml.py
===================================================================
--- z3c.form/trunk/src/z3c/form/zcml.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/zcml.py	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,102 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import os
+
+import zope.interface
+import zope.component.zcml
+import zope.schema
+import zope.configuration.fields
+from zope.configuration.exceptions import ConfigurationError
+from zope.pagetemplate.interfaces import IPageTemplate
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from z3c.form import interfaces
+from z3c.form.i18n import MessageFactory as _
+from z3c.form.widget import WidgetTemplateFactory
+
+
+class IWidgetTemplateDirective(zope.interface.Interface):
+    """Parameters for the widget template directive."""
+
+    template = zope.configuration.fields.Path(
+        title=_('Layout template.'),
+        description=_('Refers to a file containing a page template (should '
+                      'end in extension ``.pt`` or ``.html``).'),
+        required=True)
+
+    mode = zope.schema.BytesLine(
+        title=_('The mode of the template.'),
+        description=_('The mode is used for define input and display '
+                      'templates'),
+        default=interfaces.INPUT_MODE,
+        required=False)
+
+    for_ = zope.configuration.fields.GlobalObject(
+        title=_('View'),
+        description=_('The view for which the template should be available'),
+        default=zope.interface.Interface,
+        required = False)
+
+    layer = zope.configuration.fields.GlobalObject(
+        title=_('Layer'),
+        description=_('The layer for which the template should be available'),
+        default=IDefaultBrowserLayer,
+        required=False)
+
+    view = zope.configuration.fields.GlobalObject(
+        title=_('View'),
+        description=_('The view for which the template should be available'),
+        default=zope.interface.Interface,
+        required=False)
+
+    field = zope.configuration.fields.GlobalObject(
+        title=_('Field'),
+        description=_('The field for which the template should be available'),
+        default=zope.schema.interfaces.IField,
+        required=False)
+
+    widget = zope.configuration.fields.GlobalObject(
+        title=_('View'),
+        description=_('The widget for which the template should be available'),
+        default=interfaces.IWidget,
+        required=False)
+
+    contentType = zope.schema.BytesLine(
+        title=_('Content Type'),
+        description=_('The content type identifies the type of data.'),
+        default='text/html',
+        required=False)
+
+
+def widgetTemplateDirective(
+    _context, template, for_=zope.interface.Interface,
+    layer=IDefaultBrowserLayer, view=None, field=None, widget=None,
+    mode=interfaces.INPUT_MODE, contentType='text/html'):
+
+    # Make sure that the template exists
+    template = os.path.abspath(str(_context.path(template)))
+    if not os.path.isfile(template):
+        raise ConfigurationError("No such file", template)
+
+    factory = WidgetTemplateFactory(template, contentType)
+    zope.interface.directlyProvides(factory, IPageTemplate)
+
+    # register the template
+    zope.component.zcml.adapter(_context, (factory,), IPageTemplate,
+        (for_, layer, view, field, widget), name=mode)


Property changes on: z3c.form/trunk/src/z3c/form/zcml.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.form/trunk/src/z3c/form/zcml.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/zcml.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/zcml.txt	2007-05-24 14:45:00 UTC (rev 75940)
@@ -0,0 +1,110 @@
+==========
+Directives
+==========
+
+Widget template directive
+-------------------------
+
+Show how we can use the widget template directive. Register the meta
+configuration for the directive.
+
+  >>> import sys
+  >>> from zope.configuration import xmlconfig
+  >>> import z3c.form
+  >>> context = xmlconfig.file('meta.zcml', z3c.form)
+
+We need a custom widget template
+
+  >>> import os, tempfile
+  >>> temp_dir = tempfile.mkdtemp()
+  >>> file = os.path.join(temp_dir, 'widget.pt')
+  >>> open(file, 'w').write('''
+  ... <input type="text" id="" name="" value="" size=""
+  ...        tal:attributes="id view/id;
+  ...                        name view/name;
+  ...                        size view/size;
+  ...                        value view/value;" />
+  ... ''')
+
+and a interface
+
+  >>> import zope.interface
+  >>> from z3c.form import interfaces
+  >>> class IMyWidget(interfaces.IWidget):
+  ...     """My widget interface."""
+
+and a widget class:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> from z3c.form.browser import text
+  >>> class MyWidget(text.TextWidget):
+  ...     zope.interface.implements(IMyWidget)
+  >>> request = TestRequest()
+  >>> myWidget = MyWidget(request)
+
+Make them available under the fake package ``custom``:
+
+  >>> sys.modules['custom'] = type(
+  ...     'Module', (),
+  ...     {'IMyWidget': IMyWidget})()
+
+and register them as a widget template within the ``z3c:widgetTemplate``
+directive:
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:widgetTemplate
+  ...       template="%s"
+  ...       widget="custom.IMyWidget"
+  ...       />
+  ... </configure>
+  ... """ % file, context=context)
+
+Let's get the template
+
+  >>> import zope.component
+  >>> from z3c.template.interfaces import IPageTemplate
+  >>> template = zope.component.queryMultiAdapter((None, request, None, None,
+  ...     myWidget), interface=IPageTemplate, name='input')
+
+and check them:
+
+  >>> template
+  <zope.app.pagetemplate.viewpagetemplatefile.ViewPageTemplateFile ...>
+
+Let's use the template within the widget.
+
+  >>> print template(myWidget)
+  <input type="text" id="" name="" value="" />
+
+We normly render the widget which returns the registered template.
+
+  >>> print myWidget.render()
+  <input type="text" id="" name="" value="" />
+
+If the template does not exist, then the widget directive should fail
+immediately:
+
+  >>> unknownFile = os.path.join(temp_dir, 'unknown.pt')
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:widgetTemplate
+  ...       template="%s"
+  ...       widget="custom.IMyWidget"
+  ...       />
+  ... </configure>
+  ... """ % unknownFile, context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: File "<string>", line 4.2-7.8
+      ConfigurationError: ('No such file', '...unknown.pt')
+
+
+Cleanup
+-------
+
+Now we need to clean up the custom module.
+
+  >>> del sys.modules['custom']


Property changes on: z3c.form/trunk/src/z3c/form/zcml.txt
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list