Skip to content

Commit

Permalink
Merged r26931 and r26932 from trunk.
Browse files Browse the repository at this point in the history
  • Loading branch information
Garrett Smith committed Aug 11, 2004
2 parents 85499c4 + 701b5a3 commit dacdc63
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 61 deletions.
6 changes: 3 additions & 3 deletions browser/boolwidgets.py
Expand Up @@ -89,17 +89,17 @@ def _getFormInput(self):


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)
102 changes: 102 additions & 0 deletions browser/ftests/support.py
@@ -0,0 +1,102 @@
##############################################################################
#
# 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.
#
##############################################################################
"""Support Functions for Widget Functional Tests
$Id$
"""
import re
from zope.configuration import xmlconfig

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">
%s
</editform>
</configure>
""" % (schema.__identifier__, ''.join(widgetsXml)))


def defineSecurity(class_, schema):
class_ = '%s.%s' % (class_.__module__, class_.__name__)
schema = schema.__identifier__
xmlconfig.string("""
<configure xmlns="http://namespaces.zope.org/zope">
<include package="zope.app.component" file="meta.zcml" />
<class class="%s">
<require
permission="zope.Public"
interface="%s"
set_schema="%s" />
</class>
</configure>
""" % (class_, schema, schema))


def defineWidgetView(field_interface, widget_class, view_type):
field_interface = field_interface.__identifier__
widget_class = '%s.%s' % (widget_class.__module__, widget_class.__name__)
view_type = '%s.%s' % (view_type.__module__, view_type.__name__)
xmlconfig.string("""
<configure xmlns="http://namespaces.zope.org/zope">
<include package="zope.app.component" file="meta.zcml" />
<view
for="%s"
type="zope.publisher.interfaces.browser.IBrowserRequest"
factory="%s"
provides="%s"
permission="zope.Public"
/>
</configure>
""" % (field_interface, widget_class, view_type))


def patternExists(pattern, source, flags=0):
return re.search(pattern, source, flags) is not None


def validationErrorExists(field, error_msg, source):
return patternExists(
'name="field.%s".*%s' % (field, error_msg), source, re.DOTALL)


def missingInputErrorExists(field, source):
return validationErrorExists(field, 'Required input is missing.', source)


def invalidValueErrorExists(field, source):
# assumes this error is displayed for select elements
return patternExists(
'name="field.%s".*</select>.*Invalid value' % field,
source, re.DOTALL)


def updatedMsgExists(source):
return patternExists('<p>Updated .*</p>', source)
153 changes: 153 additions & 0 deletions browser/ftests/test_booleanradiowidget.py
@@ -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')
61 changes: 13 additions & 48 deletions browser/itemswidgets.py
Expand Up @@ -25,7 +25,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 _


Expand Down Expand Up @@ -123,29 +124,6 @@ def convertTokensToValues(self, tokens):
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" />' % (
Expand All @@ -156,31 +134,13 @@ def hasInput(self):
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(
"_toFieldValue(input) must be implemented by a subclass\n"
"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.
Expand All @@ -190,9 +150,12 @@ class SingleDataHelper(object):

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',
Expand All @@ -218,7 +181,10 @@ def _toFieldValue(self, input):
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;
Expand All @@ -239,7 +205,7 @@ class ItemDisplayWidget(SingleDataHelper, ItemsWidgetBase):
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)
Expand Down Expand Up @@ -359,8 +325,7 @@ def __call__(self):
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()
Expand Down

0 comments on commit dacdc63

Please sign in to comment.