[Checkins] SVN: z3c.form/trunk/ - Feature: Implemented groups within forms. Groups allow you to combine a set

Stephan Richter srichter at cosmos.phy.tufts.edu
Tue Jun 26 18:35:09 EDT 2007


Log message for revision 77114:
  - Feature: Implemented groups within forms. Groups allow you to combine a set
    of fields/widgets into a logical unit. They were designed with ease of use
    in mind.
  
  

Changed:
  U   z3c.form/trunk/CHANGES.txt
  U   z3c.form/trunk/setup.py
  U   z3c.form/trunk/src/z3c/form/README.txt
  U   z3c.form/trunk/src/z3c/form/form.py
  A   z3c.form/trunk/src/z3c/form/group.py
  A   z3c.form/trunk/src/z3c/form/group.txt
  U   z3c.form/trunk/src/z3c/form/interfaces.py
  U   z3c.form/trunk/src/z3c/form/subform.txt
  A   z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt
  U   z3c.form/trunk/src/z3c/form/tests/test_doc.py

-=-
Modified: z3c.form/trunk/CHANGES.txt
===================================================================
--- z3c.form/trunk/CHANGES.txt	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/CHANGES.txt	2007-06-26 22:35:05 UTC (rev 77114)
@@ -2,6 +2,14 @@
 CHANGES
 =======
 
+Version 1.4.0 (??/??/2007)
+-------------------------
+
+- Feature: Implemented groups within forms. Groups allow you to combine a set
+  of fields/widgets into a logical unit. They were designed with ease of use
+  in mind.
+
+
 Version 1.3.0 (6/22/2007)
 -------------------------
 

