[Zope3-checkins] SVN: Zope3/branches/jhauser-filefieldwidget/src/zope/ Heads up.

Roger Ineichen roger at projekt01.ch
Fri Jan 21 10:46:49 EST 2005


Log message for revision 28905:
  Heads up.
  
  The IFile refactoring requieres a new field/widget
  for the subobject Mime in the IFile.contents field.
  
  I decide to add a SchemaWidget (Iinput) which can handle 
  subobjects like the ObjectWidget does. 
  See the difference to ObjectWidget described below.
  
  Start adding a schema input widget.
  The schema input widget allowes to define a interface and a factory id.
  The factory is used for storing the attributes given form the schema.
  This powerfull field is used for defining fields of subobjects with 
  the schema of the subobject. We will use this in the IFile.contents field.
  
  I think in a long therm we ca replace the IObject field and ObjectWidget
  with this new field/widget. This pattern supports all requiered declaration
  in the interface/schema where the Object field requieres a separate view 
  for rendering subwidgets with additional CustomWidgetFacotires.
  The factory in the SchemaWidget is aquiered via the registred IFactory
  of a content type. "getUtility(IFactory, factoryId)".

Changed:
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/app/file/interfaces.py
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/__init__.py
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/configure.zcml
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/filewidgets.py
  A   Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/mimewidget.pt
  A   Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidget.pt
  A   Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidgets.py
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/app/schema/fields.zcml
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/schema/__init__.py
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_bootstrapfields.py
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_field.py
  U   Zope3/branches/jhauser-filefieldwidget/src/zope/schema/interfaces.py

-=-
Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/app/file/interfaces.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/file/interfaces.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/file/interfaces.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -19,6 +19,7 @@
 
 from zope.schema import Bytes
 from zope.schema import Mime, MimeData, MimeDataEncoding, MimeType
+from zope.schema import Schema
 from zope.interface import Interface
 from zope.app.i18n import ZopeMessageIDFactory as _
 
@@ -89,7 +90,7 @@
 
 class IFile(Interface):
 
