[Checkins] SVN: z3c.form/trunk/src/z3c/form/ - Implemented support for FileUpload in FileWidget

Roger Ineichen roger at projekt01.ch
Thu May 31 19:16:01 EDT 2007


Log message for revision 76065:
  - Implemented support for FileUpload in FileWidget
  
  - Added helper for handling FileUpload. You can find this helpers in 
    z3c.form.util

Changed:
  A   z3c.form/trunk/src/z3c/form/CHANGES.txt
  U   z3c.form/trunk/src/z3c/form/TODO.txt
  U   z3c.form/trunk/src/z3c/form/browser/file.py
  U   z3c.form/trunk/src/z3c/form/browser/file.txt
  U   z3c.form/trunk/src/z3c/form/configure.zcml
  U   z3c.form/trunk/src/z3c/form/converter.py
  U   z3c.form/trunk/src/z3c/form/converter.txt
  U   z3c.form/trunk/src/z3c/form/util.py

-=-
Added: z3c.form/trunk/src/z3c/form/CHANGES.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/CHANGES.txt	                        (rev 0)
+++ z3c.form/trunk/src/z3c/form/CHANGES.txt	2007-05-31 23:16:01 UTC (rev 76065)
@@ -0,0 +1,20 @@
+=======
+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/trunk/src/z3c/form/TODO.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/TODO.txt	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/TODO.txt	2007-05-31 23:16:01 UTC (rev 76065)
@@ -27,3 +27,6 @@
 
 Notes from Roger
 ----------------
+
+- Define all widget attributes for the different widgets in the interfaces 
+  as fields

Modified: z3c.form/trunk/src/z3c/form/browser/file.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file.py	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/browser/file.py	2007-05-31 23:16:01 UTC (rev 76065)
@@ -28,6 +28,10 @@
     """Input type text widget implementation."""
     zope.interface.implementsOnly(interfaces.IFileWidget)
 
+    # filename and headers attribute get set by IDataConverter to the widget
+    # providedy form the FileUpload
+    headers = None
+    filename = None
     css = u'fileWidget'
 
 @zope.component.adapter(zope.schema.interfaces.IBytes, interfaces.IFormLayer)

Modified: z3c.form/trunk/src/z3c/form/browser/file.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/file.txt	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/browser/file.txt	2007-05-31 23:16:01 UTC (rev 76065)
@@ -63,3 +63,33 @@
   >>> widget.update()
   >>> widget.extract()
   <NOVALUE>
+
+Make also sure that we can handle FileUpload objects given form a file upload.
+
+  >>> import cStringIO
+  >>> from zope.publisher.browser import FileUpload
+
+Let's define a FieldStorage stub:
+
+  >>> class FieldStorageStub:
+  ...     def __init__(self, file):
+  ...         self.file = file
+  ...         self.headers = {}
+  ...         self.filename = 'foo.bar'
+
+Now build a FileUpload:
+
+  >>> myfile = cStringIO.StringIO('File upload contents.')
+  >>> aFieldStorage = FieldStorageStub(myfile)
+  >>> myUpload = FileUpload(aFieldStorage)
+
+  >>> widget.request = TestRequest(form={'widget.name': myUpload})
+  >>> widget.update()
+  >>> widget.extract()
+  <zope.publisher.browser.FileUpload object at ...>
+
+If we render them, we get a regular file upload widget:
+
+  >>> print widget.render()
+  <input type="file" id="widget.id" name="widget.name"
+         class="fileWidget" />

Modified: z3c.form/trunk/src/z3c/form/configure.zcml
===================================================================
--- z3c.form/trunk/src/z3c/form/configure.zcml	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/configure.zcml	2007-05-31 23:16:01 UTC (rev 76065)
@@ -46,6 +46,9 @@
       factory=".converter.TimedeltaDataConverter"
       />
   <adapter
+      factory=".converter.BytesDataConverter"
+      />
+  <adapter
       factory=".converter.SequenceDataConverter"
       />
   <adapter

Modified: z3c.form/trunk/src/z3c/form/converter.py
===================================================================
--- z3c.form/trunk/src/z3c/form/converter.py	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/converter.py	2007-05-31 23:16:01 UTC (rev 76065)
@@ -20,7 +20,9 @@
 import zope.interface
 import zope.component
 import zope.schema
+import zope.publisher.browser
 
+from z3c.form.i18n import MessageFactory as _
 from z3c.form import interfaces
 
 
