[Checkins] SVN: z3c.form/branches/pcardune-tweaks/src/z3c/form/ Merge from trunk

Paul Carduner paulcarduner at gmail.com
Fri Jun 22 16:27:10 EDT 2007


Log message for revision 76948:
  Merge from trunk

Changed:
  D   z3c.form/branches/pcardune-tweaks/src/z3c/form/CHANGES.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/README.txt
  D   z3c.form/branches/pcardune-tweaks/src/z3c/form/TODO.txt
  A   z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.py
  A   z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text.zcml
  A   z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text_hidden.pt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/button.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/button.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/field.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/field.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/form.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/form.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/interfaces.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/subform.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/testing.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/tests/test_doc.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/util.py
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/util.txt
  U   z3c.form/branches/pcardune-tweaks/src/z3c/form/widget.txt

-=-
Deleted: z3c.form/branches/pcardune-tweaks/src/z3c/form/CHANGES.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/CHANGES.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/CHANGES.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -1,20 +0,0 @@
-=======
-CHANGES
-=======
-
-- Added named vocabulary lookup in ChoiceTerms and CollectionTerms
-
-- Implemented support for FileUpload in FileWidget
-
-- Added helper for handling FileUpload. You can find this helpers in 
-  z3c.form.util:
-  
-  def extractContentType(form, id):
-      """Knows how to extract a filename if a IBytes/IFileWidget was used."""
-
-  def extractFileName(form, id, cleanup=True):
-      """Knows how to extract a filename if a IBytes/IFileWidget was used.
-
-      Uploads from win/IE need some cleanup because the filename includes also 
-      the path. cleanUp=True will do this for you.
-      """

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/README.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/README.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/README.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -76,7 +76,12 @@
   The ``util`` module provides several helper functions and classes. The
   components not tested otherwise are explained in this file.
 
+- ``adding.txt`` [informative]
 
+   This module provides a base class for add forms that work with the
+   ``IAdding`` interface.
+
+
 Browser Documentation
 ---------------------
 

Deleted: z3c.form/branches/pcardune-tweaks/src/z3c/form/TODO.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/TODO.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/TODO.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -1,32 +0,0 @@
-====
-TODO
-====
-
-Framework
----------
-
-
-Documentation
--------------
-
-
-Samples
--------
-
-Write some samples to show the power of the new widget and form framework.
-
-- Object name as an additional field in an add form
-
-
-Improvements
-------------
-
-Add explicit difference between error and confirmation message e.g.
-"There were some errors." and "No changes were applied."
-
-
-Notes from Roger
-----------------
-
-- Define all widget attributes for the different widgets in the interfaces 
-  as fields

Copied: z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.py (from rev 76947, z3c.form/trunk/src/z3c/form/adding.py)
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.py	                        (rev 0)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Implementation of an addform for IAdding
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from z3c.form import form
+
+class AddForm(form.AddForm):
+    """An addform for the IAdding interface."""
+
+    def add(self, object):
+        ob = self.context.add(object)
+        self._finishedAdd = True
+        return ob
+
+    def nextURL(self):
+        return self.context.nextURL()

Copied: z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.txt (from rev 76947, z3c.form/trunk/src/z3c/form/adding.txt)
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.txt	                        (rev 0)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/adding.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -0,0 +1,121 @@
+=========================
+Add Forms for ``IAdding``
+=========================
+
+While using ``IAdding``-based add forms is strongly discouraged by this
+package due to performance and code complexity concerns, there is still the
+need for add forms based on IAdding, especially when one wants to extend the
+default ZMI and use the add menu.
+
+Before we get started, we need to register a bunch of form-related components:
+
+  >>> from z3c.form import testing
+  >>> testing.setupFormDefaults()
+
+Let's first create a content component:
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IPerson(zope.interface.Interface):
+  ...
+  ...     name = zope.schema.TextLine(
+  ...         title=u'Name',
+  ...         required=True)
+
+  >>> from zope.schema.fieldproperty import FieldProperty
+  >>> class Person(object):
+  ...     zope.interface.implements(IPerson)
+  ...     name = FieldProperty(IPerson['name'])
+  ...
+  ...     def __init__(self, name):
+  ...         self.name = name
+  ...
+  ...     def __repr__(self):
+  ...         return '<%s %r>' %(self.__class__.__name__, self.name)
+
+Next we need a container to which we wish to add the person:
+
+  >>> from zope.app.container import btree
+  >>> people = btree.BTreeContainer()
+
+When creating and adding a new object using the ``IAdding`` API, the container
+is adapted to ``IAdding`` view:
+
+  >>> request = testing.TestRequest()
+
+  >>> from zope.app.container.browser.adding import Adding
+  >>> adding = Adding(people, request)
+  >>> adding
+  <zope.app.container.browser.adding.Adding object at ...>
+
+To be able to create a person using ``IAdding``, we need to create an add form
+for it now:
+
+  >>> import os
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import tests, field
+  >>> from z3c.form.adding import AddForm
+
+  >>> class AddPersonForm(AddForm):
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_edit.pt', os.path.dirname(tests.__file__))
+  ...
+  ...     fields = field.Fields(IPerson)
+  ...
+  ...     def create(self, data):
+  ...         return Person(**data)
+
+Besides the usual template and field declarations, one must also implement the
+``create()`` method. Note that the ``add()`` and ``nextURL()`` methods are
+implemented for you already in comparison to the default add form. After
+instantiating the form, ...
+
+  >>> add = AddPersonForm(adding, request)
+
+... we can now view the form:
+
+  >>> print add()
+  <html>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-name">Name</label>
+          <input type="text" id="form-widgets-name"
+                 name="form.widgets.name" class="textWidget" value="" />
+        </div>
+        <div class="action">
+          <input type="submit" id="form-buttons-add"
+                 name="form.buttons.add" class="submitWidget" value="Add" />
+        </div>
+      </form>
+    </body>
+  </html>
+
+Once the form is filled out and the add button is clicked, ...
+
+  >>> request = testing.TestRequest(
+  ...     form={'form.widgets.name': u'Stephan', 'form.buttons.add': 1})
+
+  >>> adding = Adding(people, request)
+  >>> add = AddPersonForm(adding, request)
+  >>> add.update()
+
+... the person is added to the container:
+
+  >>> sorted(people.keys())
+  [u'Person']
+  >>> people['Person']
+  <Person u'Stephan'>
+
+When the add form is rendered, nothing is returned and only the redirect
+header is set to the next URL. For this to work, we need to setup the location
+root correctly:
+
+  >>> from zope.traversing.interfaces import IContainmentRoot
+  >>> zope.interface.alsoProvides(people, IContainmentRoot)
+
+  >>> add.render()
+  ''
+
+  >>> request.response.getHeader('Location')
+  'http://127.0.0.1/@@contents.html'

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text.zcml
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text.zcml	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text.zcml	2007-06-22 20:27:09 UTC (rev 76948)
@@ -89,4 +89,11 @@
       template="text_input.pt"
       />
 
