[Zope3-checkins] CVS: Zope3/src/zope/app/browser/form - add.pt:1.7 addwizard.pt:1.2 addwizard.py:1.3 edit.pt:1.9 editwizard.pt:1.3 editwizard.py:1.5 meta.zcml:1.16 widget.py:1.40

Stuart Bishop zen@shangri-la.dropbear.id.au
Mon, 14 Jul 2003 11:28:59 -0400


Update of /cvs-repository/Zope3/src/zope/app/browser/form
In directory cvs.zope.org:/tmp/cvs-serv24906/src/zope/app/browser/form

Modified Files:
	add.pt addwizard.pt addwizard.py edit.pt editwizard.pt 
	editwizard.py meta.zcml widget.py 
Log Message:
- Make Widget validation errors humanly readable, it a half-arsed sort of way.
  Widgets have spawned an 'error' attribute, set by getData.
- Display validation errors next to the dud input in Widget.row()
- Make description a tooltip of a Widget's label. This is currently done
  by abusing the <acronym> tag.
- addwizard and editwizard work again with the recent editview changes



=== Zope3/src/zope/app/browser/form/add.pt 1.6 => 1.7 ===
--- Zope3/src/zope/app/browser/form/add.pt:1.6	Wed Jun 25 14:43:04 2003
+++ Zope3/src/zope/app/browser/form/add.pt	Mon Jul 14 11:28:23 2003
@@ -19,15 +19,11 @@
            tal:condition="status"
            tal:content="status" />
 
-        <div tal:condition="view/errors">
-           <ul>
-              <li tal:repeat="error view/errors">
-                 <strong tal:content="error/__class__" i18n:translate="">
-                    Error Type</strong>:
-                 <span tal:content="error">Error text</span>
-              </li>
-           </ul>
-        </div>
+        <p tal:condition="view/errors">
+          <span i18n:translate="">There are</span>
+          <strong tal:content="python:len(view.errors)">6</strong>
+          <span i18n:translate="">input errors.</span>
+        </p>
 
         <div metal:define-slot="extra_info" tal:replace="nothing">
         </div>


=== Zope3/src/zope/app/browser/form/addwizard.pt 1.1 => 1.2 ===
--- Zope3/src/zope/app/browser/form/addwizard.pt:1.1	Sun Jul 13 00:05:58 2003
+++ Zope3/src/zope/app/browser/form/addwizard.pt	Mon Jul 14 11:28:23 2003
@@ -40,6 +40,7 @@
              tal:content="structure widget/row">
             <div class="label">Name</div>
             <div class="field"><input type="text" style="width:100%" /></div>
+            <div class="error">Error message</div>
         </div>
         <div class="row"
              metal:define-slot="extra_bottom" tal:replace="nothing">


=== Zope3/src/zope/app/browser/form/addwizard.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/browser/form/addwizard.py:1.2	Sun Jul 13 00:47:35 2003
+++ Zope3/src/zope/app/browser/form/addwizard.py	Mon Jul 14 11:28:23 2003
@@ -99,7 +99,7 @@
 
         publish(self.context, ObjectCreatedEvent(content))
 
-        content = self.add(content)
+        content = self.context.add(content)
 
         adapted = getAdapter(content, self.schema)
 
@@ -117,10 +117,6 @@
         self.request.response.redirect(self.context.nextURL())
         return False
 
-    def add(self, content):
-        # Cut & Paste from add.py
-        return self.context.add(content)
-
 
 def AddWizardViewFactory(
     name, schema, permission, layer, panes, fields,
@@ -161,7 +157,7 @@
         layer='default', template=None, 
         for_='zope.app.interfaces.container.IAdding', class_=None, 
         arguments='',keyword_arguments='', set_before_add='', set_after_add='',
-        menu=None, title=None, use_session='yes'
+        menu=None, description='', title=None, use_session='yes'
         ):
 
         self.name = name
@@ -187,7 +183,7 @@
                                 "they must both be specified")
             actions = menuItemDirective(
                 _context, menu, for_, '@@' + name, title,
-                permission=permission)
+                permission=permission, description=description)
         else:
             actions = []
 


