[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=" ->"
+ onclick="javascript:from2to('bar')"> -></button>
+ <br />
+ <button name="to2fromButton" type="button"
+ value="<- "
+ onclick="javascript:to2from('bar')"><- </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"><source /></textarea>
+
+ >>> widget.mode = interfaces.DISPLAY_MODE
+ >>> print widget.render()
+ <source />
+
+
+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=" ->"
+ onclick="javascript:from2to('bar')"> -></button>
+ <br />
+ <button name="to2fromButton" type="button"
+ value="<- "
+ onclick="javascript:to2from('bar')"><- </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=" ->"
+ onclick="javascript:from2to('widget.name')"> -></button>
+ <br />
+ <button name="to2fromButton" type="button"
+ value="<- "
+ onclick="javascript:to2from('widget.name')"><- </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=" ->"
+ onclick="javascript:from2to()"
+ tal:attributes="onClick string:javascript:from2to('${view/name}')"
+ > -></button>
+ <br />
+ <button name="to2fromButton" type="button" value="<- "
+ onclick="javascript:to2from()"
+ tal:attributes="onClick string:javascript:to2from('${view/name}')"
+ ><- </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