+  <z3c:widgetTemplate
+      mode="hidden"
+      widget="z3c.form.interfaces.ITextWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="text_hidden.pt"
+      />
+
 </configure>

Copied: z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text_hidden.pt (from rev 76947, z3c.form/trunk/src/z3c/form/browser/text_hidden.pt)
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text_hidden.pt	                        (rev 0)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/browser/text_hidden.pt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -0,0 +1,13 @@
+<input type="hidden" id="" name="" class="hiddenWidget" size="" alt="" title=""
+       tabindex="" disabled="" readonly="" accesskey="" maxlength="" value=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       title view/title;
+                       alt view/alt;
+                       tabindex view/tabindex;
+                       disabled view/disabled;
+                       readonly view/readonly;
+                       accesskey view/accesskey;
+                       maxlength view/maxlength;
+                       value view/value;
+                       size view/size" />

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/button.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/button.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/button.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -40,7 +40,6 @@
     zope.interface.implements(interfaces.IButton)
 
     accessKey = FieldProperty(interfaces.IButton['accessKey'])
-    actionFactory = FieldProperty(interfaces.IButton['actionFactory'])
 
     def __init__(self, *args, **kwargs):
         # Provide some shortcut ways to specify the name
@@ -222,18 +221,7 @@
             if button.condition is not None and not button.condition(self.form):
                 continue
             fullName = prefix + name