-    contents = Mime(
+    contents = Schema(IMime, "zope.app.file.Mime",
         title = _(u'The file data'),
         description = _(u'The mime information and file data, which can be '
                          'read as a file.'),

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/__init__.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/__init__.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/__init__.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -29,6 +29,9 @@
 from zope.app.form.browser.textwidgets import DateDisplayWidget
 from zope.app.form.browser.textwidgets import BytesDisplayWidget
 
+# Widgets for schema-based fields
+from zope.app.form.browser.schemawidgets import SchemaWidget
+
 # Widgets for file-based fields
 from zope.app.form.browser.filewidgets import FileWidget
 from zope.app.form.browser.filewidgets import MimeWidget

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/configure.zcml
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/configure.zcml	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/configure.zcml	2005-01-21 15:46:38 UTC (rev 28905)
@@ -146,6 +146,14 @@
 
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.ISchema"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".SchemaWidget"
+      permission="zope.Public"
+      />
+
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
       for="zope.schema.interfaces.IMime"
       provides="zope.app.form.interfaces.IDisplayWidget"
       factory=".MimeDisplayWidget"

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/filewidgets.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/filewidgets.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/filewidgets.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -22,12 +22,37 @@
 
 from zope.app.form.interfaces import IInputWidget, ConversionError
 from zope.app.form.browser.widget import SimpleInputWidget, renderElement
+from zope.app.form import InputWidget
+from zope.app.form.browser.widget import BrowserWidget
 from zope.app.form.browser.textwidgets import BytesWidget
 from zope.app.form.browser.widget import DisplayWidget
 from zope.app.form.browser.textwidgets import escape
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
 
 
-class MimeWidget(SimpleInputWidget):
+# dependency
+from zope.app.file.file import Mime
+
+# used from ObjectWidget
+from zope.schema import getFieldNamesInOrder
+from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
+
+
+
+
+class MimeWidgetView:
+
+    template = ViewPageTemplateFile('mimewidget.pt')
+    
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+        
+    def __call__(self):
+        return self.template()
+
+
+class MimeWidget(BrowserWidget, InputWidget):
     """MimeWidget renders the subwidgets for the interface IMime.
     
     The widget also extracts the filename and the fileupload to the session. 
@@ -38,9 +63,117 @@
     ObjectWidget implementation in 
     'zope.app.form.browser.objectwidget.ObjectWidget'.
     """
-    pass
 
+    implements(IInputWidget)
+    
+    _object = None      # the object value (from setRenderedValue & request)
+    _request_parsed = False
 
+    def __init__(self, context, request, **kw):
+        super(MimeWidget, self).__init__(context, request)
+        
+        # define view that renders the widget
+        self.view = MimeWidgetView(self, request)
+
+        # factory used to create content that this widget (field)
+        # represents
+        self.factory = Mime
+
+        # handle foo_widget specs being passed in
+        self.names = getFieldNamesInOrder(self.context.schema)
+        for k, v in kw.items():
+            if k.endswith('_widget'):
+                setattr(self, k, v)
+
+        # set up my subwidgets
+        self._setUpEditWidgets()
+
+    def setPrefix(self, prefix):
+        super(ObjectWidget, self).setPrefix(prefix)
+        self._setUpEditWidgets()
+
+    def _setUpEditWidgets(self):
+        # subwidgets need a new name
+        setUpEditWidgets(self, self.context.schema, source=self.context,
+                         prefix=self.name, names=self.names, 
+                         context=self.context)
+
+    def __call__(self):
+        return self.view()
+        
+    def legendTitle(self):
+        return self.context.title or self.context.__name__
+
+    def getSubWidget(self, name):
+        return getattr(self, '%s_widget' % name)
+            
+    def subwidgets(self):
+        return [self.getSubWidget(name) for name in self.names]
+
+    def hidden(self):
+        """Render the list as hidden fields."""
+        result = []
+        for name in self.names:
+            result.append(getSubwidget(name).hidden())
+        return "".join(result)
+
+    def getInputValue(self):
+        """Return converted and validated widget data.
+
+        The value for this field will be represented as an `ObjectStorage`
+        instance which holds the subfield values as attributes. It will
+        need to be converted by higher-level code into some more useful
+        object (note that the default EditView calls `applyChanges`, which
+        does this).
+        """
+        content = self.factory()
+        for name in self.names:
+            setattr(content, name, self.getSubWidget(name).getInputValue())
+        return content
+
+    def applyChanges(self, content):
+        field = self.context
+
+        # create our new object value
+        value = field.query(content, None)
+        if value is None:
+            # TODO: ObjectCreatedEvent here would be nice
+            value = self.factory()
+
+        # apply sub changes, see if there *are* any changes
+        # TODO: ObjectModifiedEvent here would be nice
+        changes = applyWidgetsChanges(self, field.schema, target=value,
+                                      names=self.names)
+
+        # if there's changes, then store the new value on the content
+        if changes:
+            field.set(content, value)
+
+        return changes
+
+    def hasInput(self):
+        """Is there input data for the field
+
+        Return ``True`` if there is data and ``False`` otherwise.
+        """
+        for name in self.names:
+            if self.getSubWidget(name).hasInput():
+                return True
+        return False
+
+    def setRenderedValue(self, value):
+        """Set the default data for the widget.
+
+        The given value should be used even if the user has entered
+        data.
+        """
+        # re-call setupwidgets with the content
+        self._setUpEditWidgets()
+        for name in self.names:
+            self.getSubWidget(name).setRenderedValue(getattr(value, name, None))
+            
+
+
 class MimeDataWidget(SimpleInputWidget):
     """MimeDataWidget extracts the fileupload information from the session.
     

Added: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/mimewidget.pt
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/mimewidget.pt	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/mimewidget.pt	2005-01-21 15:46:38 UTC (rev 28905)
@@ -0,0 +1,6 @@
+<fieldset>
+  <legend tal:content="context/legendTitle">The Legend</legend>
+  <tal:block repeat="widget context/subwidgets">
+    <metal:block use-macro="context/@@form_macros/widget_row" />
+  </tal:block>
+</fieldset>


Property changes on: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/mimewidget.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidget.pt
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidget.pt	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidget.pt	2005-01-21 15:46:38 UTC (rev 28905)
@@ -0,0 +1,6 @@
+<fieldset>
+  <legend tal:content="context/legendTitle">The Legend</legend>
+  <tal:block repeat="widget context/subwidgets">
+    <metal:block use-macro="context/@@form_macros/widget_row" />
+  </tal:block>
+</fieldset>


Property changes on: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidget.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidgets.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidgets.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidgets.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -0,0 +1,157 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation 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.
+#
+##############################################################################
+"""Browser widgets for text-like data
+
+$Id: objectwidget.py 26748 2004-07-24 05:51:58Z pruggera $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.interface import implements
+from zope.schema import getFieldNamesInOrder
+from zope.component.interfaces import IFactory
+
+from zope.app.zapi import queryUtility
+from zope.app.form.interfaces import IInputWidget
+from zope.app.form import InputWidget
+from zope.app.form.browser.widget import BrowserWidget
+from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+
+
+class SchemaWidgetView:
+
+    template = ViewPageTemplateFile('schemawidget.pt')
+    
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+        
+    def __call__(self):
+        return self.template()
+    
+    
+class SchemaWidget(BrowserWidget, InputWidget):
+    """A widget over an Interface that contains Fields."""
+
+    implements(IInputWidget)
+    
+    _object = None      # the object value (from setRenderedValue & request)
+    _request_parsed = False
+
+    def __init__(self, context, request, **kw):
+        super(SchemaWidget, self).__init__(context, request)
+        
+        # define view that renders the widget
+        self.view = SchemaWidgetView(self, request)
+
+        # factory used to create content that this widget (field)
+        # represents, we get the factory id of a content type declared
+        # in the schema field
+        factoryId = self.context.factoryId
+        self.factory = queryUtility(IFactory, factoryId)
+
+        # handle foo_widget specs being passed in
+        self.names = getFieldNamesInOrder(self.context.schema)
+        for k, v in kw.items():
+            if k.endswith('_widget'):
+                setattr(self, k, v)
+
+        # set up my subwidgets
+        self._setUpEditWidgets()
+
+    def setPrefix(self, prefix):
+        super(SchemaWidget, self).setPrefix(prefix)
+        self._setUpEditWidgets()
+
+    def _setUpEditWidgets(self):
+        # subwidgets need a new name
+        setUpEditWidgets(self, self.context.schema, source=self.context,
+                         prefix=self.name, names=self.names, 
+                         context=self.context)
+
+    def __call__(self):
+        return self.view()
+        
+    def legendTitle(self):
+        return self.context.title or self.context.__name__
+
+    def getSubWidget(self, name):
+        return getattr(self, '%s_widget' % name)
+            
+    def subwidgets(self):
+        return [self.getSubWidget(name) for name in self.names]
+
+    def hidden(self):
+        """Render the list as hidden fields."""
+        result = []
+        for name in self.names:
+            result.append(getSubwidget(name).hidden())
+        return "".join(result)
+
+    def getInputValue(self):
+        """Return converted and validated widget data.
+
+        The value for this field will be represented as an `ObjectStorage`
+        instance which holds the subfield values as attributes. It will
+        need to be converted by higher-level code into some more useful
+        object (note that the default EditView calls `applyChanges`, which
+        does this).
+        """
+        content = self.factory()
+        for name in self.names:
+            setattr(content, name, self.getSubWidget(name).getInputValue())
+        return content
+
+    def applyChanges(self, content):
+        field = self.context
+
+        # create our new object value
+        value = field.query(content, None)
+        if value is None:
+            # TODO: ObjectCreatedEvent here would be nice
+            value = self.factory()
+
+        # apply sub changes, see if there *are* any changes
+        # TODO: ObjectModifiedEvent here would be nice
+        changes = applyWidgetsChanges(self, field.schema, target=value,
+                                      names=self.names)
+
+        # if there's changes, then store the new value on the content
+        if changes:
+            field.set(content, value)
+
+        return changes
+
+    def hasInput(self):
+        """Is there input data for the field
+
+        Return ``True`` if there is data and ``False`` otherwise.
+        """
+        for name in self.names:
+            if self.getSubWidget(name).hasInput():
+                return True
+        return False
+
+    def setRenderedValue(self, value):
+        """Set the default data for the widget.
+
+        The given value should be used even if the user has entered
+        data.
+        """
+        # re-call setupwidgets with the content
+        self._setUpEditWidgets()
+        for name in self.names:
+            self.getSubWidget(name).setRenderedValue(getattr(value, name, None))
+            
+


Property changes on: Zope3/branches/jhauser-filefieldwidget/src/zope/app/form/browser/schemawidgets.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/app/schema/fields.zcml
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/app/schema/fields.zcml	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/app/schema/fields.zcml	2005-01-21 15:46:38 UTC (rev 28905)
@@ -264,6 +264,19 @@
 
   </content>
 
+  <content class="zope.schema.Schema">
+
+    <factory
+	id="zope.schema.Schema"
+	title="Schema Field"
+	description="Schema Field" />
+
+    <require
+       permission="zope.ManageContent"
+       interface="zope.schema.interfaces.ISchema" />
+
+  </content>
+
   <content class="zope.schema.URI">
 
     <factory

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/schema/__init__.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/schema/__init__.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/schema/__init__.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -23,6 +23,7 @@
 from zope.schema._field import Tuple, List, Set
 from zope.schema._field import Password, Dict, Datetime, Date, SourceText
 from zope.schema._field import Object, URI, Id, DottedName
+from zope.schema._field import Schema
 from zope.schema._field import InterfaceField
 from zope.schema._schema import getFields, getFieldsInOrder
 from zope.schema._schema import getFieldNames, getFieldNamesInOrder

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_bootstrapfields.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_bootstrapfields.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_bootstrapfields.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -361,3 +361,115 @@
         v = int(str)
         self.validate(v)
         return v
+
+
+class MimeData(Field):
+    """Field holding a byte string (in an efficient data structure).
+
+    The type of the data is described by it's mime type.
+    """
+
+    def set(self, obj, value):
+        """
+        Do a two phase save, first create an empty file-like object, make it
+        persistent, than read the data into it in chunks, to reduce memory
+        consumption.
+
+        'value' is a file-like, most often an FileUpload() object.
+        """
+        if self.readonly:
+            raise TypeError("Can't set values on read-only fields "
+                            "(name=%s, class=%s.%s)"
+                            % (self.__name__,
+                               obj.__class__.__module__,
+                               obj.__class__.__name__))
+        # now create an empty file object and store it at the persistent object
+        setattr(obj, self.__name__, MimeData())
+        file = getattr(obj, self.__name__)
+        # now do the upload in chunks
+        file.data = value
+        # save additional information 
+        file.filename = self._extractFilename(value)
+        small_body = file.read(64)
+        file.seek(0) # XXX needed?
+        file.contentType = guess_content_type(name=file.filename, body=small_body)
+
+    def _validate(self, value):
+        # just test that there is a read method, more is not needed.
+        if getattr(value, 'read',''):
+            return
+        super(Bytes, self)._validate(value)
+
+    def _validate(self, value):
+        if self._type is not None and not isinstance(value, self._type):
+            raise WrongType(value, self._type)
+
+        if not self.constraint(value):
+            raise ConstraintNotSatisfied(value)
+
+    def _extractFilename(self, data):
+        # if it is a fileupload object
+        if hasattr(data,'filename'):
+            fid = data.filename
+            # sometimes the full pathname is included
+            fid=fid[max(fid.rfind('/'),
+                        fid.rfind('\\'), # escaped backslash
+                        fid.rfind(':'))+1:]
+            return fid
+        else:
+            return ''
+
+
+# TODO: add encodng vocabulary for selecting possible mime-types
+class MimeDataEncoding(Field):
+    """Field containing the encoding used for text-based files."""
+    implements(IFromUnicode)
+
+    _type = str
+
+    def fromUnicode(self, u):
+        """
+        >>> b = Bytes(constraint=lambda v: 'x' in v)
+
+        >>> b.fromUnicode(u" foo x.y.z bat")
+        ' foo x.y.z bat'
+        >>> b.fromUnicode(u" foo y.z bat")
+        Traceback (most recent call last):
+        ...
+        ConstraintNotSatisfied:  foo y.z bat
+
+        """
+        v = str(u)
+        self.validate(v)
+        return v
+
+    def constraint(self, value):
+        return '\n' not in value
+
+
+# TODO: perhaps add mime-type vocabulary for possible mime-types.
+# If so, we need also to list the mime-types from the python lib mimetypes
+class MimeType(Field):
+    """Field containing the mime-type for a file."""
+    implements(IFromUnicode)
+
+    _type = str
+
+    def fromUnicode(self, u):
+        """
+        >>> b = Bytes(constraint=lambda v: 'x' in v)
+
+        >>> b.fromUnicode(u" foo x.y.z bat")
+        ' foo x.y.z bat'
+        >>> b.fromUnicode(u" foo y.z bat")
+        Traceback (most recent call last):
+        ...
+        ConstraintNotSatisfied:  foo y.z bat
+
+        """
+        v = str(u)
+        self.validate(v)
+        return v
+
+    def constraint(self, value):
+        return '\n' not in value

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_field.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_field.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/schema/_field.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -33,6 +33,7 @@
 from zope.schema.interfaces import IBool, IInt, IFloat, IDatetime
 from zope.schema.interfaces import IChoice, ITuple, IList, ISet, IDict
 from zope.schema.interfaces import IPassword, IObject, IDate
+from zope.schema.interfaces import ISchema
 from zope.schema.interfaces import IURI, IId, IFromUnicode
 from zope.schema.interfaces import ISource, IVocabulary
 
@@ -47,6 +48,7 @@
 from zope.schema._bootstrapfields import MinMaxLen
 from zope.schema._bootstrapfields import Text, TextLine, Bool, Int, Password
 from zope.schema._bootstrapfields import MinMaxLen, ValidatedProperty
+from zope.schema._bootstrapfields import MimeData, MimeDataEncoding, MimeType
 from zope.schema.fieldproperty import FieldProperty
 from zope.schema.vocabulary import getVocabularyRegistry
 from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
@@ -67,6 +69,9 @@
 classImplements(Password, IPassword)
 classImplements(Bool, IBool)
 classImplements(Int, IInt)
+classImplements(MimeData, IMimeData)
+classImplements(MimeDataEncoding, IMimeDataEncoding)
+classImplements(MimeType, IMimeType)
 
 
 class SourceText(Text):
@@ -121,116 +126,6 @@
             raise WrongContainedType(errors)
 
 
-class MimeData(Field):
-    __doc__ = IMimeData.__doc__
-    implements(IMimeData)
-
-    def set(self, obj, value):
-        """
-        Do a two phase save, first create an empty file-like object, make it
-        persistent, than read the data into it in chunks, to reduce memory
-        consumption.
-
-        'value' is a file-like, most often an FileUpload() object.
-        """
-        if self.readonly:
-            raise TypeError("Can't set values on read-only fields "
-                            "(name=%s, class=%s.%s)"
-                            % (self.__name__,
-                               obj.__class__.__module__,
-                               obj.__class__.__name__))
-        # now create an empty file object and store it at the persistent object
-        setattr(obj, self.__name__, MimeData())
-        file = getattr(obj, self.__name__)
-        # now do the upload in chunks
-        file.data = value
-        # save additional information 
-        file.filename = self._extractFilename(value)
-        small_body = file.read(64)
-        file.seek(0) # XXX needed?
-        file.contentType = guess_content_type(name=file.filename, body=small_body)
-
-    def _validate(self, value):
-        # just test that there is a read method, more is not needed.
-        if getattr(value, 'read',''):
-            return
-        super(Bytes, self)._validate(value)
-
-    def _validate(self, value):
-        if self._type is not None and not isinstance(value, self._type):
-            raise WrongType(value, self._type)
-
-        if not self.constraint(value):
-            raise ConstraintNotSatisfied(value)
-
-    def _extractFilename(self, data):
-        # if it is a fileupload object
-        if hasattr(data,'filename'):
-            fid = data.filename
-            # sometimes the full pathname is included
-            fid=fid[max(fid.rfind('/'),
-                        fid.rfind('\\'), # escaped backslash
-                        fid.rfind(':'))+1:]
-            return fid
-        else:
-            return ''
-
-
-# TODO: add encodng vocabulary for selecting possible mime-types
-class MimeDataEncoding(Field):
-    __doc__ = IMimeDataEncoding.__doc__
-    implements(IMimeDataEncoding, IFromUnicode)
-
-    _type = str
-
-    def fromUnicode(self, u):
-        """
-        >>> b = Bytes(constraint=lambda v: 'x' in v)
-
-        >>> b.fromUnicode(u" foo x.y.z bat")
-        ' foo x.y.z bat'
-        >>> b.fromUnicode(u" foo y.z bat")
-        Traceback (most recent call last):
-        ...
-        ConstraintNotSatisfied:  foo y.z bat
-
-        """
-        v = str(u)
-        self.validate(v)
-        return v
-
-    def constraint(self, value):
-        return '\n' not in value
-
-
-# TODO: perhaps add mime-type vocabulary for possible mime-types.
-# If so, we need also to list the mime-types from the python lib mimetypes
-class MimeType(Field):
-    __doc__ = IMimeType.__doc__
-    implements(IMimeType, IFromUnicode)
-
-    _type = str
-
-    def fromUnicode(self, u):
-        """
-        >>> b = Bytes(constraint=lambda v: 'x' in v)
-
-        >>> b.fromUnicode(u" foo x.y.z bat")
-        ' foo x.y.z bat'
-        >>> b.fromUnicode(u" foo y.z bat")
-        Traceback (most recent call last):
-        ...
-        ConstraintNotSatisfied:  foo y.z bat
-
-        """
-        v = str(u)
-        self.validate(v)
-        return v
-
-    def constraint(self, value):
-        return '\n' not in value
-
-
 class ASCII(Bytes):
     __doc__ = IASCII.__doc__
     implements(IASCII)
@@ -550,6 +445,34 @@
             raise WrongContainedType(errors)
 
 
+class Schema(Field):
+    __doc__ = ISchema.__doc__
+    implements(ISchema)
+
+    schema = None
+    factoryId = None
+
+    def __init__(self, schema, factoryId, **kw):
+        if not IInterface.providedBy(schema):
+            raise WrongType
+            
+        self.schema = schema
+        self.factoryId = factoryId
+        super(Schema, self).__init__(**kw)
+        
+    def _validate(self, value):
+        super(Schema, self)._validate(value)
+        
+        # schema has to be provided by value
+        if not self.schema.providedBy(value):
+            raise SchemaNotProvided
+            
+        # check the value against schema
+        errors = _validate_fields(self.schema, value)
+        if errors:
+            raise WrongContainedType(errors)
+
+
 class Dict(MinMaxLen, Iterable, Field):
     """A field representing a Dict."""
     implements(IDict)

Modified: Zope3/branches/jhauser-filefieldwidget/src/zope/schema/interfaces.py
===================================================================
--- Zope3/branches/jhauser-filefieldwidget/src/zope/schema/interfaces.py	2005-01-21 12:52:57 UTC (rev 28904)
+++ Zope3/branches/jhauser-filefieldwidget/src/zope/schema/interfaces.py	2005-01-21 15:46:38 UTC (rev 28905)
@@ -19,6 +19,7 @@
 from zope.interface import Interface, Attribute
 from zope.schema._bootstrapfields import Field, Text, TextLine, Bool, Int
 from zope.schema._bootstrapfields import Container, Iterable
+from zope.schema._bootstrapfields import MimeData, MimeDataEncoding, MimeType
 
 from zope.i18nmessageid import MessageIDFactory
 _ = MessageIDFactory("zope")
@@ -274,9 +275,8 @@
     The value might be constrained to be with length limits.
     """
 
-class IMime(IField):
-    u"""Field describing the subwidgets of IMime used in IFile."""
 
+
 # TODO: perhaps we should use TextLine for prevent inheriting IMinMaxLen
 class IMimeData(IBytes):
     u"""Field holding a byte string (in an efficient data structure).
@@ -291,6 +291,35 @@
 # TODO: perhaps we should use TextLine for prevent inheriting IMinMaxLen
 class IMimeType(IBytes):
     u"""Field containing the mime-type for a file."""
+
+class IMime(IField):
+    u"""Field describing the subwidgets of IMime used in IFile."""
+
+    contentType = MimeType(
+        title = _(u'Content type'),
+        description=_(u'The content type identifies the type of data.'),
+        default='',
+        required=False,
+        missing_value=''
+        )
+
+    encoding = MimeDataEncoding(
+        title = _(u'Encoding type'),
+        description=_(u'The encoding of the data if it is text.'),
+        default='',
+        required=False,
+        missing_value=''
+        )
+
+    data = MimeData (
+        title=_(u'Data'),
+        description=_(u'The actual content of the file.'),
+        default='',
+        missing_value='',
+        required=False,
+        )
+
+
     
 class IASCII(IBytes):
     u"""Field containing a 7-bit ASCII string. No characters > DEL
@@ -439,6 +468,24 @@
     schema = Attribute("schema",
         _(u"The Interface that defines the Fields comprising the Object."))
 
+class ISchema(IField):
+    u"""Field containing an schema descriping the fields.
+    
+    This is like a IObject field which defines and handles the factory itself.
+    The Schema field is used for Object values. It gives a powerfull bridge
+    to not only attributes as values. This makes it possible to define a simply
+    interface schema for schema based values.
+    """
+
+    interface = Attribute("interface",
+        _(u"The Interface that defines the Fields comprising the Object."))
+
+    factoryId = TextLine(
+        title=u"factoryId",
+        description=(u"The factory id which is used for to initalize the object."),
+        required=False,
+        default=None)
+
 class IDict(IMinMaxLen, IIterable, IContainer):
     u"""Field containing a conventional dict.
 



More information about the Zope3-Checkins mailing list