[Zope3-dev] Handling of empty prefixes in zope.formlib and zope.app.form

Jacob Holm jh at improva.dk
Thu Sep 28 17:34:30 EDT 2006


Hi

A small introduction, as I believe this is my first post to this list. 
My name is Jacob, and I am the CTO of a small danish software company 
named Improva. We have been selling Zope2 based solutions since 
somewhere around version 2.5.1, and we started using Zope3 when ZopeX3 
came out. Among other things we are VARs for Zope Corporation, and have 
sprinted a bit with some of you there. I have also been lurking on this 
list almost from the beginning, so I feel like I know most of you even 
though you don't know me.

Now for the reason I'm delurking...

Using an empty prefix with zope.formlib and the standard widgets from 
zope.app.form cause the generated HTML widgets to have "name" and "id" 
attributes starting with a period.

I have two problems with this: 1) It is not valid XHTML, as ids must 
start with a letter. 2) I need to generate a form that is posted to a 
different server whose software I do not control, and it is currently 
not possible to get the name attributes correct without writing my own 
complete widget set from scratch.

Would it be OK to change the handling of empty prefixes in zope.formlib 
and zope.app.form to *not* add that leading period in case of an empty 
prefix?  The current behavior is undocumented and could be considered a 
bug (if not in the code then in the documentation and tests).

I have attached a small patch against the current trunk that does what I 
want. All current tests pass, but no tests for the new behavior has been 
added (yet). The patch is minimal in the sense that no API is changed, 
only the behavior related to empty prefix strings. Specifically it does 
not change the constructor of the (internal?) class 
zope.formlib.form.Widgets to take the actual prefix instead of just its 
length. Doing this would simplify the code and allow some sanity checks, 
but could cause breakage if the class is used anywhere else.

Comments?

Jacob

-- 
Jacob Holm
CTO
Improva ApS

-------------- next part --------------
Index: src/zope/app/form/__init__.py
===================================================================
--- src/zope/app/form/__init__.py	(revision 70426)
+++ src/zope/app/form/__init__.py	(working copy)
@@ -56,7 +56,7 @@
         return self._data is not self._data_marker
 
     def setPrefix(self, prefix):
-        if not prefix.endswith("."):
+        if prefix and not prefix.endswith("."):
             prefix += '.'
         self._prefix = prefix
         self.name = prefix + self.context.__name__
Index: src/zope/formlib/form.py
===================================================================
--- src/zope/formlib/form.py	(revision 70426)
+++ src/zope/formlib/form.py	(working copy)
@@ -208,6 +208,14 @@
         return zope.security.canAccess(context, writer.__name__)
     return zope.security.canWrite(context, field.__name__)
 
+def prefixjoin(*args):
+    return '.'.join(filter(None,args))
+
+def prefixlen(prefix):
+    if not prefix:
+        return 0
+    return len(prefix)+1
+
 def setUpWidgets(form_fields,
                  form_prefix=None, context=None, request=None, form=None,
                  data=(), adapters=None, ignore_request=False):
@@ -259,9 +267,7 @@
                 widget = component.getMultiAdapter((field, request),
                                                    IInputWidget)
 
-        prefix = form_prefix
-        if form_field.prefix:
-            prefix += '.' + form_field.prefix
+        prefix = prefixjoin(form_prefix, form_field.prefix)
 
         widget.setPrefix(prefix)
 
@@ -278,7 +284,7 @@
 
         widgets.append((not readonly, widget))
 
-    return Widgets(widgets, len(form_prefix)+1)
+    return Widgets(widgets, prefixlen(form_prefix))
 
 def setUpInputWidgets(form_fields, form_prefix, context, request,
                       form=None, ignore_request=False):
@@ -287,9 +293,7 @@
         field = form_field.field.bind(context)
         widget = _createWidget(form_field, field, request, IInputWidget)
 
-        prefix = form_prefix
-        if form_field.prefix:
-            prefix += '.' + form_field.prefix
+        prefix = prefixjoin(form_prefix, form_field.prefix)
 
         widget.setPrefix(prefix)
 
@@ -301,7 +305,7 @@
             widget.setRenderedValue(value)
 
         widgets.append((True, widget))
-    return Widgets(widgets, len(form_prefix)+1)
+    return Widgets(widgets, prefixlen(form_prefix))
 
 
 def _createWidget(form_field, field, request, iface):
@@ -313,7 +317,8 @@
 
 def getWidgetsData(widgets, form_prefix, data):
     errors = []
-    form_prefix += '.'
+    if form_prefix:
+        form_prefix += '.'
 
     for input, widget in widgets.__iter_input_and_widget__():
         if input and IInputWidget.providedBy(widget):
@@ -378,9 +383,7 @@
             iface = IInputWidget
         widget = _createWidget(form_field, field, request, iface)
 
-        prefix = form_prefix
-        if form_field.prefix:
-            prefix += '.' + form_field.prefix
+        prefix = prefixjoin(form_prefix, form_field.prefix)
 
         widget.setPrefix(prefix)
 
@@ -391,7 +394,7 @@
 
         widgets.append((not readonly, widget))
 
-    return Widgets(widgets, len(form_prefix)+1)
+    return Widgets(widgets, prefixlen(form_prefix))
 
 def setUpDataWidgets(form_fields, form_prefix, context, request, data=(),
                      for_display=False, ignore_request=False):
@@ -405,9 +408,8 @@
             iface = IInputWidget
         widget = _createWidget(form_field, field, request, iface)
 
-        prefix = form_prefix
-        if form_field.prefix:
-            prefix += '.' + form_field.prefix
+        prefix = prefixjoin(form_prefix, form_field.prefix)
+
         widget.setPrefix(prefix)
 
         if ((form_field.__name__ in data)
@@ -417,7 +419,7 @@
 
         widgets.append((not readonly, widget))
 
-    return Widgets(widgets, len(form_prefix)+1)
+    return Widgets(widgets, prefixlen(form_prefix))
 
 
 class NoInputData(interface.Invalid):
@@ -550,7 +552,7 @@
             else:
                 name = label.encode('hex')
 
-        self.__name__ = prefix + '.' + name
+        self.__name__ = prefixjoin(prefix, name)
 
         if data is None:
             data = {}
@@ -562,7 +564,7 @@
         result = self.__class__.__new__(self.__class__)
         result.__dict__.update(self.__dict__)
         result.form = form
-        result.__name__ = form.prefix + '.' + result.__name__
+        result.__name__ = prefixjoin(form.prefix, result.__name__)
         interface.alsoProvides(result, interfaces.IBoundAction)
         return result
 
@@ -570,13 +572,6 @@
         condition = self.condition
         return (condition is None) or condition(self.form, self)
 
-    def submitted(self):
-        if not self.available():
-            return False
-        form = self.form
-        name = "%s.%s" % (form.prefix, self.__name__)
-        return name in form.request.form
-
     def validate(self, data):
         if self.validator is not None:
             return self.validator(self.form, self, data)


More information about the Zope3-dev mailing list