-            # Look up a button action factory
-            if button.actionFactory is not None:
-                buttonAction = button.actionFactory(self.request, button)
-                buttonAction.name = fullName
-            else:
-                buttonAction = zope.component.queryMultiAdapter(
-                    (self.request, button), interfaces.IFieldWidget)
-                if buttonAction is not None:
-                    buttonAction.name = fullName
-                    # if one is not found, use the default
-                else:
-                    buttonAction = ButtonAction(self.request, button, fullName)
+            buttonAction = ButtonAction(self.request, button, fullName)
             # Look up a potential custom title for the action.
             title = zope.component.queryMultiAdapter(
                 (self.form, self.request, self.content, button, self),

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/button.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/button.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/button.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -63,51 +63,6 @@
   >>> actions['apply']
   <ButtonAction 'form.buttons.apply' u'Apply'>
 
-It is possible to customize how a button is transformed into an action
-by registering an adapter for the request and the button that provides
-IFieldWidget.
-
-  >>> import zope.component
-  >>> from zope.publisher.interfaces.browser import IBrowserRequest
-  >>> class CustomButtonAction(button.ButtonAction):
-  ...     """Custom Button Action Class."""
-  ...     zope.interface.implements(interfaces.IFieldWidget)
-  ...     zope.component.adapts(IBrowserRequest, interfaces.IButton)
-  ...
-  ...     def __init__(self, request, field):
-  ...         super(CustomButtonAction, self).__init__(request, field, None)
-
-  >>> zope.component.provideAdapter(CustomButtonAction,
-  ...                    (IBrowserRequest,interfaces.IButton),
-  ...                    interfaces.IFieldWidget)
-
-Now if we rerun update we will get this other ButtonAction
-implementation.
-
-  >>> actions.update()
-  >>> actions['apply']
-  <CustomButtonAction 'form.buttons.apply' u'Apply'>
-
-Alternatively, you can customize an individual button by setting its
-``actionFactory`` attribute.
-
-  >>> def customButtonActionFactory(request, field):
-  ...     print "Just to let you know, this is a custom button."
-  ...     button = CustomButtonAction(request, field)
-  ...     button.css = "happy"
-  ...     return button
-
-  >>> form.buttons['apply'].actionFactory = customButtonActionFactory
-  >>> actions.update()
-  Just to let you know, this is a custom button.
-  >>> actions['apply'].css
-  'happy'
-
-But let's not digress too much and get rid of this customization
-
-  >>> form.buttons['apply'].actionFactory = None
-  >>> actions.update()
-
 Button actions are locations:
 
   >>> apply = actions['apply']

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -68,6 +68,8 @@
 
     def toFieldValue(self, value):
         """See interfaces.IDataConverter"""
+        if value == u'':
+            return self.field.missing_value
         return datetime.date(*[int(part) for part in value.split('-')])
 
 
@@ -78,6 +80,8 @@
 
     def toFieldValue(self, value):
         """See interfaces.IDataConverter"""
+        if value == u'':
+            return self.field.missing_value
         return datetime.time(*[int(part) for part in value.split(':')])
 
 
@@ -88,6 +92,8 @@
 
     def toFieldValue(self, value):
         """See interfaces.IDataConverter"""
+        if value == u'':
+            return self.field.missing_value
         dateString, timeString = value.split(' ')
         dt = [int(part) for part in dateString.split('-')]
         dt += [int(part) for part in timeString.split(':')]
@@ -101,6 +107,8 @@
 
     def toFieldValue(self, value):
         """See interfaces.IDataConverter"""
+        if value == u'':
+            return self.field.missing_value
         daysString, crap, timeString = value.split(' ')
         days = int(daysString)
         seconds = [int(part)*60**(2-n)
@@ -120,7 +128,7 @@
     def toFieldValue(self, value):
         """See interfaces.IDataConverter"""
         if value is None or value == '':
-            return self.context.missing_value
+            return self.field.missing_value
 
         if isinstance(value, zope.publisher.browser.FileUpload):
             # By default a IBytes field is used for get a file upload widget.
@@ -141,7 +149,7 @@
                 if data or getattr(value, 'filename', ''):
                     return data
                 else:
-                    return self.context.missing_value
+                    return self.field.missing_value
         else:
             return unicode(value)
 

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/converter.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -123,7 +123,13 @@
   >>> ddc.toFieldValue(u'1980-01-25')
   datetime.date(1980, 1, 25)
 
+By default the converter converts missing input to missin_input value:
 
+  >>> ddc.toFieldValue(u'') is None
+  True
+  
+
+
 Time Data Converter
 -------------------
 
@@ -150,7 +156,12 @@
   >>> tdc.toFieldValue(u'12:00:00')
   datetime.time(12, 0)
 
+By default the converter converts missing input to missin_input value:
 
+  >>> tdc.toFieldValue(u'') is None
+  True
+
+
 Datetime Data Converter
 -----------------------
 
@@ -177,7 +188,12 @@
   >>> dtdc.toFieldValue(u'1980-01-25 12:00:00')
   datetime.datetime(1980, 1, 25, 12, 0)
 
+By default the converter converts missing input to missin_input value:
 
+  >>> dtdc.toFieldValue(u'') is None
+  True
+
+
 Timedelta Data Converter
 ------------------------
 
@@ -204,7 +220,12 @@
   >>> tddc.toFieldValue(u'1 day, 1:01:01')
   datetime.timedelta(1, 3661)
 
+By default the converter converts missing input to missin_input value:
 
+  >>> tddc.toFieldValue(u'') is None
+  True
+
+
 FileUpload Data Converter
 -------------------------
 
@@ -215,14 +236,14 @@
   >>> fileWidget = z3c.form.browser.file.FileWidget(TestRequest())
   >>> bytes = zope.schema.Bytes()
 
-  >>> bdc = converter.FileUploadDataConverter(bytes, fileWidget)
-  >>> bdc
+  >>> fudc = converter.FileUploadDataConverter(bytes, fileWidget)
+  >>> fudc
   <DataConverter from Bytes to FileWidget>
 
 Bytes are converted to unicode:
 
   >>> simple = 'foobar'
-  >>> bdc.toFieldValue(simple)
+  >>> fudc.toFieldValue(simple)
   u'foobar'
 
 The converter can also convert FileUpload objects. Setup a fields storage 
@@ -244,10 +265,47 @@
 
 and try to convert:
 
-  >>> bdc.toFieldValue(myUpload)
+  >>> fudc.toFieldValue(myUpload)
   'File upload contents.'
 
+By default the converter converts missing input to missin_input value:
 
+  >>> fudc.toFieldValue(u'') is None
+  True
+
+If we get a emtpy filename for a fileupload, we also get the missing_value,
+but this means there was a error somewhere in the upload, normaly yo are
+not able to uppload a file without a filename:
+
+  >>> class EmptyFilenameFieldStorageStub:
+  ...     def __init__(self, file):
+  ...         self.file = file
+  ...         self.headers = {}
+  ...         self.filename = ''
+  >>> myfile = cStringIO.StringIO('')
+  >>> aFieldStorage = EmptyFilenameFieldStorageStub(myfile)
+  >>> myUpload = FileUpload(aFieldStorage)
+  >>> bytes = zope.schema.Bytes()
+  >>> fudc = converter.FileUploadDataConverter(bytes, fileWidget)
+  >>> fudc.toFieldValue(myUpload) is None
+  True
+
+There is also a ValueError if we don't get a seekable file from the FieldStorage 
+during the upload:
+
+  >>> myfile = ''
+  >>> aFieldStorage = FieldStorageStub(myfile)
+  >>> myUpload = FileUpload(aFieldStorage)
+  >>> bytes = zope.schema.Bytes()
+  >>> fudc = converter.FileUploadDataConverter(bytes, fileWidget)
+  >>> fudc.toFieldValue(myUpload) is None
+  Traceback (most recent call last):
+  ...
+  ValueError: (u'Bytes data is not a file object', <...AttributeError...>)
+
+
+
+
 Bytes Data Converter
 --------------------
 
@@ -277,7 +335,12 @@
   >>> fdc.toFieldValue(u'foobar')
   'foobar'
 
+By default the converter converts missing input to missin_input value:
 
+  >>> fdc.toFieldValue(u'') is None
+  True
+
+
 Sequence Data Converter
 -----------------------
 

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -20,8 +20,7 @@
 import zope.interface
 import zope.component
 import zope.schema
-from zope.security.checker import canAccess
-from zope.security.checker import canWrite
+from zope.security.checker import canAccess, canWrite, Proxy
 
 from z3c.form import interfaces
 
@@ -40,14 +39,21 @@
         self.context = context
         self.field = field
 
-    def get(self, default=interfaces.NOVALUE):
+    def get(self):
         """See z3c.form.interfaces.IDataManager"""
         # get the right adapter or context
         context = self.context
         if self.field.interface is not None:
             context = self.field.interface(context)
-        return getattr(context, self.field.__name__, default)
+        return getattr(context, self.field.__name__)
 
+    def query(self, default=interfaces.NOVALUE):
+        """See z3c.form.interfaces.IDataManager"""
+        try:
+            return self.get()
+        except AttributeError:
+            return default
+
     def set(self, value):
         """See z3c.form.interfaces.IDataManager"""
         if self.field.readonly:
@@ -62,13 +68,16 @@
 
     def canAccess(self):
         """See z3c.form.interfaces.IDataManager"""
-        return canAccess(self.context, self.field.__name__)
+        if isinstance(self.context, Proxy):
+            return canAccess(self.context, self.field.__name__)
+        return True
 
     def canWrite(self):
         """See z3c.form.interfaces.IDataManager"""
-        return canWrite(self.context, self.field.__name__)
+        if isinstance(self.context, Proxy):
+            return canWrite(self.context, self.field.__name__)
+        return True
 
-
 class DictionaryField(DataManager):
     """Dictionary field."""
     zope.component.adapts(
@@ -80,8 +89,12 @@
         self.data = data
         self.field = field
 
-    def get(self, default=interfaces.NOVALUE):
+    def get(self):
         """See z3c.form.interfaces.IDataManager"""
+        return self.data[self.field.__name__]
+
+    def query(self, default=interfaces.NOVALUE):
+        """See z3c.form.interfaces.IDataManager"""
         return self.data.get(self.field.__name__, default)
 
     def set(self, value):

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/datamanager.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -43,6 +43,8 @@
   ...     name = zope.schema.TextLine(
   ...         title=u'Name',
   ...         default=u'<no name>')
+  ...     phone = zope.schema.TextLine(
+  ...         title=u'Phone')
 
   >>> class Person(object):
   ...     zope.interface.implements(IPerson)
@@ -57,11 +59,14 @@
   >>> nameDm = datamanager.AttributeField(stephan, IPerson['name'])
 
 The data manager consists of a few simple methods to accomplish its
-purpose. Getting the value is done using the ``get()`` method:
+purpose. Getting the value is done using the ``get()`` or ``query()`` method:
 
   >>> nameDm.get()
   u'Stephan Richter'
 
+  >>> nameDm.query()
+  u'Stephan Richter'
+
 The value can be set using ``set()``:
 
   >>> nameDm.set(u'Stephan "Caveman" Richter')
@@ -71,10 +76,33 @@
   >>> stephan.name
   u'Stephan "Caveman" Richter'
 
+If an attribute is not available, ``get()`` fails and ``query()`` returns a
+default value:
+
+  >>> phoneDm = datamanager.AttributeField(stephan, IPerson['phone'])
+
+  >>> phoneDm.get()
+  Traceback (most recent call last):
+  ...
+  AttributeError: 'Person' object has no attribute 'phone'
+
+  >>> phoneDm.query()
+  <NOVALUE>
+  >>> phoneDm.query('nothing')
+  'nothing'
+
 A final feature that is supported by the data manager is the check whether a
-value can be accessed and written. To demonstrate the feature, we first have
-to provide security declarations for our person:
+value can be accessed and written. When the context is not security proxied,
+both, accessing and writing, is allowed:
 
+  >>> nameDm.canAccess()
+  True
+  >>> nameDm.canWrite()
+  True
+
+To demonstrate the behavior for a security-proxied component, we first have to
+provide security declarations for our person:
+
   >>> from zope.security.management import endInteraction
   >>> from zope.security.management import newInteraction
   >>> from zope.security.management import setSecurityPolicy
@@ -216,6 +244,11 @@
 Let's now access the name:
 
   >>> nameDm.get()
+  Traceback (most recent call last):
+  ...
+  KeyError: 'name'
+
+  >>> nameDm.query()
   <NOVALUE>
 
 Initially we get the default value (as specified in the field), since the

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/field.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/field.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/field.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -217,12 +217,18 @@
         # Walk through each field, making a widget out of it
         for field in self.form.fields.values():
             # Step 1: Determine the mode of the widget.
-            mode = field.mode
-            if mode is None:
-                if field.field.readonly and not self.ignoreReadonly:
+            mode = self.mode
+            if field.mode is not None:
+                mode = field.mode
+            elif field.field.readonly and not self.ignoreReadonly:
                     mode = interfaces.DISPLAY_MODE
-                else:
-                    mode = self.mode
+            elif not self.ignoreContext:
+                # If we do not have enough permissions to write to the
+                # attribute, then switch to display mode.
+                dm = zope.component.getMultiAdapter(
+                    (self.content, field.field), interfaces.IDataManager)
+                if not dm.canWrite():
+                    mode = interfaces.DISPLAY_MODE
             # Step 2: Get the widget for the given field.
             factory = field.widgetFactory.get(mode)
             if factory is not None:

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/field.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/field.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/field.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -343,23 +343,28 @@
 
   >>> from z3c.form import field
 
-  >>> class AddPerson(object):
+  >>> class PersonForm(object):
   ...     prefix = 'form.'
   ...     fields = field.Fields(IPerson)
-  >>> addPerson = AddPerson()
+  >>> personForm = PersonForm()
 
-For more details on how to define fields within a form, see ``field.txt``. We
+For more details on how to define fields within a form, see ``form.txt``. We
 can now create the fields widget manager. It's discriminators are the form for
 which the widgets are created, the request, and the context that is being
-manipulated. Since we are developing an add-form the context is ``None``.
+manipulated. In the simplest case the context is ``None`` and ignored, as it
+is true for an add form.
 
   >>> from z3c.form.testing import TestRequest
   >>> request = TestRequest()
   >>> context = object()
 
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.ignoreContext = True
 
+
+Widget Mapping
+~~~~~~~~~~~~~~
+
 The main resposibility of the manager is to provide the ``IEnumerableMapping``
 interface and an ``update()`` method. Initially the mapping, going from widget
 id to widget value, is empty:
@@ -433,6 +438,10 @@
   >>> len(manager)
   3
 
+
+Properties of widgets within a manager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 When a widget is added to the widget manager, it is located:
 
   >>> lname = manager['lastName']
@@ -449,6 +458,10 @@
   >>> lname.context is context
   True
 
+
+Determination of the widget mode
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 By default, all widgets will also assume the mode of the manager:
 
    >>> manager['lastName'].mode
@@ -480,8 +493,8 @@
 In the second case, the last name will inherit the mode from the widget
 manager, while the first name will want to use a display wdget:
 
-  >>> addPerson.fields = field.Fields(IPerson).select('lastName')
-  >>> addPerson.fields += field.Fields(
+  >>> personForm.fields = field.Fields(IPerson).select('lastName')
+  >>> personForm.fields += field.Fields(
   ...     IPerson, mode=interfaces.DISPLAY_MODE).select('firstName')
 
   >>> manager.mode = interfaces.INPUT_MODE
@@ -492,6 +505,83 @@
   >>> manager['firstName'].mode
   'display'
 
+In a third case, the widget will be shown in display mode, if the attribute of
+the context is not writable. Clearly this can never occur in add forms, since
+there the context is ignored, but is an important use case in edit forms.
+
+Thus we need an implementation of the ``IPerson`` interface including some
+security declarations:
+
+  >>> from zope.security import checker
+
+  >>> class Person(object):
+  ...     zope.interface.implements(IPerson)
+  ...
+  ...     def __init__(self, firstName, lastName):
+  ...         self.id = firstName[0].lower() + lastName.lower()
+  ...         self.firstName = firstName
+  ...         self.lastName = lastName
+
+  >>> PersonChecker = checker.Checker(
+  ...     get_permissions = {'id': checker.CheckerPublic,
+  ...                        'firstName': checker.CheckerPublic,
+  ...                        'lastName': checker.CheckerPublic},
+  ...     set_permissions = {'firstName': 'test.Edit',
+  ...                        'lastName': checker.CheckerPublic}
+  ...     )
+
+  >>> srichter = checker.ProxyFactory(
+  ...     Person(u'Stephan', u'Richter'), PersonChecker)
+
+In this case, the last name is always editable but for the first name the user
+will need the edit "test.Edit" permission.
+
+We also need to register the data manager and setup a new security policy:
+
+  >>> from z3c.form import datamanager
+  >>> zope.component.provideAdapter(datamanager.AttributeField)
+
+  >>> from zope.security import management
+  >>> from z3c.form import testing
+  >>> management.endInteraction()
+  >>> newPolicy = testing.SimpleSecurityPolicy()
+  >>> oldpolicy = management.setSecurityPolicy(newPolicy)
+  >>> management.newInteraction()
+
+Now we can create the widget manager:
+
+  >>> personForm = PersonForm()
+  >>> request = TestRequest()
+  >>> manager = field.FieldWidgets(personForm, request, srichter)
+
+After updating the widget manager, the fields are available as widgets, the
+first name being in display and the last name is input mode:
+
+  >>> manager.update()
+  >>> manager['id'].mode
+  'display'
+  >>> manager['firstName'].mode
+  'display'
+  >>> manager['lastName'].mode
+  'input'
+
+However, explicitely overriding the mode in the field declaration, overrides
+this selection for you:
+
+  >>> personForm.fields['firstName'].mode = interfaces.INPUT_MODE
+
+  >>> manager.update()
+  >>> manager['id'].mode
+  'display'
+  >>> manager['firstName'].mode
+  'input'
+  >>> manager['lastName'].mode
+  'input'
+
+
+Data extraction and validation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 Besides managing widgets, the widget manager also controls the process of
 extracting and validating extracted data. Let's start with the validation
 first, which only validates the data as a whole, assuming each individual
@@ -502,7 +592,7 @@
   >>> from z3c.form import validator
   >>> zope.component.provideAdapter(validator.InvariantsValidator)
 
-  >>> addPerson.fields = field.Fields(IPerson)
+  >>> personForm.fields = field.Fields(IPerson)
   >>> manager.update()
 
   >>> manager.validate(
@@ -524,12 +614,12 @@
 
   >>> name = zope.schema.TextLine(__name__='name')
 
-  >>> class PersonForm(object):
+  >>> class PersonNameForm(object):
   ...     prefix = 'form.'
   ...     fields = field.Fields(name)
-  >>> personForm = PersonForm()
+  >>> personNameForm = PersonNameForm()
 
-  >>> manager = field.FieldWidgets(personForm, request, context)
+  >>> manager = field.FieldWidgets(personNameForm, request, context)
 
 In this case, the widget manager's ``validate()`` method should simply ignore
 the field and not try to look up any invariants:
@@ -550,7 +640,7 @@
   ...     'form.widgets.id': u'srichter',
   ...     'form.widgets.firstName': u'Stephan',
   ...     'form.widgets.lastName': u'Richter'})
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.ignoreContext = True
   >>> manager.update()
 
@@ -569,7 +659,7 @@
 
   >>> request = TestRequest(form={
   ...     'form.widgets.firstName': u'Stephan', 'form.widgets.id': u'srichter'})
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.ignoreContext = True
   >>> manager.update()
   >>> manager.extract()
@@ -581,7 +671,7 @@
   ...     'form.widgets.id': u'srichter',
   ...     'form.widgets.firstName': u'Stephan',
   ...     'form.widgets.lastName': u'Richter-Richter'})
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.ignoreContext = True
   >>> manager.update()
   >>> data, errors = manager.extract()
@@ -600,7 +690,7 @@
 Let's have a look at the default form first. Initially, the standard
 registered widgets are used:
 
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.update()
 
   >>> manager['firstName']
@@ -616,11 +706,11 @@
 
 It can be simply assigned as follows:
 
-  >>> addPerson.fields['firstName'].widgetFactory = CustomInputWidgetFactory
+  >>> personForm.fields['firstName'].widgetFactory = CustomInputWidgetFactory
 
 Now this widget should be used instead of the registered default one:
 
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.update()
   >>> manager['firstName']
   <CustomInputWidget 'form.widgets.firstName'>
@@ -629,7 +719,7 @@
 default factory for in the ``WidgetFactories`` object, which manages the
 custom widgets for all modes. Now all modes show this input widget:
 
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.mode = interfaces.DISPLAY_MODE
   >>> manager.update()
   >>> manager['firstName']
@@ -643,12 +733,12 @@
   >>> def CustomDisplayWidgetFactory(field, request):
   ...     return widget.FieldWidget(field, CustomDisplayWidget(request))
 
-  >>> addPerson.fields['firstName']\
+  >>> personForm.fields['firstName']\
   ...     .widgetFactory[interfaces.DISPLAY_MODE] = CustomDisplayWidgetFactory
 
 Now the display mode should produce the custom display widget, ...
 
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.mode = interfaces.DISPLAY_MODE
   >>> manager.update()
   >>> manager['firstName']
@@ -656,7 +746,7 @@
 
 ... while the input mode still shows the default custom input widget:
 
-  >>> manager = field.FieldWidgets(addPerson, request, context)
+  >>> manager = field.FieldWidgets(personForm, request, context)
   >>> manager.mode = interfaces.INPUT_MODE
   >>> manager.update()
   >>> manager['firstName']
@@ -664,7 +754,7 @@
 
 The widgets factories component,
 
-  >>> factories = addPerson.fields['firstName'].widgetFactory
+  >>> factories = personForm.fields['firstName'].widgetFactory
   >>> factories
   {'display': <function CustomDisplayWidgetFactory at ...>}
 

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/form.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/form.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/form.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -199,17 +199,22 @@
             (self, self.request, self.getContent()), interfaces.IWidgets)
         self.widgets.update()
 