Modified: z3c.form/trunk/setup.py
===================================================================
--- z3c.form/trunk/setup.py	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/setup.py	2007-06-26 22:35:05 UTC (rev 77114)
@@ -27,6 +27,7 @@
     [read('src', 'z3c', 'form', name)
     for name in ('README.txt',
                  'form.txt',
+                 'group.txt',
                  'subform.txt',
                  'field.txt',
                  'button.txt',
@@ -42,7 +43,7 @@
 
 setup (
     name='z3c.form',
-    version='1.3.0',
+    version='1.4.0',
     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",

Modified: z3c.form/trunk/src/z3c/form/README.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/README.txt	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/src/z3c/form/README.txt	2007-06-26 22:35:05 UTC (rev 77114)
@@ -12,6 +12,11 @@
   Describes the setup and usage of forms in the most common usages. Some
   details are provided to the structure of form components.
 
+- ``group.txt`` [must read]
+
+  This document describes how widget groups are implemented within this
+  package and how they can be used.
+
 - ``subform.txt`` [must read]
 
   Introduces the complexities surrounding sub-forms and details two classes of

Modified: z3c.form/trunk/src/z3c/form/form.py
===================================================================
--- z3c.form/trunk/src/z3c/form/form.py	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/src/z3c/form/form.py	2007-06-26 22:35:05 UTC (rev 77114)
@@ -77,17 +77,25 @@
     template = None
 
     def getContent(self):
+        '''See interfaces.IForm'''
         return self.context
 
     def updateWidgets(self):
+        '''See interfaces.IForm'''
         self.widgets = zope.component.getMultiAdapter(
             (self, self.request, self.getContent()), interfaces.IWidgets)
         self.widgets.update()
 
+    def extractData(self):
+        '''See interfaces.IForm'''
+        return self.widgets.extract()
+
     def update(self):
+        '''See interfaces.IForm'''
         self.updateWidgets()
 
     def render(self):
+        '''See interfaces.IForm'''
         # render content template
         if self.template is None:
             template = zope.component.getMultiAdapter((self, self.request),
@@ -154,7 +162,7 @@
 
     @button.buttonAndHandler(_('Add'), name='add')
     def handleAdd(self, action):
-        data, errors = self.widgets.extract()
+        data, errors = self.extractData()
         if errors:
             self.status = self.formErrorsMessage
             return
@@ -209,7 +217,7 @@
 
     @button.buttonAndHandler(_('Apply'), name='apply')
     def handleApply(self, action):
-        data, errors = self.widgets.extract()
+        data, errors = self.extractData()
         if errors:
             self.status = self.formErrorsMessage
             return

Added: z3c.form/trunk/src/z3c/form/group.py
===================================================================
--- z3c.form/trunk/src/z3c/form/group.py	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/group.py	2007-06-26 22:35:05 UTC (rev 77114)
@@ -0,0 +1,85 @@
+##############################################################################
+#
+# 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 Group Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+
+from z3c.form import form, interfaces
+
+class Group(form.BaseForm):
+
+    label = None
+
+    def __init__(self, context, request, parentForm):
+        self.context = context
+        self.request = request
+        self.parentForm = self.__parent__ = parentForm
+
+    def updateWidgets(self):
+        '''See interfaces.IForm'''
+        self.widgets = zope.component.getMultiAdapter(
+            (self, self.request, self.getContent()), interfaces.IWidgets)
+        for attrName in ('mode', 'ignoreRequest', 'ignoreContext',
+                         'ignoreReadonly'):
+            value = getattr(self.parentForm.widgets, attrName)
+            setattr(self.widgets, attrName, value)
+        self.widgets.update()
+
+
+
+class GroupForm(object):
+    """A mix-in class for add and edit forms to support groups."""
+
+    groups = ()
+
+    def extractData(self):
+        '''See interfaces.IForm'''
+        data, errors = super(GroupForm, self).extractData()
+        for group in self.groups:
+            groupData, groupErrors = group.extractData()
+            data.update(groupData)
+            if groupErrors:
+                if errors:
+                    errors += groupErrors
+                else:
+                    errors = groupErrors
+        return data, errors
+
+    def applyChanges(self, data):
+        '''See interfaces.IEditForm'''
+        changed = False
+        content = self.getContent()
+        form.applyChanges(self, content, data)
+        for group in self.groups:
+            groupChanged = form.applyChanges(group, content, data)
+            changed = changed or groupChanged
+        if changed:
+            zope.event.notify(
+                zope.lifecycleevent.ObjectModifiedEvent(content))
+        return changed
+
+    def update(self):
+        '''See interfaces.IForm'''
+        self.updateWidgets()
+        groups = []
+        for groupClass in self.groups:
+            group = groupClass(self.context, self.request, self)
+            group.update()
+            groups.append(group)
+        self.groups = tuple(groups)
+        self.updateActions()
+        self.actions.execute()


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

Added: z3c.form/trunk/src/z3c/form/group.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/group.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/group.txt	2007-06-26 22:35:05 UTC (rev 77114)
@@ -0,0 +1,425 @@
+===========
+Group Forms
+===========
+
+Group forms allow you to split up a form into several logical units without
+much overhead. To the parent form, groups should be only dealt with during
+coding and be transparent on the data extraction level.
+
+For the examples to work, we have to bring up most of the form framework:
+
+  >>> from z3c.form import testing
+  >>> testing.setupFormDefaults()
+
+So let's first define a complex content component that warrants setting up
+multiple groups:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IVehicleRegistration(zope.interface.Interface):
+  ...     firstName = zope.schema.TextLine(title=u'First Name')
+  ...     lastName = zope.schema.TextLine(title=u'Last Name')
+  ...
+  ...     license = zope.schema.TextLine(title=u'License')
+  ...     address = zope.schema.TextLine(title=u'Address')
+  ...
+  ...     model = zope.schema.TextLine(title=u'Model')
+  ...     make = zope.schema.TextLine(title=u'Make')
+  ...     year = zope.schema.Int(title=u'Year')
+
+  >>> class VehicleRegistration(object):
+  ...     zope.interface.implements(IVehicleRegistration)
+  ...
+  ...     def __init__(self, **kw):
+  ...         for name, value in kw.items():
+  ...             setattr(self, name, value)
+
+The schema above can be separated into basic, license, and car information,
+where the latter two will be placed into groups. First we create the two
+groups:
+
+  >>> from z3c.form import field, group
+
+  >>> class LicenseGroup(group.Group):
+  ...     label = u'License'
+  ...     fields = field.Fields(IVehicleRegistration).select(
+  ...         'license', 'address')
+
+  >>> class CarGroup(group.Group):
+  ...     label = u'Car'
+  ...     fields = field.Fields(IVehicleRegistration).select(
+  ...         'model', 'make', 'year')
+
+Most of the group is setup like any other (sub)form. Additionally, you can
+specify a label, which is a human-readable string that can be used for layout
+purposes.
+
+Let's now create an add form for the entire vehicle registration. In
+comparison to a regular add form, you only need to add the ``GroupForm`` as
+one of the base classes. The groups are specified in a simple tuple:
+
+  >>> import os
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import form, tests
+
+  >>> class RegistrationAddForm(group.GroupForm, form.AddForm):
+  ...     fields = field.Fields(IVehicleRegistration).select(
+  ...         'firstName', 'lastName')
+  ...     groups = (LicenseGroup, CarGroup)
+  ...
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_groupedit.pt', os.path.dirname(tests.__file__))
+  ...
+  ...     def create(self, data):
+  ...         return VehicleRegistration(**data)
+  ...
+  ...     def add(self, object):
+  ...         self.getContent()['obj1'] = object
+  ...         return object
+
+
+Note: The order of the base classes is very important here. The ``GroupForm``
+class must be left of the ``AddForm`` class, because the ``GroupForm`` class
+overrides some methods of the ``AddForm`` class.
+
+Now we can instantiate the form:
+
+  >>> request = testing.TestRequest()
+
+  >>> add = RegistrationAddForm(None, request)
+  >>> add.update()
+
+After the form is updated the tuple of group classes is converted to group
+instances:
+
+  >>> add.groups
+  (<LicenseGroup object at ...>, <CarGroup object at ...>)
+
+We can now render the form:
+
+  >>> print add.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-firstName">First Name</label>
+          <input type="text" id="form-widgets-firstName"
+         name="form.widgets.firstName" class="textWidget"
+         value="" />
+        </div>
+        <div class="row">
+          <label for="form-widgets-lastName">Last Name</label>
+          <input type="text" id="form-widgets-lastName"
+         name="form.widgets.lastName" class="textWidget"
+         value="" />
+        </div>
+        <fieldgroup>
+          <legend>License</legend>
+          <div class="row">
+            <label for="form-widgets-license">License</label>
+            <input type="text" id="form-widgets-license"
+           name="form.widgets.license" class="textWidget"
+           value="" />
+          </div>
+          <div class="row">
+            <label for="form-widgets-address">Address</label>
+            <input type="text" id="form-widgets-address"
+           name="form.widgets.address" class="textWidget"
+           value="" />
+          </div>
+        </fieldgroup>
+        <fieldgroup>
+          <legend>Car</legend>
+          <div class="row">
+            <label for="form-widgets-model">Model</label>
+            <input type="text" id="form-widgets-model"
+           name="form.widgets.model" class="textWidget" value="" />
+          </div>
+          <div class="row">
+            <label for="form-widgets-make">Make</label>
+            <input type="text" id="form-widgets-make"
+           name="form.widgets.make" class="textWidget" value="" />
+          </div>
+          <div class="row">
+            <label for="form-widgets-year">Year</label>
+            <input type="text" id="form-widgets-year"
+                   name="form.widgets.year" class="textWidget" value="" />
+          </div>
+        </fieldgroup>
+        <div class="action">
+          <input type="submit" id="form-buttons-add"
+                 name="form.buttons.add" class="submitWidget"
+                 value="Add" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+Let's now submit the form, but forgetting to enter the address:
+
+  >>> request = testing.TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.lastName': u'Richter',
+  ...     'form.widgets.license': u'MA 40387',
+  ...     'form.widgets.model': u'BMW',
+  ...     'form.widgets.make': u'325',
+  ...     'form.widgets.year': u'2005',
+  ...     'form.buttons.add': u'Add'
+  ...     })
+
+  >>> add = RegistrationAddForm(None, request)
+  >>> add.update()
+  >>> print add.render()
+  <html>
+    <body>
+      <i>There were some errors.</i>
+      <form action=".">
+        ...
+        <fieldgroup>
+          <legend>License</legend>
+          <ul>
+            <li>
+              Address: <div class="error">Required input is missing.</div>
+            </li>
+          </ul>
+          ...
+        </fieldgroup>
+        ...
+      </form>
+    </body>
+  </html>
+
+As you can see, the template is clever enough to just report the errors at the
+top of the form, but still report the actual problem within the group. So what
+happens, if errors happen inside and outside a group?
+
+  >>> request = testing.TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.license': u'MA 40387',
+  ...     'form.widgets.model': u'BMW',
+  ...     'form.widgets.make': u'325',
+  ...     'form.widgets.year': u'2005',
+  ...     'form.buttons.add': u'Add'
+  ...     })
+
+  >>> add = RegistrationAddForm(None, request)
+  >>> add.update()
+  >>> print add.render()
+  <html>
+    <body>
+      <i>There were some errors.</i>
+      <ul>
+        <li>
+          Last Name: <div class="error">Required input is missing.</div>
+        </li>
+      </ul>
+      <form action=".">
+        ...
+        <fieldgroup>
+          <legend>License</legend>
+          <ul>
+            <li>
+              Address: <div class="error">Required input is missing.</div>
+            </li>
+          </ul>
+          ...
+        </fieldgroup>
+        ...
+      </form>
+    </body>
+  </html>
+
+Let's now successfully complete the add form.
+
+  >>> from zope.app.container import btree
+  >>> context = btree.BTreeContainer()
+
+  >>> request = testing.TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.lastName': u'Richter',
+  ...     'form.widgets.license': u'MA 40387',
+  ...     'form.widgets.address': u'10 Main St, Maynard, MA',
+  ...     'form.widgets.model': u'BMW',
+  ...     'form.widgets.make': u'325',
+  ...     'form.widgets.year': u'2005',
+  ...     'form.buttons.add': u'Add'
+  ...     })
+
+  >>> add = RegistrationAddForm(context, request)
+  >>> add.update()
+
+The object is now added to the container and all attributes should be set:
+
+  >>> reg = context['obj1']
+  >>> reg.firstName
+  u'Stephan'
+  >>> reg.lastName
+  u'Richter'
+  >>> reg.license
+  u'MA 40387'
+  >>> reg.address
+  u'10 Main St, Maynard, MA'
+  >>> reg.model
+  u'BMW'
+  >>> reg.make
+  u'325'
+  >>> reg.year
+  2005
+
+Let's now have a look at an edit form for the vehicle registration:
+
+  >>> class RegistrationEditForm(group.GroupForm, form.EditForm):
+  ...     fields = field.Fields(IVehicleRegistration).select(
+  ...         'firstName', 'lastName')
+  ...     groups = (LicenseGroup, CarGroup)
+  ...
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_groupedit.pt', os.path.dirname(tests.__file__))
+
+  >>> request = testing.TestRequest()
+
+  >>> edit = RegistrationEditForm(reg, request)
+  >>> edit.update()
+
+After updating the form, we can render the HTML:
+
+  >>> print edit.render()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-firstName">First Name</label>
+          <input type="text" id="form-widgets-firstName"
+                 name="form.widgets.firstName" class="textWidget"
+                 value="Stephan" />
+        </div>
+        <div class="row">
+          <label for="form-widgets-lastName">Last Name</label>
+          <input type="text" id="form-widgets-lastName"
+                 name="form.widgets.lastName" class="textWidget"
+                 value="Richter" />
+         </div>
+        <fieldgroup>
+          <legend>License</legend>
+          <div class="row">
+            <label for="form-widgets-license">License</label>
+            <input type="text" id="form-widgets-license"
+                   name="form.widgets.license" class="textWidget"
+                   value="MA 40387" />
+          </div>
+          <div class="row">
+            <label for="form-widgets-address">Address</label>
+            <input type="text" id="form-widgets-address"
+                   name="form.widgets.address" class="textWidget"
+                   value="10 Main St, Maynard, MA" />
+          </div>
+        </fieldgroup>
+        <fieldgroup>
+          <legend>Car</legend>
+          <div class="row">
+            <label for="form-widgets-model">Model</label>
+            <input type="text" id="form-widgets-model"
+                   name="form.widgets.model" class="textWidget"
+                   value="BMW" />
+          </div>
+          <div class="row">
+            <label for="form-widgets-make">Make</label>
+            <input type="text" id="form-widgets-make"
+                   name="form.widgets.make" class="textWidget"
+                   value="325" />
+          </div>
+          <div class="row">
+            <label for="form-widgets-year">Year</label>
+            <input type="text" id="form-widgets-year"
+                   name="form.widgets.year" class="textWidget"
+                   value="2005" />
+          </div>
+        </fieldgroup>
+        <div class="action">
+          <input type="submit" id="form-buttons-apply"
+                 name="form.buttons.apply" class="submitWidget"
+                 value="Apply" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+The behavior when an error occurs is identical to that of the add form:
+
+  >>> request = testing.TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.lastName': u'Richter',
+  ...     'form.widgets.license': u'MA 40387',
+  ...     'form.widgets.model': u'BMW',
+  ...     'form.widgets.make': u'325',
+  ...     'form.widgets.year': u'2005',
+  ...     'form.buttons.apply': u'Apply'
+  ...     })
+
+  >>> edit = RegistrationEditForm(reg, request)
+  >>> edit.update()
+  >>> print edit.render()
+  <html>
+    <body>
+      <i>There were some errors.</i>
+      <form action=".">
+        ...
+        <fieldgroup>
+          <legend>License</legend>
+          <ul>
+            <li>
+              Address: <div class="error">Required input is missing.</div>
+            </li>
+          </ul>
+          ...
+        </fieldgroup>
+        ...
+      </form>
+    </body>
+  </html>
+
+Let's now complete the form successfully:
+
+  >>> request = testing.TestRequest(form={
+  ...     'form.widgets.firstName': u'Stephan',
+  ...     'form.widgets.lastName': u'Richter',
+  ...     'form.widgets.license': u'MA 4038765',
+  ...     'form.widgets.address': u'11 Main St, Maynard, MA',
+  ...     'form.widgets.model': u'Ford',
+  ...     'form.widgets.make': u'F150',
+  ...     'form.widgets.year': u'2006',
+  ...     'form.buttons.apply': u'Apply'
+  ...     })
+
+  >>> edit = RegistrationEditForm(reg, request)
+  >>> edit.update()
+
+The success message will be shown on the form, ...
+
+  >>> print edit.render()
+  <html>
+    <body>
+      <i>Data successfully updated.</i>
+      ...
+    </body>
+  </html>
+
+and the data is correctly updated:
+
+  >>> reg.firstName
+  u'Stephan'
+  >>> reg.lastName
+  u'Richter'
+  >>> reg.license
+  u'MA 4038765'
+  >>> reg.address
+  u'11 Main St, Maynard, MA'
+  >>> reg.model
+  u'Ford'
+  >>> reg.make
+  u'F150'
+  >>> reg.year
+  2006
+
+And that's 


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

