[Zope3-checkins] CVS: Zope3/src/zope/app/form/browser -
itemswidgets.py:1.1 objectwidget.py:1.1 sequencewidget.py:1.1
textwidgets.py:1.1 widget.py:1.2
Philipp von Weitershausen
philikon at philikon.de
Wed Mar 17 12:35:04 EST 2004
Update of /cvs-repository/Zope3/src/zope/app/form/browser
In directory cvs.zope.org:/tmp/cvs-serv13962/form/browser
Modified Files:
widget.py
Added Files:
itemswidgets.py objectwidget.py sequencewidget.py
textwidgets.py
Log Message:
Shorten the zope.app.form.browser.widget module by grouping widgets
logically in separate modules.
=== Added File Zope3/src/zope/app/form/browser/itemswidgets.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Browser widgets for items
$Id: itemswidgets.py,v 1.1 2004/03/17 17:35:02 philikon Exp $
"""
from zope.interface import implements
from zope.i18n import translate
from zope.proxy import removeAllProxies
from zope.app.form.interfaces import IInputWidget
from zope.app.form.browser.widget import BrowserWidget, renderElement
ListTypes = list, tuple
class CheckBoxWidget(BrowserWidget):
"""A checkbox widget used to display Bool fields.
For more detailed documentation, including sample code, see
tests/test_checkboxwidget.py.
"""
implements(IInputWidget)
type = 'checkbox'
default = 0
extra = ''
def __call__(self):
data = self._showData()
if data:
kw = {'checked': None}
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,
**kw),
)
def _convert(self, value):
return value == 'on'
def _unconvert(self, value):
return value and "on" or ""
return value == 'on'
def hasInput(self):
return self.name + ".used" in self.request.form or \
super(CheckBoxWidget, self).hasInput()
def getInputValue(self):
# When it's checked, its value is 'on'.
# When a checkbox is unchecked, it does not appear in the form data.
value = self.request.form.get(self.name, 'off')
return value == 'on'
class ItemsWidget(BrowserWidget):
"""A widget that has a number of items in it."""
implements(IInputWidget)
class SingleItemsWidget(ItemsWidget):
"""A widget with a number of items that has only a single
selectable item."""
default = ""
firstItem = False
def textForValue(self, value):
'''Returns the text for the given value.
Override this in subclasses.'''
# The text could be a MessageID, in which case we should try to
# translate it.
return translate(self.context, value, context=self.request,
default=value)
def renderItems(self, value):
name = self.name
# get items
items = self.context.allowed_values
# check if we want to select first item
if (not value and getattr(self.context, 'firstItem', False)
and len(items) > 0):
value = items[0]
cssClass = self.cssClass
# FIXME: what if we run into multiple items with same value?
rendered_items = []
count = 0
for item_value in items:
item_text = self.textForValue(item_value)
if item_value == value:
rendered_item = self.renderSelectedItem(count,
item_text,
item_value,
name,
cssClass)
else:
rendered_item = self.renderItem(count,
item_text,
item_value,
name,
cssClass)
rendered_items.append(rendered_item)
count += 1
return rendered_items
class ListWidget(SingleItemsWidget):
"""List widget."""
size = 5
def __call__(self):
renderedItems = self.renderItems(self._showData())
return renderElement('select',
name=self.name,
id=self.name,
cssClass=self.cssClass,
size=self.size,
contents="\n".join(renderedItems),
extra=self.extra)
def renderItem(self, index, text, value, name, cssClass):
return renderElement('option', contents=text, value=value,
cssClass=cssClass)
def renderSelectedItem(self, index, text, value, name, cssClass):
return renderElement('option', contents=text, value=value,
cssClass=cssClass, selected=None)
class RadioWidget(SingleItemsWidget):
"""Radio buttons widget."""
orientation = "vertical"
def __call__(self):
rendered_items = self.renderItems(self._showData())
orientation = self.orientation
if orientation == 'horizontal':
return " ".join(rendered_items)
else:
return '<br />'.join(rendered_items)
def _renderItem(self, index, text, value, name, cssClass, checked):
id = '%s.%s' % (name, index)
if checked:
element = renderElement('input',
type="radio",
cssClass=cssClass,
name=name,
id=id,
value=value,
checked=None)
else:
element = renderElement('input',
type="radio",
cssClass=cssClass,
name=name,
id=id,
value=value)
return '%s<label for="%s">%s</label>' % (element, id, text)
def renderItem(self, index, text, value, name, cssClass):
return self._renderItem(index, text, value, name, cssClass, False)
def renderSelectedItem(self, index, text, value, name, cssClass):
return self._renderItem(index, text, value, name, cssClass, True)
def label(self):
return translate(self.context, self.title, context=self.request,
default=self.title)
def row(self):
return ('<div class="%s"><label for="%s">%s</label></div>'
'<div class="field" id="%s">%s</div>' % (
self.labelClass(), self.name, self.label(), self.name, self()))
class MultiItemsWidget(ItemsWidget):
"""A widget with a number of items that has multiple selectable items."""
default = []
def _convert(self, value):
if not value:
return []
if isinstance(value, ListTypes):
return value
return [value]
def renderItems(self, value):
# need to deal with single item selects
value = removeAllProxies(value)
if not isinstance(value, ListTypes):
value = [value]
name = self.name
items = self.context.allowed_values
cssClass = self.cssClass
rendered_items = []
count = 0
for item in items:
try:
item_value, item_text = item
except ValueError:
item_value = item
item_text = item
if item_value in value:
rendered_item = self.renderSelectedItem(count,
item_text,
item_value,
name,
cssClass)
else:
rendered_item = self.renderItem(count,
item_text,
item_value,
name,
cssClass)
rendered_items.append(rendered_item)
count += 1
return rendered_items
class MultiListWidget(MultiItemsWidget):
"""List widget with multiple select."""
size = 5
def __call__(self):
rendered_items = self.renderItems(self._showData())
return renderElement('select',
name=self.name,
id=self.name,
multiple=None,
cssClass=self.cssClass,
size=self.size,
contents="\n".join(rendered_items),
extra=self.extra)
def renderItem(self, index, text, value, name, cssClass):
return renderElement('option', contents=text, value=value)
def renderSelectedItem(self, index, text, value, name, cssClass):
return renderElement('option', contents=text, value=value,
selected=None)
class MultiCheckBoxWidget(MultiItemsWidget):
"""Multiple checkbox widget."""
orientation = "vertical"
def __call__(self):
rendered_items = self.renderItems(self._showData())
orientation = self.orientation
if orientation == 'horizontal':
return " ".join(rendered_items)
else:
return "<br />".join(rendered_items)
def renderItem(self, index, text, value, name, cssClass):
return renderElement('input',
type="checkbox",
cssClass=cssClass,
name=name,
id=name,
value=value) + text
def renderSelectedItem(self, index, text, value, name, cssClass):
return renderElement('input',
type="checkbox",
cssClass=cssClass,
name=name,
id=name,
value=value,
checked=None) + text
=== Added File Zope3/src/zope/app/form/browser/objectwidget.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Browser widgets for text-like data
$Id: objectwidget.py,v 1.1 2004/03/17 17:35:02 philikon Exp $
"""
from zope.interface import implements
from zope.schema import getFieldNamesInOrder
from zope.app.form.interfaces import IInputWidget
from zope.app.form.browser.widget import BrowserWidget
from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
class ObjectWidget(BrowserWidget):
"""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)
# 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
setUpEditWidgets(self, self.context.schema, source=self.context,
prefix=self.name, names=self.names,
context=self.context)
def __call__(self):
"""Render the widget
"""
render = []
# XXX see if there's some widget layout already
# generate each widget from fields in the schema
field = self.context
title = field.title or field.__name__
render.append('<fieldset><legend>%s</legend>'%title)
for name, widget in self.getSubWidgets():
render.append(widget.row())
render.append('</fieldset>')
return '\n'.join(render)
def getSubWidgets(self):
l = []
for name in self.names:
l.append((name, getattr(self, '%s_widget'%name)))
return l
def hidden(self):
''' Render the list as hidden fields '''
for name, widget in self.getSubWidgets():
s += widget.hidden()
return s
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).
"""
content = self.factory()
for name, widget in self.getSubWidgets():
setattr(content, name, widget.getInputValue())
return content
def applyChanges(self, content):
field = self.context
# create our new object value
value = field.query(content, None)
if value is None:
# XXX ObjectCreatedEvent here would be nice
value = self.factory()
# apply sub changes, see if there *are* any changes
# XXX 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)
return changes
def hasInput(self):
"""Is there input data for the field
Return True if there is data and False otherwise.
"""
for name, widget in self.getSubWidgets():
if widget.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, widget in self.getSubWidgets():
widget.setRenderedValue(getattr(value, name, None))
=== Added File Zope3/src/zope/app/form/browser/sequencewidget.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Browser widgets for sequences
$Id: sequencewidget.py,v 1.1 2004/03/17 17:35:02 philikon Exp $
"""
import re
from zope.interface import implements
from zope.i18n import translate
from zope.app import zapi
from zope.app.form.interfaces import IInputWidget
from zope.app.form.browser.widget import BrowserWidget
from zope.app.i18n import ZopeMessageIDFactory as _
class SequenceWidget(BrowserWidget):
"""A widget baseclass for a sequence of fields.
subwidget - Optional CustomWidget used to generate widgets for the
items in the sequence
"""
implements(IInputWidget)
_type = tuple
_data = () # pre-existing sequence items (from setRenderedValue)
def __init__(self, context, request, subwidget=None):
super(SequenceWidget, self).__init__(context, request)
self.subwidget = None
def __call__(self):
"""Render the widget
"""
# XXX we really shouldn't allow value_type of None
if self.context.value_type is None:
return ''
render = []
# length of sequence info
sequence = list(self._generateSequence())
num_items = len(sequence)
min_length = self.context.min_length
max_length = self.context.max_length
# ensure minimum number of items in the form
if num_items < min_length:
for i in range(min_length - num_items):
sequence.append(None)
num_items = len(sequence)
# generate each widget from items in the sequence - adding a
# "remove" button for each one
for i in range(num_items):
value = sequence[i]
render.append('<tr><td>')
if num_items > min_length:
render.append(
'<input type="checkbox" name="%s.remove_%d" />' % (
self.name, i)
)
widget = self._getWidget(i)
widget.setRenderedValue(value)
render.append(widget() + '</td></tr>')
# possibly generate the "remove" and "add" buttons
buttons = ''
if render and num_items > min_length:
button_label = _('remove-selected-items', "Remove selected items")
button_label = translate(self.context, button_label,
context=self.request, default=button_label)
buttons += '<input type="submit" value="%s" />' % button_label
if max_length is None or num_items < max_length:
field = self.context.value_type
button_label = _('Add %s')
button_label = translate(self.context, button_label,
context=self.request, default=button_label)
button_label = button_label % (field.title or field.__name__)
buttons += '<input type="submit" name="%s.add" value="%s" />' % (
self.name, button_label)
if buttons:
render.append('<tr><td>%s</td></tr>' % buttons)
return '<table border="0">' + ''.join(render) + '</table>'
def _getWidget(self, i):
field = self.context.value_type
if self.subwidget:
widget = self.subwidget(field, self.request)
else:
widget = zapi.getViewProviding(field, IInputWidget, self.request,
context=self.context)
widget.setPrefix('%s.%d.'%(self.name, i))
return widget
def hidden(self):
''' Render the list as hidden fields '''
# length of sequence info
sequence = self._generateSequence()
num_items = len(sequence)
min_length = self.context.min_length
# ensure minimum number of items in the form
if num_items < min_length:
for i in range(min_length - num_items):
sequence.append(None)
num_items = len(sequence)
# generate hidden fields for each value
s = ''
for i in range(num_items):
value = sequence[i]
widget = self._getWidget(i)
widget.setRenderedValue(value)
s += widget.hidden()
return s
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 returned in the case of one or more
errors encountered, inputting, converting, or validating the data.
"""
sequence = self._generateSequence()
# validate the input values
for value in sequence:
self.context.value_type.validate(value)
return self._type(sequence)
# XXX 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 len(self._generateSequence()) != 0
def setRenderedValue(self, value):
"""Set the default data for 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.
"""
len_prefix = len(self.name)
adding = False
removing = []
subprefix = re.compile(r'(\d+)\.(.*)$')
if self.context.value_type is None:
return []
# pre-populate
found = {}
if self._data is not None:
found = dict(enumerate(self._data))
# now look through the request for interesting values
for key in self.request.keys():
if not key.startswith(self.name):
continue
token = key[len_prefix+1:] # skip the '.'
if token == 'add':
# append a new blank field to the sequence
adding = True
elif token.startswith('remove_'):
# remove the index indicated
removing.append(int(token[7:]))
else:
match = subprefix.match(token)
if match is None:
continue
# key refers to a sub field
i = int(match.group(1))
# find a widget for the sub-field and use that to parse the
# request data
widget = self._getWidget(i)
value = widget.getInputValue()
found[i] = value
# remove the indicated indexes
for i in removing:
del found[i]
# generate the list, sorting the dict's contents by key
items = found.items()
items.sort()
sequence = [value for key, value in items]
# add an entry to the list if the add button has been pressed
if adding:
sequence.append(None)
return sequence
class TupleSequenceWidget(SequenceWidget):
pass
class ListSequenceWidget(SequenceWidget):
_type = list
=== Added File Zope3/src/zope/app/form/browser/textwidgets.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Browser widgets with text-based input
$Id: textwidgets.py,v 1.1 2004/03/17 17:35:02 philikon Exp $
"""
from zope.interface import implements
from zope.app.form.interfaces import IInputWidget, ConversionError
from zope.app.form.browser.widget import BrowserWidget, renderElement
from zope.app.datetimeutils import parseDatetimetz
from zope.app.datetimeutils import DateTimeError
class TextWidget(BrowserWidget):
"""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"
/>
"""
implements(IInputWidget)
default = ''
displayWidth = 20
displayMaxWidth = ""
extra = ''
# XXX Alex Limi doesn't like this!
# style = "width:100%"
style = ''
__values = None
def __init__(self, *args):
super(TextWidget, self).__init__(*args)
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=self._showData(),
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=self._showData(),
cssClass=self.cssClass,
style=self.style,
size=self.displayWidth,
extra=self.extra)
class Bytes(BrowserWidget):
def _convert(self, value):
value = super(Bytes, self)._convert(value)
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 ASCII(Bytes):
"""ASCII"""
class ASCIIWidget(BytesWidget):
"""ASCII widget.
Single-line data (string) input
"""
class TextAreaWidget(BrowserWidget):
"""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\r
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>
"""
implements(IInputWidget)
default = ""
width = 60
height = 15
extra = ""
style = ''
def _convert(self, value):
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,
id=self.name,
cssClass=self.cssClass,
rows=self.height,
cols=self.width,
style=self.style,
contents=self._showData(),
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 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 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
if displayMaxWidth > 0:
return renderElement(self.tag,
type=self.type,
name=self.name,
id=self.name,
cssClass=self.cssClass,
size=self.displayWidth,
maxlength=displayMaxWidth,
extra=self.extra)
else:
return renderElement(self.tag,
type=self.type,
name=self.name,
id=self.name,
cssClass=self.cssClass,
size=self.displayWidth,
extra=self.extra)
def hasInput(self):
file = self.request.form.get(self.name)
if file is None:
return False
if getattr(file, 'filename', ''):
return True
try:
seek = file.seek
read = file.read
except AttributeError:
return False
seek(0)
if read(1):
return True
return False
def _convert(self, value):
try:
seek = value.seek
read = value.read
except AttributeError, e:
raise ConversionError('Value is not a file object', e)
else:
seek(0)
data = read()
if data or getattr(value, 'filename', ''):
return data
else:
return self.context.missing_value
class IntWidget(TextWidget):
displayWidth = 10
def _convert(self, value):
if value == self._missing:
return self.context.missing_value
else:
try:
return int(value)
except ValueError, v:
raise ConversionError("Invalid integer data", v)
class FloatWidget(TextWidget):
implements(IInputWidget)
displayWidth = 10
def _convert(self, value):
if value == self._missing:
return self.context.missing_value
else:
try:
return float(value)
except ValueError, v:
raise ConversionError("Invalid floating point data", v)
class DatetimeWidget(TextWidget):
"""Datetime entry widget."""
displayWidth = 20
def _convert(self, value):
if value == self._missing:
return self.context.missing_value
else:
try:
return parseDatetimetz(value)
except (DateTimeError, ValueError, IndexError), v:
raise ConversionError("Invalid datetime data", v)
class DateWidget(TextWidget):
"""Date entry widget.
"""
displayWidth = 20
def _convert(self, value):
if value == self._missing:
return self.context.missing_value
else:
try:
return parseDatetimetz(value).date()
except (DateTimeError, ValueError, IndexError), v:
raise ConversionError("Invalid datetime data", v)
=== Zope3/src/zope/app/form/browser/widget.py 1.1 => 1.2 ===
--- Zope3/src/zope/app/form/browser/widget.py:1.1 Sat Mar 13 20:11:34 2004
+++ Zope3/src/zope/app/form/browser/widget.py Wed Mar 17 12:35:02 2004
@@ -21,25 +21,14 @@
from xml.sax.saxutils import quoteattr
from zope.interface import implements
-from zope.proxy import removeAllProxies
-from zope.schema import getFieldNamesInOrder
from zope.schema.interfaces import ValidationError
from zope.publisher.browser import BrowserView
-from zope.i18n import translate
from zope.app import zapi
from zope.app.tests import ztapi
-from zope.app.form.interfaces import IInputWidget
-from zope.app.form.browser.interfaces import IBrowserWidget
from zope.app.form.widget import Widget
-from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
-from zope.app.form.interfaces import ConversionError, WidgetInputError
-from zope.app.form.interfaces import MissingInputError
-from zope.app.datetimeutils import parseDatetimetz
-from zope.app.datetimeutils import DateTimeError
-from zope.app.i18n import ZopeMessageIDFactory as _
-
-ListTypes = list, tuple
+from zope.app.form.interfaces import WidgetInputError, MissingInputError
+from zope.app.form.browser.interfaces import IBrowserWidget
class BrowserWidget(Widget, BrowserView):
"""A field widget that knows how to display itself as HTML.
@@ -313,1022 +302,12 @@
return '<div class="%s">%s</div><div class="field">%s</div>' % (
self.labelClass(), self.label(), self())
-
class DisplayWidget(BrowserWidget):
def __call__(self):
return self._showData()
-
-class CheckBoxWidget(BrowserWidget):
- """A checkbox widget used to display Bool fields.
-
- For more detailed documentation, including sample code, see
- tests/test_checkboxwidget.py.
- """
-
- implements(IInputWidget)
-
- type = 'checkbox'
- default = 0
- extra = ''
-
- def __call__(self):
- data = self._showData()
- if data:
- kw = {'checked': None}
- 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,
- **kw),
- )
-
- def _convert(self, value):
- return value == 'on'
-
- def _unconvert(self, value):
- return value and "on" or ""
- return value == 'on'
-
- def hasInput(self):
- return self.name + ".used" in self.request.form or \
- super(CheckBoxWidget, self).hasInput()
-
- def getInputValue(self):
- # When it's checked, its value is 'on'.
- # When a checkbox is unchecked, it does not appear in the form data.
- value = self.request.form.get(self.name, 'off')
- return value == 'on'
-
-
-class TextWidget(BrowserWidget):
- """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"
- />
-
- """
-
- implements(IInputWidget)
-
- default = ''
- displayWidth = 20
- displayMaxWidth = ""
- extra = ''
- # XXX Alex Limi doesn't like this!
- # style = "width:100%"
- style = ''
- __values = None
-
- def __init__(self, *args):
- super(TextWidget, self).__init__(*args)
-
- 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=self._showData(),
- 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=self._showData(),
- cssClass=self.cssClass,
- style=self.style,
- size=self.displayWidth,
- extra=self.extra)
-
-class Bytes(BrowserWidget):
-
- def _convert(self, value):
- value = super(Bytes, self)._convert(value)
- 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 ASCII(Bytes):
- """ASCII"""
-
-
-class ASCIIWidget(BytesWidget):
- """ASCII widget.
-
- Single-line data (string) input
- """
-
-class IntWidget(TextWidget):
-
- displayWidth = 10
-
- def _convert(self, value):
- if value == self._missing:
- return self.context.missing_value
- else:
- try:
- return int(value)
- except ValueError, v:
- raise ConversionError("Invalid integer data", v)
-
-
-class FloatWidget(TextWidget):
-
- implements(IInputWidget)
- displayWidth = 10
-
- def _convert(self, value):
- if value == self._missing:
- return self.context.missing_value
- else:
- try:
- return float(value)
- except ValueError, v:
- raise ConversionError("Invalid floating point data", v)
-
-
-class DatetimeWidget(TextWidget):
- """Datetime entry widget."""
-
- displayWidth = 20
-
- def _convert(self, value):
- if value == self._missing:
- return self.context.missing_value
- else:
- try:
- return parseDatetimetz(value)
- except (DateTimeError, ValueError, IndexError), v:
- raise ConversionError("Invalid datetime data", v)
-
-
-class DateWidget(TextWidget):
- """Date entry widget.
- """
-
- displayWidth = 20
-
- def _convert(self, value):
- if value == self._missing:
- return self.context.missing_value
- else:
- try:
- return parseDatetimetz(value).date()
- except (DateTimeError, ValueError, IndexError), v:
- raise ConversionError("Invalid datetime data", v)
-
-
-class TextAreaWidget(BrowserWidget):
- """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\r
- 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>
-
- """
-
- implements(IInputWidget)
-
- default = ""
- width = 60
- height = 15
- extra = ""
- style = ''
-
- def _convert(self, value):
- 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,
- id=self.name,
- cssClass=self.cssClass,
- rows=self.height,
- cols=self.width,
- style=self.style,
- contents=self._showData(),
- 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 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 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
- if displayMaxWidth > 0:
- return renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- size=self.displayWidth,
- maxlength=displayMaxWidth,
- extra=self.extra)
- else:
- return renderElement(self.tag,
- type=self.type,
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- size=self.displayWidth,
- extra=self.extra)
-
- def hasInput(self):
- file = self.request.form.get(self.name)
- if file is None:
- return False
-
- if getattr(file, 'filename', ''):
- return True
-
- try:
- seek = file.seek
- read = file.read
- except AttributeError:
- return False
-
- seek(0)
- if read(1):
- return True
-
- return False
-
- def _convert(self, value):
- try:
- seek = value.seek
- read = value.read
- except AttributeError, e:
- raise ConversionError('Value is not a file object', e)
- else:
- seek(0)
- data = read()
- if data or getattr(value, 'filename', ''):
- return data
- else:
- return self.context.missing_value
-
-
-class ItemsWidget(BrowserWidget):
- """A widget that has a number of items in it."""
- # What the heck is this for?
-
- implements(IInputWidget)
-
-
-class SingleItemsWidget(ItemsWidget):
- """A widget with a number of items that has only a single
- selectable item."""
-
- default = ""
- firstItem = False
-
- def textForValue(self, value):
- '''Returns the text for the given value.
-
- Override this in subclasses.'''
- # The text could be a MessageID, in which case we should try to
- # translate it.
- return translate(self.context, value, context=self.request,
- default=value)
-
- def renderItems(self, value):
- name = self.name
- # get items
- items = self.context.allowed_values
-
- # check if we want to select first item
- if (not value and getattr(self.context, 'firstItem', False)
- and len(items) > 0):
- value = items[0]
-
- cssClass = self.cssClass
-
- # FIXME: what if we run into multiple items with same value?
- rendered_items = []
- count = 0
- for item_value in items:
- item_text = self.textForValue(item_value)
-
- if item_value == value:
- rendered_item = self.renderSelectedItem(count,
- item_text,
- item_value,
- name,
- cssClass)
- else:
- rendered_item = self.renderItem(count,
- item_text,
- item_value,
- name,
- cssClass)
-
- rendered_items.append(rendered_item)
- count += 1
-
- return rendered_items
-
-
-class ListWidget(SingleItemsWidget):
- """List widget."""
-
- size = 5
-
- def __call__(self):
- renderedItems = self.renderItems(self._showData())
- return renderElement('select',
- name=self.name,
- id=self.name,
- cssClass=self.cssClass,
- size=self.size,
- contents="\n".join(renderedItems),
- extra=self.extra)
-
- def renderItem(self, index, text, value, name, cssClass):
- return renderElement('option', contents=text, value=value,
- cssClass=cssClass)
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- return renderElement('option', contents=text, value=value,
- cssClass=cssClass, selected=None)
-
-
-class RadioWidget(SingleItemsWidget):
- """Radio buttons widget."""
-
- orientation = "vertical"
-
- def __call__(self):
- rendered_items = self.renderItems(self._showData())
- orientation = self.orientation
- if orientation == 'horizontal':
- return " ".join(rendered_items)
- else:
- return '<br />'.join(rendered_items)
-
- def _renderItem(self, index, text, value, name, cssClass, checked):
- id = '%s.%s' % (name, index)
- if checked:
- element = renderElement('input',
- type="radio",
- cssClass=cssClass,
- name=name,
- id=id,
- value=value,
- checked=None)
- else:
- element = renderElement('input',
- type="radio",
- cssClass=cssClass,
- name=name,
- id=id,
- value=value)
-
- return '%s<label for="%s">%s</label>' % (element, id, text)
-
- def renderItem(self, index, text, value, name, cssClass):
- return self._renderItem(index, text, value, name, cssClass, False)
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- return self._renderItem(index, text, value, name, cssClass, True)
-
- def label(self):
- return translate(self.context, self.title, context=self.request,
- default=self.title)
-
- def row(self):
- return ('<div class="%s"><label for="%s">%s</label></div>'
- '<div class="field" id="%s">%s</div>' % (
- self.labelClass(), self.name, self.label(), self.name, self()))
-
-
-class MultiItemsWidget(ItemsWidget):
- """A widget with a number of items that has multiple selectable items."""
-
- default = []
-
- def _convert(self, value):
- if not value:
- return []
- if isinstance(value, ListTypes):
- return value
- return [value]
-
- def renderItems(self, value):
- # need to deal with single item selects
- value = removeAllProxies(value)
-
- if not isinstance(value, ListTypes):
- value = [value]
- name = self.name
- items = self.context.allowed_values
- cssClass = self.cssClass
- rendered_items = []
- count = 0
- for item in items:
- try:
- item_value, item_text = item
- except ValueError:
- item_value = item
- item_text = item
-
- if item_value in value:
- rendered_item = self.renderSelectedItem(count,
- item_text,
- item_value,
- name,
- cssClass)
- else:
- rendered_item = self.renderItem(count,
- item_text,
- item_value,
- name,
- cssClass)
-
- rendered_items.append(rendered_item)
- count += 1
-
- return rendered_items
-
-
-class MultiListWidget(MultiItemsWidget):
- """List widget with multiple select."""
-
- size = 5
-
- def __call__(self):
- rendered_items = self.renderItems(self._showData())
- return renderElement('select',
- name=self.name,
- id=self.name,
- multiple=None,
- cssClass=self.cssClass,
- size=self.size,
- contents="\n".join(rendered_items),
- extra=self.extra)
-
- def renderItem(self, index, text, value, name, cssClass):
- return renderElement('option', contents=text, value=value)
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- return renderElement('option', contents=text, value=value,
- selected=None)
-
-
-class MultiCheckBoxWidget(MultiItemsWidget):
- """Multiple checkbox widget."""
-
- orientation = "vertical"
-
- def __call__(self):
- rendered_items = self.renderItems(self._showData())
- orientation = self.orientation
- if orientation == 'horizontal':
- return " ".join(rendered_items)
- else:
- return "<br />".join(rendered_items)
-
- def renderItem(self, index, text, value, name, cssClass):
- return renderElement('input',
- type="checkbox",
- cssClass=cssClass,
- name=name,
- id=name,
- value=value) + text
-
- def renderSelectedItem(self, index, text, value, name, cssClass):
- return renderElement('input',
- type="checkbox",
- cssClass=cssClass,
- name=name,
- id=name,
- value=value,
- checked=None) + text
-
-
-class SequenceWidget(BrowserWidget):
- """A widget baseclass for a sequence of fields.
-
- subwidget - Optional CustomWidget used to generate widgets for the
- items in the sequence
- """
-
- implements(IInputWidget)
-
- _type = tuple
- _data = () # pre-existing sequence items (from setRenderedValue)
-
- def __init__(self, context, request, subwidget=None):
- super(SequenceWidget, self).__init__(context, request)
-
- self.subwidget = None
-
- def __call__(self):
- """Render the widget
- """
- # XXX we really shouldn't allow value_type of None
- if self.context.value_type is None:
- return ''
-
- render = []
-
- # length of sequence info
- sequence = list(self._generateSequence())
- num_items = len(sequence)
- min_length = self.context.min_length
- max_length = self.context.max_length
-
- # ensure minimum number of items in the form
- if num_items < min_length:
- for i in range(min_length - num_items):
- sequence.append(None)
- num_items = len(sequence)
-
- # generate each widget from items in the sequence - adding a
- # "remove" button for each one
- for i in range(num_items):
- value = sequence[i]
- render.append('<tr><td>')
- if num_items > min_length:
- render.append(
- '<input type="checkbox" name="%s.remove_%d" />' % (
- self.name, i)
- )
- widget = self._getWidget(i)
- widget.setRenderedValue(value)
- render.append(widget() + '</td></tr>')
-
- # possibly generate the "remove" and "add" buttons
- buttons = ''
- if render and num_items > min_length:
- button_label = _('remove-selected-items', "Remove selected items")
- button_label = translate(self.context, button_label,
- context=self.request, default=button_label)
- buttons += '<input type="submit" value="%s" />' % button_label
- if max_length is None or num_items < max_length:
- field = self.context.value_type
- button_label = _('Add %s')
- button_label = translate(self.context, button_label,
- context=self.request, default=button_label)
- button_label = button_label % (field.title or field.__name__)
- buttons += '<input type="submit" name="%s.add" value="%s" />' % (
- self.name, button_label)
- if buttons:
- render.append('<tr><td>%s</td></tr>' % buttons)
-
- return '<table border="0">' + ''.join(render) + '</table>'
-
- def _getWidget(self, i):
- field = self.context.value_type
- if self.subwidget:
- widget = self.subwidget(field, self.request)
- else:
- widget = zapi.getViewProviding(field, IInputWidget, self.request,
- context=self.context)
- widget.setPrefix('%s.%d.'%(self.name, i))
- return widget
-
- def hidden(self):
- ''' Render the list as hidden fields '''
- # length of sequence info
- sequence = self._generateSequence()
- num_items = len(sequence)
- min_length = self.context.min_length
-
- # ensure minimum number of items in the form
- if num_items < min_length:
- for i in range(min_length - num_items):
- sequence.append(None)
- num_items = len(sequence)
-
- # generate hidden fields for each value
- s = ''
- for i in range(num_items):
- value = sequence[i]
- widget = self._getWidget(i)
- widget.setRenderedValue(value)
- s += widget.hidden()
- return s
-
- 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 returned in the case of one or more
- errors encountered, inputting, converting, or validating the data.
- """
- sequence = self._generateSequence()
- # validate the input values
- for value in sequence:
- self.context.value_type.validate(value)
- return self._type(sequence)
-
- # XXX 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 len(self._generateSequence()) != 0
-
- def setRenderedValue(self, value):
- """Set the default data for 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.
- """
- len_prefix = len(self.name)
- adding = False
- removing = []
- subprefix = re.compile(r'(\d+)\.(.*)$')
- if self.context.value_type is None:
- return []
-
- # pre-populate
- found = {}
- if self._data is not None:
- found = dict(enumerate(self._data))
-
- # now look through the request for interesting values
- for key in self.request.keys():
- if not key.startswith(self.name):
- continue
- token = key[len_prefix+1:] # skip the '.'
- if token == 'add':
- # append a new blank field to the sequence
- adding = True
- elif token.startswith('remove_'):
- # remove the index indicated
- removing.append(int(token[7:]))
- else:
- match = subprefix.match(token)
- if match is None:
- continue
- # key refers to a sub field
- i = int(match.group(1))
-
- # find a widget for the sub-field and use that to parse the
- # request data
- widget = self._getWidget(i)
- value = widget.getInputValue()
- found[i] = value
-
- # remove the indicated indexes
- for i in removing:
- del found[i]
-
- # generate the list, sorting the dict's contents by key
- items = found.items()
- items.sort()
- sequence = [value for key, value in items]
-
- # add an entry to the list if the add button has been pressed
- if adding:
- sequence.append(None)
-
- return sequence
-
-
-class TupleSequenceWidget(SequenceWidget):
- pass
-
-class ListSequenceWidget(SequenceWidget):
-
- _type = list
-
-
-class ObjectWidget(BrowserWidget):
- """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)
-
- # 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
- setUpEditWidgets(self, self.context.schema, source=self.context,
- prefix=self.name, names=self.names,
- context=self.context)
-
- def __call__(self):
- """Render the widget
- """
- render = []
-
- # XXX see if there's some widget layout already
-
- # generate each widget from fields in the schema
- field = self.context
- title = field.title or field.__name__
- render.append('<fieldset><legend>%s</legend>'%title)
- for name, widget in self.getSubWidgets():
- render.append(widget.row())
- render.append('</fieldset>')
-
- return '\n'.join(render)
-
- def getSubWidgets(self):
- l = []
- for name in self.names:
- l.append((name, getattr(self, '%s_widget'%name)))
- return l
-
- def hidden(self):
- ''' Render the list as hidden fields '''
- for name, widget in self.getSubWidgets():
- s += widget.hidden()
- return s
-
- 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).
- """
- content = self.factory()
- for name, widget in self.getSubWidgets():
- setattr(content, name, widget.getInputValue())
- return content
-
- def applyChanges(self, content):
- field = self.context
-
- # create our new object value
- value = field.query(content, None)
- if value is None:
- # XXX ObjectCreatedEvent here would be nice
- value = self.factory()
-
- # apply sub changes, see if there *are* any changes
- # XXX 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)
-
- return changes
-
- def hasInput(self):
- """Is there input data for the field
-
- Return True if there is data and False otherwise.
- """
- for name, widget in self.getSubWidgets():
- if widget.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, widget in self.getSubWidgets():
- widget.setRenderedValue(getattr(value, name, None))
-
-
# XXX Note, some HTML quoting is needed in renderTag and renderElement.
-
def renderTag(tag, **kw):
"""Render the tag. Well, not all of it, as we may want to / it."""
attr_list = []
More information about the Zope3-Checkins
mailing list