+    def applyChanges(self, data):
+        content = self.getContent()
+        changed = applyChanges(self, content, data)
+        if changed:
+            zope.event.notify(
+                zope.lifecycleevent.ObjectModifiedEvent(content))
+        return changed
+
     @button.buttonAndHandler(_('Apply'), name='apply')
     def handleApply(self, action):
         data, errors = self.widgets.extract()
         if errors:
             self.status = self.formErrorsMessage
             return
-        content = self.getContent()
-        changed = applyChanges(self, content, data)
+        changed = self.applyChanges(data)
         if changed:
-            zope.event.notify(
-                zope.lifecycleevent.ObjectModifiedEvent(content))
             self.status = self.successMessage
         else:
             self.status = self.noChangesMessage

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/form.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/form.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/form.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -168,7 +168,7 @@
 * No widget is created, if the data is not accessible in the content.
 * A custom widget manager has been registered to particularly ignore a field.
 
-In this simple case, all fields will be converted to widgets.
+In our simple test case, all fields will be converted to widgets.
 
 Widget Creation
 ~~~~~~~~~~~~~~~
@@ -200,7 +200,7 @@
 2. The content of the form, which represents the displayed data.
 3. The request, in case a form has not been submitted or an error occurred.
 
-Since we are currently building an add form, only the first two places are
+Since we are currently building an add form, the second place is not
 effective. And since we do not have anything in the request, the value should
 be the field's default value or be empty.
 