Modified: z3c.form/trunk/src/z3c/form/interfaces.py
===================================================================
--- z3c.form/trunk/src/z3c/form/interfaces.py	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/src/z3c/form/interfaces.py	2007-06-26 22:35:05 UTC (rev 77114)
@@ -582,11 +582,28 @@
         default=None,
         required=False)
 
+    def getContent():
+        '''Return the content to be displayed and/or edited.'''
 
+    def updateWidgets(self):
+        '''Update the widgets for the form.
+
+        This method is commonly called from the ``update()`` method and is
+        mainly meant to be a hook for subclasses.
+        '''
+
+    def extractData():
+        '''Extract the data of the form.'''
+
+    def update():
+        '''Update the form.'''
+
+    def render():
+        '''Render the form.'''
+
 class ISubForm(IForm):
     """A subform."""
 
-
 class IInputForm(zope.interface.Interface):
     """A form that is meant to process the input of the form controls."""
 
@@ -648,9 +665,6 @@
 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."""
 
@@ -673,3 +687,20 @@
         description=_('A button manager describing the buttons to be used for '
                       'the form.'),
         schema=IButtons)
+
+class IGroup(IForm):
+    """A group of fields/widgets within a form."""
+
+    label = zope.schema.TextLine(
+        title=u'Label',
+        description=u'A test describing the group. Commonly used for the UI.')
+
+
+class IGroupForm(object):
+    """A form that supports groups."""
+
+    groups = zope.schema.Tuple(
+        title=u'Groups',
+        description=(u'Initially a collection of group classes, which are '
+                     u'converted to group instances when the form is '
+                     u'updated.'))

Modified: z3c.form/trunk/src/z3c/form/subform.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/subform.txt	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/src/z3c/form/subform.txt	2007-06-26 22:35:05 UTC (rev 77114)
@@ -306,7 +306,7 @@
           <div class="row">
             <label for="owner-widgets-license">License</label>
             <input type="text" id="owner-widgets-license"
-                   name="owner.widgets.license" class="textWidget" 
+                   name="owner.widgets.license" class="textWidget"
                    value="MA-97097A87" />
           </div>
         </fieldset>
@@ -486,15 +486,15 @@
 easily.
 
 
-Context less subform (for Mats) 
--------------------------------
+Context-free subforms
+---------------------
 
-Ok, that was easy. But what about write a form including a subform wihtout a 
+Ok, that was easy. But what about write a form including a subform wihtout a
 context? Let's show how we can write a form wihtout any concext using the
-sample above. Note, this sample for do not include actions which store the 
+sample above. Note, this sample for do not include actions which store the
 form input. You can store the values like in any other forms using the forms
-widget method ``self.widgets.extract()`` which will return the form and subform
-input values. 
+widget method ``self.widgets.extract()`` which will return the form and
+subform input values.
 
   >>> from z3c.form.interfaces import IWidgets
   >>> class OwnerAddForm(form.EditForm):
@@ -502,7 +502,7 @@
   ...         'simple_owneredit.pt', templatePath)
   ...     fields = field.Fields(IOwner)
   ...     prefix = 'owner'
-  ... 
+  ...
   ...     def updateWidgets(self):
   ...         self.widgets = zope.component.getMultiAdapter(
   ...             (self, self.request, self.getContent()), IWidgets)
@@ -516,13 +516,13 @@
   ...     template = viewpagetemplatefile.ViewPageTemplateFile(
   ...         'simple_caredit.pt', templatePath)
   ...     prefix = 'car'
-  ... 
+  ...
   ...     def updateWidgets(self):
   ...         self.widgets = zope.component.getMultiAdapter(
   ...             (self, self.request, self.getContent()), IWidgets)
   ...         self.widgets.ignoreContext = True
   ...         self.widgets.update()
-  ... 
+  ...
   ...     def update(self):
   ...         self.owner = OwnerAddForm(None, self.request)
   ...         self.owner.update()

Added: z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/tests/simple_groupedit.pt	2007-06-26 22:35:05 UTC (rev 77114)
@@ -0,0 +1,36 @@
+<html>
+  <body>
+    <i tal:condition="view/status" tal:content="view/status"/>
+    <ul tal:condition="view/widgets/errors"
+        metal:define-macro="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"
+           metal:define-macro="rows"
+           tal:repeat="widget view/widgets/values">
+        <b tal:condition="widget/error"
+           tal:content="structure widget/error/render"
+        /><label for=""
+               tal:attributes="for widget/id"
+               tal:content="widget/label" />
+        <input type="text" tal:replace="structure widget/render"
+      /></div>
+      <fieldgroup tal:condition="view/groups|nothing"
+                  tal:repeat="view view/groups">
+        <legend tal:condition="view/label"
+                tal:content="view/label">Label</legend>
+        <div metal:use-macro="template/macros/errors" />
+        <div metal:use-macro="template/macros/rows" />
+      </fieldgroup>
+      <div class="action"
+           tal:condition="view/actions|nothing"
+           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_groupedit.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.form/trunk/src/z3c/form/tests/test_doc.py
===================================================================
--- z3c.form/trunk/src/z3c/form/tests/test_doc.py	2007-06-26 20:36:26 UTC (rev 77113)
+++ z3c.form/trunk/src/z3c/form/tests/test_doc.py	2007-06-26 22:35:05 UTC (rev 77114)
@@ -85,6 +85,11 @@
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
             ),
         doctest.DocFileSuite(
+            '../group.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,



More information about the Checkins mailing list