Skip to content

Commit

Permalink
add new widgetmanager to mix fields and content providers in forms (m…
Browse files Browse the repository at this point in the history
…erge from fieldsandcontentproviders branch)
  • Loading branch information
jfroche committed Jul 1, 2010
2 parents 46c40e0 + 2f7ea63 commit c386df7
Show file tree
Hide file tree
Showing 13 changed files with 551 additions and 28 deletions.
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ CHANGES
2.3.5 (unreleased)
------------------

- Feature : mix fields and content providers in forms. This allow to enrich
the form by interlacing html snippets produced by content providers.
Adding html outside the widgets avoids the systematic need of
subclassing or changing the full widget rendering.

- Bug: Radio widget was not treating value as a list in hidden mode.


Expand Down
6 changes: 5 additions & 1 deletion buildout.cfg
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[buildout]
develop = . benchmark
parts = test test-no-z3zpt checker coverage-test coverage-report docs i18n
benchmark python
benchmark python omelette

[test-environment]
CHAMELEON_DEBUG = False
Expand Down Expand Up @@ -77,3 +77,7 @@ CHAMELEON_CACHE = True
recipe = zc.recipe.egg
eggs = z3c.form
interpreter = python

[omelette]
recipe = collective.recipe.omelette
eggs = ${test:eggs}
55 changes: 29 additions & 26 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import xml.sax.saxutils
from setuptools import setup, find_packages


def read(*rnames):
text = open(os.path.join(os.path.dirname(__file__), *rnames)).read()
text = unicode(text, 'utf-8').encode('ascii', 'xmlcharrefreplace')
return xml.sax.saxutils.escape(text)


chapters = '\n'.join(
[read('src', 'z3c', 'form', name)
for name in ('README.txt',
Expand All @@ -35,32 +37,33 @@ def read(*rnames):
'zcml.txt',
'validator.txt',
'widget.txt',
'contentprovider.txt',
'action.txt',
'value.txt',
'datamanager.txt',
'converter.txt',
'term.txt',
'util.txt')])
'util.txt',
)])