@@ -477,7 +477,14 @@
   >>> print addForm.widgets['age'].error.render()
   <div class="error">The value cannot be a negative number.</div>
 
+Note: The ``adapts()`` call might look very strange initially. However, an
+error view snippet is an adapter that requires 6 input objects -- error,
+request, widget, field, form, content. By specifying only the error, we tell
+the system that we do not care about the other discriminators, which then can
+be anything. I could also have used ``zope.interface.Interface`` instead,
+which would be equivalent.
 
+
 Additional Form Attributes and API
 ----------------------------------
 
@@ -878,7 +885,8 @@
 The only step the developer has to complete is to re-implement the form's
 ``getContent()`` method to return the dictionary:
 
-  >>> personDict = {'id': u'rineichen', 'name': u'Roger Ineichen'}
+  >>> personDict = {'id': u'rineichen', 'name': u'Roger Ineichen',
+  ...               'gender': None, 'age': None}
   >>> class PersonDictEditForm(PersonEditForm):
   ...     def getContent(self):
   ...         return personDict
@@ -907,7 +915,7 @@
           <select id="form-widgets-gender" name="form.widgets.gender:list"
                   class="selectWidget" size="1">
             <option id="form-widgets-gender-novalue"
-                    value="--NOVALUE--">no value</option>
+                    value="--NOVALUE--" selected="selected">no value</option>
             <option id="form-widgets-gender-0" value="male">male</option>
             <option id="form-widgets-gender-1" value="female">female</option>
           </select>
