[Checkins] SVN: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/ First attempt to write an ObjectWidget. This is far from finished...

Christophe Combelles ccomb at free.fr
Sun Dec 9 15:50:50 EST 2007


Log message for revision 82213:
  First attempt to write an ObjectWidget. This is far from finished...
  

Changed:
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/README.txt
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/configure.zcml
  A   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.pt
  A   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.py
  A   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.txt
  A   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.zcml
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/tests.py
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/configure.zcml
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/converter.py
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/field.py
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/form.txt
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/interfaces.py
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/subform.py
  U   Sandbox/ccomb/z3c.form/trunk/src/z3c/form/widget.py

-=-
Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/README.txt
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/README.txt	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/README.txt	2007-12-09 20:50:50 UTC (rev 82213)
@@ -520,10 +520,90 @@
 Object
 ------
 
-By default, we are not going to provide widgets for an object, since we
-believe this is better done using sub-forms.
+Object widgets can be handled by subforms.
+The object widget just creates a subform.
 
+  >>> import zope.interface
+  >>> class IMySubObject(zope.interface.Interface):
+  ...     foofield = zope.schema.Int(default=1111)
+  ...     barfield = zope.schema.Int(default=2222)
+  >>> field = zope.schema.Object(title=u'my object', schema=IMySubObject)
+  >>> widget = setupWidget(field)
+  >>> widget.update()
 
+Since we have no form, we have no subform, and the widget is rendered alone:
+
+  >>> print widget.render()
+  <fieldset id="foo" name="bar" class="object-widget required">
+  <legend>my object</legend>
+  </fieldset>
+  >>> widget.mode = interfaces.DISPLAY_MODE
+  >>> print widget.render()
+  <fieldset id="foo" name="bar" class="object-widget required">
+  <legend>my object</legend>
+  </fieldset>
+
+We define an interface containing a subobject, and an addform for it:
+
+  >>> from z3c.form import form, field
+  >>> class IMyObject(zope.interface.Interface):
+  ...     subobject = zope.schema.Object(title=u'my object', schema=IMySubObject)
+  >>> class MyAddForm(form.AddForm):
+  ...     fields = field.Fields(IMyObject)
+  ...     def create(self, data):
+  ...         pass
+  ...     def add(self, obj):
+  ...         pass
+  ...     def nextURL(self):
+  ...         pass
+
+We create the form and try to update it:
+
+  >>> request = TestRequest()
+  >>> myaddform =  MyAddForm(root, request)
+  >>> myaddform.update()
+
+As usually, the form contains a widget manager with the expected widget
+
+  >>> myaddform.widgets.keys()
+  ['subobject']
+  >>> myaddform.widgets.values()
+  [<ObjectWidget 'form.widgets.subobject'>]
+
+But now, the addform contains a subform, that the user didn't need to create
+and which is already updated:
+
+  >>> myaddform.subobject
+  <z3c.form.subform.EditSubForm object at ...>
+  >>> myaddform.subobject.widgets.keys()
+  ['foofield', 'barfield']
+
+If we want to render the addform, we must give it a template:
+
+  >>> import os
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import tests
+  >>> myaddform.template = viewpagetemplatefile.BoundPageTemplate(
+  ...         viewpagetemplatefile.ViewPageTemplateFile(
+  ...             'simple_edit.pt', os.path.dirname(tests.__file__)), myaddform)
+
+Now rendering the addform renders the subform as well:
+
+  >>> myaddform.render()
+  <fieldset id="foo" name="bar" class="object-widget required">
+  <input type="text" id="foo" name="bar" class="text-widget required int-field"
+         value="1,111" /><br/>
+  <input type="text" id="foo" name="bar" class="text-widget required int-field"
+         value="2,222" />
+  <legend>my object</legend>
+
+But if the user had previously created a subform by himself,
+the subform is untouched:
+
+  ...?...
+
+
+
 Password
 --------
 

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/configure.zcml
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/configure.zcml	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/configure.zcml	2007-12-09 20:50:50 UTC (rev 82213)
@@ -14,5 +14,6 @@
   <include file="submit.zcml" />
   <include file="text.zcml" />
   <include file="textarea.zcml" />
+  <include file="object.zcml" />
 
 </configure>

