[Checkins] SVN: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/ Move widget implementations to zope.formlib.
Martijn Faassen
faassen at startifact.com
Wed Dec 30 14:46:05 EST 2009
Log message for revision 107377:
Move widget implementations to zope.formlib.
Changed:
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/__init__.py
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/boolwidgets.py
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/itemswidgets.py
D zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.pt
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.py
D zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/orderedSelectionList.pt
D zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.pt
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.py
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_itemswidget.py
U zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/textwidgets.py
-=-
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/__init__.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/__init__.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/__init__.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -17,64 +17,67 @@
"""
__docformat__ = 'restructuredtext'
+# the implementation of widgets has moved to zope.formlib.widgets
+# import directly from there instead.
+
from zope.formlib.widget import BrowserWidget, DisplayWidget
from zope.formlib.widget import UnicodeDisplayWidget
-from zope.app.form.browser.textwidgets import TextWidget, BytesWidget
-from zope.app.form.browser.textwidgets import TextAreaWidget, BytesAreaWidget
-from zope.app.form.browser.textwidgets import PasswordWidget, FileWidget
-from zope.app.form.browser.textwidgets import ASCIIWidget, ASCIIAreaWidget
-from zope.app.form.browser.textwidgets import IntWidget, FloatWidget
-from zope.app.form.browser.textwidgets import DecimalWidget
-from zope.app.form.browser.textwidgets import DatetimeWidget, DateWidget
-from zope.app.form.browser.textwidgets import DatetimeI18nWidget
-from zope.app.form.browser.textwidgets import DateI18nWidget
-from zope.app.form.browser.textwidgets import DatetimeDisplayWidget
-from zope.app.form.browser.textwidgets import DateDisplayWidget
-from zope.app.form.browser.textwidgets import BytesDisplayWidget
-from zope.app.form.browser.textwidgets import ASCIIDisplayWidget
-from zope.app.form.browser.textwidgets import URIDisplayWidget
+from zope.formlib.widgets import TextWidget, BytesWidget
+from zope.formlib.widgets import TextAreaWidget, BytesAreaWidget
+from zope.formlib.widgets import PasswordWidget, FileWidget
+from zope.formlib.widgets import ASCIIWidget, ASCIIAreaWidget
+from zope.formlib.widgets import IntWidget, FloatWidget
+from zope.formlib.widgets import DecimalWidget
+from zope.formlib.widgets import DatetimeWidget, DateWidget
+from zope.formlib.widgets import DatetimeI18nWidget
+from zope.formlib.widgets import DateI18nWidget
+from zope.formlib.widgets import DatetimeDisplayWidget
+from zope.formlib.widgets import DateDisplayWidget
+from zope.formlib.widgets import BytesDisplayWidget
+from zope.formlib.widgets import ASCIIDisplayWidget
+from zope.formlib.widgets import URIDisplayWidget
# Widgets for boolean fields
-from zope.app.form.browser.boolwidgets import CheckBoxWidget
-from zope.app.form.browser.boolwidgets import BooleanRadioWidget
-from zope.app.form.browser.boolwidgets import BooleanSelectWidget
-from zope.app.form.browser.boolwidgets import BooleanDropdownWidget
+from zope.formlib.widgets import CheckBoxWidget
+from zope.formlib.widgets import BooleanRadioWidget
+from zope.formlib.widgets import BooleanSelectWidget
+from zope.formlib.widgets import BooleanDropdownWidget
# Choice and Sequence Display Widgets
-from zope.app.form.browser.itemswidgets import ItemDisplayWidget
-from zope.app.form.browser.itemswidgets import ItemsMultiDisplayWidget
-from zope.app.form.browser.itemswidgets import SetDisplayWidget
-from zope.app.form.browser.itemswidgets import ListDisplayWidget
+from zope.formlib.widgets import ItemDisplayWidget
+from zope.formlib.widgets import ItemsMultiDisplayWidget
+from zope.formlib.widgets import SetDisplayWidget
+from zope.formlib.widgets import ListDisplayWidget
# Widgets for fields with vocabularies.
# Note that these are only dispatchers for the widgets below.
-from zope.app.form.browser.itemswidgets import ChoiceDisplayWidget
-from zope.app.form.browser.itemswidgets import ChoiceInputWidget
-from zope.app.form.browser.itemswidgets import CollectionDisplayWidget
-from zope.app.form.browser.itemswidgets import CollectionInputWidget
-from zope.app.form.browser.itemswidgets import ChoiceCollectionDisplayWidget
-from zope.app.form.browser.itemswidgets import ChoiceCollectionInputWidget
+from zope.formlib.widgets import ChoiceDisplayWidget
+from zope.formlib.widgets import ChoiceInputWidget
+from zope.formlib.widgets import CollectionDisplayWidget
+from zope.formlib.widgets import CollectionInputWidget
+from zope.formlib.widgets import ChoiceCollectionDisplayWidget
+from zope.formlib.widgets import ChoiceCollectionInputWidget
# Widgets that let you choose a single item from a list
# These widgets are multi-views on (field, vocabulary)
-from zope.app.form.browser.itemswidgets import SelectWidget
-from zope.app.form.browser.itemswidgets import DropdownWidget
-from zope.app.form.browser.itemswidgets import RadioWidget
+from zope.formlib.widgets import SelectWidget
+from zope.formlib.widgets import DropdownWidget
+from zope.formlib.widgets import RadioWidget
# Widgets that let you choose several items from a list
# These widgets are multi-views on (field, vocabulary)
-from zope.app.form.browser.itemswidgets import MultiSelectWidget
-from zope.app.form.browser.itemswidgets import MultiSelectSetWidget
-from zope.app.form.browser.itemswidgets import MultiSelectFrozenSetWidget
-from zope.app.form.browser.itemswidgets import MultiCheckBoxWidget
-from zope.app.form.browser.itemswidgets import OrderedMultiSelectWidget
+from zope.formlib.widgets import MultiSelectWidget
+from zope.formlib.widgets import MultiSelectSetWidget
+from zope.formlib.widgets import MultiSelectFrozenSetWidget
+from zope.formlib.widgets import MultiCheckBoxWidget
+from zope.formlib.widgets import OrderedMultiSelectWidget
# Widgets that let you enter several items in a sequence
# These widgets are multi-views on (sequence type, value type)
-from zope.app.form.browser.sequencewidget import SequenceWidget
-from zope.app.form.browser.sequencewidget import TupleSequenceWidget
-from zope.app.form.browser.sequencewidget import ListSequenceWidget
-from zope.app.form.browser.sequencewidget import SequenceDisplayWidget
+from zope.formlib.widgets import SequenceWidget
+from zope.formlib.widgets import TupleSequenceWidget
+from zope.formlib.widgets import ListSequenceWidget
+from zope.formlib.widgets import SequenceDisplayWidget
-from zope.app.form.browser.objectwidget import ObjectWidget
+from zope.formlib.widgets import ObjectWidget
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/boolwidgets.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/boolwidgets.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/boolwidgets.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -15,121 +15,12 @@
$Id$
"""
-__docformat__ = 'restructuredtext'
+# implementation moved to zope.formlib.boolwidgets
-from zope.interface import implements
-from zope.schema.vocabulary import SimpleVocabulary
-
-from zope.formlib.widget import SimpleInputWidget, renderElement
-from zope.formlib.widget import DisplayWidget
-from zope.app.form.browser.i18n import _
-from zope.app.form.browser.itemswidgets import RadioWidget
-from zope.app.form.browser.itemswidgets import SelectWidget, DropdownWidget
-from zope.formlib.interfaces import IInputWidget
-
-
-class CheckBoxWidget(SimpleInputWidget):
- """A checkbox widget used to display Bool fields.
+from zope.formlib.boolwidgets import (
+ CheckBoxWidget,
+ BooleanRadioWidget,
+ BooleanSelectWidget,
+ BooleanDropdownWidget,
+ BooleanDisplayWidget)
- For more detailed documentation, including sample code, see
- ``tests/test_checkboxwidget.py``.
- """
- type = 'checkbox'
- default = 0
- extra = ''
-
- def __init__(self, context, request):
- super(CheckBoxWidget, self).__init__(context, request)
- self.required = False
-
- def __call__(self):
- """Render the widget to HTML."""
- value = self._getFormValue()
- if value == 'on':
- kw = {'checked': 'checked'}
- else:
- kw = {}
- return "%s %s" % (
- renderElement(self.tag,
- type='hidden',
- name=self.name+".used",
- id=self.name+".used",
- value=""
- ),
- renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- extra=self.extra,
- value="on",
- **kw),
- )
-
- def _toFieldValue(self, input):
- """Convert from HTML presentation to Python bool."""
- return input == 'on'
-
- def _toFormValue(self, value):
- """Convert from Python bool to HTML representation."""
- return value and 'on' or ''
-
- def hasInput(self):
- """Check whether the field is represented in the form."""
- return self.name + ".used" in self.request.form or \
- super(CheckBoxWidget, self).hasInput()
-
- def _getFormInput(self):
- """Returns the form input used by `_toFieldValue`.
-
- Return values:
-
- ``'on'`` checkbox is checked
- ``''`` checkbox is not checked
- ``None`` form input was not provided
-
- """
- if self.request.get(self.name) == 'on':
- return 'on'
- elif self.name + '.used' in self.request:
- return ''
- else:
- return None
-
-
-def BooleanRadioWidget(field, request, true=_('on'), false=_('off')):
- vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
- widget = RadioWidget(field, vocabulary, request)
- widget.required = False
- return widget
-
-
-def BooleanSelectWidget(field, request, true=_('on'), false=_('off')):
- vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
- widget = SelectWidget(field, vocabulary, request)
- widget.size = 2
- widget.required = False
- return widget
-
-
-def BooleanDropdownWidget(field, request, true=_('on'), false=_('off')):
- vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
- widget = DropdownWidget(field, vocabulary, request)
- widget.required = False
- return widget
-
-
-_msg_true = _("True")
-_msg_false = _("False")
-
-class BooleanDisplayWidget(DisplayWidget):
-
- def __call__(self):
- if self._renderedValueSet():
- value = self._data
- else:
- value = self.context.default
- if value:
- return _msg_true
- else:
- return _msg_false
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/itemswidgets.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/itemswidgets.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/itemswidgets.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -16,619 +16,33 @@
$Id$
"""
__docformat__ = 'restructuredtext'
-from xml.sax.saxutils import escape
-from zope import component
-from zope.interface import implements
-from zope.i18n import translate
-from zope.schema.interfaces import ValidationError, InvalidValue
-from zope.schema.interfaces import ConstraintNotSatisfied, ITitledTokenizedTerm
+# the implementation has moved to zope.formlib.itemswidgets
-from zope.formlib.widget import SimpleInputWidget, renderElement
-from zope.formlib.interfaces import IInputWidget, IDisplayWidget
-from zope.formlib.interfaces import ConversionError
-from zope.app.form.browser.i18n import _
-from zope.browserpage import ViewPageTemplateFile
-
-
-# For choices, we want to make the widget a view of the field and vocabulary.
-
-def ChoiceDisplayWidget(field, request):
- return component.getMultiAdapter((field, field.vocabulary, request),
- IDisplayWidget)
-
-def ChoiceInputWidget(field, request):
- return component.getMultiAdapter((field, field.vocabulary, request),
- IInputWidget)
-
-# for collections, we want to make the widget a view of the field and the
-# value_type. If the value_type is None we may fall over. We may
-# not be able to do any better than that.
-
-def CollectionDisplayWidget(field, request):
- return component.getMultiAdapter((field, field.value_type, request),
- IDisplayWidget)
-
-def CollectionInputWidget(field, request):
- return component.getMultiAdapter((field, field.value_type, request),
- IInputWidget)
-
-# for collections of choices, we want to make the widget a view of the field,
-# the value type, and the vocabulary.
-
-def ChoiceCollectionDisplayWidget(field, value_type, request):
- return component.getMultiAdapter((field, value_type.vocabulary, request),
- IDisplayWidget)
-
-def ChoiceCollectionInputWidget(field, value_type, request):
- return component.getMultiAdapter((field, value_type.vocabulary, request),
- IInputWidget)
-
-class TranslationHook(object):
- """A mixin class that provides the translation capabilities."""
-
- def translate(self, msgid):
- return translate(msgid, context=self.request, default=msgid)
-
-class ItemsWidgetBase(TranslationHook, SimpleInputWidget):
- """Convenience base class for widgets displaying items/choices."""
-
- extra = ""
-
- def __init__(self, field, vocabulary, request):
- """Initialize the widget."""
- # only allow this to happen for a bound field
- assert field.context is not None
- self.vocabulary = vocabulary
- super(ItemsWidgetBase, self).__init__(field, request)
- self.empty_marker_name = self.name + "-empty-marker"
-
- def setPrefix(self, prefix):
- """Set the prefixes for the field names of the form."""
- super(ItemsWidgetBase, self).setPrefix(prefix)
- # names for other information from the form
- self.empty_marker_name = self.name + "-empty-marker"
-
- def __call__(self):
- """Render the widget to HTML."""
- raise NotImplementedError(
- "__call__() must be implemented by a subclass; use _getFormValue()"
- )
-
- def textForValue(self, term):
- """Extract a string from the `term`.
-
- The `term` must be a vocabulary tokenized term.
-
- This can be overridden to support more complex `term`
- objects. The token is returned here since it's the only thing
- known to be a string, or str()able.
-
- """
- titled = ITitledTokenizedTerm(term, None)
- if titled is None:
- return term.token
- return self.translate(titled.title)
-
- def convertTokensToValues(self, tokens):
- """Convert term tokens to the terms themselves.
-
- Tokens are used in the HTML form to represent terms. This method takes
- the form tokens and converts them back to terms.
- """
- values = []
- for token in tokens:
- try:
- term = self.vocabulary.getTermByToken(token)
- except LookupError, error:
- raise InvalidValue("token %r not found in vocabulary" % token)
- else:
- values.append(term.value)
- return values
-
- def _emptyMarker(self):
- """Mark the form so that empty selections are also valid."""
- return '<input name="%s" type="hidden" value="1" />' % (
- self.empty_marker_name)
-
- def hasInput(self):
- """Check whether we have any input."""
- return (self.name in self.request.form or
- self.empty_marker_name in self.request.form)
-
- def _toFieldValue(self, input):
- """See `SimpleInputWidget`"""
- raise NotImplementedError(
- "_toFieldValue(input) must be implemented by a subclass\n"
- "It may be inherited from the mix-in classes SingleDataHelper\n"
- "or MultiDataHelper")
-
-
-class SingleDataHelper(object):
- """Mix-in helper class for getting the term from the HTML form.
-
- This is used when we expect a single input, i.e. the Choice field.
- """
-
- def _toFieldValue(self, input):
- if input:
- try:
- return self.convertTokensToValues([input])[0]
- except InvalidValue, e:
- raise ConversionError(_("Invalid value"), e)
- else:
- return self.context.missing_value
-
- def hidden(self):
- #XXX: _getFormValue() should return a string value that can be
- # used in a HTML form, but it doesn't. When
- # http://www.zope.org/Collectors/Zope3-dev/584 gets fixed
- # this hack should be reverted.
- # -- Bjorn Tillenius, 2006-04-12
- value = self._getFormValue()
- if value == self._missing:
- form_value = ''
- else:
- form_value = self.vocabulary.getTerm(value).token
- return renderElement(u'input',
- type='hidden',
- name=self.name,
- id=self.name,
- value=form_value,
- cssClass=self.cssClass,
- extra=self.extra)
-
-
-class MultiDataHelper(object):
- """Mix-in helper class for getting the term from the HTML form.
-
- This is used when we expect a multiple inputs, i.e. Sequence fields with a
- Choice field as value_type.
- """
-
- def _toFieldValue(self, input):
- """See SimpleInputWidget"""
- if input is None:
- input = []
- elif not isinstance(input, list):
- input = [input]
- try:
- values = self.convertTokensToValues(input)
- except InvalidValue, e:
- raise ConversionError(_("Invalid value"), e)
-
- # All AbstractCollection fields have a `_type` attribute specifying
- # the type of collection. Use it to generate the correct type,
- # otherwise return a list. TODO: this breaks encapsulation.
- if hasattr(self.context, '_type'):
- _type = self.context._type
- if isinstance(_type, tuple):
- _type = _type[0]
- return _type(values)
- else:
- return values
-
-
- def _getDefault(self):
- # Return the default value for this widget;
- # may be overridden by subclasses.
- val = self.context.default
- if val is None:
- val = []
- return val
-
-
-## Display-Widgets for Items-related fields.
-
-class ItemDisplayWidget(SingleDataHelper, ItemsWidgetBase):
- """Simple single-selection display that can be used in many cases."""
-
- def __init__(self, *args, **kw):
- ItemsWidgetBase.__init__(self, *args, **kw)
- self.required = False
-
- _messageNoValue = _("item-missing-single-value-for-display", "")
-
- def __call__(self):
- """See IBrowserWidget."""
- value = self._getFormValue()
- if not value:
- return self.translate(self._messageNoValue)
- else:
- term = self.vocabulary.getTerm(value)
- return self.textForValue(term)
-
-
-class ItemsMultiDisplayWidget(MultiDataHelper, ItemsWidgetBase):
- """Displays a sequence of items."""
-
- def __init__(self, *args, **kw):
- ItemsWidgetBase.__init__(self, *args, **kw)
- self.required = False
-
- _messageNoValue = _("vocabulary-missing-multiple-value-for-display", "")
-
- itemTag = 'li'
- tag = 'ol'
-
- def __call__(self):
- """See IBrowserWidget."""
- value = self._getFormValue()
- if value:
- rendered_items = self.renderItems(value)
- return renderElement(self.tag,
- id=self.name,
- cssClass=self.cssClass,
- contents="\n".join(rendered_items),
- extra=self.extra)
- else:
- return self.translate(self._messageNoValue)
-
- def renderItems(self, value):
- """Render items of sequence."""
- items = []
- cssClass = self.cssClass or ''
- if cssClass:
- cssClass += "-item"
- tag = self.itemTag
- for item in value:
- term = self.vocabulary.getTerm(item)
- items.append(renderElement(
- tag,
- cssClass=cssClass,
- contents=escape(self.textForValue(term))))
- return items
-
-class ListDisplayWidget(ItemsMultiDisplayWidget):
- """Display widget for ordered multi-selection fields.
-
- This can be used for both Sequence, List, and Tuple fields.
- """
- tag = 'ol'
-
-class SetDisplayWidget(ItemsMultiDisplayWidget):
- """Display widget for unordered multi-selection fields.
-
- This can be used for both Set field.
- """
- tag = 'ul'
-
-
-## Edit-Widgets for Items-related fields.
-
-# BBB Set to False to never display an item for the missing value if the field
-# is required, which was the behaviour of versions up to and including 3.5.0.
-EXPLICIT_EMPTY_SELECTION = True
-
-
-class ItemsEditWidgetBase(SingleDataHelper, ItemsWidgetBase):
- """Widget Base for rendering item-related fields.
-
- These widgets work with Choice fields and Sequence fields that have Choice
- as value_type.
- """
- implements(IInputWidget)
-
- size = 5
- tag = 'select'
-
- _displayItemForMissingValue = True
-
- # Whether an empty selection should always be made explicit, i.e. even
- # if the field is required.
- explicit_empty_selection = False
-
- def __init__(self, field, vocabulary, request):
- """Initialize the widget."""
- super(ItemsEditWidgetBase, self).__init__(field, vocabulary, request)
-
- def setPrefix(self, prefix):
- """Set the prefix of the input name.
-
- Once we set the prefix of input field, we use the name of the input
- field and the postfix '-query' for the associated query view.
- """
- super(ItemsEditWidgetBase, self).setPrefix(prefix)
-
-
- def __call__(self):
- """See IBrowserWidget."""
- value = self._getFormValue()
- contents = []
- have_results = False
-
- contents.append(self._div('value', self.renderValue(value)))
- contents.append(self._emptyMarker())
-
- return self._div(self.cssClass, "\n".join(contents))
-
-
- def _div(self, cssClass, contents, **kw):
- """Render a simple div tag."""
- if contents:
- return renderElement('div',
- cssClass=cssClass,
- contents="\n%s\n" % contents,
- **kw)
- return ""
-
-
- def renderItemsWithValues(self, values):
- """Render the list of possible values, with those found in
- `values` being marked as selected."""
-
- cssClass = self.cssClass
-
- # multiple items with the same value are not allowed from a
- # vocabulary, so that need not be considered here
- rendered_items = []
- count = 0
-
- # Handle case of missing value
- missing = self._toFormValue(self.context.missing_value)
-
- if self._displayItemForMissingValue and (
- not self.context.required or
- EXPLICIT_EMPTY_SELECTION and
- self.explicit_empty_selection and
- missing in values and
- self.context.default is None):
-
- if missing in values:
- render = self.renderSelectedItem
- else:
- render = self.renderItem
-
- missing_item = render(count,
- self.translate(self._messageNoValue),
- missing,
- self.name,
- cssClass)
- rendered_items.append(missing_item)
- count += 1
-
- # Render normal values
- for term in self.vocabulary:
- item_text = self.textForValue(term)
-
- if term.value in values:
- render = self.renderSelectedItem
- else:
- render = self.renderItem
-
- rendered_item = render(count,
- item_text,
- term.token,
- self.name,
- cssClass)
-
- rendered_items.append(rendered_item)
- count += 1
-
- return rendered_items
-
- def renderItem(self, index, text, value, name, cssClass):
- """Render an item for a particular `value`."""
- return renderElement('option',
- contents=escape(text),
- value=value,
- cssClass=cssClass)
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- """Render an item for a particular `value` that is selected."""
- return renderElement('option',
- contents=escape(text),
- value=value,
- cssClass=cssClass,
- selected='selected')
-
-
-class SelectWidget(ItemsEditWidgetBase):
- """Provide a selection list for the item."""
-
- _messageNoValue = _("vocabulary-missing-single-value-for-edit",
- "(no value)")
-
- def renderValue(self, value):
- rendered_items = self.renderItems(value)
- contents = "\n%s\n" %"\n".join(rendered_items)
- return renderElement('select',
- name=self.name,
- id=self.name,
- contents=contents,
- size=self.size,
- extra=self.extra)
-
- def renderItems(self, value):
- return self.renderItemsWithValues([value])
-
-
-class DropdownWidget(SelectWidget):
- """Variation of the SelectWidget that uses a drop-down list."""
- size = 1
- explicit_empty_selection = True
-
-
-class RadioWidget(SelectWidget):
- """Radio widget for single item choices.
-
- This widget can be used when the number of selections is going
- to be small.
- """
- orientation = "vertical"
-
- _messageNoValue = _("vocabulary-missing-single-value-for-edit",
- "(no value)")
-
- def renderItem(self, index, text, value, name, cssClass):
- """Render an item of the list."""
- return self._renderItem(index, text, value, name, cssClass)
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- """Render a selected item of the list."""
- return self._renderItem(index, text, value, name, cssClass,
- checked=True)
-
- def _renderItem(self, index, text, value, name, cssClass, checked=False):
- kw = {}
- if checked:
- kw['checked'] = 'checked'
- id = '%s.%s' % (name, index)
- elem = renderElement(u'input',
- value=value,
- name=name,
- id=id,
- cssClass=cssClass,
- type='radio',
- **kw)
- return renderElement(u'label',
- contents='%s %s' % (elem, text),
- **{'for': id})
-
- def renderValue(self, value):
- rendered_items = self.renderItems(value)
- if self.orientation == 'horizontal':
- return " ".join(rendered_items)
- else:
- return "<br />".join(rendered_items)
-
-
-class ItemsMultiEditWidgetBase(MultiDataHelper, ItemsEditWidgetBase):
- """Items widget supporting multiple selections."""
-
- _messageNoValue = _("vocabulary-missing-multiple-value-for-edit",
- "(no values)")
- _displayItemForMissingValue = False
-
- def renderItems(self, value):
- if value == self.context.missing_value:
- values = []
- else:
- values = list(value)
- return self.renderItemsWithValues(values)
-
- def renderValue(self, value):
- # All we really add here is the ':list' in the name argument
- # and mutliple='multiple' to renderElement().
- rendered_items = self.renderItems(value)
- return renderElement(self.tag,
- name=self.name + ':list',
- id=self.name,
- multiple='multiple',
- size=self.size,
- contents="\n".join(rendered_items),
- extra=self.extra)
-
- def hidden(self):
- items = []
- for item in self._getFormValue():
- items.append(
- renderElement(u'input',
- type='hidden',
- name=self.name+':list',
- id=self.name,
- value=self.vocabulary.getTerm(item).token,
- cssClass=self.cssClass,
- extra=self.extra))
- return '\n'.join(items)
-
-
-class MultiSelectWidget(ItemsMultiEditWidgetBase):
- """Provide a selection list for the list to be selected."""
-
-
-class MultiSelectSetWidget(MultiSelectWidget):
- """Provide a selection list for the set to be selected."""
-
- def _toFieldValue(self, input):
- value = super(MultiSelectSetWidget, self)._toFieldValue(input)
- if isinstance(value, list):
- value = set(value)
- return value
-
-
-class MultiSelectFrozenSetWidget(MultiSelectWidget):
- """Provide a selection list for the set to be selected."""
-
- def _toFieldValue(self, input):
- value = super(MultiSelectFrozenSetWidget, self)._toFieldValue(input)
- if isinstance(value, list):
- value = frozenset(value)
- return value
-
-class OrderedMultiSelectWidget(ItemsMultiEditWidgetBase):
- """A multi-selection widget with ordering support."""
-
- template = ViewPageTemplateFile('orderedSelectionList.pt')
-
- def choices(self):
- """Return a set of tuples (text, value) that are available."""
- # Not all content objects must necessarily support the attributes
- if hasattr(self.context.context, self.context.__name__):
- available_values = self.context.get(self.context.context)
- else:
- available_values = []
- return [{'text': self.textForValue(term), 'value': term.token}
- for term in self.vocabulary
- if term.value not in available_values]
-
- def selected(self):
- """Return a list of tuples (text, value) that are selected."""
- # Get form values
- values = self._getFormValue()
- # Not all content objects must necessarily support the attributes
- if hasattr(self.context.context, self.context.__name__):
- # merge in values from content
- for value in self.context.get(self.context.context):
- if value not in values:
- values.append(value)
-
- terms = [self.vocabulary.getTerm(value)
- for value in values]
- return [{'text': self.textForValue(term), 'value': term.token}
- for term in terms]
-
- def __call__(self):
- return self.template()
-
-
-class MultiCheckBoxWidget(ItemsMultiEditWidgetBase):
- """Provide a list of checkboxes that provide the choice for the list."""
-
- orientation = "vertical"
-
- _joinButtonToMessageTemplate = u"%s %s"
-
- def renderValue(self, value):
- rendered_items = self.renderItems(value)
- if self.orientation == 'horizontal':
- return " ".join(rendered_items)
- else:
- return "<br />".join(rendered_items)
-
- def renderItem(self, index, text, value, name, cssClass):
- """Render an item of the list."""
- return self._renderItem(index, text, value, name, cssClass)
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- """Render a selected item of the list."""
- return self._renderItem(index, text, value, name, cssClass,
- checked=True)
-
- def _renderItem(self, index, text, value, name, cssClass, checked=False):
- kw = {}
- if checked:
- kw['checked'] = 'checked'
- id = '%s.%s' % (name, index)
- elem = renderElement('input',
- type="checkbox",
- cssClass=cssClass,
- name=name,
- id=id,
- value=value,
- **kw)
- contents = self._joinButtonToMessageTemplate % (elem, text)
- return renderElement(u'label',
- contents=contents,
- **{'for': id})
-
+from zope.formlib.itemswidgets import (
+ ChoiceDisplayWidget,
+ ChoiceInputWidget,
+ CollectionDisplayWidget,
+ CollectionInputWidget,
+ ChoiceCollectionDisplayWidget,
+ ChoiceCollectionInputWidget,
+ TranslationHook,
+ ItemsWidgetBase,
+ SingleDataHelper,
+ MultiDataHelper,
+ ItemsWidgetBase,
+ ItemDisplayWidget,
+ ItemsMultiDisplayWidget,
+ ListDisplayWidget,
+ SetDisplayWidget,
+ ItemsEditWidgetBase,
+ EXPLICIT_EMPTY_SELECTION,
+ SelectWidget,
+ DropdownWidget,
+ RadioWidget,
+ ItemsMultiEditWidgetBase,
+ MultiSelectWidget,
+ MultiSelectSetWidget,
+ MultiSelectFrozenSetWidget,
+ OrderedMultiSelectWidget,
+ MultiCheckBoxWidget)
Deleted: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.pt
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.pt 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.pt 2009-12-30 19:46:00 UTC (rev 107377)
@@ -1,7 +0,0 @@
-<fieldset>
- <legend tal:content="context/legendTitle"
- i18n:translate="">The Legend</legend>
- <div class="row" tal:repeat="widget context/subwidgets">
- <metal:block use-macro="context/@@form_macros/widget_row" />
- </div>
-</fieldset>
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -15,179 +15,8 @@
$Id$
"""
-__docformat__ = 'restructuredtext'
+# implementation moved to zope.formlib.objectwidget
-from zope import component
-from zope.interface import implements
-from zope.schema import getFieldNamesInOrder
-
-from zope.formlib.interfaces import IInputWidget
-from zope.formlib.widget import InputWidget
-from zope.formlib.widget import BrowserWidget
-from zope.app.form.utility import setUpWidgets, applyWidgetsChanges
-from zope.browserpage import ViewPageTemplateFile
-from zope.app.form.browser.interfaces import IWidgetInputErrorView
-
-
-class ObjectWidgetView:
-
- template = ViewPageTemplateFile('objectwidget.pt')
-
- def __init__(self, context, request):
- self.context = context
- self.request = request
-
- def __call__(self):
- return self.template()
-
-
-class ObjectWidget(BrowserWidget, InputWidget):
- """A widget over an Interface that contains Fields.
-
- ``factory``
-
- factory used to create content that this widget (field) represents
-
- ``*_widget``
-
- Optional CustomWidgets used to generate widgets for the fields in this
- widget
- """
-
- implements(IInputWidget)
-
- _object = None # the object value (from setRenderedValue & request)
- _request_parsed = False
-
- def __init__(self, context, request, factory, **kw):
- super(ObjectWidget, self).__init__(context, request)
-
- # define view that renders the widget
- self.view = ObjectWidgetView(self, request)
-
- # factory used to create content that this widget (field)
- # represents
- self.factory = factory
-
- # 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
- setUpWidgets(self, self.context.schema, IInputWidget,
- 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 object as hidden fields."""
- result = []
- for name in self.names:
- result.append(self.getSubwidget(name).hidden())
- return "".join(result)
-
- def error(self):
- if self._error:
- errormessages = []
- keys = self._error.keys(); keys.sort()
- for key in keys:
- errormessages.append(str(key) + ': ')
- errormessages.append(component.getMultiAdapter(
- (self._error[key], self.request),
- IWidgetInputErrorView).snippet())
- errormessages.append(str(key) + ', ')
- return ''.join(errormessages[0:-1])
- return ""
-
- 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).
- """
-
- errors = []
- content = self.factory()
- for name in self.names:
- try:
- setattr(content, name, self.getSubWidget(name).getInputValue())
- except Exception, e:
- errors.append(e)
- if self._error is None:
- self._error = {}
-
- if name not in self._error:
- self._error[name] = e
-
- if errors:
- raise errors[0]
-
- 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)
- # TODO: If value implements ILocation, set name to field name and
- # parent to content
-
- 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))
+from zope.formlib.objectwidget import (
+ ObjectWidgetView,
+ ObjectWidget)
Deleted: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/orderedSelectionList.pt
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/orderedSelectionList.pt 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/orderedSelectionList.pt 2009-12-30 19:46:00 UTC (rev 107377)
@@ -1,198 +0,0 @@
-<script type="text/javascript">
-
-function moveItems(from, to)
- {
- // shortcuts for selection fields
- var src = document.getElementById(from);
- var tgt = document.getElementById(to);
-
- if (src.selectedIndex == -1) selectionError();
- else
- {
- // iterate over all selected items
- // --> attribute "selectedIndex" doesn't support multiple selection.
- // Anyway, it works here, as a moved item isn't selected anymore,
- // thus "selectedIndex" indicating the "next" selected item :)
- while (src.selectedIndex > -1)
- if (src.options[src.selectedIndex].selected)
- {
- // create a new virtal object with values of item to copy
- temp = new Option(src.options[src.selectedIndex].text,
- src.options[src.selectedIndex].value);
- // append virtual object to targe
- tgt.options[tgt.length] = temp;
- // want to select newly created item
- temp.selected = true;
- // delete moved item in source
- src.options[src.selectedIndex] = null;
- }
- }
- }
-
-// move item from "from" selection to "to" selection
-function from2to(name)
- {
- moveItems(name+".from", name+".to");
- copyDataForSubmit(name);
- }
-
-// move item from "to" selection back to "from" selection
-function to2from(name)
- {
- moveItems(name+".to", name+".from");
- copyDataForSubmit(name);
- }
-
-function swapFields(a, b)
- {
- // swap text
- var temp = a.text;
- a.text = b.text;
- b.text = temp;
- // swap value
- temp = a.value;
- a.value = b.value;
- b.value = temp;
- // swap selection
- temp = a.selected;
- a.selected = b.selected;
- b.selected = temp;
- }
-
-// move selected item in "to" selection one up
-function moveUp(name)
- {
- // shortcuts for selection field
- var toSel = document.getElementById(name+".to");
-
- if (toSel.selectedIndex == -1)
- selectionError();
- else if (toSel.options[0].selected)
- alert("Cannot move further up!");
- else for (var i = 0; toSel.length > i; i++)
- if (toSel.options[i].selected)
- {
- swapFields(toSel.options[i-1], toSel.options[i]);
- copyDataForSubmit(name);
- }
- }
-
-// move selected item in "to" selection one down
-function moveDown(name)
- {
- // shortcuts for selection field
- var toSel = document.getElementById(name+".to");
-
- if (toSel.selectedIndex == -1) {
- selectionError();
- } else if (toSel.options[toSel.length-1].selected) {
- alert("Cannot move further down!");
- } else {
- for (var i = toSel.length-1; i >= 0; i--) {
- if (toSel.options[i].selected) {
- swapFields(toSel.options[i+1], toSel.options[i]);
- }
- }
- copyDataForSubmit(name);
- }
- }
-
-// copy each item of "toSel" into one hidden input field
-function copyDataForSubmit(name)
- {
- // shortcuts for selection field and hidden data field
- var toSel = document.getElementById(name+".to");
- var toDataContainer = document.getElementById(name+".toDataContainer");
-
- // delete all child nodes (--> complete content) of "toDataContainer" span
- while (toDataContainer.hasChildNodes())
- toDataContainer.removeChild(toDataContainer.firstChild);
-
- // create new hidden input fields - one for each selection item of
- // "to" selection
- for (var i = 0; toSel.options.length > i; i++)
- {
- // create virtual node with suitable attributes
- var newNode = document.createElement("input");
- var newAttr = document.createAttribute("name");
- newAttr.nodeValue = name;
- newNode.setAttributeNode(newAttr);
-
- newAttr = document.createAttribute("type");
- newAttr.nodeValue = "hidden";
- newNode.setAttributeNode(newAttr);
-
- newAttr = document.createAttribute("value");
- newAttr.nodeValue = toSel.options[i].value;
- newNode.setAttributeNode(newAttr);
-
- // actually append virtual node to DOM tree
- toDataContainer.appendChild(newNode);
- }
- }
-
-// error message for missing selection
-function selectionError()
- {alert("Must select something!")}
-
-</script>
-
-<table border="0" class="ordered-selection-field">
- <tr>
- <td>
- <select id="from" name="from" size="5" multiple=""
- tal:attributes="name string:${view/name}.from;
- id string:${view/name}.from;
- size view/size"
- >
- <option tal:repeat="entry view/choices"
- tal:attributes="value entry/value"
- tal:content="entry/text" i18n:translate=""/>
- </select>
- </td>
- <td>
- <button name="from2toButton" type="button" value=" ->"
- onclick="javascript:from2to()"
- tal:attributes="onClick string:javascript:from2to('${view/name}')"
- > -></button>
- <br />
- <button name="to2fromButton" type="button" value="<- "
- onclick="javascript:to2from()"
- tal:attributes="onClick string:javascript:to2from('${view/name}')"
- ><- </button>
- </td>
- <td>
- <select id="to" name="to" size="5" multiple=""
- tal:attributes="name string:${view/name}.to;
- id string:${view/name}.to;
- size view/size">
- <option tal:repeat="entry view/selected"
- tal:attributes="value entry/value"
- tal:content="entry/text" i18n:translate=""/>
- </select>
- <input name="foo-empty-marker" type="hidden"
- tal:attributes="name string:${view/name}-empty-marker"/>
- <span id="toDataContainer"
- tal:attributes="id string:${view/name}.toDataContainer">
- <script type="text/javascript" tal:content="string:
- copyDataForSubmit('${view/name}');">
- // initial copying of field "field.to" --> "field"
- copyDataForSubmit("<i tal:replace="${view/name}"/>");
- </script>
- </span>
- </td>
- <td>
- <button
- name="upButton" type="button" value="^"
- onclick="javascript:moveUp()"
- tal:attributes="onClick string:javascript:moveUp('${view/name}')"
- >^</button>
- <br />
- <button
- name="downButton" type="button" value="v"
- onclick="javascript:moveDown()"
- tal:attributes="onClick string:javascript:moveDown('${view/name}')"
- >v</button>
- </td>
- </tr>
-</table>
Deleted: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.pt
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.pt 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.pt 2009-12-30 19:46:00 UTC (rev 107377)
@@ -1,29 +0,0 @@
-<table border="0" class="sequencewidget"
- i18n:domain="zope">
- <tr tal:repeat="widget view/widgets">
- <td>
- <input class="editcheck" type="checkbox"
- tal:attributes="
- name string:${view/name}.remove_${repeat/widget/index}"
- tal:condition="view/need_delete" />
- </td>
- <td>
- <span tal:define="error widget/error"
- tal:replace="structure error" tal:condition="error" />
- <input tal:replace="structure widget" />
- </td>
- </tr>
- <tr>
- <td colspan="2">
- <input type="submit" value="Remove selected items"
- tal:condition="view/need_delete"
- tal:attributes="name string:${view/name}.remove"
- i18n:attributes="value remove-selected-items" />
- <input type="submit" value="Add foo"
- tal:condition="view/need_add"
- tal:attributes="name string:${view/name}.add;
- value view/addButtonLabel" />
- </td>
- </tr>
-</table>
-<input tal:replace="structure view/marker" />
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/sequencewidget.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -15,301 +15,9 @@
$Id$
"""
-__docformat__ = 'restructuredtext'
-
-from zope import component
-from zope.interface import implements
-from zope.i18n import translate
-from zope.schema.interfaces import ValidationError
-
-from zope.formlib.interfaces import IDisplayWidget, IInputWidget
-from zope.formlib.interfaces import WidgetInputError, MissingInputError
-from zope.formlib.widget import InputWidget
-from zope.app.form.browser.i18n import _
-from zope.formlib.widget import BrowserWidget
-from zope.formlib.widget import DisplayWidget, renderElement
-from zope.browserpage import ViewPageTemplateFile
-
-
-class SequenceWidget(BrowserWidget, InputWidget):
- """A widget baseclass for a sequence of fields.
-
- subwidget - Optional CustomWidget used to generate widgets for the
- items in the sequence
- """
-
- implements(IInputWidget)
-
- template = ViewPageTemplateFile('sequencewidget.pt')
-
- _type = tuple
-
- def __init__(self, context, field, request, subwidget=None):
- super(SequenceWidget, self).__init__(context, request)
- self.subwidget = subwidget
-
- # The subwidgets are cached in this dict if preserve_widgets is True.
- self._widgets = {}
- self.preserve_widgets = False
-
- def __call__(self):
- """Render the widget"""
- self._update()
- return self.template()
-
- def _update(self):
- """Set various attributes for the template"""
- sequence = self._getRenderedValue()
- num_items = len(sequence)
- self.need_add = (not self.context.max_length
- or num_items < self.context.max_length)
- self.need_delete = num_items and num_items > self.context.min_length
- self.marker = self._getPresenceMarker(num_items)
-
- def widgets(self):
- """Return a list of widgets to display"""
- sequence = self._getRenderedValue()
- result = []
- for i, value in enumerate(sequence):
- widget = self._getWidget(i)
- widget.setRenderedValue(value)
- result.append(widget)
- return result
-
- def addButtonLabel(self):
- button_label = _('Add %s')
- button_label = translate(button_label, context=self.request,
- default=button_label)
- title = self.context.title or self.context.__name__
- title = translate(title, context=self.request, default=title)
- return button_label % title
-
-
- def _getWidget(self, i):
- """Return a widget for the i-th number of the sequence.
-
- Normally this method creates a new widget each time, but when
- the ``preserve_widgets`` attribute is True, it starts caching
- widgets. We need it so that the errors on the subwidgets
- would appear only if ``getInputValue`` was called.
-
- ``getInputValue`` on the subwidgets gets called on each
- request that has data.
- """
- if i not in self._widgets:
- field = self.context.value_type
- if self.subwidget is not None:
- widget = self.subwidget(field, self.request)
- else:
- widget = component.getMultiAdapter(
- (field, self.request), IInputWidget)
- widget.setPrefix('%s.%d.' % (self.name, i))
- if not self.preserve_widgets:
- return widget
- self._widgets[i] = widget
- return self._widgets[i]
-
- def hidden(self):
- """Render the list as hidden fields."""
- # length of sequence info
- sequence = self._getRenderedValue()
- num_items = len(sequence)
-
- # generate hidden fields for each value
- parts = [self._getPresenceMarker(num_items)]
- for i in range(num_items):
- value = sequence[i]
- widget = self._getWidget(i)
- widget.setRenderedValue(value)
- parts.append(widget.hidden())
- return "\n".join(parts)
-
- def _getRenderedValue(self):
- """Returns a sequence from the request or _data"""
- if self._renderedValueSet():
- if self._data is self.context.missing_value:
- sequence = []
- else:
- sequence = list(self._data)
- elif self.hasInput():
- sequence = self._generateSequence()
- elif self.context.default is not None:
- sequence = self.context.default
- else:
- sequence = []
- # ensure minimum number of items in the form
- while len(sequence) < self.context.min_length:
- # Shouldn't this use self.field.value_type.missing_value,
- # instead of None?
- sequence.append(None)
- return sequence
-
- def _getPresenceMarker(self, count=0):
- return ('<input type="hidden" name="%s.count" value="%d" />'
- % (self.name, count))
-
- def getInputValue(self):
- """Return converted and validated widget data.
-
- If there is no user input and the field is required, then a
- ``MissingInputError`` will be raised.
-
- If there is no user input and the field is not required, then
- the field default value will be returned.
-
- A ``WidgetInputError`` is raised in the case of one or more
- errors encountered, inputting, converting, or validating the data.
- """
- if self.hasInput():
- self.preserve_widgets = True
- sequence = self._type(self._generateSequence())
- if sequence != self.context.missing_value:
- # catch and set field errors to ``_error`` attribute
- try:
- self.context.validate(sequence)
- except WidgetInputError, error:
- self._error = error
- raise self._error
- except ValidationError, error:
- self._error = WidgetInputError(
- self.context.__name__, self.label, error)
- raise self._error
- elif self.context.required:
- raise MissingInputError(self.context.__name__,
- self.context.title)
- return sequence
- raise MissingInputError(self.context.__name__, self.context.title)
-
- # TODO: applyChanges isn't reporting "change" correctly (we're
- # re-generating the sequence with every edit, and need to be smarter)
- def applyChanges(self, content):
- field = self.context
- value = self.getInputValue()
- change = field.query(content, self) != value
- if change:
- field.set(content, value)
- return change
-
- def hasInput(self):
- """Is there input data for the field
-
- Return ``True`` if there is data and ``False`` otherwise.
- """
- return (self.name + ".count") in self.request.form
-
- def _generateSequence(self):
- """Extract the values of the subwidgets from the request.
-
- Returns a list of values.
-
- This can only be called if self.hasInput() returns true.
- """
- if self.context.value_type is None:
- # Why would this ever happen?
- return []
- # the marker field tells how many individual items were
- # included in the input; we check for exactly that many input
- # widgets
- try:
- count = int(self.request.form[self.name + ".count"])
- except ValueError:
- # could not convert to int; the input was not generated
- # from the widget as implemented here
- raise WidgetInputError(self.context.__name__, self.context.title)
-
- # pre-populate
- sequence = [None] * count
-
- # now look through the request for interesting values
- # in reverse so that we can remove items as we go
- removing = self.name + ".remove" in self.request.form
- for i in reversed(range(count)):
- widget = self._getWidget(i)
- if widget.hasValidInput():
- # catch and set sequence widget errors to ``_error`` attribute
- try:
- sequence[i] = widget.getInputValue()
- except WidgetInputError, error:
- self._error = error
- raise self._error
-
- remove_key = "%s.remove_%d" % (self.name, i)
- if remove_key in self.request.form and removing:
- del sequence[i]
-
- # add an entry to the list if the add button has been pressed
- if self.name + ".add" in self.request.form:
- # Should this be using self.context.value_type.missing_value
- # instead of None?
- sequence.append(None)
-
- return sequence
-
-
-class TupleSequenceWidget(SequenceWidget):
- _type = tuple
-
-
-class ListSequenceWidget(SequenceWidget):
- _type = list
-
-
-# Basic display widget
-
-class SequenceDisplayWidget(DisplayWidget):
-
- _missingValueMessage = _("sequence-value-not-provided",
- u"(no value available)")
-
- _emptySequenceMessage = _("sequence-value-is-empty",
- u"(no values)")
-
- tag = "ol"
- itemTag = "li"
- cssClass = "sequenceWidget"
- extra = ""
-
- def __init__(self, context, field, request, subwidget=None):
- super(SequenceDisplayWidget, self).__init__(context, request)
- self.subwidget = subwidget
-
- def __call__(self):
- # get the data to display:
- if self._renderedValueSet():
- data = self._data
- else:
- data = self.context.get(self.context.context)
-
- # deal with special cases:
- if data == self.context.missing_value:
- return translate(self._missingValueMessage, self.request)
- data = list(data)
- if not data:
- return translate(self._emptySequenceMessage, self.request)
-
- parts = []
- for i, item in enumerate(data):
- widget = self._getWidget(i)
- widget.setRenderedValue(item)
- s = widget()
- if self.itemTag:
- s = "<%s>%s</%s>" % (self.itemTag, s, self.itemTag)
- parts.append(s)
- contents = "\n".join(parts)
- if self.tag:
- contents = "\n%s\n" % contents
- contents = renderElement(self.tag,
- cssClass=self.cssClass,
- extra=self.extra,
- contents=contents)
- return contents
-
- def _getWidget(self, i):
- field = self.context.value_type
- if self.subwidget is not None:
- widget = self.subwidget(field, self.request)
- else:
- widget = component.getMultiAdapter(
- (field, self.request), IDisplayWidget)
- widget.setPrefix('%s.%d.' % (self.name, i))
- return widget
+# implementation moved to zope.formlib.sequencewidget
+from zope.formlib.sequencewidget import (
+ SequenceWidget,
+ TupleSequenceWidget,
+ ListSequenceWidget,
+ SequenceDisplayWidget)
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_itemswidget.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_itemswidget.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_itemswidget.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -331,14 +331,14 @@
u'<option value="token3">Three</option>'])
try:
# test BBB starting with 3.6.0
- zope.app.form.browser.itemswidgets.EXPLICIT_EMPTY_SELECTION = False
+ zope.formlib.itemswidgets.EXPLICIT_EMPTY_SELECTION = False
self.assertEqual(
widget.renderItems(widget._toFormValue(widget.context.missing_value)),
[u'<option value="token1">One</option>',
u'<option value="token2">Two</option>',
u'<option value="token3">Three</option>'])
finally:
- zope.app.form.browser.itemswidgets.EXPLICIT_EMPTY_SELECTION = True
+ zope.formlib.itemswidgets.EXPLICIT_EMPTY_SELECTION = True
class RadioWidgetTest(ItemsEditWidgetBaseTest):
Modified: zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/textwidgets.py
===================================================================
--- zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/textwidgets.py 2009-12-30 19:21:24 UTC (rev 107376)
+++ zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/textwidgets.py 2009-12-30 19:46:00 UTC (rev 107377)
@@ -15,671 +15,28 @@
$Id$
"""
-__docformat__ = 'restructuredtext'
-
-import decimal
-from xml.sax import saxutils
-from zope.interface import implements
-from zope.datetime import parseDatetimetz
-from zope.datetime import DateTimeError
-from zope.i18n.format import DateTimeParseError
-
-from zope.formlib.interfaces import IInputWidget, ConversionError
-from zope.app.form.browser.i18n import _
-from zope.app.form.browser.interfaces import ITextBrowserWidget
-from zope.formlib.widget import SimpleInputWidget, renderElement
-from zope.formlib.widget import DisplayWidget
-
-
-def escape(str):
- if str is not None:
- str = saxutils.escape(str)
- return str
-
-class TextWidget(SimpleInputWidget):
- """Text widget.
-
- Single-line text (unicode) input
-
- >>> from zope.publisher.browser import TestRequest
- >>> from zope.schema import TextLine
- >>> field = TextLine(__name__='foo', title=u'on')
- >>> request = TestRequest(form={'field.foo': u'Bob'})
- >>> widget = TextWidget(field, request)
- >>> widget.hasInput()
- True
- >>> widget.getInputValue()
- u'Bob'
-
- >>> def normalize(s):
- ... return '\\n '.join(filter(None, s.split(' ')))
-
- >>> print normalize( widget() )
- <input
- class="textType"
- id="field.foo"
- name="field.foo"
- size="20"
- type="text"
- value="Bob"
- />
-
- >>> print normalize( widget.hidden() )
- <input
- class="hiddenType"
- id="field.foo"
- name="field.foo"
- type="hidden"
- value="Bob"
- />
-
- Calling `setRenderedValue` will change what gets output:
-
- >>> widget.setRenderedValue("Barry")
- >>> print normalize( widget() )
- <input
- class="textType"
- id="field.foo"
- name="field.foo"
- size="20"
- type="text"
- value="Barry"
- />
-
- Check that HTML is correctly encoded and decoded:
-
- >>> request = TestRequest(
- ... form={'field.foo': u'<h1>©</h1>'})
- >>> widget = TextWidget(field, request)
- >>> widget.getInputValue()
- u'<h1>©</h1>'
-
- >>> print normalize( widget() )
- <input
- class="textType"
- id="field.foo"
- name="field.foo"
- size="20"
- type="text"
- value="<h1>&copy;</h1>"
- />
- """
-
- implements(ITextBrowserWidget)
-
- default = ''
- displayWidth = 20
- displayMaxWidth = ""
- extra = ''
- style = ''
- convert_missing_value = True
-
- def __init__(self, *args):
- super(TextWidget, self).__init__(*args)
-
- def __call__(self):
- value = self._getFormValue()
- if value is None or value == self.context.missing_value:
- value = ''
-
- kwargs = {'type': self.type,
- 'name': self.name,
- 'id': self.name,
- 'value': value,
- 'cssClass': self.cssClass,
- 'style': self.style,
- 'size': self.displayWidth,
- 'extra': self.extra}
- if self.displayMaxWidth:
- kwargs['maxlength'] = self.displayMaxWidth # TODO This is untested.
-
- return renderElement(self.tag, **kwargs)
-
- def _toFieldValue(self, input):
- if self.convert_missing_value and input == self._missing:
- value = self.context.missing_value
- else:
- # We convert everything to unicode. This might seem a bit crude,
- # but anything contained in a TextWidget should be representable
- # as a string. Note that you always have the choice of overriding
- # the method.
- try:
- value = unicode(input)
- except ValueError, v:
- raise ConversionError(_("Invalid text data"), v)
- return value
-
-
-class Bytes(SimpleInputWidget):
-
- def _toFieldValue(self, input):
- value = super(Bytes, self)._toFieldValue(input)
- if type(value) is unicode:
- try:
- value = value.encode('ascii')
- except UnicodeError, v:
- raise ConversionError(_("Invalid textual data"), v)
- return value
-
-class BytesWidget(Bytes, TextWidget):
- """Bytes widget.
-
- Single-line data (string) input
-
- >>> from zope.publisher.browser import TestRequest
- >>> from zope.schema import BytesLine
- >>> field = BytesLine(__name__='foo', title=u'on')
- >>> request = TestRequest(form={'field.foo': u'Bob'})
- >>> widget = BytesWidget(field, request)
- >>> widget.hasInput()
- True
- >>> widget.getInputValue()
- 'Bob'
- """
-
-class BytesDisplayWidget(DisplayWidget):
- """Bytes display widget"""
-
- def __call__(self):
- if self._renderedValueSet():
- content = self._data
- else:
- content = self.context.default
- return renderElement("pre", contents=escape(content))
-
-class ASCII(Bytes):
- """ASCII"""
-
-
-class ASCIIWidget(BytesWidget):
- """ASCII widget.
-
- Single-line data (string) input
- """
-
-class ASCIIDisplayWidget(BytesDisplayWidget):
- """ASCII display widget"""
-
-class URIDisplayWidget(DisplayWidget):
- """URI display widget.
-
- :ivar linkTarget:
- The value of the ``target`` attribute for the generated hyperlink.
- If this is not set, no ``target`` attribute is generated.
-
- """
-
- linkTarget = None
-
- def __call__(self):
- if self._renderedValueSet():
- content = self._data
- else:
- content = self.context.default
- if not content:
- # If there is no content it is not useful to render an anchor.
- return ''
- content = escape(content)
- kw = dict(contents=content, href=content)
- if self.linkTarget:
- kw["target"] = self.linkTarget
- return renderElement("a", **kw)
-
-
-class TextAreaWidget(SimpleInputWidget):
- """TextArea widget.
-
- Multi-line text (unicode) input.
-
- >>> from zope.publisher.browser import TestRequest
- >>> from zope.schema import Text
- >>> field = Text(__name__='foo', title=u'on')
- >>> request = TestRequest(form={'field.foo': u'Hello\\r\\nworld!'})
- >>> widget = TextAreaWidget(field, request)
- >>> widget.hasInput()
- True
- >>> widget.getInputValue()
- u'Hello\\nworld!'
-
- >>> def normalize(s):
- ... return '\\n '.join(filter(None, s.split(' ')))
-
- >>> print normalize( widget() )
- <textarea
- cols="60"
- id="field.foo"
- name="field.foo"
- rows="15"
- >Hello\r
- world!</textarea>
-
- >>> print normalize( widget.hidden() )
- <input
- class="hiddenType"
- id="field.foo"
- name="field.foo"
- type="hidden"
- value="Hello world!"
- />
-
- Calling `setRenderedValue` will change what gets output:
-
- >>> widget.setRenderedValue("Hey\\ndude!")
- >>> print normalize( widget() )
- <textarea
- cols="60"
- id="field.foo"
- name="field.foo"
- rows="15"
- >Hey\r
- dude!</textarea>
-
- Check that HTML is correctly encoded and decoded:
-
- >>> request = TestRequest(
- ... form={'field.foo': u'<h1>©</h1>'})
- >>> widget = TextAreaWidget(field, request)
- >>> widget.getInputValue()
- u'<h1>©</h1>'
-
- >>> print normalize( widget() )
- <textarea
- cols="60"
- id="field.foo"
- name="field.foo"
- rows="15"
- ><h1>&copy;</h1></textarea>
-
- There was a but which caused the content of <textarea> tags not to be
- rendered correctly when there was a conversion error. Make sure the quoting
- works correctly::
-
- >>> from zope.schema import Text
- >>> field = Text(__name__='description', title=u'Description')
-
- >>> from zope.formlib.interfaces import ConversionError
- >>> class TestTextAreaWidget(TextAreaWidget):
- ... def _toFieldValue(self, input):
- ... if 'foo' in input:
- ... raise ConversionError("I don't like foo.")
- ... return input
- ...
-
- >>> request = TestRequest(form={'field.description': u'<p>bar</p>'})
- >>> widget = TestTextAreaWidget(field, request)
- >>> widget.getInputValue()
- u'<p>bar</p>'
- >>> print normalize( widget() )
- <textarea
- cols="60"
- id="field.description"
- name="field.description"
- rows="15"
- ><p>bar</p></textarea>
-
- >>> request = TestRequest(form={'field.description': u'<p>foo</p>'})
- >>> widget = TestTextAreaWidget(field, request)
- >>> try:
- ... widget.getInputValue()
- ... except ConversionError, error:
- ... print error.doc()
- I don't like foo.
- >>> print normalize( widget() )
- <textarea
- cols="60"
- id="field.description"
- name="field.description"
- rows="15"
- ><p>foo</p></textarea>
- """
-
- default = ""
- width = 60
- height = 15
- extra = ""
- style = ''
-
- def _toFieldValue(self, value):
- value = super(TextAreaWidget, self)._toFieldValue(value)
- if value:
- try:
- value = unicode(value)
- except ValueError, v:
- raise ConversionError(_("Invalid unicode data"), v)
- else:
- value = value.replace("\r\n", "\n")
- return value
-
- def _toFormValue(self, value):
- value = super(TextAreaWidget, self)._toFormValue(value)
- if value:
- value = value.replace("\n", "\r\n")
- else:
- value = u''
-
- return value
-
- def __call__(self):
- return renderElement("textarea",
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- rows=self.height,
- cols=self.width,
- style=self.style,
- contents=escape(self._getFormValue()),
- extra=self.extra)
-
-class BytesAreaWidget(Bytes, TextAreaWidget):
- """BytesArea widget.
-
- Multi-line string input.
-
- >>> from zope.publisher.browser import TestRequest
- >>> from zope.schema import Bytes
- >>> field = Bytes(__name__='foo', title=u'on')
- >>> request = TestRequest(form={'field.foo': u'Hello\\r\\nworld!'})
- >>> widget = BytesAreaWidget(field, request)
- >>> widget.hasInput()
- True
- >>> widget.getInputValue()
- 'Hello\\nworld!'
- """
-
-class ASCIIAreaWidget(ASCII, TextAreaWidget):
- """ASCIIArea widget.
-
- Multi-line string input.
-
- >>> from zope.publisher.browser import TestRequest
- >>> from zope.schema import ASCII
- >>> field = ASCII(__name__='foo', title=u'on')
- >>> request = TestRequest(form={'field.foo': u'Hello\\r\\nworld!'})
- >>> widget = ASCIIAreaWidget(field, request)
- >>> widget.hasInput()
- True
- >>> widget.getInputValue()
- 'Hello\\nworld!'
- """
-
-class PasswordWidget(TextWidget):
- """Password Widget"""
-
- type = 'password'
-
- def __call__(self):
- displayMaxWidth = self.displayMaxWidth or 0
- if displayMaxWidth > 0:
- return renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- value='',
- cssClass=self.cssClass,
- style=self.style,
- size=self.displayWidth,
- maxlength=displayMaxWidth,
- extra=self.extra)
- else:
- return renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- value='',
- cssClass=self.cssClass,
- style=self.style,
- size=self.displayWidth,
- extra=self.extra)
-
- def _toFieldValue(self, input):
- try:
- existing = self.context.get(self.context.context)
- except AttributeError:
- existing = False
- if (not input) and existing:
- return self.context.UNCHANGED_PASSWORD
- return super(PasswordWidget, self)._toFieldValue(input)
-
- def hidden(self):
- raise NotImplementedError(
- 'Cannot get a hidden tag for a password field')
-
-
-class FileWidget(TextWidget):
- """File Widget"""
-
- type = 'file'
-
- def __call__(self):
- displayMaxWidth = self.displayMaxWidth or 0
- hidden = renderElement(self.tag,
- type='hidden',
- name=self.name+".used",
- id=self.name+".used",
- value="")
- if displayMaxWidth > 0:
- elem = renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- size=self.displayWidth,
- maxlength=displayMaxWidth,
- extra=self.extra)
- else:
- elem = renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- size=self.displayWidth,
- extra=self.extra)
- return "%s %s" % (hidden, elem)
-
- def _toFieldValue(self, input):
- if input is None or input == '':
- return self.context.missing_value
- try:
- seek = input.seek
- read = input.read
- except AttributeError, e:
- raise ConversionError(_('Form input is not a file object'), e)
- else:
- seek(0)
- data = read()
- if data or getattr(input, 'filename', ''):
- return data
- else:
- return self.context.missing_value
-
- def hasInput(self):
- return ((self.name+".used" in self.request.form)
- or
- (self.name in self.request.form)
- )
-
-class IntWidget(TextWidget):
- """Integer number widget.
-
- Let's make sure that zeroes are rendered properly:
-
- >>> from zope.schema import Int
- >>> field = Int(__name__='foo', title=u'on')
- >>> widget = IntWidget(field, None)
- >>> widget.setRenderedValue(0)
-
- >>> 'value="0"' in widget()
- True
-
- """
-
- displayWidth = 10
-
- def _toFieldValue(self, input):
- if input == self._missing:
- return self.context.missing_value
- else:
- try:
- return int(input)
- except ValueError, v:
- raise ConversionError(_("Invalid integer data"), v)
-
-
-class FloatWidget(TextWidget):
- implements(IInputWidget)
- displayWidth = 10
-
- def _toFieldValue(self, input):
- if input == self._missing:
- return self.context.missing_value
- else:
- try:
- return float(input)
- except ValueError, v:
- raise ConversionError(_("Invalid floating point data"), v)
-
-class DecimalWidget(TextWidget):
- implements(IInputWidget)
- displayWidth = 10
-
- def _toFieldValue(self, input):
- if input == self._missing:
- return self.context.missing_value
- else:
- try:
- return decimal.Decimal(input)
- except decimal.InvalidOperation, v:
- raise ConversionError(_("Invalid decimal data"), v)
-
- def _toFormValue(self, value):
- if value == self.context.missing_value:
- value = self._missing
- else:
- return unicode(value)
-
-
-class DatetimeWidget(TextWidget):
- """Datetime entry widget."""
-
- displayWidth = 20
-
- def _toFieldValue(self, input):
- if input == self._missing:
- return self.context.missing_value
- else:
- try:
- # TODO: Currently datetimes return in local (server)
- # time zone if no time zone information was given.
- # Maybe offset-naive datetimes should be returned in
- # this case? (DV)
- return parseDatetimetz(input)
- except (DateTimeError, ValueError, IndexError), v:
- raise ConversionError(_("Invalid datetime data"), v)
-
-class DateWidget(DatetimeWidget):
- """Date entry widget.
- """
-
- def _toFieldValue(self, input):
- v = super(DateWidget, self)._toFieldValue(input)
- if v != self.context.missing_value:
- v = v.date()
- return v
-
-class DateI18nWidget(TextWidget):
- """I18n date entry widget.
-
- The `displayStyle` attribute may be set to control the formatting of the
- value.
-
- `displayStyle` must be one of 'full', 'long', 'medium', 'short',
- or None ('' is accepted an an alternative to None to support
- provision of a value from ZCML).
- """
-
- _category = "date"
-
- displayWidth = 20
-
- displayStyle = None
-
- def _toFieldValue(self, input):
- if input == self._missing:
- return self.context.missing_value
- else:
- try:
- formatter = self.request.locale.dates.getFormatter(
- self._category, (self.displayStyle or None))
- return formatter.parse(input)
- except (DateTimeParseError, ValueError), v:
- raise ConversionError(_("Invalid datetime data"),
- "%s (%r)" % (v, input))
-
- def _toFormValue(self, value):
- value = super(DateI18nWidget, self)._toFormValue(value)
- if value:
- formatter = self.request.locale.dates.getFormatter(
- self._category, (self.displayStyle or None))
- value = formatter.format(value)
- return value
-
-class DatetimeI18nWidget(DateI18nWidget):
- """I18n datetime entry widget.
-
- The `displayStyle` attribute may be set to control the formatting of the
- value.
-
- `displayStyle` must be one of 'full', 'long', 'medium', 'short',
- or None ('' is accepted an an alternative to None to support
- provision of a value from ZCML).
-
- NOTE: If you need timezone information you need to set `displayStyle`
- to either 'long' or 'full' since other display styles just ignore it.
- """
-
- _category = "dateTime"
-
-class DateDisplayWidget(DisplayWidget):
- """Date display widget.
-
- The `cssClass` and `displayStyle` attributes may be set to control
- the formatting of the value.
-
- `displayStyle` must be one of 'full', 'long', 'medium', 'short',
- or None ('' is accepted an an alternative to None to support
- provision of a value from ZCML).
- """
-
- cssClass = "date"
- displayStyle = None
-
- _category = "date"
-
- def __call__(self):
- if self._renderedValueSet():
- content = self._data
- else:
- content = self.context.default
- if content == self.context.missing_value:
- return ""
- formatter = self.request.locale.dates.getFormatter(
- self._category, (self.displayStyle or None))
- content = formatter.format(content)
- return renderElement("span", contents=escape(content),
- cssClass=self.cssClass)
-
-
-class DatetimeDisplayWidget(DateDisplayWidget):
- """Datetime display widget.
-
- The `cssClass` and `displayStyle` attributes may be set to control
- the formatting of the value.
-
- `displayStyle` must be one of 'full', 'long', 'medium', 'short',
- or None ('' is accepted an an alternative to None to support
- provision of a value from ZCML).
- """
-
- cssClass = "dateTime"
-
- _category = "dateTime"
+# implementation moved to zope.formlib.textwidgets
+from zope.formlib.textwidgets import (
+ escape,
+ TextWidget,
+ Bytes,
+ BytesWidget,
+ BytesDisplayWidget,
+ ASCII,
+ ASCIIWidget,
+ ASCIIDisplayWidget,
+ URIDisplayWidget,
+ TextAreaWidget,
+ BytesAreaWidget,
+ ASCIIAreaWidget,
+ PasswordWidget,
+ FileWidget,
+ IntWidget,
+ FloatWidget,
+ DecimalWidget,
+ DatetimeWidget,
+ DateWidget,
+ DateI18nWidget,
+ DatetimeI18nWidget,
+ DateDisplayWidget,
+ DatetimeDisplayWidget)
More information about the checkins
mailing list