@@ -1122,3 +1130,36 @@
          name="form.widgets.name" class="MyCSS"
          value="Claudia Richter" />
   ...
+
+
+Hidden fields
+-------------
+
+Another important part of a form is that we can generate hidden widgets. We can 
+do this in a form by defining a widget mode. We can do this by override the
+setUpWidgets method.
+
+  >>> class HiddenFieldEditForm(form.EditForm):
+  ...
+  ...     fields = field.Fields(IPerson)
+  ...     fields['name'].widgetFactory = MyFieldWidget
+  ...     
+  ...     def updateWidgets(self):
+  ...         super(HiddenFieldEditForm, self).updateWidgets()
+  ...         self.widgets['age'].mode = interfaces.HIDDEN_MODE
+
+We can see that the widget gets rendered as hidden:
+
+  >>> hiddenEdit = HiddenFieldEditForm(root[u'srichter'], TestRequest())
+  >>> addTemplate(hiddenEdit)
+  >>> hiddenEdit.update()
+  >>> print hiddenEdit.render()
+  <html>...
+  <input type="text" id="form-widgets-name"
+         name="form.widgets.name" class="MyCSS"
+         value="Claudia Richter" />
+  ...
+  <input type="hidden" id="form-widgets-age"
+         name="form.widgets.age" class="hiddenWidget"
+         value="27" />
+  ...

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/interfaces.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/interfaces.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/interfaces.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -28,6 +28,7 @@
 
 INPUT_MODE = 'input'
 DISPLAY_MODE = 'display'
