[Zope3-checkins] CVS: Zope3/src/zope/app/form/browser -
objectwidget.pt:1.1.2.1 add.pt:1.3.2.1 interfaces.py:1.3.2.1
objectwidget.py:1.1.8.1 widget.py:1.8.2.1 widget_macros.pt:1.1.2.1
Martijn Faassen
m.faassen at vet.uu.nl
Tue May 11 05:10:56 EDT 2004
Update of /cvs-repository/Zope3/src/zope/app/form/browser
In directory cvs.zope.org:/tmp/cvs-serv20906/src/zope/app/form/browser
Modified Files:
Tag: faassen-interfaces-branch
add.pt interfaces.py objectwidget.py widget.py
widget_macros.pt
Added Files:
Tag: faassen-interfaces-branch
objectwidget.pt
Log Message:
Sync up with changes in HEAD (so I can generate up to date patch file).
=== Added File Zope3/src/zope/app/form/browser/objectwidget.pt ===
<fieldset>
<legend tal:content="context/legendTitle">The Legend</legend>
<tal:block repeat="widget context/subwidgets">
<metal:block use-macro="context/@@form_macros/widget_row" />
</tal:block>
</fieldset>
=== Zope3/src/zope/app/form/browser/add.pt 1.3 => 1.3.2.1 ===
--- Zope3/src/zope/app/form/browser/add.pt:1.3 Thu May 6 11:46:05 2004
+++ Zope3/src/zope/app/form/browser/add.pt Tue May 11 05:10:19 2004
@@ -51,7 +51,7 @@
i18n:attributes='value refresh-button' />
<input type='submit' value='Add' name='UPDATE_SUBMIT'
i18n:attributes='value add-button' />
- <span tal:condition="context/nameAllowed" tal:omit-tag="">
+ <span tal:condition="context/nameAllowed|nothing" tal:omit-tag="">
<b i18n:translate="">Object Name</b>
<input type='text' name='add_input_name'
tal:attributes="value context/contentName" />
=== Zope3/src/zope/app/form/browser/interfaces.py 1.3 => 1.3.2.1 ===
--- Zope3/src/zope/app/form/browser/interfaces.py:1.3 Sat Apr 24 19:19:42 2004
+++ Zope3/src/zope/app/form/browser/interfaces.py Tue May 11 05:10:19 2004
@@ -18,7 +18,7 @@
from zope.app.form.interfaces import IWidget
class IAddFormCustomization(Interface):
- """This interface defined methods of add forms that can be overridden
+ """API for add form customization.
Classes supplied when defining add forms may need to override some
of these methods.
@@ -39,7 +39,6 @@
content = <create the content from the data>
content = self.add(content) # content wrapped in some context
<set after-add attributes on content>
-
"""
def createAndAdd(data):
@@ -54,7 +53,7 @@
"""
def add(content):
- """Add the given content
+ """Add the given content.
This method is overridden when the context of the add form is
not an IAdding. In this case, the class that customizes the
@@ -76,40 +75,22 @@
i.e. it delegates to the IAdding view.
"""
-
class IBrowserWidget(IWidget):
- """A field widget contains all the properties that are required
- to represent a field. Properties include css_sheet,
- default value and so on.
- """
+ """A widget for use in a web browser UI."""
- def __call__(): # XXX promote to IWidget?
- """Render the widget
- """
+ def __call__():
+ """Render the widget."""
def hidden():
- """Render the widget as a hidden field
- """
-
- def label():
- """Render a label tag"""
+ """Render the widget as a hidden field."""
- def error(): # XXX promote to IWidget?
+ def error():
"""Render the validation error for the widget, or return
an empty string if no error"""
- def row():
- """Render the widget as two or three div elements,
- for the label, the field and possibly the validation error
-
- For example:
- <div class="label">label</div><div class="field">field</div>
- <div class="error">Validation error message</div>
- """
-
class IFormCollaborationView(Interface):
- """Views that collaborate to create a single form
+ """Views that collaborate to create a single form.
When a form is applied, the changes in the form need to
be applied to individual views, which update objects as
@@ -134,8 +115,7 @@
"""
def update():
- """Update the form with data from the request.
- """
+ """Update the form with data from the request."""
class IVocabularyQueryView(Interface):
=== Zope3/src/zope/app/form/browser/objectwidget.py 1.1 => 1.1.8.1 ===
--- Zope3/src/zope/app/form/browser/objectwidget.py:1.1 Wed Mar 17 12:35:02 2004
+++ Zope3/src/zope/app/form/browser/objectwidget.py Tue May 11 05:10:19 2004
@@ -21,7 +21,21 @@
from zope.app.form.interfaces import IInputWidget
from zope.app.form.browser.widget import BrowserWidget
from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+
+class ObjectWidgetView:
+
+ template = ViewPageTemplateFile('objectwidget.pt')
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self):
+ return self.template()
+
+
class ObjectWidget(BrowserWidget):
"""A widget over an Interface that contains Fields.
@@ -38,6 +52,9 @@
def __init__(self, context, request, factory, **kw):
super(ObjectWidget, self).__init__(context, request)
+
+ # define view that renders the widget
+ self.view = ObjectWidgetView(self, request)
# factory used to create content that this widget (field)
# represents
@@ -63,33 +80,23 @@
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
+ return self.view()
+
+ def legendTitle(self):
+ return self.context.title or self.context.__name__
+
+ def getSubWidget(self, name):
+ return getattr(self, '%s_widget' % name)
+
+ def subwidgets(self):
+ return [self.getSubWidget(name) for name in self.names]
def hidden(self):
- ''' Render the list as hidden fields '''
- for name, widget in self.getSubWidgets():
- s += widget.hidden()
- return s
+ """Render the list as hidden fields."""
+ result = []
+ for name in self.names:
+ result.append(getSubwidget(name).hidden())
+ return "".join(result)
def getInputValue(self):
"""Return converted and validated widget data.
@@ -101,8 +108,8 @@
does this).
"""
content = self.factory()
- for name, widget in self.getSubWidgets():
- setattr(content, name, widget.getInputValue())
+ for name in self.names:
+ setattr(content, name, self.getSubWidget(name).getInputValue())
return content
def applyChanges(self, content):
@@ -130,8 +137,8 @@
Return True if there is data and False otherwise.
"""
- for name, widget in self.getSubWidgets():
- if widget.hasInput():
+ for name in self.names:
+ if self.getSubWidget(name).hasInput():
return True
return False
@@ -143,7 +150,7 @@
"""
# re-call setupwidgets with the content
self._setUpEditWidgets()
- for name, widget in self.getSubWidgets():
- widget.setRenderedValue(getattr(value, name, None))
+ for name in self.names:
+ self.getSubWidget(name).setRenderedValue(getattr(value, name, None))
=== Zope3/src/zope/app/form/browser/widget.py 1.8 => 1.8.2.1 ===
--- Zope3/src/zope/app/form/browser/widget.py:1.8 Sat Apr 24 19:19:42 2004
+++ Zope3/src/zope/app/form/browser/widget.py Tue May 11 05:10:19 2004
@@ -38,94 +38,123 @@
labels, titles, and descriptions are translated and the
errors are rendered with the view machinery, so we need to set up
a lot of machinery to support translation and views:
-
- >>> setUp() # now we have to set up an error view...
- >>> from zope.app.form.interfaces import IWidgetInputError
- >>> from zope.app.publisher.browser import BrowserView
- >>> from cgi import escape
- >>> class SnippetErrorView(BrowserView):
- ... implements(IWidgetInputErrorView)
- ... def snippet(self):
- ... return escape(self.context.errors[0])
- ...
- >>> ztapi.browserViewProviding(IWidgetInputError, SnippetErrorView,
- ... IWidgetInputErrorView)
- >>> from zope.publisher.browser import TestRequest
-
- And now the tests proper...
-
- >>> from zope.schema import Field
- >>> import re
- >>> isFriendly=re.compile(".*hello.*").match
- >>> field = Field(__name__='foo', title=u'Foo', constraint=isFriendly)
- >>> request = TestRequest(form={
- ... 'field.foo': u'hello\\r\\nworld',
- ... 'baz.foo': u'bye world'})
- >>> widget = BrowserWidget(field, request)
- >>> widget.name
- 'field.foo'
- >>> widget.title
- u'Foo'
- >>> widget.hasInput()
- True
- >>> widget.getInputValue()
- u'hello\\r\\nworld'
- >>> widget.required
- True
- >>> widget._error is None
- True
- >>> widget.error()
- ''
- >>> widget.setRenderedValue('Hey\\nfolks')
- >>> widget.getInputValue()
- u'hello\\r\\nworld'
- >>> widget._error is None
- True
- >>> widget.error()
- ''
-
- >>> widget.setPrefix('baz')
- >>> widget.name
- 'baz.foo'
- >>> widget.error()
- ''
- >>> try:
- ... widget.getInputValue()
- ... except WidgetInputError:
- ... print widget._error.errors
- bye world
- >>> widget.error()
- u'bye world'
- >>> widget._error = None # clean up for next round of tests
-
- >>> widget.setPrefix('test')
- >>> widget.name
- 'test.foo'
- >>> widget._error is None
- True
- >>> widget.error()
- ''
- >>> widget.hasInput()
- False
- >>> widget.getInputValue()
- Traceback (most recent call last):
- ...
- MissingInputError: ('test.foo', u'Foo', None)
- >>> field.required = False
- >>> widget.request.form['test.foo'] = u''
- >>> widget.required
- False
- >>> widget.getInputValue() == field.missing_value
- True
- >>> widget._error is None
- True
- >>> widget.error()
- ''
- >>> print widget.label()
- <label for="test.foo">Foo</label>
-
- Now we clean up.
+ >>> setUp() # now we have to set up an error view...
+ >>> from zope.app.form.interfaces import IWidgetInputError
+ >>> from zope.app.publisher.browser import BrowserView
+ >>> from cgi import escape
+ >>> class SnippetErrorView(BrowserView):
+ ... implements(IWidgetInputErrorView)
+ ... def snippet(self):
+ ... return escape(self.context.errors[0])
+ ...
+ >>> ztapi.browserViewProviding(IWidgetInputError, SnippetErrorView,
+ ... IWidgetInputErrorView)
+ >>> from zope.publisher.browser import TestRequest
+
+ >>> from zope.schema import Field
+ >>> import re
+ >>> isFriendly=re.compile(".*hello.*").match
+ >>> field = Field(__name__='foo', title=u'Foo', constraint=isFriendly)
+ >>> request = TestRequest(form={
+ ... 'field.foo': u'hello\\r\\nworld',
+ ... 'baz.foo': u'bye world'})
+ >>> widget = BrowserWidget(field, request)
+
+ Widgets are named using their field's name:
+
+ >>> widget.name
+ 'field.foo'
+
+ The default implementation for the widget label is to use the field title:
+
+ >>> widget.label
+ u'Foo'
+
+ According the request, the widget has input because 'field.foo' is
+ present:
+
+ >>> widget.hasInput()
+ True
+ >>> widget.getInputValue()
+ u'hello\\r\\nworld'
+
+ Widgets maintain an error state, which is used to communicate invalid
+ input or other errors:
+
+ >>> widget._error is None
+ True
+ >>> widget.error()
+ ''
+
+ setRenderedValue is used to specify the value displayed by the widget to
+ the user. This value, however, is not the same as the input value, which
+ is read from the request:
+
+ >>> widget.setRenderedValue('Hey\\nfolks')
+ >>> widget.getInputValue()
+ u'hello\\r\\nworld'
+ >>> widget._error is None
+ True
+ >>> widget.error()
+ ''
+
+ You can modify the prefix used to create the widget name as follows:
+
+ >>> widget.setPrefix('baz')
+ >>> widget.name
+ 'baz.foo'
+
+ The modification of the widget's name changes the input the widget reads
+ from the request. Instead of reading input from 'field.foo', the widget
+ now reads input from 'baz.foo'. In this case, the input from 'baz.foo'
+ (see request above) violates the isFriendly constraint on field 'foo':
+
+ >>> widget.error()
+ ''
+ >>> try:
+ ... widget.getInputValue()
+ ... except WidgetInputError:
+ ... print widget._error.errors
+ bye world
+ >>> widget.error()
+ u'bye world'
+ >>> widget._error = None # clean up for next round of tests
+
+ Changing the widget prefix/name, again, changes the input the widget uses
+ from the request:
+
+ >>> widget.setPrefix('test')
+ >>> widget.name
+ 'test.foo'
+ >>> widget._error is None
+ True
+ >>> widget.error()
+ ''
+ >>> widget.hasInput()
+ False
+ >>> widget.getInputValue()
+ Traceback (most recent call last):
+ ...
+ MissingInputError: ('test.foo', u'Foo', None)
+
+ Whether or not the widget requires input depends on its field's required
+ attribute:
+
+ >>> field.required
+ True
+ >>> widget.required
+ True
+ >>> field.required = False
+ >>> widget.request.form['test.foo'] = u''
+ >>> widget.required
+ False
+ >>> widget.getInputValue() == field.missing_value
+ True
+ >>> widget._error is None
+ True
+ >>> widget.error()
+ ''
>>> tearDown()
"""
@@ -138,7 +167,7 @@
extra = u''
_missing = u''
_error = None
-
+
required = property(lambda self: self.context.required)
def hasInput(self):
@@ -170,7 +199,7 @@
# form input is required, otherwise raise an error
input = self.request.form.get(self.name)
if input is None:
- raise MissingInputError(self.name, self.title, None)
+ raise MissingInputError(self.name, self.label, None)
# convert input to suitable value - may raise conversion error
value = self._convert(input)
@@ -184,7 +213,7 @@
field.validate(value)
except ValidationError, v:
self._error = WidgetInputError(
- self.context.__name__, self.title, v)
+ self.context.__name__, self.label, v)
raise self._error
return value
@@ -247,8 +276,7 @@
return self._unconvert(value)
def _getDefault(self):
- # Return the default value for this widget;
- # may be overridden by subclasses.
+ """Return the default value for this widget."""
return self.context.default
def __call__(self):
@@ -282,37 +310,18 @@
self.setRenderedValue(value)
return self.hidden()
- def label(self):
- kw = {"for": self.name,
- "contents": cgi.escape(self.title)}
- if self.context.description:
- kw["title"] = self.context.description
- return renderElement("label", **kw)
-
def error(self):
if self._error:
return zapi.getViewProviding(self._error, IWidgetInputErrorView,
self.request).snippet()
return ""
- def labelClass(self):
- return self.context.required and "label required" or "label"
- def row(self):
- if self._error:
- return u'<div class="%s">%s</div><div class="field">%s</div>' \
- u'<div class="error">%s</div>' % (self.labelClass(),
- self.label(), self(),
- self.error())
- else:
- return u'<div class="%s">%s</div><div class="field">%s</div>' % (
- self.labelClass(), self.label(), self())
-
class DisplayWidget(BrowserWidget):
def __call__(self):
return self._showData()
-
+
# 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."""
@@ -374,14 +383,14 @@
return u"%s>%s</%s>" % (renderTag(tag, **kw), contents, tag)
else:
return renderTag(tag, **kw) + " />"
-
+
def setUp():
import zope.app.tests.placelesssetup
global setUp
setUp = zope.app.tests.placelesssetup.setUp
setUp()
-
+
def tearDown():
import zope.app.tests.placelesssetup
=== Zope3/src/zope/app/form/browser/widget_macros.pt 1.1 => 1.1.2.1 ===
--- Zope3/src/zope/app/form/browser/widget_macros.pt:1.1 Mon May 3 22:01:24 2004
+++ Zope3/src/zope/app/form/browser/widget_macros.pt Tue May 11 05:10:19 2004
@@ -2,14 +2,20 @@
<body>
<metal:block define-macro="widget_rows">
<div class="row" tal:repeat="widget view/widgets">
- <div class="label" tal:content="structure widget/label">Name</div>
- <div class="field" tal:content="structure widget">
- <input type="text" style="width:100%"/>
- </div>
- <div class="error" tal:define="error widget/error"
- tal:condition="error" tal:content="structure error">
- The Error
- </div>
+ <metal:block define-macro="widget_row">
+ <div class="label">
+ <label for="field.name" title="The widget's hint"
+ tal:attributes="for widget/name; title widget/hint"
+ tal:content="widget/label">The Label</label>
+ </div>
+ <div class="field" tal:content="structure widget">
+ <input type="text" style="width:100%"/>
+ </div>
+ <div class="error" tal:define="error widget/error"
+ tal:condition="error" tal:content="structure error">
+ The Error
+ </div>
+ </metal:block>
</div>
</metal:block>
</body>
More information about the Zope3-Checkins
mailing list