Added: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.pt
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.pt	                        (rev 0)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.pt	2007-12-09 20:50:50 UTC (rev 82213)
@@ -0,0 +1,31 @@
+<fieldset id="" name="" class="" title="" lang="" disabled=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       class view/klass;
+                       style view/style;
+                       title view/title;
+                       lang view/lang;
+                       onclick view/onclick;
+                       ondblclick view/ondblclick;
+                       onmousedown view/onmousedown;
+                       onmouseup view/onmouseup;
+                       onmouseover view/onmouseover;
+                       onmousemove view/onmousemove;
+                       onmouseout view/onmouseout;
+                       onkeypress view/onkeypress;
+                       onkeydown view/onkeydown;
+                       onkeyup view/onkeyup;
+                       value view/value;
+                       disabled view/disabled;
+                       tabindex view/tabindex;
+                       onfocus view/onfocus;
+                       onblur view/onblur;
+                       onchange view/onchange;">
+<legend tal:content="view/label">foobar</legend>
+
+<tal:block repeat="widget view/subform/widgets/values">
+    <tal:block content="structure widget/render" />
+    <br/>
+</tal:block>
+
+</fieldset>
\ No newline at end of file

Added: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.py	                        (rev 0)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -0,0 +1,48 @@
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema.interfaces
+
+from z3c.form import interfaces
+from z3c.form.widget import Widget, FieldWidget
+from z3c.form.field import FieldWidgets, Fields
+from z3c.form.browser import widget
+from z3c.form.subform import EditSubForm
+
+# FIXME: see if there is some interesting specific attributes for <fieldset>
+# We should then create a HTMLFieldsetElement and use it instead of base HTMLFormElement
+
+# FIXME where and when does the object creation takes place?
+
+class ObjectWidget(widget.HTMLFormElement, Widget):
+    zope.interface.implementsOnly(interfaces.IObjectWidget)
+
+    klass = u'object-widget'
+    widgets = None
+
+    def update(self):
+        if interfaces.IFormAware.providedBy(self) \
+                                        and not hasattr(self.form, self.name):
+            #FIXME None for add form, widget.context.? for editform
+            #FIXME: use a factory to create the subform, to be able
+            # to derive it from things like AddFormLayoutSupport
+            subform = EditSubForm(None, self.request, self.form)
+            subform.fields = Fields(self.field.schema)
+            setattr(self.form, self.field.__name__, subform)
+            zope.interface.alsoProvides(self, interfaces.ISubformAware)
+            self.subform = subform
+        if interfaces.ISubformAware.providedBy(self):
+            self.subform.update()
+        super(ObjectWidget, self).update()
+    def render(self):
+        return super(ObjectWidget, self).render()
+    def extract(self, default=interfaces.NOVALUE):
+        extracted = self.subform.widgets.extract()
+        return extracted
+
+ at zope.component.adapter(zope.schema.interfaces.IObject, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def ObjectFieldWidget(field, request):
+    """IFieldWidget factory for IObjectWidget."""
+    return FieldWidget(field, ObjectWidget(request))
+

Added: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.txt
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.txt	                        (rev 0)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.txt	2007-12-09 20:50:50 UTC (rev 82213)
@@ -0,0 +1,63 @@
+============
+ObjectWidget
+============
+FIXME: maybe move the more complete test from README to this file
+
+The widget can render a fieldset containing other widgets:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from zope.app.form.interfaces import IInputWidget
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import object
+
+The ObjectWidget is a widget:
+
+ >>> verifyClass(interfaces.IWidget, object.ObjectWidget)
+  True
+
+The widget can render a fieldset by adapting only the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> widget = object.ObjectWidget(request)
+
+Such a field provides IWidget:
+
+ >>> interfaces.IWidget.providedBy(widget)
+  True
+
+We also need to register the template for at least the widget and request:
+
+  >>> import os.path
+  >>> import zope.interface
+  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> import z3c.form.browser
+  >>> import z3c.form.widget
+  >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+  ...     'object.pt')
+  >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+  >>> zope.component.provideAdapter(factory,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+  ...     IPageTemplate, name='input')
+
+If we render the widget we get the HTML:
+
+  >>> print widget.render()
+  <fieldset class="object-widget">
+  <legend></legend>
+  </fieldset>
+
+Adding some more attributes to the widget will make it display more:
+
+  >>> widget.id = 'id'
+  >>> widget.name = 'name'
+  >>> widget.style = u'color: blue'
+  >>> widget.label = u'custom label of the widget'
+
+  >>> print widget.render()
+  <fieldset id="id" name="name" class="object-widget"
+            style="color: blue">
+  <legend>custom label of the widget</legend>
+  </fieldset>
+

Added: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.zcml
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.zcml	                        (rev 0)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/object.zcml	2007-12-09 20:50:50 UTC (rev 82213)
@@ -0,0 +1,32 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <class class=".object.ObjectWidget">
+    <require
+        permission="zope.Public"
+        interface="z3c.form.interfaces.IObjectWidget"
+        />
+  </class>
+
+<adapter
+      factory=".object.ObjectFieldWidget"
+      for="zope.schema.interfaces.IObject
+           z3c.form.interfaces.IFormLayer"
+      />
+
+<z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.IObjectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="object.pt"
+      />
+<z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.IObjectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="object.pt"
+      />
+
+</configure>

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/tests.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/tests.py	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/browser/tests.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -65,4 +65,8 @@
                      setUp=testing.setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),