+HIDDEN_MODE = 'hidden'
 
 class NOVALUE(object):
     def __repr__(self):
@@ -466,14 +467,7 @@
         max_length=1,
         required=False)
 
-    actionFactory = zope.schema.Field(
-        title=_('Action Factory'),
-        description=_('The action factory.'),
-        required=False,
-        default=None,
-        missing_value=None)
 
-
 class IButtons(ISelectionManager):
     """Button manager."""
 

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/subform.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/subform.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/subform.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -484,3 +484,125 @@
 complex object widgets to handle objects within forms. We never considered
 this a good way of programming, since one looses control over the layout too
 easily.
+
+
+Context less subform (for Mats) 
+-------------------------------
+
+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 
+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. 
+
+  >>> from z3c.form.interfaces import IWidgets
+  >>> class OwnerAddForm(form.EditForm):
+  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
+  ...         'simple_owneredit.pt', templatePath)
+  ...     fields = field.Fields(IOwner)
+  ...     prefix = 'owner'
+  ... 
+  ...     def updateWidgets(self):
+  ...         self.widgets = zope.component.getMultiAdapter(
+  ...             (self, self.request, self.getContent()), IWidgets)
+  ...         self.widgets.ignoreContext = True
+  ...         self.widgets.update()
+
+Next we define the car form, which has the owner form as a sub-form.
+
+  >>> class CarAddForm(form.EditForm):
+  ...     fields = field.Fields(ICar).select('model', 'make')
+  ...     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()
+  ...         super(CarAddForm, self).update()
+
+Let's now instantiate the form and render it. but first setup a simple container
+which we can use for the add form context:
+
+  >>> class Container(object):
+  ...    """Simple context simulation a container."""
+  >>> container = Container()
+
+Setup a test request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+
+And render the form. As you can see, the widget get rendered withut any ``real``
+context.
+
+  >>> carForm = CarAddForm(container, request)
+  >>> carForm.update()
+  >>> print carForm.render()
+  <html>
+  <body>
+  <form action=".">
+    <div class="row">
+      <label for="car-widgets-model">Model</label>
+      <input type="text" id="car-widgets-model"
+           name="car.widgets.model" class="textWidget" value="" />
+    </div>
+    <div class="row">
+      <label for="car-widgets-make">Make</label>
+      <input type="text" id="car-widgets-make"
+           name="car.widgets.make" class="textWidget" value="" />
+    </div>
+    <fieldset>
+    <legend>Owner</legend>
+    <div class="row">
+      <label for="owner-widgets-name">Name</label>
+      <input type="text" id="owner-widgets-name"
+           name="owner.widgets.name" class="textWidget" value="" />
+    </div>
+    <div class="row">
+      <label for="owner-widgets-license">License</label>
+      <input type="text" id="owner-widgets-license"
+           name="owner.widgets.license" class="textWidget"
+           value="" />
+    </div>
+    <div class="action">
+      <input type="submit" id="owner-buttons-apply"
+           name="owner.buttons.apply" class="submitWidget"
+           value="Apply" />
+    </div>
+    </fieldset>
+    <div class="action">
+      <input type="submit" id="car-buttons-apply"
+           name="car.buttons.apply" class="submitWidget"
+           value="Apply" />
+    </div>
+  </form>
+  </body>
+  </html>
+
+Let's show how we can extract input values of the form and subform. First give
+them some input:
+
+  >>> request = TestRequest(form={
+  ...     'car.widgets.model': u'Ford',
+  ...     'car.widgets.make': u'F150',
+  ...     'owner.widgets.name': u'Stephan Richter',
+  ...     'owner.widgets.license': u'MA-991723FDG',
+  ...     })
+  >>> carForm = CarAddForm(container, request)
+  >>> carForm.update()
+
+Now get the form values. This is normaly done in a action handler:
+
+  >>> carForm.widgets.extract()
+  ({'model': u'Ford', 'make': u'F150'}, ())
+
+  >>> carForm.owner.widgets.extract()
+  ({'name': u'Stephan Richter', 'license': u'MA-991723FDG'}, ())

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/testing.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/testing.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/testing.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -97,6 +97,10 @@
         widget.WidgetTemplateFactory(getPath('text_display.pt'), 'text/html'),
         (None, None, None, None, interfaces.ITextWidget),
         IPageTemplate, name=interfaces.DISPLAY_MODE)
+    zope.component.provideAdapter(
+        widget.WidgetTemplateFactory(getPath('text_hidden.pt'), 'text/html'),
+        (None, None, None, None, interfaces.ITextWidget),
+        IPageTemplate, name=interfaces.HIDDEN_MODE)
     # Radio Field Widget
     zope.component.provideAdapter(radio.RadioFieldWidget)
     zope.component.provideAdapter(

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/tests/test_doc.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/tests/test_doc.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/tests/test_doc.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -94,4 +94,9 @@
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
             ),
+        doctest.DocFileSuite(
+            '../adding.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
         ))

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/util.py
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/util.py	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/util.py	2007-06-22 20:27:09 UTC (rev 76948)
@@ -22,7 +22,7 @@
 import zope.contenttype
 
 from z3c.form import interfaces
-from z3c.i18n import MessageFactory as _
+from z3c.form.i18n import MessageFactory as _
 
 
 _identifier = re.compile('[A-Za-z][a-zA-Z0-9_]*$')
@@ -72,32 +72,34 @@
 
 def getWidgetById(form, id):
     """Get a widget by it's rendered DOM element id."""
+    # convert the id to a name
+    name = id.replace('-', '.')
     prefix = form.prefix + form.widgets.prefix
-    if not id.startswith(prefix):
-        raise ValueError("Id %r must start with prefix %r" %(id, prefix))
-    shortName = id[len(prefix):]
+    if not name.startswith(prefix):
+        raise ValueError("Name %r must start with prefix %r" %(name, prefix))
+    shortName = name[len(prefix):]
     return form.widgets.get(shortName, None)
 
 
 def extractContentType(form, id):
