[Zope3-checkins] CVS: Zope3/src/zope/app/browser/form - configure.zcml:1.9.2.1 editview.py:1.23.8.1 meta.zcml:1.10.12.1 schemadisplay.py:1.2.12.1 vocabularywidget.py:1.6.2.1 widget.py:1.30.2.1
Grégoire Weber
zope@i-con.ch
Sun, 22 Jun 2003 10:24:10 -0400
Update of /cvs-repository/Zope3/src/zope/app/browser/form
In directory cvs.zope.org:/tmp/cvs-serv24874/src/zope/app/browser/form
Modified Files:
Tag: cw-mail-branch
configure.zcml editview.py meta.zcml schemadisplay.py
vocabularywidget.py widget.py
Log Message:
Synced up with HEAD
=== Zope3/src/zope/app/browser/form/configure.zcml 1.9 => 1.9.2.1 ===
--- Zope3/src/zope/app/browser/form/configure.zcml:1.9 Wed May 21 20:39:33 2003
+++ Zope3/src/zope/app/browser/form/configure.zcml Sun Jun 22 10:22:39 2003
@@ -104,6 +104,8 @@
<!-- Vocabulary fields share special widget factories that redirect
to the vocabularies they reference. -->
+
+ <!-- Single selection -->
<view
permission="zope.Public"
type="zope.publisher.interfaces.browser.IBrowserPresentation"
@@ -122,24 +124,83 @@
factory=".vocabularywidget.VocabularyFieldEditWidget"
/>
+ <!-- Bags -->
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
+ for="zope.schema.vocabulary.IVocabularyBagField"
+ name="display"
+ factory=".vocabularywidget.VocabularyBagFieldDisplayWidget"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
+ for="zope.schema.vocabulary.IVocabularyBagField"
+ name="edit"
+ factory=".vocabularywidget.VocabularyBagFieldEditWidget"
+ />
+
+ <!-- Lists -->
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
+ for="zope.schema.vocabulary.IVocabularyListField"
+ name="display"
+ factory=".vocabularywidget.VocabularyListFieldDisplayWidget"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
+ for="zope.schema.vocabulary.IVocabularyListField"
+ name="edit"
+ factory=".vocabularywidget.VocabularyListFieldEditWidget"
+ />
+
+ <!-- Sets -->
<view
permission="zope.Public"
type="zope.publisher.interfaces.browser.IBrowserPresentation"
allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
- for="zope.schema.vocabulary.IVocabularyMultiField"
+ for="zope.schema.vocabulary.IVocabularySetField"
name="display"
- factory=".vocabularywidget.VocabularyMultiFieldDisplayWidget"
+ factory=".vocabularywidget.VocabularySetFieldDisplayWidget"
/>
<view
permission="zope.Public"
type="zope.publisher.interfaces.browser.IBrowserPresentation"
allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
- for="zope.schema.vocabulary.IVocabularyMultiField"
+ for="zope.schema.vocabulary.IVocabularySetField"
name="edit"
- factory=".vocabularywidget.VocabularyMultiFieldEditWidget"
+ factory=".vocabularywidget.VocabularySetFieldEditWidget"
/>
+ <!-- Unique lists -->
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
+ for="zope.schema.vocabulary.IVocabularyUniqueListField"
+ name="display"
+ factory=".vocabularywidget.VocabularyUniqueListFieldDisplayWidget"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
+ for="zope.schema.vocabulary.IVocabularyUniqueListField"
+ name="edit"
+ factory=".vocabularywidget.VocabularyUniqueListFieldEditWidget"
+ />
+
+ <!-- Query view helpers -->
<view
permission="zope.Public"
type="zope.publisher.interfaces.browser.IBrowserPresentation"
@@ -156,6 +217,38 @@
for="zope.schema.interfaces.IIterableVocabularyQuery"
name="widget-query-multi-helper"
factory=".vocabularywidget.IterableVocabularyQueryMultiView"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ factory=".vocabularywidget.VocabularyEditWidget"
+ name="field-edit-widget"
+ for="zope.schema.interfaces.IVocabulary"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ factory=".vocabularywidget.VocabularyDisplayWidget"
+ name="field-display-widget"
+ for="zope.schema.interfaces.IVocabulary"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ factory=".vocabularywidget.VocabularyMultiEditWidget"
+ name="field-multi-edit"
+ for="zope.schema.interfaces.IVocabulary"
+ />
+
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ factory=".vocabularywidget.VocabularyMultiDisplayWidget"
+ name="field-multi-display"
+ for="zope.schema.interfaces.IVocabulary"
/>
<!-- Default simple display view -->
=== Zope3/src/zope/app/browser/form/editview.py 1.23 => 1.23.8.1 ===
--- Zope3/src/zope/app/browser/form/editview.py:1.23 Fri May 2 14:27:07 2003
+++ Zope3/src/zope/app/browser/form/editview.py Sun Jun 22 10:22:39 2003
@@ -24,7 +24,7 @@
from zope.schema import getFieldNamesInOrder
from zope.configuration.action import Action
-from zope.proxy.context import ContextWrapper
+from zope.app.context import ContextWrapper
from zope.publisher.interfaces.browser import IBrowserPresentation
from zope.publisher.browser import BrowserView
from zope.security.checker import defineChecker, NamesChecker
@@ -76,7 +76,7 @@
widget.setPrefix(prefix)
def widgets(self):
- return [getattr(self, name)
+ return [getattr(self, name+'_widget')
for name in self.fieldNames]
def apply_update(self, data):
=== Zope3/src/zope/app/browser/form/meta.zcml 1.10 => 1.10.12.1 ===
--- Zope3/src/zope/app/browser/form/meta.zcml:1.10 Wed Apr 30 17:53:20 2003
+++ Zope3/src/zope/app/browser/form/meta.zcml Sun Jun 22 10:22:39 2003
@@ -122,12 +122,12 @@
<description>
Define an automatically generated add form
- The addForm directive creates and register's a view for
+ The addform directive creates and register's a view for
adding an object based on a schema.
Adding an object is a bit trickier than editing an object,
because the object the schema applies to isn't available when
- forms are being rendered. The addForm directive provides an
+ forms are being rendered. The addform directive provides an
customization interface to overcome this difficulty.
See zope.app.interfaces.browser.form.IAddFormCustomization.
=== Zope3/src/zope/app/browser/form/schemadisplay.py 1.2 => 1.2.12.1 ===
--- Zope3/src/zope/app/browser/form/schemadisplay.py:1.2 Wed Apr 30 19:37:51 2003
+++ Zope3/src/zope/app/browser/form/schemadisplay.py Sun Jun 22 10:22:39 2003
@@ -20,7 +20,7 @@
from zope.schema import getFieldNamesInOrder
from zope.configuration.action import Action
-from zope.proxy.context import ContextWrapper
+from zope.app.context import ContextWrapper
from zope.publisher.interfaces.browser import IBrowserPresentation
from zope.publisher.browser import BrowserView
from zope.security.checker import defineChecker, NamesChecker
@@ -69,7 +69,7 @@
widget.setPrefix(prefix)
def widgets(self):
- return [getattr(self, name)
+ return [getattr(self, name+'_widget')
for name in self.fieldNames]
=== Zope3/src/zope/app/browser/form/vocabularywidget.py 1.6 => 1.6.2.1 ===
--- Zope3/src/zope/app/browser/form/vocabularywidget.py:1.6 Thu May 22 02:32:13 2003
+++ Zope3/src/zope/app/browser/form/vocabularywidget.py Sun Jun 22 10:22:39 2003
@@ -22,32 +22,64 @@
from xml.sax.saxutils import quoteattr
+from zope.interface import implements, implementedBy
from zope.app.browser.form import widget
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.interfaces.browser.form import IVocabularyQueryView
+from zope.app.interfaces.form import WidgetInputError, MissingInputError
+from zope.interface.declarations import directlyProvides
from zope.publisher.browser import BrowserView
from zope.component import getView
from zope.schema.interfaces import IIterableVocabulary, IVocabularyQuery
from zope.schema.interfaces import IIterableVocabularyQuery
+from zope.schema.interfaces import ValidationError
# These widget factories delegate to the vocabulary on the field.
+# Display
+
def VocabularyFieldDisplayWidget(field, request):
"""Return a display widget based on a vocabulary field."""
return _get_vocabulary_widget(field, request, "display")
+def VocabularyBagFieldDisplayWidget(field, request):
+ """Return a display widget based on a vocabulary field."""
+ return _get_vocabulary_widget(field, request, "display-bag")
+
+def VocabularyListFieldDisplayWidget(field, request):
+ """Return a display widget based on a vocabulary field."""
+ return _get_vocabulary_widget(field, request, "display-list")
+
+def VocabularySetFieldDisplayWidget(field, request):
+ """Return a display widget based on a vocabulary field."""
+ return _get_vocabulary_widget(field, request, "display-set")
+
+def VocabularyUniqueListFieldDisplayWidget(field, request):
+ """Return a display widget based on a vocabulary field."""
+ return _get_vocabulary_widget(field, request, "display-unique-list")
+
+# Edit
+
def VocabularyFieldEditWidget(field, request):
"""Return a value-selection widget based on a vocabulary field."""
- return _get_vocabulary_edit_widget(field, request, ismulti=False)
+ return _get_vocabulary_edit_widget(field, request)
-def VocabularyMultiFieldDisplayWidget(field, request):
- """Return a display widget based on a vocabulary field."""
- return _get_vocabulary_widget(field, request, "display-multi")
+def VocabularyBagFieldEditWidget(field, request):
+ """Return a value-selection widget based on a vocabulary field."""
+ return _get_vocabulary_edit_widget(field, request, "bag")
+
+def VocabularyListFieldEditWidget(field, request):
+ """Return a value-selection widget based on a vocabulary field."""
+ return _get_vocabulary_edit_widget(field, request, "list")
+
+def VocabularySetFieldEditWidget(field, request):
+ """Return a value-selection widget based on a vocabulary field."""
+ return _get_vocabulary_edit_widget(field, request, "set")
-def VocabularyMultiFieldEditWidget(field, request):
+def VocabularyUniqueListFieldEditWidget(field, request):
"""Return a value-selection widget based on a vocabulary field."""
- return _get_vocabulary_edit_widget(field, request, ismulti=True)
+ return _get_vocabulary_edit_widget(field, request, "unique-list")
# Helper functions for the factories:
@@ -57,57 +89,71 @@
view.setField(field)
return view
-def _get_vocabulary_edit_widget(field, request, ismulti):
- if ismulti:
- viewname = "edit-multi"
- queryname = "widget-query-multi-helper"
- else:
- viewname = "edit"
- queryname = "widget-query-helper"
+def _get_vocabulary_edit_widget(field, request, modifier=''):
+ if modifier:
+ modifier = "-" + modifier
+ viewname = "edit" + modifier
view = _get_vocabulary_widget(field, request, viewname)
query = field.vocabulary.getQuery()
- if query is None and IIterableVocabulary.isImplementedBy(field.vocabulary):
- query = IterableVocabularyQuery(field.vocabulary)
if query is not None:
+ queryname = "widget-query%s-helper" % modifier
queryview = getView(query, queryname, request)
view.setQuery(query, queryview)
+ queryview.setWidget(view)
return view
-class IterableVocabularyQuery:
+class IterableVocabularyQuery(object):
"""Simple query object used to invoke the simple selection mechanism."""
- __implements__ = IIterableVocabularyQuery
+ implements(IIterableVocabularyQuery)
- def __init__(self, vocabulary):
+ def __init__(self, vocabulary, *interfaces):
self.vocabulary = vocabulary
+ if interfaces:
+ directlyProvides(self, *interfaces)
+
+
+class TranslationHook:
+
+ def translate(self, msgid):
+ # XXX This is where we should be calling on the translation service
+ return msgid.default
+
+def message(msgid, default):
+ msgid.default = default
+ return msgid
# Widget implementation:
-class ViewSupport:
+class ViewSupport(object, TranslationHook):
"""Helper class for vocabulary and vocabulary-query widgets."""
def textForValue(self, term):
- # Extract the value from the term. This can be overridden to
- # support more complex term objects.
- return term.value
+ # Extract a string from the 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.
+ return term.token
def mkselectionlist(self, type, info, name):
- L = ["<table>\n"]
- for term, selected, disabled in info:
- flag = ""
- if selected:
- flag = "checked "
- if disabled:
- flag += "disabled "
- L.append("<tr><td>"
- "<input type='%s' value='%s' name='%s' %s/>"
- "</td>\n <td>%s</td>"
- "</tr>\n"
- % (type, term.value, name, flag, self.textForValue(term)))
- L.append("</table>")
- return ''.join(L)
+ L = [self.mkselectionitem(type, name, *item) for item in info]
+ return widget.renderElement("table",
+ contents="\n%s\n" % "\n".join(L))
+
+ def mkselectionitem(self, type, name, term, selected, disabled):
+ flag = ""
+ if selected:
+ flag = " checked='checked'"
+ if disabled:
+ flag += " disabled='disabled'"
+ if flag:
+ flag = "\n " + flag
+ return ("<tr><td>"
+ "<input type='%s' value='%s' name='%s'%s />"
+ "</td>\n <td>%s</td></tr>"
+ % (type, term.token, name, flag, self.textForValue(term)))
class VocabularyWidgetBase(ViewSupport, widget.BrowserWidget):
@@ -117,44 +163,141 @@
extra = ""
type = "vocabulary"
+ context = None
def __init__(self, context, request):
self.request = request
- self.context = None
-
- def _getDefault(self):
- # Override this since the context is not the field for
- # vocabulary-based widgets.
- return self.context.default
+ self.vocabulary = context
+ # self.context is set to the field in setField below
def setField(self, field):
assert self.context is None
# only allow this to happen for a bound field
assert field.context is not None
self.context = field
- self.name = self._prefix + field.__name__
+ self.setPrefix(self._prefix)
+ assert self.name
+
+ def setPrefix(self, prefix):
+ super(VocabularyWidgetBase, self).setPrefix(prefix)
+ # names for other information from the form
+ self.empty_marker_name = self.name + "-empty-marker"
def __call__(self):
- if self.haveData():
- value = self._showData()
+ if self._data is None:
+ if self.haveData():
+ data = self.getData(True)
+ else:
+ data = self._getDefault()
else:
- value = self.context.get(self.context.context)
- return self.render(value)
+ data = self._data
+ return self.render(data)
def render(self, value):
raise NotImplementedError(
"render() must be implemented by a subclass")
+ def convertTokensToValues(self, tokens):
+ L = []
+ for token in tokens:
+ try:
+ term = self.context.vocabulary.getTermByToken(token)
+ except LookupError:
+ raise WidgetInputError(
+ self.context.__name__,
+ self.context.title,
+ "token %r not found in vocabulary" % token)
+ else:
+ L.append(term.value)
+ return L
-class VocabularyDisplayWidget(VocabularyWidgetBase):
+ _have_field_data = False
+
+ def getData(self, optional=False):
+ data = self._compute_data()
+ field = self.context
+ if data is None:
+ if field.required and not optional:
+ raise MissingInputError(field.__name__, field.title,
+ 'the field is required')
+ return self._getDefault()
+ elif not optional:
+ try:
+ field.validate(data)
+ except ValidationError, v:
+ raise WidgetInputError(self.context.__name__, self.title, v)
+ return data
+
+ def _emptyMarker(self):
+ return "<input name='%s' type='hidden' value='1' />" % (
+ self.empty_marker_name)
+
+ def haveData(self):
+ return (self.name in self.request.form or
+ self.empty_marker_name in self.request.form)
+
+ def setData(self, value):
+ self._data = value
+
+ def _compute_data(self):
+ raise NotImplementedError(
+ "_compute_data() must be implemented by a subclass\n"
+ "It may be inherited from the mix-in classes SingleDataHelper\n"
+ "or MultiDataHelper (from zope.app.browser.form.vocabularywidget)")
+
+ def _showData(self):
+ raise NotImplementedError(
+ "vocabulary-based widgets don't use the _showData() method")
+
+ def _convert(self, value):
+ raise NotImplementedError(
+ "vocabulary-based widgets don't use the _convert() method")
+
+ def _unconvert(self, value):
+ raise NotImplementedError(
+ "vocabulary-based widgets don't use the _unconvert() method")
+
+class SingleDataHelper(object):
+
+ _msg_no_value = message(_("vocabulary-no-value"), "(no value)")
+
+ def _compute_data(self):
+ token = self.request.form.get(self.name)
+ if token:
+ return self.convertTokensToValues([token])[0]
+ else:
+ return None
+
+class MultiDataHelper(object):
+
+ def _compute_data(self):
+ if self.name in self.request.form:
+ tokens = self.request.form[self.name]
+ if not isinstance(tokens, list):
+ tokens = [tokens]
+ return self.convertTokensToValues(tokens)
+ return []
+
+ 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
+
+class VocabularyDisplayWidget(SingleDataHelper, VocabularyWidgetBase):
"""Simple single-selection display that can be used in many cases."""
def render(self, value):
- term = self.context.vocabulary.getTerm(value)
- return self.textForValue(term)
+ if value is None:
+ return self.translate(self._msg_no_value)
+ else:
+ term = self.context.vocabulary.getTerm(value)
+ return self.textForValue(term)
-class VocabularyMultiDisplayWidget(VocabularyWidgetBase):
+class VocabularyMultiDisplayWidget(MultiDataHelper, VocabularyWidgetBase):
propertyNames = (VocabularyWidgetBase.propertyNames
+ ['itemTag', 'tag'])
@@ -163,22 +306,22 @@
tag = 'ol'
def render(self, value):
- if value == self._missing:
- return widget.renderElement('span',
+ if value:
+ rendered_items = self.renderItems(value)
+ return widget.renderElement(self.getValue('tag'),
type=self.getValue('type'),
name=self.name,
id=self.name,
cssClass=self.getValue('cssClass'),
- contents=_("(no values)"),
+ contents="\n".join(rendered_items),
extra=self.getValue('extra'))
else:
- rendered_items = self.renderItems(value)
- return widget.renderElement(self.getValue('tag'),
+ return widget.renderElement('span',
type=self.getValue('type'),
name=self.name,
id=self.name,
cssClass=self.getValue('cssClass'),
- contents="\n".join(rendered_items),
+ contents=_("(no values)"),
extra=self.getValue('extra'))
def renderItems(self, value):
@@ -196,7 +339,25 @@
return L
-class ActionHelper:
+class VocabularyListDisplayWidget(VocabularyMultiDisplayWidget):
+ """Display widget for ordered multi-selection fields.
+
+ This can be used for both VocabularyListField and
+ VocabularyUniqueListField fields.
+ """
+ tag = 'ol'
+
+
+class VocabularyBagDisplayWidget(VocabularyMultiDisplayWidget):
+ """Display widget for unordered multi-selection fields.
+
+ This can be used for both VocabularyBagField and
+ VocabularySetField fields.
+ """
+ tag = 'ul'
+
+
+class ActionHelper(object, TranslationHook):
__actions = None
def addAction(self, action, msgid):
@@ -206,6 +367,8 @@
self.__actions[action] = msgid
def getAction(self):
+ assert self.__actions is not None, \
+ "getAction() called on %r with no actions defined" % self
get = self.request.form.get
for action in self.__actions.iterkeys():
name = "%s.action-%s" % (self.name, action)
@@ -216,12 +379,8 @@
def renderAction(self, action, disabled=False):
msgid = self.__actions[action]
return ("<input type='submit' name='%s.action-%s' value=%s %s/>"
- % (self.name, action, self.translate(msgid),
- disabled and "disabled " or ""))
-
- def translate(self, msgid):
- # XXX This is where we should be calling on the translation service
- return msgid.default
+ % (self.name, action, quoteattr(self.translate(msgid)),
+ disabled and "\n disabled='disabled' " or ""))
class VocabularyEditWidgetBase(VocabularyWidgetBase):
@@ -248,14 +407,14 @@
queryview.setName(self.name + "-query")
def setPrefix(self, prefix):
- VocabularyWidgetBase.setPrefix(self, prefix)
+ super(VocabularyEditWidgetBase, self).setPrefix(prefix)
if self.queryview is not None:
self.queryview.setName(self.name + "-query")
def render(self, value):
contents = []
have_results = False
- if self.queryview:
+ if self.queryview is not None:
s = self.queryview.renderResults(value)
if s:
contents.append(self._div('queryresults', s))
@@ -263,7 +422,8 @@
contents.append(self._div('queryinput', s))
have_results = True
contents.append(self._div('value', self.renderValue(value)))
- if self.queryview and not have_results:
+ contents.append(self._emptyMarker())
+ if self.queryview is not None and not have_results:
s = self.queryview.renderInput()
if s:
contents.append(self._div('queryinput', s))
@@ -290,19 +450,18 @@
rendered_items = []
count = 0
for term in self.context.vocabulary:
- item_value = term.value
item_text = self.textForValue(term)
- if item_value in values:
+ if term.value in values:
rendered_item = self.renderSelectedItem(count,
item_text,
- item_value,
+ term.token,
self.name,
cssClass)
else:
rendered_item = self.renderItem(count,
item_text,
- item_value,
+ term.token,
self.name,
cssClass)
@@ -325,13 +484,13 @@
selected=None)
-class VocabularyEditWidget(VocabularyEditWidgetBase):
+class SelectListWidget(SingleDataHelper, VocabularyEditWidgetBase):
"""Vocabulary-backed single-selection edit widget.
This widget can be used when the number of selections isn't going
to be very large.
"""
- __implements__ = widget.SingleItemsWidget.__implements__
+ implements(implementedBy(widget.SingleItemsWidget))
propertyNames = VocabularyEditWidgetBase.propertyNames + ['firstItem']
firstItem = False
@@ -344,8 +503,7 @@
size=self.getValue('size'))
def renderItems(self, value):
- vocabulary = self.context
-
+ vocabulary = self.context.vocabulary
# check if we want to select first item
if (value == self._missing
and getattr(self.context, 'firstItem', False)
@@ -356,18 +514,22 @@
values = [value]
else:
values = ()
+ L = self.renderItemsWithValues(values)
+ if not self.context.required:
+ option = ("<option name='%s' value=''>%s</option>"
+ % (self.name, self.translate(self._msg_no_value)))
+ L.insert(0, option)
+ return L
- return VocabularyEditWidgetBase.renderItemsWithValues(self, values)
+# more general alias
+VocabularyEditWidget = SelectListWidget
- def hidden(self):
- return widget.renderElement('input',
- type='hidden',
- name=self.name,
- id=self.name,
- value=self._showData())
+class DropdownListWidget(SelectListWidget):
+ """Variation of the SelectListWidget that uses a drop-down list."""
+ size = 1
-class VocabularyMultiEditWidget(VocabularyEditWidgetBase):
+class VocabularyMultiEditWidget(MultiDataHelper, VocabularyEditWidgetBase):
"""Vocabulary-backed widget supporting multiple selections."""
def renderItems(self, value):
@@ -375,7 +537,7 @@
values = ()
else:
values = list(value)
- return VocabularyEditWidgetBase.renderItemsWithValues(self, values)
+ return self.renderItemsWithValues(values)
def renderValue(self, value):
# All we really add here is the ':list' in the name argument
@@ -387,27 +549,17 @@
size=self.getValue('size'),
contents="\n".join(rendered_items))
- def hidden(self):
- L = []
- for v in self._showData():
- s = widget.renderElement('input',
- type='hidden',
- name=self.name + ':list',
- value=v)
- assert s[-1] == '>'
- L.append(s[:-1])
- L.append('\n>')
- return ''.join(L)
-
class VocabularyQueryViewBase(ActionHelper, ViewSupport, BrowserView):
"""Vocabulary query support base class."""
- __implements__ = IVocabularyQueryView
+ implements(IVocabularyQueryView)
# This specifically isn't a widget in it's own right, but is a
# form of BrowserView (at least conceptually).
+ widget = None
+
def __init__(self, context, request):
self.vocabulary = context.vocabulary
self.context = context
@@ -418,6 +570,11 @@
assert not name.endswith(".")
self.name = name
+ def setWidget(self, widget):
+ assert self.widget is None
+ assert widget is not None
+ self.widget = widget
+
def renderInput(self):
return self.renderQueryInput()
@@ -442,24 +599,14 @@
# there isn't a query in the form, returns None.
return None
- _performed_action = False
-
def performAction(self, value):
- """Make sure the real action, performQueryAction(), only runs once."""
- if self._performed_action:
- return value
- self._performed_action = True
- return self.performQueryAction(value)
+ return value
ADD_DONE = "adddone"
ADD_MORE = "addmore"
MORE = "more"
-def _message(msgid, default):
- msgid.default = default
- return msgid
-
class IterableVocabularyQueryViewBase(VocabularyQueryViewBase):
"""Query view for IIterableVocabulary objects without more
@@ -469,44 +616,69 @@
getQuery() returns None.
"""
- __implements__ = IVocabularyQueryView
+ implements(IVocabularyQueryView)
queryResultBatchSize = 8
- action = None
-
- _msg_add_done = _message(_("vocabulary-query-button-add-done"), "Add")
- _msg_add_more = _message(_("vocabulary-query-button-add-more"), "Add+More")
- _msg_more = _message(_("vocabulary-query-button-more"), "More")
+ _msg_add_done = message(_("vocabulary-query-button-add-done"),
+ "Add+Done")
+ _msg_add_more = message(_("vocabulary-query-button-add-more"),
+ "Add+More")
+ _msg_more = message(_("vocabulary-query-button-more"),
+ "More")
+ _msg_no_results = message(_("vocabulary-query-message-no-results"),
+ "No Results")
+ _msg_results_header = message(_("vocabulary-query-header-results"),
+ "Search results")
- def setName(self, name):
- VocabularyQueryViewBase.setName(self, name)
+ def __init__(self, *args, **kw):
+ super(IterableVocabularyQueryViewBase, self).__init__(*args, **kw)
self.addAction(ADD_DONE, self._msg_add_done)
self.addAction(ADD_MORE, self._msg_add_more)
self.addAction(MORE, self._msg_more)
+
+ def setName(self, name):
+ super(IterableVocabularyQueryViewBase, self).setName(name)
name = self.name
self.query_index_name = name + ".start"
self.query_selections_name = name + ".picks"
#
get = self.request.form.get
self.action = self.getAction()
- try:
- self.query_index = int(get(self.query_index_name, 0))
- except ValueError:
- self.query_index = 0
- else:
- if self.query_index < 0:
- self.query_index = 0
- self.query_selections = get(self.query_selections_name, [])
- if not isinstance(self.query_selections, list):
- self.query_selections = [self.query_selections]
+ self.query_index = None
+ if self.query_index_name in self.request.form:
+ try:
+ index = int(self.request.form[self.query_index_name])
+ except ValueError:
+ pass
+ else:
+ if index >= 0:
+ self.query_index = index
+ QS = get(self.query_selections_name, [])
+ if not isinstance(QS, list):
+ QS = [QS]
+ self.query_selections = []
+ for token in QS:
+ try:
+ term = self.vocabulary.getTermByToken(token)
+ except LookupError:
+ # XXX unsure what to pass to exception constructor
+ raise WidgetInputError(
+ "(query view for %s)" % self.context,
+ "(query view for %s)" % self.context,
+ "token %r not in vocabulary" % token)
+ else:
+ self.query_selections.append(term.value)
def renderQueryInput(self):
# There's no query support, so we can't actually have input.
return ""
def getResults(self):
- return self.vocabulary
+ if self.query_index is not None:
+ return self.vocabulary
+ else:
+ return None
def renderQueryResults(self, results, value):
# display query results batch
@@ -517,6 +689,9 @@
for xxx in range(qi):
it.next()
except StopIteration:
+ # we should only get here with a botched request; ADD_MORE
+ # and MORE will normally be disabled if there are no results
+ # (see below)
have_more = False
items = []
QS = []
@@ -533,24 +708,28 @@
# see if there's anything else:
it.next()
except StopIteration:
+ if not items:
+ return "<div class='results'>%s</div>" % (
+ self.translate(self._msg_no_results))
have_more = False
self.query_selections = QS
- L = ["<div class='results'>\n",
+ return ''.join(
+ ["<div class='results'>\n",
+ "<h4>%s</h4>\n" % (
+ self.translate(self._msg_results_header)),
self.makeSelectionList(items, self.query_selections_name),
"\n",
self.renderAction(ADD_DONE), "\n",
self.renderAction(ADD_MORE, not have_more), "\n",
- self.renderAction(MORE, not have_more), "\n"]
- if qi:
- L.append("<input type='hidden' name='%s' value='%d' />\n"
- % (self.query_index_name, qi))
- L.append("</div>")
- return ''.join(L)
+ self.renderAction(MORE, not have_more), "\n"
+ "<input type='hidden' name='%s' value='%d' />\n"
+ % (self.query_index_name, qi),
+ "</div>"])
- def performQueryAction(self, value):
+ def performAction(self, value):
if self.action == ADD_DONE:
value = self.addSelections(value)
- self.query_index = 0
+ self.query_index = None
self.query_selections = []
elif self.action == ADD_MORE:
value = self.addSelections(value)
@@ -573,8 +752,8 @@
return self.mkselectionlist("radio", items, name)
def renderQueryResults(self, results, value):
- return IterableVocabularyQueryViewBase.renderQueryResults(
- self, results, [value])
+ return super(IterableVocabularyQueryView, self).renderQueryResults(
+ results, [value])
class IterableVocabularyQueryMultiView(IterableVocabularyQueryViewBase):
=== Zope3/src/zope/app/browser/form/widget.py 1.30 => 1.30.2.1 ===
--- Zope3/src/zope/app/browser/form/widget.py:1.30 Mon May 19 16:54:03 2003
+++ Zope3/src/zope/app/browser/form/widget.py Sun Jun 22 10:22:39 2003
@@ -17,7 +17,9 @@
__metaclass__ = type
-from zope.proxy.introspection import removeAllProxies
+import warnings
+from zope.interface import implements
+from zope.proxy import removeAllProxies
from zope.publisher.browser import BrowserView
from zope.app.interfaces.browser.form import IBrowserWidget
from zope.app.form.widget import Widget
@@ -32,9 +34,52 @@
class BrowserWidget(Widget, BrowserView):
- """A field widget that knows how to display itself as HTML."""
+ """A field widget that knows how to display itself as HTML.
- __implements__ = IBrowserWidget
+ >>> from zope.publisher.browser import TestRequest
+ >>> from zope.schema import Field
+ >>> field = Field(__name__='foo', title=u'Foo')
+ >>> request = TestRequest(form={'field.foo': u'hello\\r\\nworld'})
+ >>> widget = BrowserWidget(field, request)
+ >>> widget.name
+ 'field.foo'
+ >>> widget.title
+ u'Foo'
+ >>> int(widget.haveData())
+ 1
+ >>> widget.getData()
+ u'hello\\r\\nworld'
+ >>> int(widget.required)
+ 1
+ >>> widget.setData('Hey\\nfolks')
+ >>> widget.getData()
+ u'hello\\r\\nworld'
+
+ >>> widget.setPrefix('test')
+ >>> widget.name
+ 'test.foo'
+ >>> int(widget.haveData())
+ 0
+ >>> widget.getData()
+ Traceback (most recent call last):
+ ...
+ MissingInputError: ('foo', u'Foo', 'the field is required')
+ >>> field.required = False
+ >>> int(widget.required)
+ 0
+ >>> widget.getData()
+
+ When we generate labels, the labels are translated, so we need to set up
+ a lot of machinery to support translation:
+
+ >>> setUp()
+ >>> print widget.label()
+ <label for="test.foo">Foo</label>
+ >>> tearDown()
+
+ """
+
+ implements(IBrowserWidget)
propertyNames = (Widget.propertyNames +
['tag', 'type', 'cssClass', 'extra'])
@@ -47,7 +92,7 @@
def haveData(self):
if self.name in self.request.form:
- return self._convert(self.request[self.name]) is not None
+ return self._convert(self.request[self.name]) != self._missing
return False
def getData(self, optional=0):
@@ -116,10 +161,15 @@
extra = self.getValue('extra'))
def render(self, value):
+ warnings.warn("The widget render method is deprecated",
+ DeprecationWarning, 2)
+
self.setData(value)
return self()
def renderHidden(self, value):
+ warnings.warn("The widget render method is deprecated",
+ DeprecationWarning, 2)
self.setData(value)
return self.hidden()
@@ -143,7 +193,66 @@
return self._showData()
class CheckBoxWidget(BrowserWidget):
- """Checkbox widget"""
+ """Checkbox widget
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> from zope.schema import Bool
+ >>> field = Bool(__name__='foo', title=u'on')
+ >>> request = TestRequest(form={'field.foo.used': u'on',
+ ... 'field.foo': u'on'})
+ >>> widget = CheckBoxWidget(field, request)
+ >>> int(widget.haveData())
+ 1
+ >>> int(widget.getData())
+ 1
+
+ >>> def normalize(s):
+ ... return '\\n '.join(s.split())
+
+ >>> print normalize( widget() )
+ <input
+ class="hiddenType"
+ id="field.foo.used"
+ name="field.foo.used"
+ type="hidden"
+ value=""
+ />
+ <input
+ class="checkboxType"
+ checked="checked"
+ id="field.foo"
+ name="field.foo"
+ type="checkbox"
+ />
+
+ >>> print normalize( widget.hidden() )
+ <input
+ class="hiddenType"
+ id="field.foo"
+ name="field.foo"
+ type="hidden"
+ value="on"
+ />
+
+ Calling setData will change what gets output:
+
+ >>> widget.setData(False)
+ >>> print normalize( widget() )
+ <input
+ class="hiddenType"
+ id="field.foo.used"
+ name="field.foo.used"
+ type="hidden"
+ value=""
+ />
+ <input
+ class="checkboxType"
+ id="field.foo"
+ name="field.foo"
+ type="checkbox"
+ />
+
+ """
propertyNames = BrowserWidget.propertyNames + \
['extra', 'default']
@@ -157,19 +266,35 @@
kw = {'checked': None}
else:
kw = {}
- return renderElement(self.getValue('tag'),
+ return "%s %s" % (
+ renderElement(self.getValue('tag'),
+ type = 'hidden',
+ name = self.name+".used",
+ id = self.name+".used",
+ value=""
+ ),
+ renderElement(self.getValue('tag'),
type = self.getValue('type'),
name = self.name,
id = self.name,
cssClass = self.getValue('cssClass'),
extra = self.getValue('extra'),
- **kw)
+ **kw),
+ )
def _convert(self, value):
return value == 'on'
+ def _unconvert(self, value):
+ return value and "on" or ""
+ return value == 'on'
+
def haveData(self):
- return True
+ return (
+ self.name+".used" in self.request.form
+ or
+ self.name in self.request.form
+ )
def getData(self, optional=0):
# When it's checked, its value is 'on'.
@@ -178,24 +303,65 @@
value = self.request.form.get(self.name, 'off')
return value == 'on'
-class PossiblyEmptyMeansMissing:
-
- def haveData(self):
- v = self.request.form.get(self.name)
- if v is None:
- return False
- if not v and getattr(self.context, 'min_length', 1) > 0:
- return False
- return True
+class PossiblyEmptyMeansMissing(BrowserWidget):
def _convert(self, value):
- v = self.request.form.get(self.name)
- if not v and getattr(self.context, 'min_length', 1) > 0:
+ value = super(PossiblyEmptyMeansMissing, self)._convert(value)
+ if not value and getattr(self.context, 'min_length', 1) > 0:
return None
- return v
+ return value
class TextWidget(PossiblyEmptyMeansMissing, BrowserWidget):
- """Text widget."""
+ """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)
+ >>> int(widget.haveData())
+ 1
+ >>> widget.getData()
+ 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 setData will change what gets output:
+
+ >>> widget.setData("Barry")
+ >>> print normalize( widget() )
+ <input
+ class="textType"
+ id="field.foo"
+ name="field.foo"
+ size="20"
+ type="text"
+ value="Barry"
+ />
+
+ """
propertyNames = (BrowserWidget.propertyNames +
['displayWidth', 'displayMaxWidth', 'extra', 'default']
)
@@ -208,11 +374,6 @@
style = ''
__values = None
- def _convert(self, value):
- if self.context.min_length and not value:
- return None
- return value
-
def __init__(self, *args):
super(TextWidget, self).__init__(*args)
@@ -278,12 +439,9 @@
size = self.getValue('displayWidth'),
extra = self.getValue('extra'))
-class Bytes:
+class Bytes(BrowserWidget):
def _convert(self, value):
- if self.context.min_length and not value:
- return None
-
value = super(Bytes, self)._convert(value)
if type(value) is unicode:
try:
@@ -294,7 +452,21 @@
return value
class BytesWidget(Bytes, TextWidget):
- pass
+ """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)
+ >>> int(widget.haveData())
+ 1
+ >>> widget.getData()
+ 'Bob'
+
+ """
class IntWidget(TextWidget):
displayWidth = 10
@@ -329,7 +501,55 @@
raise ConversionError("Invalid datetime data", v)
class TextAreaWidget(PossiblyEmptyMeansMissing, BrowserWidget):
- """Textarea widget."""
+ """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)
+ >>> int(widget.haveData())
+ 1
+ >>> widget.getData()
+ 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\r
+ world!"
+ />
+
+ Calling setData will change what gets output:
+
+ >>> widget.setData("Hey\\ndude!")
+ >>> print normalize( widget() )
+ <textarea
+ cols="60"
+ id="field.foo"
+ name="field.foo"
+ rows="15"
+ >Hey\r
+ dude!</textarea>
+
+ """
propertyNames = BrowserWidget.propertyNames + ['width', 'height', 'extra']
default = ""
@@ -339,10 +559,17 @@
style = ''
def _convert(self, value):
- if self.context.min_length and not value:
- return None
+ value = super(TextAreaWidget, self)._convert(value)
+ if value:
+ value = value.replace("\r\n", "\n")
return value
+ def _unconvert(self, value):
+ value = super(TextAreaWidget, self)._unconvert(value)
+ if value:
+ value = value.replace("\n", "\r\n")
+ return value
+
def __call__(self):
return renderElement("textarea",
name = self.name,
@@ -362,7 +589,21 @@
self.label(), self())
class BytesAreaWidget(Bytes, TextAreaWidget):
- pass
+ """BytesArea widget.
+
+ Multi-line text (unicode) 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)
+ >>> int(widget.haveData())
+ 1
+ >>> widget.getData()
+ 'Hello\\nworld!'
+
+ """
class PasswordWidget(TextWidget):
"""Password Widget"""
@@ -512,7 +753,6 @@
class ListWidget(SingleItemsWidget):
"""List widget."""
- __implements__ = SingleItemsWidget.__implements__
propertyNames = (SingleItemsWidget.propertyNames +
['firstItem', 'items', 'size', 'extra']
)
@@ -734,7 +974,9 @@
extra = ""
# handle other attributes
- for key, value in kw.items():
+ items = kw.items()
+ items.sort()
+ for key, value in items:
if value == None:
value = key
attr_list.append('%s="%s"' % (key, str(value)))
@@ -750,3 +992,16 @@
return "%s>%s</%s>" % (renderTag(tag, **kw), contents, tag)
else:
return renderTag(tag, **kw) + " />"
+
+def setUp():
+ import zope.app.tests.placelesssetup
+ global setUp
+ setUp = zope.app.tests.placelesssetup.setUp
+ setUp()
+
+def tearDown():
+ import zope.app.tests.placelesssetup
+ global tearDown
+ tearDown = zope.app.tests.placelesssetup.tearDown
+ tearDown()
+