[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/ Fix for
http://collector.zope.org/Zope3-dev/251
Garrett Smith
garrett at mojave-corp.com
Thu Aug 5 19:43:16 EDT 2004
Log message for revision 26931:
Fix for http://collector.zope.org/Zope3-dev/251
Changes include:
- Fixed incorrectly specified vocab terms for boolean widgets (boolwidgets.py).
- New booleanradionwidget ftest, which is primarily used to verify the fix, but also flushed out some other itemswidget-related bugs.
- Some (hopefully non-controversial) cleanup of itemswidgets.py:
- Deleted some SimpleInputWidget overrides, which were originally
copied/pasted, but since became stale
- Removed conditional use of _emptyMarker() -- it's always used now
(this was the primary bug associated with the issue)
- Raising ConversionError in _toFieldValue
- Updated tests as needed.
Changed:
U Zope3/trunk/src/zope/app/configure.zcml
U Zope3/trunk/src/zope/app/form/browser/boolwidgets.py
U Zope3/trunk/src/zope/app/form/browser/ftests/support.py
A Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py
U Zope3/trunk/src/zope/app/form/browser/itemswidgets.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py
-=-
Modified: Zope3/trunk/src/zope/app/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/configure.zcml 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/configure.zcml 2004-08-05 23:43:16 UTC (rev 26931)
@@ -60,7 +60,9 @@
<include package="zope.app.uniqueid" />
<!-- Misc. Service Manager objects -->
+ <!-- Aktari delete - we don't want this
<include package="zope.app.module" />
+ -->
<!-- Broken-object support -->
<include package="zope.app.broken" />
Modified: Zope3/trunk/src/zope/app/form/browser/boolwidgets.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/boolwidgets.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/boolwidgets.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -91,17 +91,17 @@
def BooleanRadioWidget(field, request, true=_('on'), false=_('off')):
- vocabulary = SimpleVocabulary.fromItems( ((True, true), (False, false)) )
+ vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
return RadioWidget(field, vocabulary, request)
def BooleanSelectWidget(field, request, true=_('on'), false=_('off')):
- vocabulary = SimpleVocabulary.fromItems( ((True, true), (False, false)) )
+ vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
widget = SelectWidget(field, vocabulary, request)
widget.size = 2
return widget
def BooleanDropdownWidget(field, request, true=_('on'), false=_('off')):
- vocabulary = SimpleVocabulary.fromItems( ((True, true), (False, false)) )
+ vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
return DropdownWidget(field, vocabulary, request)
Modified: Zope3/trunk/src/zope/app/form/browser/ftests/support.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/ftests/support.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/ftests/support.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -18,16 +18,30 @@
import re
from zope.configuration import xmlconfig
-def registerEditForm(schema):
+def registerEditForm(schema, widgets={}):
+ """Registers an edit form for the specified schema.
+
+ widgets is a mapping of field name to dict. The dict for each field must
+ contain a 'class' item, which is the widget class, and any additional
+ widget attributes (e.g. text field size, rows, cols, etc.)
+ """
+ widgetsXml = []
+ for field in widgets:
+ widgetsXml.append('<widget field="%s"' % field)
+ for attr in widgets[field]:
+ widgetsXml.append(' %s="%s"' % (attr, widgets[field][attr]))
+ widgetsXml.append(' />')
xmlconfig.string("""
<configure xmlns="http://namespaces.zope.org/browser">
<include package="zope.app.form.browser" file="meta.zcml" />
<editform
name="edit.html"
schema="%s"
- permission="zope.View" />
+ permission="zope.View">
+ %s
+ </editform>
</configure>
- """ % schema.__identifier__)
+ """ % (schema.__identifier__, ''.join(widgetsXml)))
def defineSecurity(class_, schema):
Added: Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -0,0 +1,153 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Radio Widget Functional Tests
+
+$Id: $
+"""
+import unittest
+from persistent import Persistent
+from transaction import get_transaction
+
+from support import *
+
+from zope.interface import Interface
+from zope.interface import implements
+
+from zope.schema import Bool
+
+from zope.app.traversing.api import traverse
+
+from zope.app.tests.functional import BrowserTestCase
+
+
+class IFoo(Interface):
+
+ bar = Bool(title=u'Bar')
+
+registerEditForm(IFoo, widgets={
+ 'bar': { 'class': 'zope.app.form.browser.BooleanRadioWidget' }})
+
+
+class Foo(Persistent):
+
+ implements(IFoo)
+
+ def __init__(self):
+ self.bar = True
+
+defineSecurity(Foo, IFoo)
+
+
+class Test(BrowserTestCase):
+
+
+ def test_display_editform(self):
+ self.getRootFolder()['foo'] = Foo()
+ get_transaction().commit()
+
+ # display edit view
+ response = self.publish('/foo/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # bar field should be displayed as two radio buttons
+ self.assert_(patternExists(
+ '<input .*checked="checked".*name="field.bar".*type="radio".*'
+ 'value="on".* />',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<input .*name="field.bar".*type="radio".*value="off".* />',
+ response.getBody()))
+
+ # a hidden element is used to note that the field is present
+ self.assert_(patternExists(
+ '<input name="field.bar-empty-marker" type="hidden" value="1".* />',
+ response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['foo'] = Foo()
+ get_transaction().commit()
+
+ # submit edit view
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar' : 'off'})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'foo')
+ self.assertEqual(object.bar, False)
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['foo'] = Foo()
+ get_transaction().commit()
+
+ # temporarily make bar field not required
+ IFoo['bar'].required = False
+
+ # submit missing value for bar
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar-empty-marker' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # confirm use of missing_value as new object value
+ self.assert_(IFoo['bar'].missing_value is None)
+ object = traverse(self.getRootFolder(), 'foo')
+ self.assert_(object.bar is None)
+
+ # restore bar required state
+ IFoo['bar'].required = True
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['foo'] = Foo()
+ get_transaction().commit()
+
+ self.assert_(IFoo['bar'].required)
+
+ # submit missing value for required field bar
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar-empty-marker' : '1'})
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('bar', response.getBody()))
+
+
+ def test_invalid_allowed_value(self):
+ self.getRootFolder()['foo'] = Foo()
+ get_transaction().commit()
+
+ # submit a value for bar isn't allowed
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar' : 'bogus' })
+ self.assertEqual(response.getStatus(), 200)
+
+ self.assert_(validationErrorExists('bar', 'Invalid value',
+ response.getBody()))
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Modified: Zope3/trunk/src/zope/app/form/browser/itemswidgets.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/itemswidgets.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/itemswidgets.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -28,7 +28,8 @@
from zope.app.form.browser.widget import SimpleInputWidget, renderElement
from zope.app.form.browser.interfaces import IVocabularyQueryView
from zope.app.form.interfaces import IInputWidget, IDisplayWidget
-from zope.app.form.interfaces import WidgetInputError
+from zope.app.form.interfaces import ConversionError
+
from zope.app.i18n import ZopeMessageIDFactory as _
@@ -126,29 +127,6 @@
values.append(term.value)
return values
- def getInputValue(self):
- """Get the value that was inputed."""
- input = self.request.form.get(self.name, self._data_marker)
- field = self.context
-
- # missing value is okay if field is not required
- if input == self._data_marker and not field.required and \
- self.empty_marker_name in self.request.form:
- return field.missing_value
-
- # Now let's see whether we have a valid input value
- try:
- value = self._toFieldValue(input)
- except ValidationError, error:
- self._error = WidgetInputError(
- self.context.__name__,
- self.context.title,
- error)
-
- raise self._error
-
- return value
-
def _emptyMarker(self):
"""Mark the form so that empty selections are also valid."""
return '<input name="%s" type="hidden" value="1" />' % (
@@ -159,10 +137,6 @@
return (self.name in self.request.form or
self.empty_marker_name in self.request.form)
- def setRenderedValue(self, value):
- """Store the ready-for-HTML value."""
- self._data = value
-
def _toFieldValue(self, input):
"""See `SimpleInputWidget`"""
raise NotImplementedError(
@@ -170,21 +144,7 @@
"It may be inherited from the mix-in classes SingleDataHelper\n"
"or MultiDataHelper")
- def _getFormValue(self):
- if self._data is self._data_marker:
- # The data has not been retrieved from the form, so let's do that
- if self.hasInput():
- try:
- value = self.getInputValue()
- except WidgetInputError:
- value = self.request.form.get(self.name, self._missing)
- else:
- value = self._getDefault()
- else:
- value = self._data
- return value
-
class SingleDataHelper(object):
"""Mix-in helper class for getting the term from the HTML form.
@@ -193,9 +153,12 @@
def _toFieldValue(self, input):
if input:
- return self.convertTokensToValues([input])[0]
+ try:
+ return self.convertTokensToValues([input])[0]
+ except InvalidValue, e:
+ raise ConversionError("Invalid value", e)
else:
- return None
+ return self.context.missing_value
def hidden(self):
return renderElement(u'input',
@@ -221,7 +184,10 @@
return []
if not isinstance(input, list):
input = [input]
- return self.convertTokensToValues(input)
+ try:
+ return self.convertTokensToValues(input)
+ except InvalidValue, e:
+ raise ConversionError("Invalid value", e)
def _getDefault(self):
# Return the default value for this widget;
@@ -242,7 +208,7 @@
def __call__(self):
"""See IBrowserWidget."""
value = self._getFormValue()
- if value is None:
+ if not value:
return self.translate(self._messageNoValue)
else:
term = self.vocabulary.getTerm(value)
@@ -363,8 +329,7 @@
have_results = True
contents.append(self._div('value', self.renderValue(value)))
- if not self.context.required:
- contents.append(self._emptyMarker())
+ contents.append(self._emptyMarker())
if self.queryview is not None and not have_results:
html = self.queryview.renderInput()
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -22,7 +22,7 @@
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from zope.publisher.browser import TestRequest
-from zope.app.form.interfaces import WidgetInputError
+from zope.app.form.interfaces import ConversionError
from zope.app.form.browser.itemswidgets import ItemsWidgetBase
from zope.app.form.browser.itemswidgets import ItemDisplayWidget
from zope.app.form.browser.itemswidgets import ItemsMultiDisplayWidget
@@ -201,7 +201,7 @@
widget = self._makeWidget(form={'field.choice': 'ten'})
widget.setPrefix('field.')
widget._getFormValue()
- self.assert_(isinstance(widget._error, WidgetInputError))
+ self.assert_(isinstance(widget._error, ConversionError))
def test_hidden(self):
widget = self._makeWidget(form={'field.choice': 'token2'})
@@ -398,7 +398,7 @@
widget = self._makeWidget(form={'field.numbers': ['ten']})
widget.setPrefix('field.')
widget._getFormValue()
- self.assert_(isinstance(widget._error, WidgetInputError))
+ self.assert_(isinstance(widget._error, ConversionError))
def test_hidden(self):
widget = self._makeWidget(
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -103,6 +103,25 @@
self.verifyResult(self._widget.hidden(), check_list)
+ def testHasInput(self):
+ self._widget.request.form.clear()
+ self.assert_(not self._widget.hasInput())
+ self._widget.request.form['field.foo-empty-marker'] = '1'
+ self.assert_(self._widget.hasInput())
+ self._widget.request.form['field.foo'] = u'Foo Value'
+ self.assert_(self._widget.hasInput())
+ del self._widget.request.form['field.foo-empty-marker']
+ self.assert_(self._widget.hasInput())
+
+
+ def testRenderEmptyMarker(self):
+ self.verifyResult(self._widget(), ('field.foo-empty-marker',))
+ self._widget.setRenderedValue(u'bar')
+ self.verifyResult(self._widget(), ('field.foo-empty-marker',))
+ self._widget.setRenderedValue(u'not a legal value')
+ self.verifyResult(self._widget(), ('field.foo-empty-marker',))
+
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(RadioWidgetTest),
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py 2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py 2004-08-05 23:43:16 UTC (rev 26931)
@@ -47,6 +47,7 @@
<option value="&blah&">&blah&</option>
</select>
</div>
+<input name="field.terms-empty-marker" type="hidden" value="1" />
</div>'''
class SelectWidgetHTMLEncodingTest(unittest.TestCase):
More information about the Zope3-Checkins
mailing list