[Zope3-checkins] CVS: Zope3/src/zope/app/form -
utility.py:1.25.14.1 widget.py:1.11.6.1
Garrett Smith
garrett at mojave-corp.com
Thu Feb 26 00:11:28 EST 2004
Update of /cvs-repository/Zope3/src/zope/app/form
In directory cvs.zope.org:/tmp/cvs-serv25181/src/zope/app/form
Modified Files:
Tag: garrett-widgets2-branch
utility.py widget.py
Log Message:
Yet another branch for widgets refactoring (YABFWR).
=== Zope3/src/zope/app/form/utility.py 1.25 => 1.25.14.1 ===
--- Zope3/src/zope/app/form/utility.py:1.25 Fri Jan 23 12:00:20 2004
+++ Zope3/src/zope/app/form/utility.py Thu Feb 26 00:10:55 2004
@@ -35,203 +35,153 @@
__metaclass__ = type
from warnings import warn
-from zope.component import getView, getDefaultViewName
from zope.schema import getFieldsInOrder
from zope.schema.interfaces import ValidationError
+from zope.app import zapi
from zope.app.interfaces.form import IWidget
from zope.app.interfaces.form import WidgetsError, MissingInputError
from zope.app.interfaces.form import InputErrors
+from zope.app.interfaces.form import IEditWidget, IDisplayWidget
from zope.component.interfaces import IViewFactory
+# A marker that indicates 'no value' for any of the utility functions that
+# accept a 'value' argument.
+no_value = object()
+
def _fieldlist(names, schema):
if not names:
fields = getFieldsInOrder(schema)
else:
fields = [ (name, schema[name]) for name in names ]
return fields
+
+
+def _createWidget(context, field, viewType, request):
+ """Creates a widget given a context, field, and viewType.
+
+ Uses zapi.getViewProviding to lookup a view for the field and the
+ viewType.
+ """
+ field = field.bind(context)
+ return zapi.getViewProviding(field, viewType, request)
-def _whine(view, name):
- url = view.request.URL
- vname = view.__class__.__name__
- warn(
- "View (%s) saved a widget (%s) without a '_widget' suffix.\n"
- "Url: %s"
- % (vname, name, url),
- DeprecationWarning, stacklevel=4,
- )
-
-class WhiningWidget:
-
- def __init__(self, view, name, widget):
- self.__widget = widget
- self.__whineargs = view, name
-
- def __whine(self):
- whineargs = self.__whineargs
- if whineargs:
- _whine(*whineargs)
- self.__whineargs = ()
-
- def __call__(self, *args, **kw):
- self.__whine()
- return self.__widget(*args, **kw)
-
- def __repr__(self):
- self.__whine()
- return `self.__widget`
-
- def __str__(self):
- self.__whine()
- return str(self.__widget)
-
- def __getattr__(self, name):
- self.__whine()
- return getattr(self.__widget, name)
-
-def setUpWidget(view, name, field, value=None, prefix=None,
- force=False, vname=None, context=None):
- """Set up a single view widget
+def _widgetHasStickyValue(widget):
+ """Returns True if the widget has a sticky value.
+
+ A sticky value is input from the user that should not be overridden
+ by an object's current field value. E.g. a user may enter an invalid
+ postal code, submit the form, and receive a validation error - the postal
+ code should be treated as 'sticky' until the user successfully updates
+ the object.
+ """
+ return IEditWidget.isImplementedBy(widget) and widget.hasInput()
+
+def setUpWidget(view, name, field, viewType, value=no_value, prefix=None,
+ ignoreStickyValues=False, context=None):
+ """Sets up a single view widget.
The widget will be an attribute of the view. If there is already
an attribute of the given name, it must be a widget and it will be
- initialized with the given value if not None.
+ initialized with the given value if not no_value.
If there isn't already a view attribute of the given name, then a
widget will be created and assigned to the attribute.
"""
- # Has a (custom) widget already been defined?
-
- wname = name+'_widget'
-
- widget = getattr(view, wname, None)
- installold = False
- if widget is None:
- widget = getattr(view, name, None)
- if widget is not None:
- if IViewFactory.isImplementedBy(widget):
- # Old custom widget definition.
- # We'll accept it, but we'll whine
- _whine(view, name)
-
- # we also need to remember to install the widget
- installold = True
- elif IWidget.isImplementedBy(widget):
- # Old widget definition. We'll accept it, but we'll whine
- _whine(view, name)
- else:
- # we found something else, which is innocent.
- widget = None
- installold = True
-
if context is None:
context = view.context
-
+ widgetName = name + '_widget'
+
+ # check if widget already exists
+ widget = getattr(view, widgetName, None)
if widget is None:
- # There isn't already a widget, create one
- field = field.bind(context)
- if vname is None:
- vname = getDefaultViewName(field, view.request)
- widget = getView(field, vname, view.request)
- setattr(view, wname, widget)
- if not hasattr(view, name):
- setattr(view, name, WhiningWidget(view, name, widget))
-
- else:
- # We have an attribute of the right name, is it really a widget
- if IViewFactory.isImplementedBy(widget):
- # This is a view factory, probably a custom widget.
- # Try to make it into a widget.
- field = field.bind(context)
- widget = widget(field, view.request)
- if IWidget.isImplementedBy(widget):
- # Yee ha! We have a widget now, save it
- setattr(view, wname, widget)
- if installold or not hasattr(view, name):
- setattr(view, name, WhiningWidget(view, name, widget))
-
- if not IWidget.isImplementedBy(widget):
- raise TypeError(
- "The %s view attribute named, %s, should be a widget, "
- "but isn't."
- % (view.__class__.__name__, name))
-
- if not hasattr(view, wname):
- setattr(view, wname, widget)
-
+ # does not exist - create it
+ widget = _createWidget(context, field, viewType, view.request)
+ setattr(view, widgetName, widget)
+ elif IViewFactory.isImplementedBy(widget):
+ # exists, but is actually a factory - use it to create the widget
+ widget = widget(field.bind(context), view.request)
+ setattr(view, widgetName, widget)
+
+ # widget must implement IWidget
+ if not IWidget.isImplementedBy(widget):
+ raise TypeError(
+ "Unable to configure a widget for %s - attribute %s does not "
+ "implement IWidget" % (name, widgetName))
+
if prefix:
widget.setPrefix(prefix)
-
- if force or not widget.hasInput() and value is not None:
- # XXX: The doc string sez value should not be None; maybe it should not
- # be field.missing_value instead?
+
+ if value is not no_value and (
+ ignoreStickyValues or not _widgetHasStickyValue(widget)):
widget.setRenderedValue(value)
-def setUpWidgets(view, schema, prefix=None, force=False,
+def setUpWidgets(view, schema, viewType, prefix=None, ignoreStickyValues=False,
initial={}, names=None, context=None):
- """Set up widgets for the fields defined by a schema
-
+ """Sets up widgets for the fields defined by a schema.
+
+ view is the view that will be configured with widgets.
+
+ schema is an interface containing the fields that widgets will be
+ created for.
+
+ prefix is a string that is appended to the widget names in the generated
+ HTML. This can be used to differentiate widgets for different schemas.
+
+ ignoreStickyValues is a flag that, when True, will cause widget sticky
+ values to be replaced with the context field value or a value specified
+ in initial.
+
+ initial is a mapping of field names to initial values.
+
+ names is an optional iterable that provides an ordered list of field
+ names to use. If names is None, the list of fields will be defined by
+ the schema.
+
+ context provides an alternative context that will be used instead of the
+ view context.
"""
for (name, field) in _fieldlist(names, schema):
- setUpWidget(view, name, field, initial.get(name),
- prefix=prefix, force=force, context=context)
+ setUpWidget(view, name, field, viewType,
+ value=initial.get(name, no_value),
+ prefix=prefix,
+ ignoreStickyValues=ignoreStickyValues,
+ context=context)
-
-def setUpEditWidgets(view, schema, content=None, prefix=None, force=False,
+def setUpEditWidgets(view, schema, prefix=None, ignoreStickyValues=False,
names=None, context=None):
- """Set up widgets for the fields defined by a schema
-
- Initial data is provided by content object attributes.
- No initial data is provided if the content lacks a named
- attribute, or if the named attribute value is None.
+ """Sets up widgets for an edit form.
+
+ See setUpWidgets for details on this method's arguments.
"""
- _setUpWidgets(view, schema, content, prefix, force,
- names, context, 'display', 'edit')
+ _setUpFormWidgets(view, schema, prefix, ignoreStickyValues, names,
+ context, IDisplayWidget, IEditWidget)
-def setUpDisplayWidgets(view, schema, content=None, prefix=None, force=False,
+def setUpDisplayWidgets(view, schema, prefix=None, ignoreStickyValues=False,
names=None, context=None):
- """Set up widgets for the fields defined by a schema
-
- Initial data is provided by content object attributes.
- No initial data is provided if the content lacks a named
- attribute, or if the named attribute value is None.
+ """Sets up widgets for a display (read-only) form.
+
+ See setUpWidgets for details on this method's arguments.
"""
- _setUpWidgets(view, schema, content, prefix, force,
- names, context, 'display', 'display')
-
-def _setUpWidgets(view, schema, content, prefix, force,
- names, context, displayname, editname):
- # Set up widgets for the fields defined by a schema.
- #
- # displayname is the name of the view used for a field that is
- # marked read-only; editname is the name of the view used for a
- # field that is editable.
- #
- # Initial data is provided by content object attributes.
- # No initial data is provided if the content lacks a named
- # attribute, or if the named attribute value is None.
- #
- if content is None:
- if context is None:
- content = view.context
- else:
- content = context
+ _setUpFormWidgets(view, schema, prefix, ignoreStickyValues, names,
+ context, IDisplayWidget, IDisplayWidget)
+def _setUpFormWidgets(view, schema, prefix, ignoreStickyValues, names,
+ context, displayType, editType):
+ """A helper function used by setUpDisplayWidget and setUpEditWidget."""
+ if context is None:
+ context = view.context
for name, field in _fieldlist(names, schema):
if field.readonly:
- vname = displayname
+ viewType = displayType
else:
- vname = editname
-
+ viewType = editType
try:
- value = field.get(content)
+ value = field.get(context)
except AttributeError, v:
- if v.__class__ != AttributeError:
- raise
- value = None
-
- setUpWidget(view, name, field, value,
- prefix=prefix, force=force, vname=vname, context=context)
+ value = no_value
+ setUpWidget(view, name, field, viewType, value, prefix,
+ ignoreStickyValues, context)
def viewHasInput(view, schema, names=None):
"""Check if we have any user-entered data defined by a schema.
@@ -240,181 +190,76 @@
the user.
"""
for name, field in _fieldlist(names, schema):
- if getattr(view, name+'_widget').hasInput():
+ if getattr(view, name + '_widget').hasInput():
return True
return False
-def applyWidgetsChanges(view, content, schema, strict=True,
- names=None, set_missing=True, do_not_raise=False,
- exclude_readonly=False):
- """Apply changes in widgets to the object.
+def applyWidgetsChanges(view, schema, names=None, context=None):
+ """Apply changes in widgets to the object."""
- XXX this needs to be thoroughly documented.
- """
errors = []
changed = False
+ if context is None:
+ context = view.context
+
for name, field in _fieldlist(names, schema):
- widget = getattr(view, name+'_widget')
- if exclude_readonly and field.readonly:
- continue
- if widget.hasInput():
+ widget = getattr(view, name + '_widget')
+ if IEditWidget.isImplementedBy(widget) and widget.hasInput():
try:
- changed = widget.applyChanges(content) or changed
+ changed = widget.applyChanges(context) or changed
except InputErrors, v:
errors.append(v)
-
- if errors and not do_not_raise:
+ if errors:
raise WidgetsError(*errors)
-
+
return changed
-def getWidgetsData(view, schema, strict=True, names=None, set_missing=True,
- do_not_raise=False, exclude_readonly=False):
- """Collect the user-entered data defined by a schema
-
- Data is collected from view widgets. For every field in the
- schema, we look for a view of the same name and get it's data.
-
- The data are returned in a mapping from field name to value.
-
- If the strict argument is true, then all of the data defined by
- the schema will be returned. If some required data are missing
- from the input, an error will be raised.
-
- If set_missing is true and the widget has no data, then the
- field's value is set to its missing value. Otherwise, a widget
- with no data is ignored. (However, if that field is required and
- strict is true, an error will be raised.)
-
- E.g., a typical text line widget should have a min_length of 1,
- and if it is required, it has got to have something in, otherwise
- WidgetsError is raised. If it's not required and it's empty, its
- value will be the appropriate missing value. Right now this is
- hardcoded as None, but it should be changed so the field can
- provide it as an empty string.
-
- do_not_raise is used if a call to getWidgetsData raises an exception,
- and you want to make use of the data that *is* available in your
- error-handler.
-
- Normally, readonly fields are included. To exclude readonly fields,
- provide a exclude_readonly keyword argument with a true value.
-
+def getWidgetsData(view, schema, names=None):
+ """Returnd user entered data for a set of schema fields.
+
+ The return value is a map of field names to user entered data.
+
+ view is the view containing the widgets. schema is the schema that
+ defines the widget fields. An optional names argument can be provided
+ to specify an alternate list of field values to return. If names is
+ not specified, or is None, getWidgetsData will attempt to return values
+ for all of the fields in the schema.
+
+ A requested field value may be omitted from the result for one of two
+ reasons:
+
+ - The field is read only, in which case its widget will not have
+ user input.
+
+ - The field is editable and not required but its widget does not
+ contain user input.
+
+ If a field is required and its widget does not have input, getWidgetsData
+ raises an error.
+
+ A widget may raise a validation error if it cannot return a value that
+ satisfies its field's contraints.
+
+ Errors, if any, are collected for all fields and reraised as a single
+ WidgetsError.
"""
-
result = {}
errors = []
-
+
for name, field in _fieldlist(names, schema):
- widget = getattr(view, name+'_widget')
- if exclude_readonly and widget.context.readonly:
- continue
- if widget.hasInput():
- try:
- result[name] = widget.getInputValue()
- except InputErrors, v:
- errors.append(v)
- elif strict and field.required:
- errors.append(MissingInputError(name, widget.title,
- 'the field is required')
- )
- elif set_missing:
- result[name] = field.missing_value
-
- if errors and not do_not_raise:
- raise WidgetsError(*errors)
-
- return result
-
-def getWidgetsDataForContent(view, schema, content=None, strict=True,
- names=None, set_missing=True):
- """Collect the user-entered data defined by a schema
-
- Data is collected from view widgets. For every field in the
- schema, we look for a view of the same name and get it's data.
-
- The data are assigned to the given content object.
-
- If the strict argument is true, then if some required data are
- missing from the input, an error will be raised.
-
- If set_missing is true and the widget has no data, then the
- field's value is set to its missing value. Otherwise, a widget
- with no data is ignored. (However, if that field is required and
- strict is true, an error will be raised.)
-
- If the strict argument is true, then all of the data defined by
- the schema will be set, at least for required fields. If some data
- for required fields are missing from the input, an error will be
- raised.
-
- """
- data = getWidgetsData(view, schema, strict, names)
-
- if content is None:
- content = view.context
-
- errors = []
-
- for name in data:
- try:
- field = schema[name]
- field.set(content, data[name])
- except ValidationError, v:
- errors.append(v)
-
+ widget = getattr(view, name + '_widget')
+ if IEditWidget.isImplementedBy(widget):
+ if widget.hasInput():
+ try:
+ result[name] = widget.getInputValue()
+ except InputErrors, v:
+ errors.append(v)
+ elif field.required:
+ errors.append(MissingInputError(
+ name, widget.title, 'the field is required'))
+
if errors:
raise WidgetsError(*errors)
-
-def getWidgetsDataFromAdapter(adapter, schema, strict=True,
- names=None, set_missing=True,
- do_not_raise=True):
- """Collect the user-entered data defined by a schema
-
- Data is collected from adapter properties. For every field in the
- schema, we look for a property of the same name and get it's data.
-
- The data are returned in a mapping from field name to value.
-
- If the strict argument is true, then all of the data defined by
- the schema will be returned. If some required data are missing
- from the input, an error will be raised.
-
- If set_missing is true and the widget has no data, then the
- field's value is set to its missing value. Otherwise, a widget
- with no data is ignored. (However, if that field is required and
- strict is true, an error will be raised.)
-
- E.g., a typical text line widget should have a min_length of 1,
- and if it is required, it has got to have something in, otherwise
- WidgetsError is raised. If it's not required and it's empty, its
- value will be the appropriate missing value. Right now this is
- hardcoded as None, but it should be changed so the field can
- provide it as an empty string.
-
- do_not_raise is used if a call to getWidgetsData raises an exception,
- and you want to make use of the data that *is* available in your
- error-handler.
- """
-
- result = {}
- errors = []
-
- for name, field in _fieldlist(names, schema):
- prop = getattr(adapter, name, None)
- if prop is not None:
- if callable(prop):
- value = prop()
- else:
- value = prop
- result[name] = value
- elif strict and field.required:
- errors.append(MissingInputError(name, name,
- 'the field is required'))
- elif set_missing:
- result[name] = field.missing_value
-
- if errors and not do_not_raise:
- raise WidgetsError(*errors)
-
+
return result
+
=== Zope3/src/zope/app/form/widget.py 1.11 => 1.11.6.1 ===
--- Zope3/src/zope/app/form/widget.py:1.11 Thu Feb 19 15:15:17 2004
+++ Zope3/src/zope/app/form/widget.py Thu Feb 26 00:10:55 2004
@@ -16,35 +16,52 @@
"""
import traceback
from warnings import warn
+from zope.app import zapi
from zope.app.interfaces.form import IWidget
from zope.component.interfaces import IViewFactory
from zope.interface import implements
from zope.app.services.servicenames import Translation
-from zope.component import getService
__metaclass__ = type
class Widget:
- """Mix-in class providing some functionality common accross view types
- """
+ """Mixin class providing functionality common accross view types."""
+
implements(IWidget)
_prefix = 'field.'
_data_marker = object()
_data = _data_marker
+
+ visible = True
def __init__(self, context, request):
self.context = context
self.request = request
self.name = self._prefix + context.__name__
-
- # See IWidget
- propertyNames = []
-
- def getValue(self, name):
- 'See IWidget'
- if name in self.propertyNames:
- return getattr(self, name, None)
+
+ title = property(lambda self: self._translate(
+ self.context.title))
+
+ description = property(lambda self: self._translate(
+ self.context.description))
+
+ def _translate(self, text):
+ ts = zapi.queryService(self.context, Translation)
+ if ts is not None:
+ # The domain is not that important here, since the title is most
+ # likely a message id carrying the domain.
+ return (ts.translate(text, "zope", context=self.request) or text)
+ else:
+ return text
+
+ def _renderedValueSet(self):
+ """Returns True if the the widget's rendered value has been set.
+
+ This is a convenience method that widgets can use to check whether
+ or not setRenderedValue was called.
+ """
+ return self._data is not self._data_marker
def setPrefix(self, prefix):
if not prefix.endswith("."):
@@ -52,52 +69,8 @@
self._prefix = prefix
self.name = prefix + self.context.__name__
- def setData(self, value):
- if traceback.extract_stack()[-2][2] != 'setRenderedValue':
- warn("setData is deprecated - use setRenderedValue",
- DeprecationWarning, 2)
-
- # XXX - move this implementation to setRenderedValue when
- # deprecation is removed
-
- self._data = value
-
def setRenderedValue(self, value):
- self.setData(value)
-
- def hasInput(self):
- raise TypeError("hasInput has not been implemented")
-
- def hasValidInput(self):
- raise TypeError("hasValidInput has not been implemented")
-
- def getInputValue(self):
- raise TypeError("getInputValue has not been implemented")
-
- def validate(self):
- raise TypeError("validate has not been implemented")
-
- def applyChanges(self, content):
- raise TypeError("applyChanges has not been implemented")
-
- def title(self):
- ts = getService(self.context, Translation)
- # Note that the domain is not that important here, since the title
- # is most likely a message id carrying the domain anyways.
- context_title = self.context.title
- return (ts.translate(context_title, "zope", context=self.request)
- or context_title)
- title = property(title)
-
- def description(self):
- ts = getService(self.context, Translation)
- # hopefully the description is a message id with domain (?).
- context_desc = self.context.description
- return (ts.translate(context_desc, "zope", context=self.request)
- or context_desc)
- description = property(description)
-
- required = property(lambda self: self.context.required)
+ self._data = value
class CustomWidgetFactory:
"""Custom Widget Factory.
@@ -118,3 +91,6 @@
for item in self.kw.items():
setattr(instance, item[0], item[1])
return instance
+
+# XXX backward compatability, will be removed in a month (2004-02-16)
+CustomWidget = CustomWidgetFactory
More information about the Zope3-Checkins
mailing list