[Checkins] SVN: z3c.form/trunk/ - implemented the missing widget layout concept.
Roger Ineichen
cvs-admin at zope.org
Mon Aug 6 01:38:40 UTC 2012
Log message for revision 127425:
- implemented the missing widget layout concept.
Now we can call a widget and an additional layout template get used.
See CHANGES.txt for more info
Changed:
U z3c.form/trunk/CHANGES.txt
U z3c.form/trunk/setup.py
U z3c.form/trunk/src/z3c/form/browser/README.txt
U z3c.form/trunk/src/z3c/form/browser/button.py
U z3c.form/trunk/src/z3c/form/browser/checkbox.py
U z3c.form/trunk/src/z3c/form/browser/configure.zcml
U z3c.form/trunk/src/z3c/form/browser/file.py
U z3c.form/trunk/src/z3c/form/browser/image.py
U z3c.form/trunk/src/z3c/form/browser/interfaces.py
U z3c.form/trunk/src/z3c/form/browser/multi.py
U z3c.form/trunk/src/z3c/form/browser/object.py
U z3c.form/trunk/src/z3c/form/browser/password.py
U z3c.form/trunk/src/z3c/form/browser/radio.py
U z3c.form/trunk/src/z3c/form/browser/select.py
U z3c.form/trunk/src/z3c/form/browser/submit.py
U z3c.form/trunk/src/z3c/form/browser/text.py
U z3c.form/trunk/src/z3c/form/browser/textarea.py
U z3c.form/trunk/src/z3c/form/browser/widget.py
A z3c.form/trunk/src/z3c/form/browser/widget.zcml
A z3c.form/trunk/src/z3c/form/browser/widget_layout.pt
A z3c.form/trunk/src/z3c/form/browser/widget_layout_hidden.pt
U z3c.form/trunk/src/z3c/form/interfaces.py
U z3c.form/trunk/src/z3c/form/meta.zcml
U z3c.form/trunk/src/z3c/form/testing.py
U z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt
U z3c.form/trunk/src/z3c/form/util.txt
U z3c.form/trunk/src/z3c/form/widget.py
U z3c.form/trunk/src/z3c/form/zcml.py
-=-
Modified: z3c.form/trunk/CHANGES.txt
===================================================================
--- z3c.form/trunk/CHANGES.txt 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/CHANGES.txt 2012-08-06 01:38:29 UTC (rev 127425)
@@ -2,12 +2,38 @@
CHANGES
=======
-2.7.1 (unreleased)
+2.8.0 (2012-08-06)
------------------
-- Nothing changed yet.
+- Feature: Implemented widget layout concept similar to z3c.pagelet. The new
+ layout concept allows to register layout templates additional to the widget
+ templates. Such a layout template only get used if a widget get called.
+ This enhacement is optional and compatible with all previous z3c.form
+ versions and dosn't affect existing code and customm implementations
+ except if you implemented a own __call__ method for widgets which
+ wasn't implemented in previous versions. The new __call__ method will lookup
+ and return a layout template which supports additional HTML code used as
+ a wrapper for the HTML code returned from the widget render method.
+ This concept allows to define additional HTML construct provided for all
+ widget and render specific CSS classes arround the widget per context, view,
+ request, etc discriminators. Such a HTML constuct was normaly supported in
+ form macros which can't get customized on a per widget, view or context base.
+ Summary; the new layout concept allows us to define a wrapper CSS elements
+ for the widget element (label, widget, error) on a per widgte base and skip
+ the generic form macros offered from z3c.formui.
+ Note; you only could get into trouble if you define a widget in tal without
+ to prefix them with ``nocall:`` e.g. tal:define="widget view/widgets/foo"
+ Just add a nocall like tal:define="widget nocall:view/widgets/foo" if your
+ rendering engine calls the __call__method by default. Also note that the
+ following will also call the __call__ method tal:define="widget myWidget".
+
+- Fixed content type extraction test which returned different values. This
+ probably depends on a newer version of guess_content_type. Just allow
+ image/x-png and image/pjpeg as valid values.
+
+
2.7.0 (2012-07-11)
------------------
Modified: z3c.form/trunk/setup.py
===================================================================
--- z3c.form/trunk/setup.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/setup.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -45,7 +45,7 @@
setup(
name='z3c.form',
- version='2.7.1.dev0',
+ version='2.8.0',
author="Stephan Richter, Roger Ineichen and the Zope Community",
author_email="zope-dev at zope.org",
description="An advanced form and widget framework for Zope 3",
Modified: z3c.form/trunk/src/z3c/form/browser/README.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/README.txt 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/README.txt 2012-08-06 01:38:29 UTC (rev 127425)
@@ -65,7 +65,47 @@
<span id="foo" class="textarea-widget required ascii-field">This is
ASCII.</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="textarea-widget required ascii-field">This is
+ ASCII.</span>
+ </div>
+ </div>
+
+As you can see, we will get an additional error class called ``row-error`` if
+we render a widget with an error view assinged:
+
+ >>> class DummyErrorView(object):
+ ... def render(self):
+ ... return u'Dummy Error'
+ >>> widget.error = (DummyErrorView())
+ >>> print widget()
+ <div id="foo-row" class="row-error row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="textarea-widget required ascii-field">This is
+ ASCII.</span>
+ </div>
+ <div class="error">
+ Dummy Error
+ </div>
+ </div>
+
+
ASCIILine
---------
@@ -87,6 +127,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required asciiline-field">An ASCII line.</span>
+Calling the widget will return the widget including the layout
+
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required asciiline-field">An ASCII line.</span>
+ </div>
+ </div>
+
+
Bool
----
@@ -122,6 +178,21 @@
<span id="foo" class="radio-widget required bool-field"><span
class="selected-option">yes</span></span>
+Calling the widget will return the widget including the layout
+
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span>Check me</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="radio-widget required bool-field"><span class="selected-option">yes</span></span>
+ </div>
+ </div>
+
For the boolean, the checkbox widget can be used as well:
>>> from z3c.form.browser import checkbox
@@ -152,6 +223,21 @@
<span id="foo" class="checkbox-widget required bool-field"><span
class="selected-option">yes</span></span>
+Calling the widget will return the widget including the layout
+
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span>Check me</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="checkbox-widget required bool-field"><span class="selected-option">yes</span></span>
+ </div>
+ </div>
+
We can also have a single checkbox button for the boolean.
>>> widget = checkbox.SingleCheckBoxFieldWidget(field, TestRequest())
@@ -181,7 +267,22 @@
>>> widget.label
u''
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="single-checkbox-widget required bool-field"><span class="selected-option">Check me</span></span>
+ </div>
+ </div>
+
+
Button
------
@@ -243,7 +344,22 @@
>>> widget.render().strip('\n')
u'<span id="foo" class="file-widget required bytes-field">Default bytes</span>'
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="file-widget required bytes-field">Default bytes</span>
+ </div>
+ </div>
+
+
BytesLine
---------
@@ -265,7 +381,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required bytesline-field">A Bytes line.</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required bytesline-field">A Bytes line.</span>
+ </div>
+ </div>
+
+
Choice
------
@@ -296,7 +427,22 @@
<span id="foo" class="select-widget required choice-field"><span
class="selected-option">Yes</span></span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="select-widget required choice-field"><span class="selected-option">Yes</span></span>
+ </div>
+ </div>
+
+
Date
----
@@ -319,7 +465,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required date-field">07/04/01</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required date-field">07/04/01</span>
+ </div>
+ </div>
+
+
Datetime
--------
@@ -341,7 +502,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required datetime-field">07/04/01 12:00</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required datetime-field">07/04/01 12:00</span>
+ </div>
+ </div>
+
+
Decimal
-------
@@ -364,7 +540,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required decimal-field">1,265.87</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required decimal-field">1,265.87</span>
+ </div>
+ </div>
+
+
Dict
----
@@ -393,7 +584,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required dottedname-field">z3c.form</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required dottedname-field">z3c.form</span>
+ </div>
+ </div>
+
+
Float
-----
@@ -415,7 +621,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required float-field">1,265.8</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required float-field">1,265.8</span>
+ </div>
+ </div>
+
+
FrozenSet
---------
@@ -447,7 +668,21 @@
class="selected-option">1</span>, <span
class="selected-option">3</span></span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="select-widget required frozenset-field"><span class="selected-option">1</span>, <span class="selected-option">3</span></span>
+ </div>
+ </div>
+
Id
--
@@ -469,7 +704,22 @@
>>> print widget.render()
<span id="foo" class="text-widget required id-field">z3c.form</span>
+Calling the widget will return the widget including the layout
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required id-field">z3c.form</span>
+ </div>
+ </div>
+
+
ImageButton
-----------
@@ -3066,3 +3316,18 @@
>>> widget.mode = interfaces.DISPLAY_MODE
>>> print widget.render()
<span id="foo" class="text-widget required uri-field">http://zope.org</span>
+
+Calling the widget will return the widget including the layout
+
+ >>> print widget()
+ <div id="foo-row" class="row-required row">
+ <div class="label">
+ <label for="foo">
+ <span></span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <span id="foo" class="text-widget required uri-field">http://zope.org</span>
+ </div>
+ </div>
Modified: z3c.form/trunk/src/z3c/form/browser/button.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/button.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/button.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -29,6 +29,7 @@
zope.interface.implementsOnly(interfaces.IButtonWidget)
klass = u'button-widget'
+ css = u'button'
def update(self):
# We do not need to use the widget's update method, because it is
Modified: z3c.form/trunk/src/z3c/form/browser/checkbox.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/checkbox.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/checkbox.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -34,6 +34,7 @@
zope.interface.implementsOnly(interfaces.ICheckBoxWidget)
klass = u'checkbox-widget'
+ css = u'checkbox'
items = ()
def isChecked(self, term):
Modified: z3c.form/trunk/src/z3c/form/browser/configure.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/configure.zcml 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/configure.zcml 2012-08-06 01:38:29 UTC (rev 127425)
@@ -18,4 +18,6 @@
<include file="textlines.zcml" />
<include file="object.zcml" />
+ <include file="widget.zcml" />
+
</configure>
Modified: z3c.form/trunk/src/z3c/form/browser/file.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/file.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -29,6 +29,7 @@
zope.interface.implementsOnly(interfaces.IFileWidget)
klass = u'file-widget'
+ css = u'file'
# Filename and headers attribute get set by ``IDataConverter`` to the widget
# provided by the FileUpload object of the form.
Modified: z3c.form/trunk/src/z3c/form/browser/image.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/image.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/image.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -31,8 +31,11 @@
class ImageWidget(button.ButtonWidget):
"""A image button of a form."""
zope.interface.implementsOnly(interfaces.IImageWidget)
+
src = FieldProperty(IHTMLImageWidget['src'])
+
klass = u'image-widget'
+ css = u'image'
def extract(self, default=interfaces.NO_VALUE):
"""See z3c.form.interfaces.IWidget."""
Modified: z3c.form/trunk/src/z3c/form/browser/interfaces.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/interfaces.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/interfaces.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -20,6 +20,30 @@
import zope.schema
+class IWidgetLayoutSupport(zope.interface.Interface):
+
+ css = zope.schema.TextLine(
+ title=u'Widget layout CSS class name(s)',
+ description=(u'This attribute defines one or more layout class names.'),
+ default=u'row',
+ required=False)
+
+ def getCSSClass(klass=None, error=None, required=None,
+ classPattern='%(class)s', errorPattern='%(class)s-error',
+ requiredPattern='%(class)s-required'):
+ """Setup given css class (klass) with error and required postfix
+
+ If no klass name is given the widget.wrapper class name/names get used.
+ It is also possible if more then one (empty space separated) names
+ are given as klass argument.
+
+ This method can get used from your form or widget template or widget
+ layout template without to re-implement the widget itself just because
+ you a different CSS class concept.
+
+ """
+
+
class IHTMLCoreAttributes(zope.interface.Interface):
"""The HTML element 'core' attributes."""
@@ -126,8 +150,9 @@
class IHTMLFormElement(IHTMLCoreAttributes,
IHTMLI18nAttributes,
- IHTMLEventsAttributes):
- """A generic form-related element."""
+ IHTMLEventsAttributes,
+ IWidgetLayoutSupport):
+ """A generic form-related element including layout template support."""
disabled = zope.schema.Choice(
title=u'Disabled',
Modified: z3c.form/trunk/src/z3c/form/browser/multi.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/multi.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/multi.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -40,6 +40,7 @@
prefix = 'widget'
klass = u'multi-widget'
+ css = u'multi'
items = ()
showLabel = True # show labels for item subwidgets or not
Modified: z3c.form/trunk/src/z3c/form/browser/object.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/object.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/object.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -29,6 +29,7 @@
zope.interface.implements(interfaces.IObjectWidget)
klass = u'object-widget'
+ css = u'object'
@zope.component.adapter(zope.schema.interfaces.IObject, interfaces.IFormLayer)
@zope.interface.implementer(interfaces.IFieldWidget)
Modified: z3c.form/trunk/src/z3c/form/browser/password.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/password.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/password.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -28,6 +28,7 @@
zope.interface.implementsOnly(interfaces.IPasswordWidget)
klass = u'password-widget'
+ css = u'password'
@zope.component.adapter(zope.schema.interfaces.IPassword, interfaces.IFormLayer)
Modified: z3c.form/trunk/src/z3c/form/browser/radio.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/radio.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/radio.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -33,6 +33,7 @@
zope.interface.implementsOnly(interfaces.IRadioWidget)
klass = u'radio-widget'
+ css = u'radio'
items = ()
def isChecked(self, term):
Modified: z3c.form/trunk/src/z3c/form/browser/select.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/select.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/select.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -33,6 +33,7 @@
zope.interface.implementsOnly(interfaces.ISelectWidget)
klass = u'select-widget'
+ css = u'select'
prompt = False
noValueMessage = _('no value')
Modified: z3c.form/trunk/src/z3c/form/browser/submit.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/submit.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/submit.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -27,7 +27,9 @@
class SubmitWidget(button.ButtonWidget):
"""A submit button of a form."""
zope.interface.implementsOnly(interfaces.ISubmitWidget)
+
klass = u'submit-widget'
+ css = u'submit'
@zope.component.adapter(interfaces.IButton, interfaces.IFormLayer)
Modified: z3c.form/trunk/src/z3c/form/browser/text.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/text.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/text.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -29,6 +29,7 @@
zope.interface.implementsOnly(interfaces.ITextWidget)
klass = u'text-widget'
+ css = u'text'
value = u''
def update(self):
Modified: z3c.form/trunk/src/z3c/form/browser/textarea.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/textarea.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/textarea.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -30,6 +30,7 @@
zope.interface.implementsOnly(interfaces.ITextAreaWidget)
klass = u'textarea-widget'
+ css = u'textarea'
value = u''
def update(self):
Modified: z3c.form/trunk/src/z3c/form/browser/widget.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/widget.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/browser/widget.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -22,7 +22,99 @@
from z3c.form.interfaces import IFieldWidget
from z3c.form.browser import interfaces
-class HTMLFormElement(object):
+
+class WidgetLayoutSupport(object):
+ """Widget layout support"""
+
+ def wrapCSSClass(self, klass, pattern='s(class)%'):
+ """Return a list of css class names wrapped with given pattern"""
+ if klass is not None and pattern is not None:
+ return [pattern % {'class': k} for k in klass.split()]
+ else:
+ return []
+
+ def getCSSClass(self, klass=None, error=None, required=None,
+ classPattern='%(class)s', errorPattern='%(class)s-error',
+ requiredPattern='%(class)s-required'):
+ """Setup given css class (klass) with error and required postfix
+
+ If no klass name is given the widget.wrapper class name/names get used.
+ It is also possible if more then one (empty space separated) names
+ are given as klass argument.
+
+ This method can get used from your form or widget template or widget
+ layout template without to re-implement the widget itself just because
+ you a different CSS class concept.
+
+ The following sample:
+
+ <div tal:attributes="class python:widget.getCSSClass('foo bar')">
+ label widget and error
+ </div>
+
+ will render a div tag if the widget field defines required=True:
+
+ <div class="foo-error bar-error foo-required bar-required foo bar">
+ label widget and error
+ </div>
+
+ And the following sample:
+
+ <div tal:attributes="class python:widget.getCSSClass('row')">
+ label widget and error
+ </div>
+
+ will render a div tag if the widget field defines required=True
+ and an error occurs:
+
+ <div class="row-error row-required row">
+ label widget and error
+ </div>
+
+ Note; you need to define a globale widget property if you use
+ python:widget (in your form template). And you need to use the
+ view scope in your widget or layout templates.
+
+ Note, you can set the pattern to None for skip error or required
+ rendering. Or you can use a pattern like 'error' or 'required' if
+ you like to skip postfixing your default css klass name for error or
+ required rendering.
+
+ """
+ classes = []
+ # setup class names
+ if klass is not None:
+ kls = klass
+ else:
+ kls = self.css
+
+ # setup error class names
+ if error is not None:
+ error = error
+ else:
+ error = kls
+
+ # setup required class names
+ if required is not None:
+ required = required
+ else:
+ required = kls
+
+ # append error class names
+ if self.error is not None:
+ classes += self.wrapCSSClass(error, errorPattern)
+ # append required class names
+ if self.required:
+ classes += self.wrapCSSClass(required, requiredPattern)
+ # append given class names
+ classes += self.wrapCSSClass(kls, classPattern)
+ # remove duplicated class names but keep order
+ unique = []
+ [unique.append(kls) for kls in classes if kls not in unique]
+ return ' '.join(unique)
+
+
+class HTMLFormElement(WidgetLayoutSupport):
zope.interface.implements(interfaces.IHTMLFormElement)
id = FieldProperty(interfaces.IHTMLFormElement['id'])
@@ -49,6 +141,9 @@
onblur = FieldProperty(interfaces.IHTMLFormElement['onblur'])
onchange = FieldProperty(interfaces.IHTMLFormElement['onchange'])
+ # layout support
+ css = FieldProperty(interfaces.IHTMLFormElement['css'])
+
def addClass(self, klass):
"""See interfaces.IHTMLFormElement"""
if not self.klass:
Added: z3c.form/trunk/src/z3c/form/browser/widget.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/widget.zcml (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/widget.zcml 2012-08-06 01:38:29 UTC (rev 127425)
@@ -0,0 +1,27 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="z3c.form">
+
+ <!-- widget layout templates -->
+ <z3c:widgetLayout
+ mode="display"
+ widget="z3c.form.interfaces.IWidget"
+ layer="z3c.form.interfaces.IFormLayer"
+ template="widget_layout.pt"
+ />
+ <z3c:widgetLayout
+ mode="input"
+ widget="z3c.form.interfaces.IWidget"
+ layer="z3c.form.interfaces.IFormLayer"
+ template="widget_layout.pt"
+ />
+ <z3c:widgetLayout
+ mode="hidden"
+ widget="z3c.form.interfaces.IWidget"
+ layer="z3c.form.interfaces.IFormLayer"
+ template="widget_layout_hidden.pt"
+ />
+
+</configure>
Property changes on: z3c.form/trunk/src/z3c/form/browser/widget.zcml
___________________________________________________________________
Added: svn:keywords
+ Date Author Id Revision
Added: svn:eol-style
+ native
Added: z3c.form/trunk/src/z3c/form/browser/widget_layout.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/widget_layout.pt (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/widget_layout.pt 2012-08-06 01:38:29 UTC (rev 127425)
@@ -0,0 +1,23 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ tal:omit-tag="">
+<div id="" class="row"
+ tal:attributes="id string:${view/id}-row;
+ class python:view.getCSSClass('row')">
+ <div class="label">
+ <label tal:attributes="for view/id">
+ <span i18n:translate=""
+ tal:content="view/label">label</span>
+ <span class="required"
+ tal:condition="view/required">*</span>
+ </label>
+ </div>
+ <div class="widget" tal:content="structure view/render">
+ <input type="text" size="24" value="" />
+ </div>
+ <div class="error"
+ tal:condition="view/error">
+ <span tal:replace="structure view/error/render">error</span>
+ </div>
+</div>
+</div>
Property changes on: z3c.form/trunk/src/z3c/form/browser/widget_layout.pt
___________________________________________________________________
Added: svn:keywords
+ Date Author Id Revision
Added: svn:eol-style
+ native
Added: z3c.form/trunk/src/z3c/form/browser/widget_layout_hidden.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/widget_layout_hidden.pt (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/widget_layout_hidden.pt 2012-08-06 01:38:29 UTC (rev 127425)
@@ -0,0 +1,5 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ tal:omit-tag="">
+<tal:block replace="structure view/render"></tal:block>
+</div>
Property changes on: z3c.form/trunk/src/z3c/form/browser/widget_layout_hidden.pt
___________________________________________________________________
Added: svn:keywords
+ Date Author Id Revision
Added: svn:eol-style
+ native
Modified: z3c.form/trunk/src/z3c/form/interfaces.py
===================================================================
--- z3c.form/trunk/src/z3c/form/interfaces.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/interfaces.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -379,7 +379,17 @@
"""Return a default object created to be populated.
"""
+# ----[ Widget layout template ]----------------------------------------------
+class IWidgetLayoutTemplate(zope.interface.Interface):
+ """Widget layout template marker used for render the widget layout.
+
+ It is important that we don't inherit this template from IPageTemplate.
+ otherwise we will get into trouble since we lookup an IPageTemplate
+ in the widget/render method.
+
+ """
+
# ----[ Widgets ]------------------------------------------------------------
class IWidget(ILocation):
@@ -427,6 +437,7 @@
required=False)
template = zope.interface.Attribute('''The widget template''')
+ layout = zope.interface.Attribute('''The widget layout template''')
ignoreRequest = zope.schema.Bool(
title=_('Ignore Request'),
@@ -470,9 +481,12 @@
"""Setup all of the widget information used for displaying."""
def render():
- """Return the widget's text representation."""
+ """Render the plain widget without additional layout"""
+ def __call__():
+ """Render a layout template which is calling widget/render"""
+
class ISequenceWidget(IWidget):
"""Term based sequence widget base.
Modified: z3c.form/trunk/src/z3c/form/meta.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/meta.zcml 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/meta.zcml 2012-08-06 01:38:29 UTC (rev 127425)
@@ -11,6 +11,12 @@
/>
<meta:directive
+ name="widgetLayout"
+ schema=".zcml.IWidgetTemplateDirective"
+ handler=".zcml.widgetLayoutTemplateDirective"
+ />
+
+ <meta:directive
name="objectWidgetTemplate"
schema=".zcml.IObjectWidgetTemplateDirective"
handler=".zcml.objectWidgetTemplateDirective"
Modified: z3c.form/trunk/src/z3c/form/testing.py
===================================================================
--- z3c.form/trunk/src/z3c/form/testing.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/testing.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -271,7 +271,23 @@
zope.component.provideAdapter(
text.TextFieldWidget,
adapts=(zope.schema.interfaces.IURI, interfaces.IFormLayer))
+
+ # Widget Layout
zope.component.provideAdapter(
+ widget.WidgetLayoutFactory(getPath('widget_layout.pt'), 'text/html'),
+ (None, None, None, None, interfaces.IWidget),
+ interfaces.IWidgetLayoutTemplate, name=interfaces.INPUT_MODE)
+ zope.component.provideAdapter(
+ widget.WidgetLayoutFactory(getPath('widget_layout.pt'), 'text/html'),
+ (None, None, None, None, interfaces.IWidget),
+ interfaces.IWidgetLayoutTemplate, name=interfaces.DISPLAY_MODE)
+ zope.component.provideAdapter(
+ widget.WidgetLayoutFactory(getPath('widget_layout_hidden.pt'), 'text/html'),
+ (None, None, None, None, interfaces.IWidget),
+ interfaces.IWidgetLayoutTemplate, name=interfaces.HIDDEN_MODE)
+
+ # Text Field Widget
+ zope.component.provideAdapter(
widget.WidgetTemplateFactory(getPath('text_input.pt'), 'text/html'),
(None, None, None, None, interfaces.ITextWidget),
IPageTemplate, name=interfaces.INPUT_MODE)
Modified: z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/tests/simple_edit_with_providers.pt 2012-08-06 01:38:29 UTC (rev 127425)
@@ -17,7 +17,7 @@
<div class="row" tal:define="is_widget snippet/id | nothing">
<tal:widget condition="is_widget"
- define="widget snippet">
+ define="widget nocall:snippet">
<b tal:condition="widget/error"
tal:content="structure widget/error/render"
/><label tal:condition="widget/id"
@@ -28,7 +28,7 @@
</tal:widget>
<tal:provider condition="not:is_widget"
- define="contentprovider snippet"
+ define="contentprovider nocall:snippet"
replace="structure contentprovider/render" />
</div>
Modified: z3c.form/trunk/src/z3c/form/util.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/util.txt 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/util.txt 2012-08-06 01:38:29 UTC (rev 127425)
@@ -229,6 +229,11 @@
There is also a method which is able to extract the content type for a given
file upload. We can use the stub form from the previous test.
+Not sure if this an error but on my windows system this test returns
+image/pjpeg (progressive jpeg) for foo.jpg and image/x-png for foo.png. So
+let's allow this too since this depends on guess_content_type and is not
+really a part of z3c.form.
+
>>> uploadForm = FileUploadFormStub()
>>> uploadForm.setFakeFileName('foo.txt')
>>> util.extractContentType(uploadForm, 'form.widgets.data')
@@ -240,11 +245,11 @@
>>> uploadForm.setFakeFileName('foo.jpg')
>>> util.extractContentType(uploadForm, 'form.widgets.data')
- 'image/jpeg'
+ 'image/...jpeg'
>>> uploadForm.setFakeFileName('foo.png')
>>> util.extractContentType(uploadForm, 'form.widgets.data')
- 'image/png'
+ 'image/...png'
>>> uploadForm.setFakeFileName('foo.tif')
>>> util.extractContentType(uploadForm, 'form.widgets.data')
Modified: z3c.form/trunk/src/z3c/form/widget.py
===================================================================
--- z3c.form/trunk/src/z3c/form/widget.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/widget.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -51,6 +51,7 @@
error = FieldProperty(interfaces.IWidget['error'])
value = FieldProperty(interfaces.IWidget['value'])
template = None
+ layout = None
ignoreRequest = FieldProperty(interfaces.IWidget['ignoreRequest'])
setErrors = FieldProperty(interfaces.IWidget['setErrors'])
showDefault = FieldProperty(interfaces.IWidget['showDefault'])
@@ -134,8 +135,12 @@
if value is not None:
setattr(self, attrName, value.get())
+ def extract(self, default=interfaces.NO_VALUE):
+ """See z3c.form.interfaces.IWidget."""
+ return self.request.get(self.name, default)
+
def render(self):
- """See z3c.form.interfaces.IWidget."""
+ """Render the plain widget without additional layout"""
template = self.template
if template is None:
template = zope.component.getMultiAdapter(
@@ -143,9 +148,14 @@
IPageTemplate, name=self.mode)
return template(self)
- def extract(self, default=interfaces.NO_VALUE):
- """See z3c.form.interfaces.IWidget."""
- return self.request.get(self.name, default)
+ def __call__(self):
+ """Get and return layout template which is calling widget/render"""
+ layout = self.layout
+ if layout is None:
+ layout = zope.component.getMultiAdapter(
+ (self.context, self.request, self.form, self.field, self),
+ interfaces.IWidgetLayoutTemplate, name=self.mode)
+ return layout(self)
def __repr__(self):
return '<%s %r>' % (self.__class__.__name__, self.name)
@@ -454,6 +464,25 @@
return self.template
+class WidgetLayoutFactory(object):
+ """Widget layout 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(interfaces.IWidgetLayoutTemplate)(self)
+
+ def __call__(self, context, request, view, field, widget):
+ return self.template
+
+
class WidgetEvent(object):
zope.interface.implements(interfaces.IWidgetEvent)
Modified: z3c.form/trunk/src/z3c/form/zcml.py
===================================================================
--- z3c.form/trunk/src/z3c/form/zcml.py 2012-08-03 09:32:49 UTC (rev 127424)
+++ z3c.form/trunk/src/z3c/form/zcml.py 2012-08-06 01:38:29 UTC (rev 127425)
@@ -30,6 +30,7 @@
from z3c.form.i18n import MessageFactory as _
from z3c.form.widget import WidgetTemplateFactory
from z3c.form.object import ObjectWidgetTemplateFactory
+from z3c.form.widget import WidgetLayoutFactory
class IWidgetTemplateDirective(zope.interface.Interface):
@@ -110,6 +111,25 @@
(for_, layer, view, field, widget), name=mode)
+def widgetLayoutTemplateDirective(
+ _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 = WidgetLayoutFactory(template, contentType)
+ zope.interface.directlyProvides(factory, interfaces.IWidgetLayoutTemplate)
+
+ # register the template
+ zope.component.zcml.adapter(_context, (factory,),
+ interfaces.IWidgetLayoutTemplate, (for_, layer, view, field, widget),
+ name=mode)
+
+
def objectWidgetTemplateDirective(
_context, template, for_=zope.interface.Interface,
layer=IDefaultBrowserLayer, view=None, field=None, widget=None,
More information about the checkins
mailing list