=== Zope3/src/zope/app/browser/form/edit.pt 1.8 => 1.9 ===
--- Zope3/src/zope/app/browser/form/edit.pt:1.8	Thu Apr 10 02:17:34 2003
+++ Zope3/src/zope/app/browser/form/edit.pt	Mon Jul 14 11:28:23 2003
@@ -20,15 +20,11 @@
            tal:condition="status"
            tal:content="status" />
 
-        <div tal:condition="view/errors">
-           <ul>
-              <li tal:repeat="error view/errors">
-                 <strong tal:content="error/__class__" i18n:translate="">
-                    Error Type</strong>:
-                 <span tal:content="error">Error text</span>
-              </li>
-           </ul>
-        </div>
+         <p tal:condition="view/errors">
+           <span i18n:translate="">There are</span> 
+           <strong tal:content="python:len(view.errors)">6</strong>
+           <span i18n:translate="">input errors.</span>
+        </p>
 
         <div metal:define-slot="extra_info" tal:replace="nothing">
         </div>


=== Zope3/src/zope/app/browser/form/editwizard.pt 1.2 => 1.3 ===
--- Zope3/src/zope/app/browser/form/editwizard.pt:1.2	Sun Jul 13 00:05:58 2003
+++ Zope3/src/zope/app/browser/form/editwizard.pt	Mon Jul 14 11:28:23 2003
@@ -17,16 +17,6 @@
 
         <p tal:condition="view/feedback" tal:content="view/feedback" />
 
-        <div tal:condition="view/errors">
-           <ul>
-              <li tal:repeat="error view/errors">
-                 <strong tal:content="error/__class__" i18n:translate="">
-                    Error Type</strong>:
-                 <span tal:content="error">Error text</span>
-              </li>
-           </ul>
-        </div>
-
         <div metal:define-slot="extra_info" tal:replace="nothing">
         </div>
 


=== Zope3/src/zope/app/browser/form/editwizard.py 1.4 => 1.5 ===
--- Zope3/src/zope/app/browser/form/editwizard.py:1.4	Sun Jul 13 00:47:35 2003
+++ Zope3/src/zope/app/browser/form/editwizard.py	Mon Jul 14 11:28:23 2003
@@ -31,7 +31,8 @@
 from zope.security.checker import defineChecker, NamesChecker
 from zope.app.context import ContextWrapper
 from zope.component.view import provideView
-from zope.app.form.utility import setUpEditWidgets, getWidgetsData
+from zope.app.form.utility \
+        import setUpEditWidgets, getWidgetsData, applyWidgetsChanges
 from zope.app.interfaces.form import WidgetInputError
 from submit import Next, Previous, Update
 from zope.app.interfaces.form import WidgetsError
@@ -111,7 +112,7 @@
         else:
             # First page
             self._current_pane_idx = 0
-            self.errors = []
+            self.errors = {}
             self.label = self.currentPane().label
             self._choose_buttons()
             return
@@ -126,8 +127,11 @@
                 self, self.schema, strict=True, set_missing=True, 
                 names=names, exclude_readonly=True
                 )
-            self.errors = []
+            self.errors = {}
         except WidgetsError, errors:
+            errors = {}
+            for k, label, msg in errors:
+                errors[k] = msg
             self.errors = errors
 
         else:
@@ -167,6 +171,15 @@
         self.show_next = (self._current_pane_idx < len(self.panes) - 1)
 
         self.show_previous = self._current_pane_idx > 0