@@ -104,6 +106,44 @@
         return datetime.timedelta(days, sum(seconds))
 
 
+class BytesDataConverter(FieldDataConverter):
+    """A special data converter for bytes, supporting also FileUpload. 
+    
+    Since IBytes represents a file upload too, this converter can handle 
+    zope.publisher.browser.FileUpload object as given value.
+    """
+    zope.component.adapts(
+        zope.schema.interfaces.IBytes, interfaces.IWidget)
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        if value is None or value == '':
+            return self.context.missing_value
+
+        if isinstance(value, zope.publisher.browser.FileUpload):
+            # By default a IBytes field is used for get a file upload widget.
+            # But interfaces extending IBytes do not use file upload widgets.
+            # Any way if we get a FileUpload object, we'll convert it.
+            # We also store the additional FileUpload values on the widget 
+            # before we loose them.
+            self.widget.headers = value.headers
+            self.widget.filename = value.filename
+            try:
+                seek = value.seek
+                read = value.read
+            except AttributeError, e:
+                raise ValueError(_('Bytes data is not a file object'), e)
+            else:
+                seek(0)
+                data = read()
+                if data or getattr(value, 'filename', ''):
+                    return data
+                else:
+                    return self.context.missing_value
+        else:
+            return unicode(value)
+
+
 class SequenceDataConverter(FieldDataConverter):
     """Basic data converter for ISequenceWidget."""
 

Modified: z3c.form/trunk/src/z3c/form/converter.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/converter.txt	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/converter.txt	2007-05-31 23:16:01 UTC (rev 76065)
@@ -198,6 +198,48 @@
   datetime.timedelta(1, 3661)
 
 
+Bytes Data Converter
+--------------------
+
+Since the ``Bytes`` field can contain a FileUpload object, we have to make
+sure we can convert FileUpload objects to bytes too.
+
+  >>> bytes = zope.schema.Bytes()
+
+  >>> bdc = converter.BytesDataConverter(bytes, text)
+  >>> bdc
+  <DataConverter from Bytes to Widget>
+
+Bytes are converted to unicode:
+
+  >>> simple = 'foobar'
+  >>> bdc.toFieldValue(simple)
+  u'foobar'
+
+The converter can also convert FileUpload objects. Setup a fields storage 
+stub...
+
+  >>> class FieldStorageStub:
+  ...     def __init__(self, file):
+  ...         self.file = file
+  ...         self.headers = {}
+  ...         self.filename = 'foo.bar'
+
+build a FileUpload...
+
+  >>> import cStringIO
+  >>> from zope.publisher.browser import FileUpload
+  >>> myfile = cStringIO.StringIO('File upload contents.')
+  >>> aFieldStorage = FieldStorageStub(myfile)
+  >>> myUpload = FileUpload(aFieldStorage)
+
+and try to convert:
+
+  >>> bdc.toFieldValue(myUpload)
+  'File upload contents.'
+
+
+
 Sequence Data Converter
 -----------------------
 

Modified: z3c.form/trunk/src/z3c/form/util.py
===================================================================
--- z3c.form/trunk/src/z3c/form/util.py	2007-05-31 21:09:58 UTC (rev 76064)
+++ z3c.form/trunk/src/z3c/form/util.py	2007-05-31 23:16:01 UTC (rev 76065)
@@ -19,6 +19,7 @@
 import re
 import types
 import zope.interface
+import zope.contenttype
 
 from z3c.form import interfaces
 
@@ -77,6 +78,34 @@
     return form.widgets.get(shortName, None)
 
 
+def extractContentType(form, id):
+    """Knows how to extract a filename if a IBytes/IFileWidget was used."""
+    widget = getWidgetById(form, id)
+    return zope.contenttype.guess_content_type(widget.filename)[0]
+
+
+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.
+    """
+    widget = getWidgetById(form, id)
+    if cleanup:
+        # we try to use a better file name. Uploads from win/IE need some 
+        # cleanup because the filename includes also the path.
+        path = widget.filename.split('.')
+        if len(path) > 1:
+            postfix = path.pop(-1)
+            # now remove the rest of the path
+            pathStr = path.pop(-1)
+            cleanFileName = pathStr.split('\\')[-1]
+            cleanFileName = cleanFileName.split('/')[-1]
+            filename = cleanFileName +'.'+ postfix
+        return filename
+    return widget.filename
+
+
 class Manager(object):
     """Non-persistent IManager implementation."""
     zope.interface.implements(interfaces.IManager)



More information about the Checkins mailing list