[Checkins] SVN: plone.z3cform/trunk/ Attempt to make plone.z3cform work in Zope 2.12 with unwrapped forms, as well as in Zope 2.10 with wraped forms
Martin Aspeli
optilude at gmx.net
Mon Feb 15 00:36:00 EST 2010
Log message for revision 109071:
Attempt to make plone.z3cform work in Zope 2.12 with unwrapped forms, as well as in Zope 2.10 with wraped forms
Changed:
U plone.z3cform/trunk/plone/z3cform/configure.zcml
U plone.z3cform/trunk/plone/z3cform/crud/crud.py
A plone.z3cform/trunk/plone/z3cform/demo/
A plone.z3cform/trunk/plone/z3cform/demo/__init__.py
A plone.z3cform/trunk/plone/z3cform/demo/configure.zcml
A plone.z3cform/trunk/plone/z3cform/demo/demoform.py
U plone.z3cform/trunk/plone/z3cform/interfaces.py
U plone.z3cform/trunk/plone/z3cform/layout.py
U plone.z3cform/trunk/plone/z3cform/overrides.zcml
A plone.z3cform/trunk/plone/z3cform/patch.py
A plone.z3cform/trunk/plone/z3cform/templates/
A plone.z3cform/trunk/plone/z3cform/templates/form.pt
A plone.z3cform/trunk/plone/z3cform/templates/layout.pt
A plone.z3cform/trunk/plone/z3cform/templates/macros.pt
A plone.z3cform/trunk/plone/z3cform/templates/subform.pt
A plone.z3cform/trunk/plone/z3cform/templates/wrappedform.pt
A plone.z3cform/trunk/plone/z3cform/templates/wrappedsubform.pt
U plone.z3cform/trunk/plone/z3cform/templates.py
U plone.z3cform/trunk/plone/z3cform/templates.zcml
U plone.z3cform/trunk/plone/z3cform/tests.py
U plone.z3cform/trunk/plone/z3cform/traversal.py
U plone.z3cform/trunk/plone/z3cform/traversal.txt
U plone.z3cform/trunk/plone/z3cform/z2.py
U plone.z3cform/trunk/setup.py
-=-
Modified: plone.z3cform/trunk/plone/z3cform/configure.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/configure.zcml 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/configure.zcml 2010-02-15 05:35:58 UTC (rev 109071)
@@ -2,28 +2,58 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:zcml="http://namespaces.zope.org/zcml"
xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:monkey="http://namespaces.plone.org/monkey"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="plone.z3cform">
<include file="templates.zcml" />
+ <include package="collective.monkeypatcher" file="meta.zcml" />
<include package="z3c.form" file="meta.zcml" />
<include package="z3c.form" />
+ <i18n:registerTranslations directory="locales"/>
+
+ <!-- TODO: Remove -->
+ <include package=".demo" />
+
+ <!-- Monkey patch BaseForm/GroupForm's update() to apply Zope 2 input
+ parameter processing. Without this, we'll get errors because Zope is
+ sending encoded str's when z3c.form wants decoded unicode's.
+ -->
+ <monkey:patch
+ description="Zope 2 integration - decode form inputs prior to form processing"
+ class="z3c.form.form.BaseForm"
+ original="update"
+ replacement=".patch.BaseForm_update"
+ />
+ <monkey:patch
+ description="Zope 2 integration - decode form inputs prior to form processing"
+ class="z3c.form.group.GroupForm"
+ original="update"
+ replacement=".patch.GroupForm_update"
+ />
+
+ <!-- Override the FileUploadDataConverter from z3c.form.
+ We register it for the class so that it is more specific than the one
+ registered in z3c.form. This avoids the need for a messy
+ overrides.zcml.
+ -->
+ <adapter
+ for="zope.schema.Bytes z3c.form.interfaces.IFileWidget"
+ factory=".converter.FileUploadDataConverter"
+ />
+
<!-- Backported from z3c.from trunk -->
<include package=".textlines" file="textlines.zcml"
zcml:condition="not-installed z3c.form.browser.textlines"/>
<!-- These are not backported, but represent useful policy -->
- <adapter
- factory=".textlines.textlines.TextLinesSetConverter"
- />
- <adapter
- factory=".textlines.textlines.TextLinesFrozenSetConverter"
- />
+ <adapter factory=".textlines.textlines.TextLinesSetConverter" />
+ <adapter factory=".textlines.textlines.TextLinesFrozenSetConverter" />
+
+ <!-- ++widget++ namespace -->
+ <adapter factory=".traversal.FormWidgetTraversal" name="widget" />
+ <adapter factory=".traversal.WrapperWidgetTraversal" name="widget" />
- <adapter factory=".traversal.WidgetTraversal" name="widget" />
-
- <i18n:registerTranslations directory="locales"/>
-
</configure>
Modified: plone.z3cform/trunk/plone/z3cform/crud/crud.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/crud.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/crud/crud.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -333,7 +333,6 @@
class AddForm(form.Form):
label = _(u"Add")
- template = viewpagetemplatefile.ViewPageTemplateFile('../form.pt')
ignoreContext = True
ignoreRequest = True
Added: plone.z3cform/trunk/plone/z3cform/demo/configure.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/demo/configure.zcml (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/demo/configure.zcml 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,27 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:grok="http://namespaces.zope.org/grok"
+ xmlns:i18n="http://namespaces.zope.org/i18n"
+ i18n_domain="plone.z3cform">
+
+ <include package="plone.formwidget.autocomplete" />
+ <include package="plone.directives.form" file="meta.zcml" />
+
+ <grok:grok package="." />
+
+ <browser:view
+ name="z3c-demo-form"
+ class=".demoform.DemoForm"
+ permission="zope2.View"
+ for="*"
+ />
+
+ <browser:view
+ name="z3c-demo-form-wrapped"
+ class=".demoform.WrappedDemoForm"
+ permission="zope2.View"
+ for="*"
+ />
+
+</configure>
Added: plone.z3cform/trunk/plone/z3cform/demo/demoform.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/demo/demoform.py (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/demo/demoform.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,65 @@
+from zope.interface import Interface
+from zope import schema
+
+from z3c.form import field, form, button
+
+from plone.z3cform import layout
+from plone.app.z3cform.wysiwyg import WysiwygFieldWidget
+
+from plone.formwidget.autocomplete import AutocompleteFieldWidget
+from plone.formwidget.autocomplete.demo import KeywordSourceBinder
+
+import plone.directives.form
+from five import grok
+
+class IDemoFormFields(plone.directives.form.Schema):
+
+ text = schema.TextLine(title=u"Text field", description=u"With description")
+ number = schema.Int(title=u"Integer field")
+ bytes = schema.Bytes(title=u"Bytes field", required=False)
+ autocomplete = schema.Choice(title=u"Choice field", source=KeywordSourceBinder())
+
+ plone.directives.form.widget(wysiwyg=WysiwygFieldWidget)
+ wysiwyg = schema.Text(title=u"Edit this", description=u"Stuff here")
+
+# Form class - registered directly, and used in the wrapper scenario below
+class DemoForm(form.Form):
+
+ fields = field.Fields(IDemoFormFields)
+ fields['autocomplete'].widgetFactory = AutocompleteFieldWidget
+ fields['wysiwyg'].widgetFactory = WysiwygFieldWidget
+
+ ignoreContext = True
+ label = u"Demo form"
+ description = u"Some description"
+
+ @button.buttonAndHandler(u'Ok')
+ def handleApply(self, action):
+ data, errors = self.extractData()
+ print data, errors
+ if errors:
+ self.status = self.formErrorsMessage
+ return
+
+# Old-style wrapped form
+WrappedDemoForm = layout.wrap_form(DemoForm)
+
+# Grokked form
+
+class SchemaForm(plone.directives.form.SchemaForm):
+ schema = IDemoFormFields
+ grok.name('z3c-demo-form-grokked')
+ grok.require('zope2.Public')
+ grok.context(Interface)
+ ignoreContext = True
+
+ label = u"Demo form (grokked)"
+ description = u"Some description"
+
+ @button.buttonAndHandler(u'Ok')
+ def handleApply(self, action):
+ data, errors = self.extractData()
+ print data, errors
+ if errors:
+ self.status = self.formErrorsMessage
+ return
Modified: plone.z3cform/trunk/plone/z3cform/interfaces.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/interfaces.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/interfaces.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -4,10 +4,19 @@
from zope.pagetemplate.interfaces import IPageTemplate
from z3c.form.interfaces import IForm
-
class IFormWrapper(Interface):
- """Marker interface for the form wrapper
+ """Form wrapper class.
+
+ This class allows "two-step" rendering, with an outer view rendering
+ part of the page and the form class rendering the form area.
+
+ In Zope < 2.12, this is the only way to get z3c.form support, because
+ the view class takes care of the acquisition requirement.
+
+ In Zope 2.12 and later, this approach is optional: you may register the
+ form class directly as a browser view.
"""
+
def update():
"""We use the content provider update/render couple.
"""
@@ -32,3 +41,15 @@
required = False,
schema = IPageTemplate
)
+
+class IWrappedForm(Interface):
+ """Marker interface applied to wrapped forms during rendering.
+
+ This allows different handling of templates, for example.
+ """
+
+class IWrappedSubForm(IWrappedForm):
+ """Marker interface applied to wrapped sub-forms during rendering.
+
+ This allows different handling of templates, for example.
+ """
Modified: plone.z3cform/trunk/plone/z3cform/layout.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/layout.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/layout.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -46,6 +46,12 @@
Override this method if you have more than one form.
"""
+
+ if z3c.form.interfaces.ISubForm.providedBy(self.form_instance):
+ zope.interface.alsoProvides(self.form_instance, interfaces.IWrappedSubForm)
+ else:
+ zope.interface.alsoProvides(self.form_instance, interfaces.IWrappedForm)
+
z2.switch_on(self, request_layer=self.request_layer)
self.form_instance.update()
# A z3c.form.form.AddForm do a redirect in its render method.
Modified: plone.z3cform/trunk/plone/z3cform/overrides.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/overrides.zcml 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/overrides.zcml 2010-02-15 05:35:58 UTC (rev 109071)
@@ -1,7 +1,7 @@
<configure xmlns="http://namespaces.zope.org/zope">
- <adapter
- factory=".converter.FileUploadDataConverter"
- />
+ <!-- This file is no longer necessary, but left here in case people
+ reference it
+ -->
</configure>
Added: plone.z3cform/trunk/plone/z3cform/patch.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/patch.py (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/patch.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,30 @@
+"""A small monkey patch for z3c.form's BaseForm.update() and
+GroupForm.update(). We need to call z2.processInputs() before the request is
+used, because z3c.form expects them to have been converted to unicode first.
+"""
+
+from z3c.form.form import BaseForm
+from z3c.form.group import GroupForm
+
+from plone.z3cform.z2 import processInputs
+
+_original_BaseForm_update = BaseForm.update
+_original_GroupForm_update = GroupForm.update
+
+def BaseForm_update(self):
+ # This monkey patch ensures that processInputs() is called before
+ # z3c.form does any work on the request. This is because z3c.form expects
+ # charset negotiation to have taken place in the publisher, and will
+ # complain about non-unicode strings
+
+ processInputs(self.request)
+ _original_BaseForm_update(self)
+
+def GroupForm_update(self):
+ # This monkey patch ensures that processInputs() is called before
+ # z3c.form does any work on the request. This is because z3c.form expects
+ # charset negotiation to have taken place in the publisher, and will
+ # complain about non-unicode strings
+
+ processInputs(self.request)
+ _original_GroupForm_update(self)
Added: plone.z3cform/trunk/plone/z3cform/templates/form.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates/form.pt (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates/form.pt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ i18n:domain="plone.z3cform"
+ metal:use-macro="context/@@standard_macros/page">
+
+ <body metal:fill-slot="body">
+
+ <h1 tal:content="view/label | nothing" />
+
+ <div class="description"
+ tal:condition="view/description | nothing"
+ tal:content="structure view/description">Form description</div>
+
+ <metal:block use-macro="context/@@ploneform-macros/titlelessform" />
+
+ </body>
+
+</html>
Added: plone.z3cform/trunk/plone/z3cform/templates/layout.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates/layout.pt (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates/layout.pt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,18 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ metal:use-macro="context/main_template/macros/master">
+<body>
+
+<metal:slot metal:fill-slot="header" i18n:domain="cmf_default">
+ <h1 tal:content="view/label">View Title</h1>
+</metal:slot>
+
+<metal:slot metal:fill-slot="main" i18n:domain="cmf_default">
+ <div id="layout-contents">
+ <span tal:replace="structure view/contents" />
+ </div>
+</metal:slot>
+</body>
+</html>
Added: plone.z3cform/trunk/plone/z3cform/templates/macros.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates/macros.pt (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates/macros.pt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,89 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ i18n:domain="plone.z3cform"
+ tal:omit-tag="">
+
+ <head></head>
+
+ <body>
+
+ <div class="form" metal:define-macro="form">
+
+ <h3 tal:condition="view/label | nothing">
+ <span tal:replace="view/label">Form title</span>:</h3>
+
+ <div class="description"
+ tal:condition="view/description | nothing"
+ tal:content="structure view/description">Form description</div>
+ <metal:define define-macro="titlelessform">
+ <div class="portalMessage"
+ tal:condition="view/status" tal:content="view/status">
+ </div>
+
+
+ <form action="." method="post"
+ tal:attributes="action request/getURL; enctype view/enctype">
+
+ <metal:define define-macro="fields">
+
+ <tal:widgets repeat="widget view/widgets/values">
+ <div class="row"
+ tal:define="hidden python:widget.mode == 'hidden'"
+ tal:omit-tag="hidden">
+
+ <metal:field define-macro="field">
+ <div class="field"
+ tal:define="error widget/error"
+ tal:attributes="class python:'field' + (error and ' error' or '')">
+ <label for=""
+ tal:attributes="for widget/id"
+ tal:condition="not:hidden">
+ <span i18n:translate=""
+ tal:content="widget/label">label</span>
+ </label>
+
+ <span class="fieldRequired" title="Required"
+ tal:condition="python:widget.required and not hidden"
+ i18n:translate="label_required"
+ i18n:attributes="title title_required;">
+ (Required)
+ </span>
+
+ <div class="formHelp"
+ tal:define="description widget/field/description"
+ i18n:translate=""
+ tal:content="description"
+ tal:condition="python:description and not hidden"
+ >field description</div>
+
+ <div tal:condition="error"
+ tal:content="structure error/render">
+ Error
+ </div>
+
+ <div class="widget">
+ <input type="text" tal:replace="structure widget/render" />
+ </div>
+ </div>
+ </metal:field>
+
+ </div>
+ </tal:widgets>
+
+ </metal:define>
+
+ <metal:define define-macro="actions">
+ <div class="action" tal:repeat="action view/actions/values|nothing">
+ <input type="submit" tal:replace="structure action/render" />
+ </div>
+ </metal:define>
+
+ </form>
+ </metal:define>
+ </div>
+
+
+ </body>
+</html>
Added: plone.z3cform/trunk/plone/z3cform/templates/subform.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates/subform.pt (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates/subform.pt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,10 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ tal:attributes="class view/css_class|nothing">
+ <h3 tal:replace="structure view/heading|nothing" />
+ <div tal:replace="structure view/contents_top|nothing" />
+ <metal:use use-macro="context/@@ploneform-macros/fields" />
+ <metal:use use-macro="context/@@ploneform-macros/actions" />
+ <div tal:replace="structure view/contents_bottom|nothing" />
+</div>
Added: plone.z3cform/trunk/plone/z3cform/templates/wrappedform.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates/wrappedform.pt (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates/wrappedform.pt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1 @@
+<metal:use use-macro="context/@@ploneform-macros/titlelessform" />
Added: plone.z3cform/trunk/plone/z3cform/templates/wrappedsubform.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates/wrappedsubform.pt (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates/wrappedsubform.pt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -0,0 +1,10 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ tal:attributes="class view/css_class|nothing">
+ <h3 tal:replace="structure view/heading|nothing" />
+ <div tal:replace="structure view/contents_top|nothing" />
+ <metal:use use-macro="context/@@ploneform-macros/fields" />
+ <metal:use use-macro="context/@@ploneform-macros/actions" />
+ <div tal:replace="structure view/contents_bottom|nothing" />
+</div>
Modified: plone.z3cform/trunk/plone/z3cform/templates.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/templates.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -1,7 +1,8 @@
"""This module provides "form template factories" that can be
-registered to provide default form templates for forms and subforms
-that have a Plone style. These default templates draw from a macro
-page template which you can use by itself to render parts of it.
+registered to provide default form templates for forms and subforms.
+
+The default templates draw from a macro page template which you can use by
+itself to render parts of it.
"""
import os
@@ -9,10 +10,11 @@
import zope.app.pagetemplate.viewpagetemplatefile
import z3c.form.interfaces
+import z3c.form.form
+import z3c.form.widget
+
from z3c.form import util
from zope.pagetemplate.interfaces import IPageTemplate
-from z3c.form.form import FormTemplateFactory
-from z3c.form.widget import WidgetTemplateFactory
try:
# chameleon-compatible page templates
@@ -26,42 +28,49 @@
import plone.z3cform
import plone.z3cform.layout
-path = lambda p: os.path.join(os.path.dirname(plone.z3cform.__file__), p)
+path = lambda p: os.path.join(os.path.dirname(plone.z3cform.__file__), 'templates', p)
-class FormTemplateFactory(FormTemplateFactory):
- """Form template factory that maybe uses chameleon"""
+# Zope 2-compatible form and widget template factory classes.
- def __init__(self, filename, contentType='text/html', form=None,
- request=None):
+class FormTemplateFactory(z3c.form.form.FormTemplateFactory):
+ """Form template factory that will use Chameleon if installed (via
+ five.pt), or the Zope 2 ViewPageTemplateFile from Products.Five if not.
+
+ You can use this for a wrapped form, but not for a form that is going
+ to be rendered as a standalone view. Use ZopeTwoFormTemplateFactory for
+ that instead.
+ """
+
+ 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)
-z3c.form.form.FormTemplateFactory = FormTemplateFactory
-class ZopeTwoFormTemplateFactory(FormTemplateFactory):
- """Form template factory for Zope 2 page templates"""
+class ZopeTwoFormTemplateFactory(z3c.form.form.FormTemplateFactory):
+ """Form template factory for Zope 2 page templates.
+
+ Use this for any form which is going to be rendered as a view, or any
+ form wrapper view.
+ """
- def __init__(self, filename, contentType='text/html', form=None,
- request=None):
+ def __init__(self, filename, contentType='text/html', form=None, request=None):
self.template = ZopeTwoPageTemplateFile(filename, content_type=contentType)
zope.component.adapter(
util.getSpecification(form),
util.getSpecification(request))(self)
zope.interface.implementer(IPageTemplate)(self)
-layout_factory = ZopeTwoFormTemplateFactory(
- path('layout.pt'), form=plone.z3cform.interfaces.IFormWrapper)
-form_factory = FormTemplateFactory(
- path('form.pt'), form=z3c.form.interfaces.IForm)
-subform_factory = FormTemplateFactory(
- path('subform.pt'), form=z3c.form.interfaces.ISubForm)
-
-class ZopeTwoWidgetTemplateFactory(WidgetTemplateFactory):
- def __init__(self, filename, contentType='text/html',
- context=None, request=None, view=None,
- field=None, widget=None):
+class ZopeTwoWidgetTemplateFactory(z3c.form.widget.WidgetTemplateFactory):
+ """A variant of z3c.form's widget.WidgetTemplateFactory which uses Zope 2
+ page templates. This should only be necessary if you strictly need the
+ extra Zope 2-isms of Five's ViewPageTemplateFile.
+ """
+
+ 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),
@@ -70,10 +79,33 @@
util.getSpecification(field),
util.getSpecification(widget))(self)
zope.interface.implementer(IPageTemplate)(self)
-z3c.form.widget.WidgetTemplateFactory = ZopeTwoWidgetTemplateFactory
+# View containing common macros
+
class Macros(zope.publisher.browser.BrowserView):
- template = ViewPageTemplateFile('macros.pt')
+
+ def __getitem__(self, key):
+ return self.index.macros[key]
- def __getitem__(self, key):
- return self.template.macros[key]
+# Default templates for the wrapped layout view use case
+
+layout_factory = ZopeTwoFormTemplateFactory(path('layout.pt'),
+ form=plone.z3cform.interfaces.IFormWrapper
+ )
+
+wrapped_form_factory = FormTemplateFactory(path('wrappedform.pt'),
+ form=plone.z3cform.interfaces.IWrappedForm,
+ )
+wrapped_subform_factory = FormTemplateFactory(path('wrappedsubform.pt'),
+ form=plone.z3cform.interfaces.IWrappedSubForm,
+ )
+
+# Default templates for the standalone form use case
+
+standalone_form_factory = ZopeTwoFormTemplateFactory(path('form.pt'),
+ form=z3c.form.interfaces.IForm
+ )
+
+standalone_subform_factory = ZopeTwoFormTemplateFactory(path('subform.pt'),
+ form=z3c.form.interfaces.ISubForm
+ )
Modified: plone.z3cform/trunk/plone/z3cform/templates.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates.zcml 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/templates.zcml 2010-02-15 05:35:58 UTC (rev 109071)
@@ -13,13 +13,18 @@
name="ploneform-macros"
for="*"
class=".templates.Macros"
- template="macros.pt"
+ template="templates/macros.pt"
allowed_interface="zope.interface.common.mapping.IItemMapping"
permission="zope.Public"
/>
- <adapter factory=".templates.form_factory" />
- <adapter factory=".templates.subform_factory" />
+ <!-- Form templates for wrapped layout use case -->
<adapter factory=".templates.layout_factory" />
-
+ <adapter factory=".templates.wrapped_form_factory" />
+ <adapter factory=".templates.wrapped_subform_factory" />
+
+ <!-- Form templates for standalone form use case -->
+ <adapter factory=".templates.standalone_form_factory" />
+ <adapter factory=".templates.standalone_subform_factory" />
+
</configure>
Modified: plone.z3cform/trunk/plone/z3cform/tests.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/tests.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/tests.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -13,6 +13,8 @@
import plone.z3cform.templates
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+
def create_eventlog(event=interface.Interface):
value = []
@component.adapter(event)
@@ -32,6 +34,9 @@
zope.traversing.namespace.view, (None, None), name='view')
# Setup ploneform macros
+
+ plone.z3cform.templates.Macros.index = ViewPageTemplateFile(plone.z3cform.templates.path('macros.pt'))
+
component.provideAdapter(
plone.z3cform.templates.Macros,
(None, None),
@@ -41,7 +46,7 @@
from zope.pagetemplate.interfaces import IPageTemplate
component.provideAdapter(
- plone.z3cform.templates.form_factory,
+ plone.z3cform.templates.wrapped_form_factory,
(None, None),
IPageTemplate)
Modified: plone.z3cform/trunk/plone/z3cform/traversal.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/traversal.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/traversal.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -4,14 +4,16 @@
from zope.traversing.interfaces import ITraversable
from zope.publisher.interfaces.browser import IBrowserRequest
+from z3c.form.interfaces import IForm
+
from plone.z3cform.interfaces import IFormWrapper
from plone.z3cform import z2
from Acquisition import aq_inner
-class WidgetTraversal(object):
+class FormWidgetTraversal(object):
"""Allow traversal to widgets via the ++widget++ namespace. The context
- is the from layout wrapper.
+ is the from itself (used when the layout wrapper view is not used).
Note that to support security in Zope 2.10, the widget being traversed to
must have an __of__ method, i.e. it must support acquisition. The easiest
@@ -31,16 +33,18 @@
"""
implements(ITraversable)
- adapts(IFormWrapper, IBrowserRequest)
+ adapts(IForm, IBrowserRequest)
def __init__(self, context, request=None):
self.context = context
self.request = request
-
+
+ def _prepareForm(self):
+ return self.context
+
def traverse(self, name, ignored):
- form = self.context.form_instance
- z2.switch_on(self.context, request_layer=self.context.request_layer)
+ form = self._prepareForm()
form.update()
@@ -59,3 +63,17 @@
return widget
return None
+
+class WrapperWidgetTraversal(FormWidgetTraversal):
+ """Allow traversal to widgets via the ++widget++ namespace. The context
+ is the from layout wrapper.
+
+ The caveat about security above still applies!
+ """
+
+ adapts(IFormWrapper, IBrowserRequest)
+
+ def _prepareForm(self):
+ form = self.context.form_instance
+ z2.switch_on(self.context, request_layer=self.context.request_layer)
+ return form
Modified: plone.z3cform/trunk/plone/z3cform/traversal.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/traversal.txt 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/traversal.txt 2010-02-15 05:35:58 UTC (rev 109071)
@@ -1,10 +1,31 @@
Traversal
=========
-plone.z3cform allows you to traverse to a widget using the ++wiget++ namespace
-adapter on a form view. Note that widgets may need to mix in
-Acquisition.Explicit to be truly traversable in Zope 2.
+plone.z3cform allows you to traverse to a widget using the ++widget++
+namespace adapter on a form view.
+Note that widgets may need to mix in Acquisition.Explicit to be truly
+traversable in Zope 2.10. In Zope 2.12, that is not required. However, you
+may get an error if you mix in Explicit in Zope 2.12 and the parent view
+(normally the form or the form layout wrapper) does not, as is likely to be
+the case. To be compatible with both Zope 2.10 and 2.12, you'll need to
+mix in Acquisition.Explicit, but make sure the widget does *not* provide the
+IAcquirer interface. One way to do that is by using implementsOnly(), e.g.::
+
+ class MyWidget(Aquisition.Explicit):
+ implementsOnly(IMyWidget)
+
+ ...
+
+If you are only targeting Zope 2.12 and later, you can avoid mixing in any
+kind of acquisition altogether.
+
+The ++widget++ namespace works both in the case of a layout wrapper view,
+and in the case of a form used directly (in Zope 2.12 or later).
+
+Traversal on a standalone form
+------------------------------
+
First, we create a simple form and context.
>>> from zope.interface import alsoProvides
@@ -21,6 +42,72 @@
>>> from zope import interface, schema
>>> from z3c.form import form, field, button
+
+ >>> class MySchema(interface.Interface):
+ ... age = schema.Int(title=u"Age")
+
+ >>> from z3c.form.interfaces import IFieldsForm
+ >>> from zope.interface import implements
+ >>> class MyForm(form.Form):
+ ... implements(IFieldsForm)
+ ... fields = field.Fields(MySchema)
+ ... ignoreContext = True # don't use context to get widget data
+ ...
+ ... def update(self):
+ ... print "Updating test form"
+ ... super(MyForm, self).update()
+
+ >>> from zope.component import provideAdapter
+ >>> from zope.publisher.interfaces.browser import IBrowserRequest
+ >>> from zope.interface import Interface
+
+ >>> provideAdapter(adapts=(Interface, IBrowserRequest),
+ ... provides=Interface,
+ ... factory=MyForm,
+ ... name=u"test-form")
+
+ >>> from Acquisition import Implicit
+ >>> class Bar(Implicit):
+ ... __allow_access_to_unprotected_subobjects__ = 1
+ ... implements(Interface)
+
+ >>> from zope.component import getMultiAdapter
+ >>> context = Bar()
+ >>> request = make_request()
+
+Now, let's emulate the publisher and look up the namespace traversal
+adapter. For example, let's say we'd traversed to
+../@@test-form/++widget++age. The publisher would then do:
+
+ >>> form = getMultiAdapter((context, request), name=u"test-form")
+
+ >>> from zope.traversing.interfaces import ITraversable
+ >>> traverser = getMultiAdapter((form, request), name=u"widget")
+ >>> traverser.traverse('age', [])
+ Updating test form
+ <TextWidget 'form.widgets.age'>
+
+Please note that this point, the form has been updated, but not rendered.
+
+Traversal on a layout wrapper view
+-----------------------------------
+
+Again, we create a simple form and context.
+
+ >>> from zope.interface import alsoProvides
+ >>> from zope.publisher.browser import TestRequest
+ >>> from zope.annotation.interfaces import IAttributeAnnotatable
+ >>> from z3c.form.interfaces import IFormLayer
+
+ >>> def make_request(form={}):
+ ... request = TestRequest()
+ ... request.form.update(form)
+ ... alsoProvides(request, IFormLayer)
+ ... alsoProvides(request, IAttributeAnnotatable)
+ ... return request
+
+ >>> from zope import interface, schema
+ >>> from z3c.form import form, field, button
>>> from plone.z3cform.layout import FormWrapper
>>> class MySchema(interface.Interface):
Modified: plone.z3cform/trunk/plone/z3cform/z2.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/z2.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/plone/z3cform/z2.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -1,14 +1,69 @@
from zope import interface
-from zope.i18n.interfaces import IUserPreferredLanguages
+
+from zope.publisher.browser import isCGI_NAME
+from zope.publisher.interfaces.browser import IBrowserApplicationRequest
+
+from zope.i18n.interfaces import IUserPreferredLanguages, IUserPreferredCharsets
from zope.i18n.locales import locales, LoadLocaleError
-from Products.Five.browser import decode
+
import z3c.form.interfaces
class IFixedUpRequest(interface.Interface):
- pass
+ """Marker interface used to ensure we don't fix up the request twice
+ """
-# XXX This is ripped from zope.publisher.http.HTTPRequest; we should
-# move this into Five
+class IProcessedRequest(interface.Interface):
+ """Marker interface used to ensure we don't process the request inputs
+ twice.
+ """
+
+# Safer versions of the functions in Products.Five.browser.decode
+
+def processInputs(request, charsets=None):
+ """Process the values in request.form to decode strings to unicode, using
+ the passed-in list of charsets. If none are passed in, look up the user's
+ preferred charsets. The default is to use utf-8.
+ """
+
+ if IProcessedRequest.providedBy(request):
+ return
+
+ if charsets is None:
+ envadapter = IUserPreferredCharsets(request, None)
+ if envadapter is None:
+ charsets = ['utf-8']
+ else:
+ charsets = envadapter.getPreferredCharsets() or ['utf-8']
+
+ for name, value in request.form.items():
+ if not (isCGI_NAME(name) or name.startswith('HTTP_')):
+ if isinstance(value, str):
+ request.form[name] = _decode(value, charsets)
+ elif isinstance(value, (list, tuple,)):
+ newValue = []
+ for val in value:
+ if isinstance(val, str):
+ val = _decode(val, charsets)
+ newValue.append(val)
+
+ if isinstance(value, tuple):
+ newValue = tuple(value)
+
+ request.form[name] = newValue
+
+ interface.alsoProvides(request, IProcessedRequest)
+
+def _decode(text, charsets):
+ for charset in charsets:
+ try:
+ text = unicode(text, charset)
+ break
+ except UnicodeError:
+ pass
+ return text
+
+# This is ripped from zope.publisher.http.HTTPRequest; it is only
+# necessary in Zope < 2.11
def setup_locale(request):
envadapter = IUserPreferredLanguages(request, None)
if envadapter is None:
@@ -27,8 +82,8 @@
# which is guaranteed to exist
return locales.getLocale(None, None, None)
-# XXX Add a getURL method on the request object; we should move this
-# into Five
+# XXX Add a getURL method on the request object; this is only necessary in
+# Zope < 2.11
def add_getURL(request):
def getURL(level=0, path_only=False):
assert level == 0 and path_only == False
@@ -36,12 +91,21 @@
request.getURL = getURL
def switch_on(view, request_layer=z3c.form.interfaces.IFormLayer):
+ """Fix up the request and apply the given layer. This is mainly useful
+ in Zope < 2.10 when using a wrapper layout view.
+ """
+
request = view.request
- from zope.publisher.interfaces.browser import IBrowserApplicationRequest
- if not IFixedUpRequest.providedBy(request) and \
- not IBrowserApplicationRequest.providedBy(request):
+
+ if (not IFixedUpRequest.providedBy(request) and
+ not IBrowserApplicationRequest.providedBy(request)
+ ):
+
interface.alsoProvides(request, IFixedUpRequest)
interface.alsoProvides(request, request_layer)
- request.locale = setup_locale(request)
- add_getURL(request)
- decode.processInputs(request)
+
+ if getattr(request, 'locale', None) is None:
+ request.locale = setup_locale(request)
+
+ if not hasattr(request, 'getURL'):
+ add_getURL(request)
Modified: plone.z3cform/trunk/setup.py
===================================================================
--- plone.z3cform/trunk/setup.py 2010-02-14 22:24:30 UTC (rev 109070)
+++ plone.z3cform/trunk/setup.py 2010-02-15 05:35:58 UTC (rev 109071)
@@ -42,6 +42,7 @@
'z3c.batching',
'zope.i18n>=3.4',
'zope.component',
+ 'collective.monkeypatcher',
],
extras_require = {
'test': ['lxml']
More information about the checkins
mailing list