-    """Knows how to extract a filename if a IBytes/IFileWidget was used."""
+    """Extract the content type of the widget with the given id."""
     widget = getWidgetById(form, id)
     return zope.contenttype.guess_content_type(widget.filename)[0]
 
 
 def extractFileName(form, id, cleanup=True, allowEmtpyPostFix=False):
-    """Knows how to extract a filename if a IBytes/IFileWidget was used.
-    
-    Uploads from win/IE need some cleanup because the filename includes also 
-    the path. The option cleanup=True will do this for you. The option 
-    allowEmtpyPostFix allows to pass filename without extensions. By default
-    this option is set to False and will raise a ValueError if a filename 
-    doesn't contain a extension.
+    """Extract the filename of the widget with the given id.
+
+    Uploads from win/IE need some cleanup because the filename includes also
+    the path. The option ``cleanup=True`` will do this for you. The option
+    ``allowEmtpyPostFix`` allows to have a filename without extensions. By
+    default this option is set to ``False`` and will raise a ``ValueError`` if
+    a filename doesn't contain a extension.
     """
     widget = getWidgetById(form, id)
     if not allowEmtpyPostFix or cleanup:
-        # we need to strip out the path part even if we not reomve them later,
-        # because we just need ot check the filename extension
+        # We need to strip out the path section even if we do not reomve them
+        # later, because we just need to check the filename extension.
         cleanFileName = widget.filename.split('\\')[-1]
         cleanFileName = cleanFileName.split('/')[-1]
         dottedParts = cleanFileName.split('.')

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/util.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/util.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/util.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -32,20 +32,21 @@
 
 We can now ask for the widget:
 
-  >>> util.getWidgetById(addPerson, 'form.widgets.name')
+  >>> util.getWidgetById(addPerson, 'form-widgets-name')
   <TextWidget 'form.widgets.name'>
 
 The widget id can be split into a prefix and a widget name. The id must always
 start with the correct prefix, otherwise a value error is raised:
 
-  >>> util.getWidgetById(addPerson, 'myform.widgets.name')
+  >>> util.getWidgetById(addPerson, 'myform-widgets-name')
   Traceback (most recent call last):
   ...
-  ValueError: Id 'myform.widgets.name' must start with prefix 'form.widgets.'
+  ValueError: Name 'myform.widgets.name' must start with prefix 'form.widgets.'
 
 If the widget is not found but the prefix is correct, ``None`` is returned:
 
-  >>> util.getWidgetById(addPerson, 'form.widgets.myname')
+  >>> util.getWidgetById(addPerson, 'form-widgets-myname') is None
+  True
 
 
 extractFileName
@@ -181,3 +182,43 @@
   Traceback (most recent call last):
   ...
   ValueError: Missing filename extension.
+
+
+extractContentType
+------------------
+
+there s alos a method which is able to extract the content type for a given
+file upload. We can use the stub form form the previous test.
+
+  >>> uploadForm = FileUploadFormStub()
+  >>> uploadForm.setFakeFileName('foo.txt')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'text/plain'
+
+  >>> uploadForm.setFakeFileName('foo.gif')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'image/gif'
+
+  >>> uploadForm.setFakeFileName('foo.jpg')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'image/jpeg'
+
+  >>> uploadForm.setFakeFileName('foo.png')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'image/png'
+
+  >>> uploadForm.setFakeFileName('foo.tif')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'image/tiff'
+
+  >>> uploadForm.setFakeFileName('foo.doc')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'application/msword'
+
+  >>> uploadForm.setFakeFileName('foo.zip')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'application/zip'
+
+  >>> uploadForm.setFakeFileName('foo.unknown')
+  >>> util.extractContentType(uploadForm, 'form.widgets.data')
+  'text/x-unknown-content-type'

Modified: z3c.form/branches/pcardune-tweaks/src/z3c/form/widget.txt
===================================================================
--- z3c.form/branches/pcardune-tweaks/src/z3c/form/widget.txt	2007-06-22 18:52:43 UTC (rev 76947)
+++ z3c.form/branches/pcardune-tweaks/src/z3c/form/widget.txt	2007-06-22 20:27:09 UTC (rev 76948)
@@ -262,7 +262,50 @@
   >>> print ageWidget.render()
   <input type="text" name="age" value="25" />
 
+But what happens if the object we are working on is securoty proxied? In
+particular, what happens, if the access to the attribute is denied. To see
+what happens, we have to create a proxied person:
 
+  >>> from zope.security import checker
+  >>> PersonChecker = checker.Checker({'age': 'Access'}, {'age': 'Edit'})
+
+  >>> ageWidget.request = TestRequest()
+  >>> ageWidget.context = checker.ProxyFactory(Person(), PersonChecker)
+
+After changing the security policy, ...
+
+  >>> from zope.security import management
+  >>> from z3c.form import testing
+  >>> management.endInteraction()
+  >>> newPolicy = testing.SimpleSecurityPolicy()
+  >>> oldPolicy = management.setSecurityPolicy(newPolicy)
+  >>> management.newInteraction()
+
+it is not possible anymore to update the widget:
+
+  >>> ageWidget.update()
+  Traceback (most recent call last):
+  ...
+  Unauthorized: (<Person object at ...>, 'age', 'Access')
+
+If no security declaration has been made at all, we get a
+``ForbiddenAttribute`` error:
+
+  >>> ageWidget.context = checker.ProxyFactory(Person(), checker.Checker({}))
+  >>> ageWidget.update()
+  Traceback (most recent call last):
+  ...
+  ForbiddenAttribute: ('age', <Person object at ...>)
+
+Let's clean up the setup:
+
+  >>> management.endInteraction()
+  >>> newPolicy = management.setSecurityPolicy(oldPolicy)
+  >>> management.newInteraction()
+
+  >>> ageWidget.context = Person()
+
+
 Dynamically Changing Attribute Values
 -------------------------------------
 



More information about the Checkins mailing list