+
+    def apply_update(self, storage):
+        ''' Save changes to our content object '''
+        for k,v in storage.items():
+            getattr(self,k).setData(v)
+        return not applyWidgetsChanges(
+                self, self.adapted, self.schema,
+                names=self.fieldNames, exclude_readonly=True
+                )
 
     def renderHidden(self):
         ''' Render state as hidden fields. Also render hidden fields to 


=== Zope3/src/zope/app/browser/form/meta.zcml 1.15 => 1.16 ===
--- Zope3/src/zope/app/browser/form/meta.zcml:1.15	Mon Jul 14 05:07:08 2003
+++ Zope3/src/zope/app/browser/form/meta.zcml	Mon Jul 14 11:28:23 2003
@@ -136,6 +136,16 @@
         </description>
       </attribute>
 
+      <attribute name="description" required="no">
+        <description>
+         A longer description of the add form.
+
+          A UI may display this with the item or display it when the
+          user requests more assistance.
+        </description>
+        </attribute>
+
+
       <attribute name="use_session" required="no">
         <description>
           If 'no', hidden input controls are used to maintain state


=== Zope3/src/zope/app/browser/form/widget.py 1.39 => 1.40 ===
--- Zope3/src/zope/app/browser/form/widget.py:1.39	Sun Jul 13 03:57:43 2003
+++ Zope3/src/zope/app/browser/form/widget.py	Mon Jul 14 11:28:23 2003
@@ -37,6 +37,7 @@
 from zope.app.datetimeutils import DateTimeError
 from zope.schema import getFieldNamesInOrder
 from zope.schema.interfaces import ValidationError
+from zope.schema.errornames import RequiredMissing
 
 ListTypes = list, tuple
 
@@ -59,23 +60,34 @@
     u'hello\\r\\nworld'
     >>> int(widget.required)
     1
+    >>> widget.error is None
+    1
     >>> widget.setData('Hey\\nfolks')
     >>> widget.getData()
     u'hello\\r\\nworld'
+    >>> widget.error is None
+    1
 
     >>> widget.setPrefix('test')
     >>> widget.name
     'test.foo'
+    >>> widget.error is None
+    1
     >>> int(widget.haveData())
     0
     >>> widget.getData()
     Traceback (most recent call last):
     ...
-    MissingInputError: ('foo', u'Foo', 'the field is required')
+    MissingInputError: ('foo', u'Foo', u'Input is required')
+    >>> widget.error is not None
+    1
     >>> field.required = False
     >>> int(widget.required)
     0
-    >>> widget.getData()
+    >>> widget.getData() is None
+    1
+    >>> widget.error is None
+    1
 
     When we generate labels, the labels are translated, so we need to set up
     a lot of machinery to support translation:
@@ -97,6 +109,7 @@
     cssClass = ''
     extra = ''
     _missing = None
+    error = None
 
     def haveData(self):
         if self.name in self.request.form:
@@ -106,11 +119,14 @@
     def getData(self, optional=0):
         field = self.context
         value = self.request.form.get(self.name, self) # self used as marker
+        self.error = None
         if value is self:
             # No user input
             if field.required and not optional:
-                raise MissingInputError(field.__name__, field.title,
-                                        'the field is required')
+                self.error = MissingInputError(
+                        field.__name__, field.title,  RequiredMissing
+                        )
+                raise self.error
             return field.default
 
         value = self._convert(value)
@@ -118,9 +134,9 @@
             try:
                 field.validate(value)
             except ValidationError, v:
-                raise WidgetInputError(self.context.__name__,
-                                       self.title, str(v))
-
+                self.error = WidgetInputError(self.context.__name__,
+                                       self.title, v)
+                raise self.error
         return value
 
     def validate(self):
@@ -196,14 +212,27 @@
         title = ts.translate(self.title, "zope", context=self.request)
         if title is None:
             title = self.title
-        return '<label for="%s">%s</label>' % (
-            self.name,
-            title,
-            )
+        # TODO: Use a JavaScript tooltip instead of an abuse of the
+        # acronym HTML tag.
+        desc = self.context.description
+        if desc:
+            return '<label for="%s"><acronym title="%s">%s</acronym></label>'%(
+                    self.name, desc, title,
+                    )
+        else:
+            return '<label for="%s">%s</label>' % (
+                    self.name, title,
+                    )
 
     def row(self):
-        return '<div class="label">%s</div><div class="field">%s</div>' % (
-                self.label(), self())
+        if self.error:
+            error = zapi.getView(self.error, 'snippet', self.request)()
+            return '<div class="label">%s</div><div class="field">%s</div>' \
+                '<div class="error">%s</div>' % (self.label(), self(), error)
+        else:
+            return '<div class="label">%s</div><div class="field">%s</div>' % (
+                self.label(), self()
+                )
 
 class DisplayWidget(BrowserWidget):