[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/form/browser/ -
clean up the sequence input widget
Fred L. Drake, Jr.
fdrake at gmail.com
Fri Jun 3 11:22:18 EDT 2005
Log message for revision 30623:
- clean up the sequence input widget
- add a general sequence display widget
Changed:
U Zope3/trunk/src/zope/app/form/browser/__init__.py
U Zope3/trunk/src/zope/app/form/browser/configure.zcml
U Zope3/trunk/src/zope/app/form/browser/sequencewidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
-=-
Modified: Zope3/trunk/src/zope/app/form/browser/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/__init__.py 2005-06-03 15:11:49 UTC (rev 30622)
+++ Zope3/trunk/src/zope/app/form/browser/__init__.py 2005-06-03 15:22:18 UTC (rev 30623)
@@ -64,8 +64,11 @@
from zope.app.form.browser.itemswidgets import MultiCheckBoxWidget
from zope.app.form.browser.itemswidgets 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.app.form.browser.objectwidget import ObjectWidget
Modified: Zope3/trunk/src/zope/app/form/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/configure.zcml 2005-06-03 15:11:49 UTC (rev 30622)
+++ Zope3/trunk/src/zope/app/form/browser/configure.zcml 2005-06-03 15:22:18 UTC (rev 30623)
@@ -265,6 +265,15 @@
permission="zope.Public"
/>
+ <view
+ type="zope.publisher.interfaces.browser.IBrowserRequest"
+ for="zope.schema.interfaces.ISequence
+ zope.schema.interfaces.IField"
+ provides="zope.app.form.interfaces.IDisplayWidget"
+ factory=".SequenceDisplayWidget"
+ permission="zope.Public"
+ />
+
<!-- Choice collections. dispatch to field + vocabulary lookup.
We must register the collection + choice factories for all ICollection
subclasses because the field (the collection) has precedence: therefore
Modified: Zope3/trunk/src/zope/app/form/browser/sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2005-06-03 15:11:49 UTC (rev 30622)
+++ Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2005-06-03 15:22:18 UTC (rev 30623)
@@ -22,10 +22,11 @@
from zope.i18n import translate
from zope.app import zapi
-from zope.app.form.interfaces import IInputWidget
+from zope.app.form.interfaces import IDisplayWidget, IInputWidget
from zope.app.form.interfaces import WidgetInputError, MissingInputError
from zope.app.form import InputWidget
from zope.app.form.browser.widget import BrowserWidget
+from zope.app.form.browser.widget import DisplayWidget, renderElement
from zope.app.i18n import ZopeMessageIDFactory as _
class SequenceWidget(BrowserWidget, InputWidget):
@@ -38,7 +39,6 @@
implements(IInputWidget)
_type = tuple
- _data = None # pre-existing sequence items (from setRenderedValue)
def __init__(self, context, field, request, subwidget=None):
super(SequenceWidget, self).__init__(context, request)
@@ -96,7 +96,7 @@
def _getWidget(self, i):
field = self.context.value_type
- if self.subwidget:
+ if self.subwidget is not None:
widget = self.subwidget(field, self.request)
else:
widget = zapi.getMultiAdapter((field, self.request), IInputWidget)
@@ -119,12 +119,12 @@
return "\n".join(parts)
def _getRenderedValue(self):
- sequence = self._data
- if sequence is None:
- if self.hasInput():
- sequence = list(self._generateSequence())
- else:
- sequence = []
+ if self._renderedValueSet():
+ sequence = self._data
+ elif self.hasInput():
+ sequence = list(self._generateSequence())
+ else:
+ sequence = []
# ensure minimum number of items in the form
if len(sequence) < self.context.min_length:
sequence = list(sequence)
@@ -132,7 +132,6 @@
# Shouldn't this use self.field.value_type.missing_value,
# instead of None?
sequence.append(None)
- sequence = self._type(sequence)
return sequence
def _getPresenceMarker(self, count=0):
@@ -178,15 +177,6 @@
"""
return (self.name + ".count") in self.request.form
- def setRenderedValue(self, value):
- """Set the data that should be rendered by the widget.
-
- The given value should be used even if the user has entered
- data.
- """
- # the current list of values derived from the "value" parameter
- self._data = value
-
def _generateSequence(self):
"""Take sequence info in the self.request and _data.
@@ -243,3 +233,66 @@
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,
+ self._missingValueMessage.default)
+ data = list(data)
+ if not data:
+ return translate(self._emptySequenceMessage, self.request,
+ self._emptySequenceMessage.default)
+
+ 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 = zapi.getMultiAdapter(
+ (field, self.request), IDisplayWidget)
+ widget.setPrefix('%s.%d.'%(self.name, i))
+ return widget
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2005-06-03 15:11:49 UTC (rev 30622)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2005-06-03 15:22:18 UTC (rev 30623)
@@ -25,24 +25,20 @@
from zope.app import zapi
from zope.app.testing import ztapi
-from zope.app.form.browser import TextWidget, ObjectWidget
+from zope.app.form.browser import TextWidget, ObjectWidget, DisplayWidget
from zope.app.form.browser import TupleSequenceWidget, ListSequenceWidget
+from zope.app.form.browser import SequenceDisplayWidget
from zope.app.form.browser import SequenceWidget
+from zope.app.form.interfaces import IDisplayWidget
from zope.app.form.interfaces import IInputWidget, MissingInputError
from zope.app.form import CustomWidgetFactory
from zope.app.form import CustomSequenceWidgetFactory
+from zope.app.form.browser.tests.support import VerifyResults
from zope.app.form.browser.tests.test_browserwidget import BrowserWidgetTest
-class SequenceWidgetTest(BrowserWidgetTest):
- """Documents and tests the tuple and list (sequence) widgets.
-
- >>> verifyClass(IInputWidget, TupleSequenceWidget)
- True
- >>> verifyClass(IInputWidget, ListSequenceWidget)
- True
- """
+class SequenceWidgetTestHelper(object):
def setUpContent(self, desc=u'', title=u'Foo Title'):
class ITestContent(Interface):
@@ -54,27 +50,35 @@
implements(ITestContent)
self.content = TestObject()
- field = ITestContent['foo']
- field = field.bind(self.content)
- request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
- request.form['field.foo'] = u'Foo Value'
- self._widget = self._WidgetFactory(field, TextLine(), request)
+ self.field = ITestContent['foo'].bind(self.content)
+ self.request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
+ self.request.form['field.foo'] = u'Foo Value'
+ self._widget = self._WidgetFactory(
+ self.field, self.field.value_type, self.request)
def _FieldFactory(self, **kw):
kw.update({
'__name__': u'foo',
'value_type': TextLine(__name__=u'bar')})
return Tuple(**kw)
+
+
+class SequenceWidgetTest(SequenceWidgetTestHelper, BrowserWidgetTest):
+ """Documents and tests the tuple and list (sequence) widgets.
+
+ >>> verifyClass(IInputWidget, TupleSequenceWidget)
+ True
+ >>> verifyClass(IInputWidget, ListSequenceWidget)
+ True
+ """
+
_WidgetFactory = TupleSequenceWidget
def testRender(self):
pass
def setUp(self):
- BrowserWidgetTest.setUp(self)
- self.field = Tuple(
- __name__=u'foo',
- value_type=TextLine(__name__=u'bar'))
+ super(SequenceWidgetTest, self).setUp()
ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
def test_haveNoData(self):
@@ -111,13 +115,14 @@
"""This test verifies that the specified subwidget is not ignored.
(Issue #293)
"""
- self.field = List( __name__=u'foo',
- value_type=TextLine(__name__=u'bar'))
+ self.field = List(__name__=u'foo',
+ value_type=TextLine(__name__=u'bar'))
request = TestRequest()
class PollOption(object) : pass
ow = CustomWidgetFactory(ObjectWidget, PollOption)
- widget = SequenceWidget(self.field, TextLine(), request, subwidget=ow)
+ widget = SequenceWidget(
+ self.field, self.field.value_type, request, subwidget=ow)
assert widget.subwidget is ow
def test_list(self):
@@ -125,25 +130,29 @@
__name__=u'foo',
value_type=TextLine(__name__=u'bar'))
request = TestRequest()
- widget = ListSequenceWidget(self.field, TextLine(), request)
+ widget = ListSequenceWidget(
+ self.field, self.field.value_type, request)
self.failIf(widget.hasInput())
self.assertRaises(MissingInputError, widget.getInputValue)
request = TestRequest(form={'field.foo.add': u'Add bar',
'field.foo.count': u'0'})
- widget = ListSequenceWidget(self.field, TextLine(), request)
+ widget = ListSequenceWidget(
+ self.field, self.field.value_type, request)
self.assert_(widget.hasInput())
self.assertRaises(ValidationError, widget.getInputValue)
request = TestRequest(form={'field.foo.0.bar': u'Hello world!',
'field.foo.count': u'1'})
- widget = ListSequenceWidget(self.field, TextLine(), request)
+ widget = ListSequenceWidget(
+ self.field, self.field.value_type, request)
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), [u'Hello world!'])
def test_new(self):
request = TestRequest()
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
self.failIf(widget.hasInput())
self.assertRaises(MissingInputError, widget.getInputValue)
check_list = ('input', 'name="field.foo.add"')
@@ -152,7 +161,8 @@
def test_add(self):
request = TestRequest(form={'field.foo.add': u'Add bar',
'field.foo.count': u'0'})
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
self.assert_(widget.hasInput())
self.assertRaises(ValidationError, widget.getInputValue)
check_list = (
@@ -164,13 +174,15 @@
def test_request(self):
request = TestRequest(form={'field.foo.0.bar': u'Hello world!',
'field.foo.count': u'1'})
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), (u'Hello world!',))
def test_existing(self):
request = TestRequest()
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
widget.setRenderedValue((u'existing',))
self.failIf(widget.hasInput())
self.assertRaises(MissingInputError, widget.getInputValue)
@@ -200,7 +212,8 @@
'field.foo.0.bar': u'existing', 'field.foo.1.bar': u'second',
'field.foo.remove': u'Remove selected items',
'field.foo.count': u'2'})
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
widget.setRenderedValue((u'existing', u'second'))
self.assertEquals(widget.getInputValue(), (u'second',))
check_list = (
@@ -216,7 +229,8 @@
def test_min(self):
request = TestRequest()
self.field.min_length = 2
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
widget.setRenderedValue((u'existing',))
self.assertRaises(MissingInputError, widget.getInputValue)
check_list = (
@@ -231,7 +245,8 @@
def test_max(self):
request = TestRequest()
self.field.max_length = 1
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
widget.setRenderedValue((u'existing',))
self.assertRaises(MissingInputError, widget.getInputValue)
s = widget()
@@ -240,7 +255,8 @@
def test_anonymousfield(self):
self.field = Tuple(__name__=u'foo', value_type=TextLine())
request = TestRequest()
- widget = TupleSequenceWidget(self.field, TextLine(), request)
+ widget = TupleSequenceWidget(
+ self.field, self.field.value_type, request)
widget.setRenderedValue((u'existing',))
s = widget()
check_list = (
@@ -251,13 +267,76 @@
self.verifyResult(s, check_list, inorder=True)
+class SequenceDisplayWidgetTest(
+ VerifyResults, SequenceWidgetTestHelper, unittest.TestCase):
+
+ def _WidgetFactory(self, *args, **kw):
+ w = SequenceDisplayWidget(*args, **kw)
+ w.cssClass = "testwidget"
+ return w
+
+ def setUp(self):
+ self.setUpContent()
+ self.request = TestRequest()
+ self.widget = self._WidgetFactory(
+ self.field, self.field.value_type, self.request)
+ ztapi.browserViewProviding(ITextLine, DisplayWidget, IDisplayWidget)
+
+ def test_render_empty(self):
+ self.content.foo = ()
+ self.assertEquals(self.widget(), '(no values)')
+
+ def test_render_missing(self):
+ self.content.foo = self.field.missing_value
+ self.assertEquals(self.widget(), '(no value available)')
+
+ def test_render_single(self):
+ self.content.foo = (u'one value',)
+ check_list = ['<ol', 'class=', 'testwidget',
+ '<li', 'one value', '</li', '</ol']
+ self.verifyResult(self.widget(), check_list, inorder=True)
+
+ def test_render_multiple(self):
+ self.content.foo = (u'one', u'two', u'three', u'four')
+ check_list = ['<ol', 'class=', 'testwidget',
+ '<li', 'one', '</li',
+ '<li', 'two', '</li',
+ '<li', 'three', '</li',
+ '<li', 'four', '</li',
+ '</ol']
+ self.verifyResult(self.widget(), check_list, inorder=True)
+
+ def test_render_alternate_cssClass(self):
+ self.content.foo = (u'one value',)
+ check_list = ['<ol', 'class=', 'altclass',
+ '<li', 'one value', '</li', '</ol']
+ self.widget.cssClass = 'altclass'
+ self.verifyResult(self.widget(), check_list, inorder=True)
+
+ def test_honors_subwidget(self):
+ self.widget = self._WidgetFactory(
+ self.field, self.field.value_type, self.request,
+ subwidget=UppercaseDisplayWidget)
+ self.content.foo = (u'first value', u'second value')
+ check_list = ['<ol', 'class=', 'testwidget',
+ '<li', 'FIRST VALUE', '</li',
+ '<li', 'SECOND VALUE', '</li',
+ '</ol']
+ self.verifyResult(self.widget(), check_list, inorder=True)
+
+
+class UppercaseDisplayWidget(DisplayWidget):
+
+ def __call__(self):
+ return super(UppercaseDisplayWidget, self).__call__().upper()
+
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(SequenceWidgetTest),
doctest.DocTestSuite(),
+ unittest.makeSuite(SequenceDisplayWidgetTest),
))
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
-
-
More information about the Zope3-Checkins
mailing list