+        DocFileSuite('object.txt',
+                     setUp=testing.setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
         ))

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/configure.zcml
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/configure.zcml	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/configure.zcml	2007-12-09 20:50:50 UTC (rev 82213)
@@ -68,6 +68,9 @@
       />
   <adapter
       factory=".converter.FieldWidgetDataConverter"
+      />  
+  <adapter
+      factory=".converter.ObjectWidgetDataConverter"
       />
 
   <!-- ITerms -->

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/converter.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/converter.py	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/converter.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -303,3 +303,26 @@
         if value and value[0] == 'selected':
             return True
         return False
+
+class ObjectWidgetDataConverter(BaseDataConverter):
+    "A converter for ObjectWidget"
+    
+    zope.component.adapts(
+        zope.schema.interfaces.IObject, interfaces.IObjectWidget)
+
+    def toWidgetValue(self, value):
+        print 'toWidgetValue'
+        print value
+
+    def toFieldValue(self, value):
+        print 'toFieldValue'
+        print value
+
+
+
+
+
+
+
+
+

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/field.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/field.py	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/field.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -225,8 +225,8 @@
             if field.mode is not None:
                 mode = field.mode
             elif field.field.readonly and not self.ignoreReadonly:
-                    mode = interfaces.DISPLAY_MODE
-            elif not self.ignoreContext:
+                    mode = interfaces.DISPLAY_MODE 
+            elif not self.ignoreContext and self.content is not None:
                 # If we do not have enough permissions to write to the
                 # attribute, then switch to display mode.
                 dm = zope.component.getMultiAdapter(

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/form.txt
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/form.txt	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/form.txt	2007-12-09 20:50:50 UTC (rev 82213)
@@ -254,7 +254,14 @@
 Since we have no customization components registered, all of those fields will
 remain as set before.
 
+Subform for ObjectWidgets
+~~~~~~~~~~~~~~~~~~~~~~~~~
 
+If the field is an Object field, an ObjectWidget is created, and a
+subform is created and stored in the form. The widget itself
+can access to the subform through its subform attribute (provided by
+the ISubformAware interface)
+
 Find an action manager, update and execute it
 ---------------------------------------------
 

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/interfaces.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/interfaces.py	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/interfaces.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -470,7 +470,10 @@
 class IPasswordWidget(ITextWidget):
     """Password widget."""
 
+class IObjectWidget(IWidget):
+    """Object widget."""
 
+
 class IWidgets(IManager):
     """A widget manager"""
 
@@ -715,7 +718,15 @@
 
     form = zope.schema.Field()
 
+class ISubformAware(zope.interface.Interface):
+    """Offers a subform attribute.
 
+    Object Widgets need to access the subform they are associated to.
+    """
+
+    subform = zope.schema.Field()
+
+
 class IForm(zope.interface.Interface):
     """Form"""
 

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/subform.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/subform.py	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/subform.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -53,9 +53,21 @@
 
     def update(self):
         super(EditSubForm, self).update()
-        for action in self.parentForm.actions.executedActions:
-            adapter = zope.component.queryMultiAdapter(
-                (self, self.request, self.getContent(), action),
-                interface=interfaces.IActionHandler)
-            if adapter:
-                adapter()
+        #FIXME: the code below may not be executed at all!
+        # The problem is that we update the subform while updating
+        # the parentForm. That means at this moment the actions manager
+        # has not been looked up and the parentForm.actions does not exist.
+        # The right order may be !
+        # -update form widgets
+        # -update subform widgets
+        # -update form actions
+        # -update subform actions?
+        
+    def updateActions(self):
+        if hasattr(self.parentForm, 'actions'):
+            for action in self.parentForm.actions.executedActions:
+                adapter = zope.component.queryMultiAdapter(
+                    (self, self.request, self.getContent(), action),
+                    interface=interfaces.IActionHandler)
+                if adapter:
+                    adapter()

Modified: Sandbox/ccomb/z3c.form/trunk/src/z3c/form/widget.py
===================================================================
--- Sandbox/ccomb/z3c.form/trunk/src/z3c/form/widget.py	2007-12-09 14:31:48 UTC (rev 82212)
+++ Sandbox/ccomb/z3c.form/trunk/src/z3c/form/widget.py	2007-12-09 20:50:50 UTC (rev 82213)
@@ -92,7 +92,7 @@
             #              context is to be used to extract a value, get
             #              it now via a data manager.
             if (interfaces.IContextAware.providedBy(self) and
-                not self.ignoreContext):
+                not self.ignoreContext and self.context is not None):
                 value = zope.component.getMultiAdapter(
                     (self.context, self.field), interfaces.IDataManager).get()
             # Step 1.2.2: If we still do not have a value, we can always use



More information about the Checkins mailing list