setup (
setup(
name='z3c.form',
version = '2.3.5dev',
author = "Stephan Richter, Roger Ineichen and the Zope Community",
author_email = "zope-dev@zope.org",
description = "An advanced form and widget framework for Zope 3",
version='2.3.5dev',
author="Stephan Richter, Roger Ineichen and the Zope Community",
author_email="zope-dev@zope.org",
description="An advanced form and widget framework for Zope 3",
long_description=(
read('README.txt')
+ '\n\n' +
'Detailed Documentation\n'
'**********************\n'
+ '\n' + chapters
+ '\n\n' +
read('CHANGES.txt')
),
license = "ZPL 2.1",
keywords = "zope3 form widget",
classifiers = [
+ '\n\n'
+ read('CHANGES.txt')),
license="ZPL 2.1",
keywords="zope3 form widget",
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
Expand All @@ -70,17 +73,17 @@ def read(*rnames):
'Operating System :: OS Independent',
'Topic :: Internet :: WWW/HTTP',
'Framework :: Zope3'],
url = 'http://pypi.python.org/pypi/z3c.form',
packages = find_packages('src'),
include_package_data = True,
package_dir = {'':'src'},
namespace_packages = ['z3c'],
extras_require = dict(
extra = [
url='http://pypi.python.org/pypi/z3c.form',
packages=find_packages('src'),
include_package_data=True,
package_dir={'': 'src'},
namespace_packages=['z3c'],
extras_require=dict(
extra=[
'z3c.pt >= 1.0b4',
'z3c.ptcompat',
],
test = [
test=[
'lxml >= 2.1.1',
'z3c.coverage',
'z3c.template',
Expand All @@ -93,16 +96,16 @@ def read(*rnames):
'zope.app.testing',
'zope.testing',
],
zope34 = [
zope34=[
'zope.app.component',
],
latest = [
latest=[
'zope.site',
],
adding = ['zope.app.container'],
docs = ['z3c.recipe.sphinxdoc'],
adding=['zope.app.container'],
docs=['z3c.recipe.sphinxdoc'],
),
install_requires = [
install_requires=[
'setuptools',
'zope.browser',
'zope.component',
Expand All @@ -122,5 +125,5 @@ def read(*rnames):
#'zope.site' or 'zope.app.component',
'zope.traversing',
],
zip_safe = False,
zip_safe=False,
)
5 changes: 5 additions & 0 deletions src/z3c/form/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ files. The documents are ordered in the way they should be read:
Explains in detail the design goals surrounding widgets and widget managers
and how they were realized with the implemented API.

- ``contentprovider.txt`` [advanced users]

Explains how to mix content providers in forms to render more html around
widgets.

- ``action.txt`` [advanced users]

Explains in detail the design goals surrounding action managers and
Expand Down
4 changes: 4 additions & 0 deletions src/z3c/form/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
factory=".field.FieldWidgets"
/>

<adapter
factory=".contentprovider.FieldWidgetsAndProviders"
/>

<!-- Data Converters -->
<adapter
factory=".converter.FieldDataConverter"
Expand Down
115 changes: 115 additions & 0 deletions src/z3c/form/contentprovider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import zope.component
import zope.interface
import zope.location
import zope.schema.interfaces
from z3c.form.error import MultipleErrors
from zope.contentprovider.interfaces import IContentProvider

from z3c.form.field import FieldWidgets
from z3c.form import interfaces
from z3c.form.interfaces import IContentProviders


class BaseProvider(object):
__slots__ = ('position')

lookup_ = BaseProvider()


class ContentProviders(dict):
zope.interface.implements(IContentProviders)

def __init__(self, names=None):
super(ContentProviders, self).__init__()
if names is not None:
for position, name in enumerate(names):
self[name] = lookup_

def __setitem__(self, key, value):
factory = ContentProviderFactory(factory=value, name=key)
super(ContentProviders, self).__setitem__(key, factory)


class ContentProviderFactory(object):

def __init__(self, factory, name):
self.factory = factory
self.name = name
self.position = getattr(factory, 'position', None)

def __call__(self, manager):
if self.factory != lookup_:
contentProvider = self.factory(manager.content, manager.request, manager.form)
else:
contentProvider = zope.component.getMultiAdapter((manager.content, manager.request, manager.form),
IContentProvider, self.name)
return contentProvider


class FieldWidgetsAndProviders(FieldWidgets):
zope.component.adapts(
interfaces.IFieldsAndContentProvidersForm, interfaces.IFormLayer, zope.interface.Interface)
zope.interface.implementsOnly(interfaces.IWidgets)

def update(self):
super(FieldWidgetsAndProviders, self).update()
uniqueOrderedKeys = self._data_keys
for name in self.form.contentProviders:
factory = self.form.contentProviders[name]
if factory.position is None:
raise ValueError("Position of the following"
" content provider should be an integer: '%s'." % name)
contentProvider = factory(self)
shortName = name
contentProvider.update()
uniqueOrderedKeys.insert(factory.position, shortName)
self._data_values.insert(factory.position, contentProvider)
self._data[shortName] = contentProvider
zope.location.locate(contentProvider, self, shortName)
# allways ensure that we add all keys and keep the order given from
# button items
self._data_keys = uniqueOrderedKeys

def extract(self):
"""See interfaces.IWidgets"""
data = {}
errors = ()
for name, widget in self.items():
if IContentProvider.providedBy(widget):
continue
if widget.mode == interfaces.DISPLAY_MODE:
continue
value = widget.field.missing_value
try:
widget.setErrors = self.setErrors
raw = widget.extract()
if raw is not interfaces.NO_VALUE:
value = interfaces.IDataConverter(widget).toFieldValue(raw)
zope.component.getMultiAdapter(
(self.content,
self.request,
self.form,
getattr(widget, 'field', None),
widget),
interfaces.IValidator).validate(value)
except (zope.interface.Invalid,
ValueError, MultipleErrors), error:
view = zope.component.getMultiAdapter(
(error, self.request, widget, widget.field,
self.form, self.content), interfaces.IErrorViewSnippet)
view.update()
if self.setErrors:
widget.error = view
errors += (view,)
else:
name = widget.__name__
data[name] = value
for error in self.validate(data):
view = zope.component.getMultiAdapter(
(error, self.request, None, None, self.form, self.content),
interfaces.IErrorViewSnippet)
view.update()
errors += (view,)
if self.setErrors:
self.errors = errors
return data, errors
Loading

0 comments on commit c386df7

Please sign in to comment.