Skip to main content

Additional zope.formlib Widgets

Project description

Changes for z3c.widget

0.3.0 (2010-11-16)

  • Added translation for french.

  • Updated tests to run with zope.formlib 4.0 and zope.schema 3.6.

  • Using Python’s doctest module instead of depreacted zope.testing.doctest[unit].

  • Added doctests to long_description to be visible on PyPI.

2008/01/29 0.2.1

  • add translation for hungarian and romanian languages

2007/09/21 0.2.0

  • feature: Added a date-selection widget.

  • feature: Added a social security number widget.

  • feature: Added a US phone number widget.

2007/09/19 0.1.7

  • added translation for english to get i18n working in some browsers

2007/09/19 0.1.6

  • added cheesehop classification, registered there

  • fixed typos

2007/09/05 0.1.5

  • bugfix: browse butten will now be enabled after cancel was clicked in the open file dialog

  • do not set the progessbar to 100% before the upload was started

  • bugfix: files will not be uploaded if no limit was set

2007/09/05 0.1.4

  • dealing file size during upload. If one or more files are bigger than the passed size each one of them will be ignored during upload but listed below the progress bar after the upload of the working files is done.

  • displaying the maximal allowed file size (if it’s contained in the config file)

2007/09/03 0.1.3

  • bugfix: too much quoting.

2007/09/03 0.1.2

  • using passed config url instead of hard coded flashuploadvars.xml

2007/08/06 0.1.1

  • flashupload: better skinnability for upload.swf. cleanup folder structure for flash stuff.

2007/06/14 0.1.0:

  • z3c.widget.image: added translations for es

  • update to newest bootstrap.py version

Autocomplete Widgets

Autocomplete widgets are an alternative to normal select widgets.

>>> from z3c.widget.autocomplete.widget import AutoCompleteWidget

Let us create a vocabulary.

>>> from zope.schema.vocabulary import SimpleVocabulary
>>> from zope.publisher.browser import TestRequest
>>> from zope import schema, component, interface
>>> items = ((u'value1',1,u'Title1'),
...          (u'value2',2,u'Title2'),
...          (u'value3',3,u'Title3'))
>>> terms = map(lambda i: SimpleVocabulary.createTerm(*i),items)
>>> voc = SimpleVocabulary(terms)
>>> [term.title for term in voc]
[u'Title1', u'Title2', u'Title3']
>>> field = schema.Choice(__name__='foo',
...     missing_value=None,
...     vocabulary=voc)
>>> request = TestRequest()
 >>> widget =  AutoCompleteWidget(field, request)
>>> widget
<z3c.widget.autocomplete.widget.AutoCompleteWidget object at ...>
>>> print widget()
<input class="textType" id="field.foo" name="field.foo" type="text" value=""  />
<div id="field.foo.target" class="autoComplete"></div>
<script type="text/javascript">
new Ajax.Autocompleter('field.foo','field.foo.target',
'http://127.0.0.1/++widget++field.foo/suggestions'
,options={
paramName: 'value'
});
</script>

Let’s add some input. Note that the input must match the title of the vocabulary term.

>>> request.form['field.foo']=u'Title1'
>>> widget.getInputValue()
u'value1'

If we have no matching title a ConversionError is raised.

>>> request.form['field.foo']=u'Unknown'
>>> widget.getInputValue()
Traceback (most recent call last):
...
ConversionError: ('Invalid value', u'Unknown')

Also the form value is the title of the term with the given value.

>>> widget._toFormValue('value1')
u'Title1'
>>> suggestions = widget.getSuggestions('Title')
>>> [title for title in suggestions]
[u'Title1', u'Title2', u'Title3']
>>> suggestions = widget.getSuggestions('Title1')
>>> [title for title in suggestions]
[u'Title1']
>>> suggestions = widget.getSuggestions('ABC')
>>> [title for title in suggestions]
[]
>>> suggestions = widget.getSuggestions('title')
>>> [title for title in suggestions]
[u'Title1', u'Title2', u'Title3']

AutoCompleteWidget Demo

This demo packe provides a simple content class which uses the z3c autocomplete widget.

>>> from zope.testbrowser.testing import Browser
>>> browser = Browser()
>>> browser.handleErrors = False
>>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
>>> browser.open('http://localhost/@@contents.html')

It can be added by clicking on the “Autocomplete Widget Demo” link in the add menu. And giving it a name.

>>> link = browser.getLink('Autocomplete Widget Demo')
>>> link.click()
>>> nameCtrl = browser.getControl(name='new_value')
>>> nameCtrl.value = 'mydemo'
>>> applyCtrl = browser.getControl('Apply')
>>> applyCtrl.click()
>>> link = browser.getLink('mydemo')
>>> link.click()
>>> browser.url
'http://localhost/mydemo/@@edit.html'

Let us test the widget rendering by direct access.

>>> browser.open('http://localhost/mydemo/@@edit.html/++widget++country')
>>> print browser.contents
<input class="textType" ...
</script>

The suggestions are proveded by its own view.

>>> browser.open('http://localhost/mydemo/@@edit.html/++widget++country/suggestions')
>>> print browser.contents
>>> browser.open('http://localhost/++lang++en/mydemo/@@edit.html/++widget++country/suggestions?value=a')
>>> print browser.contents
<BLANKLINE>
 <ul>
  <li>Algeria</li>
  <li>Andorra</li>
  <li>Antigua and Barbuda</li>
  <li>Afghanistan</li>
  <li>Anguilla</li>
  <li>Armenia</li>
  <li>Albania</li>
  <li>Angola</li>
  <li>Antarctica</li>
  <li>American Samoa</li>
  <li>Argentina</li>
  <li>Australia</li>
  <li>Austria</li>
  <li>Aruba</li>
  <li>Azerbaijan</li>
 </ul>
<BLANKLINE>
<BLANKLINE>

Suggestions are translated.

>>> browser.open('http://localhost/++lang++de/mydemo/@@edit.html/++widget++country/suggestions?value=a')
>>> print browser.contents
<BLANKLINE>
 <ul>
  <li>Amerikanische Jungferninseln</li>
  <li>Amerikanisch-Ozeanien</li>
  <li>Algerien</li>
  <li>Andorra</li>
  <li>Antigua und Barbuda</li>
  <li>Afghanistan</li>
  <li>Anguilla</li>
  <li>Armenien</li>
  <li>Albanien</li>
  <li>Angola</li>
  <li>Antarktis</li>
  <li>Amerikanisch-Samoa</li>
  <li>Argentinien</li>
  <li>Australien</li>
  <li>Aruba</li>
  <li>Aserbaidschan</li>
 </ul>
<BLANKLINE>
<BLANKLINE>

Country selection Widgets

This package provides widgets to select a country. The dropdown type is registered as a default for the Country schema.

The pain was to sort the options after the translation.

Before we can start, we have to do a little bit of setup:

>>> import zope.component
>>> import zope.schema
>>> import zope.app.form.browser
>>> from z3c.widget.country.widget import CountryInputDropdown
>>> from z3c.widget.country import ICountry
>>> from z3c.i18n.iso import territoryVocabularyFactory
>>> from zope.publisher.interfaces.browser import IBrowserRequest

First we have to create a field and a request:

>>> from z3c.widget.country import Country
>>> countryFld = Country(
...     __name__='country',
...     title=u'Country',
...     description=u'Select a Country')
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()

Now we can initialize the widget.

>>> class Content(object):
...     country = None
>>> content = Content()
>>> boundCountry = countryFld.bind(content)
>>> widget = CountryInputDropdown(boundCountry,
...   territoryVocabularyFactory(None), request)

Let’s make sure that all fields have the correct value:

>>> widget.name
'field.country'
>>> widget.label
u'Country'
>>> widget.hint
u'Select a Country'
>>> widget.visible
True

Let’s see how the widget is rendered:

>>> print widget()
<div>
<div class="value">
<select id="field.country" name="field.country" size="1" >
<option value="AF">Afghanistan</option>
<option value="AL">Albania</option>
<option value="DZ">Algeria</option>
...
<option value="HU">Hungary</option>
<option value="IS">Iceland</option>
<option value="IN">India</option>
...
<option value="ZM">Zambia</option>
<option value="ZW">Zimbabwe</option>
</select>
...

#Let’s see the german translation: #z3c.i18n registrations required!!! # # >>> request = TestRequest(HTTP_ACCEPT_LANGUAGE=’de’) # # >>> widget = CountryInputDropdown(boundCountry, # … territoryVocabularyFactory(None), request) # # >>> print widget() # <div> # <div class=”value”> # <select id=”field.country” name=”field.country” size=”1” > # <option value=”AF”>Afghanistan</option> # <option value=”AL”>Albania</option> # <option value=”DZ”>Algeria</option> # … # <option value=”HU”>Hungary</option> # <option value=”IS”>Iceland</option> # <option value=”IN”>India</option> # … # <option value=”ZM”>Zambia</option> # <option value=”ZW”>Zimbabwe</option> # </select> # …

Date Selection Widget

The DateSelectWidget widget provides three select boxes presenting the day, month and year.

First we have to create a field and a request. Note that we can set the year range in this widget:

>>> import datetime
>>> from z3c.schema.dateselect import DateSelect
>>> from z3c.widget.dateselect.browser import DateSelectWidget
>>> field = DateSelect(
...     title=u'Birthday',
...     description=u'Somebodys birthday',
...     yearRange=range(1930, 2007),
...     required=True)
>>> field.__name__ = 'field'
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()

Now we can initialize widget.

>>> widget = DateSelectWidget(field, request)

Let’s make sure that all fields have the correct value:

>>> widget.name
'field.field'
>>> widget.label
u'Birthday'
>>> widget.hint
u'Somebodys birthday'
>>> widget.visible
True
>>> widget.required
True

The constructor should have also created 3 widgets:

>>> widget.widgets['year']
<z3c.widget.dateselect.browser.DropdownWidget object at ...>
>>> widget.widgets['month']
<z3c.widget.dateselect.browser.DropdownWidget object at ...>
>>> widget.widgets['day']
<z3c.widget.dateselect.browser.DropdownWidget object at ...>

let’s also test the year range:

>>> '1929' in widget.widgets['year'].vocabulary.by_token.keys()
False
>>> '1930' in widget.widgets['year'].vocabulary.by_token.keys()
True
>>> '2006' in widget.widgets['year'].vocabulary.by_token.keys()
True
>>> '2007' in widget.widgets['year'].vocabulary.by_token.keys()
False

Test another year range:

>>> field2 = DateSelect(
...     title=u'Another Birthday',
...     yearRange=range(2000, 2010))
>>> field2.__name__ = 'field'
>>> widget2 = DateSelectWidget(field2, request)
>>> '1930' in widget2.widgets['year'].vocabulary.by_token.keys()
False
>>> '2000' in widget2.widgets['year'].vocabulary.by_token.keys()
True
>>> '2009' in widget2.widgets['year'].vocabulary.by_token.keys()
True
>>> '2010' in widget2.widgets['year'].vocabulary.by_token.keys()
False

setRenderedValue(value) Method

The first method is setRenderedValue(). The widget has two use cases, based on the type of value. If the value is a custom score system, it will send the information to the custom, min and max widget:

>>> widget = DateSelectWidget(field, request)
>>> year = 2000
>>> month = 12
>>> day = 31
>>> data = datetime.date(year, month, day)
>>> widget.setRenderedValue(data)
>>> 'value="2000"' in widget()
True
>>> 'value="12"' in widget()
True
>>> 'value="31"' in widget()
True

setPrefix(prefix) Method

The prefix determines the name of the widget and all its sub-widgets.

>>> widget.name
'field.field'
>>> widget.widgets['year'].name
'field.field.year'
>>> widget.widgets['month'].name
'field.field.month'
>>> widget.widgets['day'].name
'field.field.day'
>>> widget.setPrefix('test.')
>>> widget.name
'test.field'
>>> widget.widgets['year'].name
'test.field.year'
>>> widget.widgets['month'].name
'test.field.month'
>>> widget.widgets['day'].name
'test.field.day'

If the prefix does not end in a dot, one is added:

>>> widget.setPrefix('test')
>>> widget.name
'test.field'
>>> widget.widgets['year'].name
'test.field.year'
>>> widget.widgets['month'].name
'test.field.month'
>>> widget.widgets['day'].name
'test.field.day'

getInputValue() Method

This method returns a date object:

>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> value = widget.getInputValue()
>>> value.year
2006
>>> value.month
2
>>> value.day
24

If a set of values does not produce a valid date object, a value error is raised:

>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '29'})
>>> widget = DateSelectWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('field', u'Birthday', u'day is out of range for month')
>>> widget._error.__class__
<class 'zope.formlib.interfaces.WidgetInputError'>

applyChanges(content) Method

This method applies the new date to the passed content. However, it must be smart enough to detect whether the values really changed.

>>> class Content(object):
...     field = None
>>> content = Content()
>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> widget.applyChanges(content)
True
>>> content.field
datetime.date(2006, 2, 24)
>>> widget.applyChanges(content)
False

hasInput() Method

This method checks for any input, but does not validate it.

>>> request = TestRequest()
>>> widget = DateSelectWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.year': '2006'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.month': '2'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasInput()
True

hasValidInput() Method

Additionally to checking for any input, this method also checks whether the input is valid:

>>> request = TestRequest()
>>> widget = DateSelectWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.year': '2006'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.month': '2'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> widget.hasValidInput()
True

hidden() Method

This method is renders the output as hidden fields:

>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> print widget.hidden()
<input class="hiddenType" id="field.field.year" name="field.field.year"
       type="hidden" value="2006" />
<input class="hiddenType" id="field.field.month" name="field.field.month"
       type="hidden" value="2"  />
<input class="hiddenType" id="field.field.day" name="field.field.day"
       type="hidden" value="24"  />

error() Method

Let’s test some bad data and check the error handling.

The day field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '99'})
>>> widget = DateSelectWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
ConversionError: (u'Invalid value', InvalidValue("token '99' not found in vocabulary"))
>>> print widget.error()
<span class="error">Invalid value</span>

The month field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '0',
...     'field.field.day': '31'})
>>> widget = DateSelectWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
ConversionError: (u'Invalid value', InvalidValue("token '0' not found in vocabulary"))
>>> print widget.error()
<span class="error">Invalid value</span>

The year field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.year': '1900',
...     'field.field.month': '1',
...     'field.field.day': '31'})
>>> widget = DateSelectWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
ConversionError: (u'Invalid value', InvalidValue("token '1900' not found in vocabulary"))
>>> print widget.error()
<span class="error">Invalid value</span>

The single inputs were correct, but did not create a valid date.

>>> request = TestRequest(form={
...     'field.field.year': '1980',
...     'field.field.month': '2',
...     'field.field.day': '31'})
>>> widget = DateSelectWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('field', u'Birthday', u'day is out of range for month')
>>> print widget.error()
<span class="error">day is out of range for month</span>

No error occurred:

>>> request = TestRequest(form={
...     'field.field.year': '1980',
...     'field.field.month': '1',
...     'field.field.day': '31'})
>>> widget = DateSelectWidget(field, request)
>>> widget.getInputValue()
datetime.date(1980, 1, 31)
>>> widget.error()
''

__call__() Method

This method renders the widget using the sub-widgets. Let’s see the output:

>>> request = TestRequest(form={
...     'field.field.year': '2006',
...     'field.field.month': '2',
...     'field.field.day': '24'})
>>> widget = DateSelectWidget(field, request)
>>> print widget()
<select id="field.field.day" name="field.field.day" size="1" >
<option value="1">1</option>
...
<option value="23">23</option>
<option selected="selected" value="24">24</option>
<option value="25">25</option>
...
<option value="31">31</option>
</select><input name="field.field.day-empty-marker" type="hidden"
                value="1" />&nbsp;
<select id="field.field.month" name="field.field.month" size="1" >
<option value="1">1</option>
<option selected="selected" value="2">2</option>
<option value="3">3</option>
...
<option value="12">12</option>
</select><input name="field.field.month-empty-marker" type="hidden"
                value="1" />&nbsp;
<select id="field.field.year" name="field.field.year" size="1" >
<option value="1930">1930</option>
...
<option value="2005">2005</option>
<option selected="selected" value="2006">2006</option>
</select><input
    name="field.field.year-empty-marker" type="hidden" value="1" />
<BLANKLINE>

FLASH UPLOAD WIDGET

the flashupload vars page configures the flash frontend

>>> from z3c.widget.flashupload import upload
>>> from zope.publisher.browser import TestRequest
>>> from zope.app.pagetemplate import ViewPageTemplateFile
>>> from zope.app.pagetemplate.simpleviewclass import SimpleViewClass
>>> request = TestRequest()
>>> context = object()
>>> viewClass = SimpleViewClass(
...     'flashuploadvars.pt', bases=(upload.FlashUploadVars,))
>>> view = viewClass(context, request)
>>> print view()
<?xml version="1.0" ?>
<var>
    <var name="file_progress">File Progress</var>
    <var name="overall_progress">Overall Progress</var>
    <var name="error">Error on uploading files</var>
    <var name="uploadcomplete">all files uploaded</var>
    <var name="uploadpartial">files uploaded</var>
    <var name="notuploaded">files were not uploaded because
       they're too big</var>
    <var name="maxfilesize">maximum file size is</var>
</var>
>>> view.allowedFileTypes = ('.jpg', '.gif')
>>> print view()
<?xml version="1.0" ?>
<var>
...
    <var name="allowedFileType">.jpg</var>
    <var name="allowedFileType">.gif</var>
 </var>

The Image Widget

this image widget should be used as a custom_widget for image fields. comparing to the default widget in zope3 it does not delete the data in the field if the “delete” checkbox is not explicitly selected.

Adding an Image

>>> import zope.schema
>>> from zope.publisher.browser import TestRequest
>>> from zope import interface
>>> from zope.schema.fieldproperty import FieldProperty
>>> from zope.app.file.interfaces import IImage
>>> from z3c.widget.image.widget import ImageWidget
>>> from zope.app.file.image import Image

create a content type with an image field.

>>> class ITestObject(interface.Interface):
...     image = zope.schema.Object(
...     title=u'Image',
...     schema=IImage)
>>> class TestObject(object):
...     interface.implements(ITestObject)
...     image = FieldProperty(ITestObject['image'])
>>> obj = TestObject()
>>> field = ITestObject['image'].bind(obj)

Send the request without any image information. the empty field should not be changed…

>>> request = TestRequest(form={'field.image' : u''})
>>> widget = ImageWidget(field, request)
>>> widget._getFormInput() is None
True

Send some Image information to the field. the image information should be stored in the field as a Image Object

>>> request = TestRequest(form={'field.image' : u'PNG123Test'})
>>> widget = ImageWidget(field, request)
>>> widget._getFormInput()
<zope.app.file.image.Image object at ...>

Now we save the field again, but without any new image data. the old image information should not be lost

>>> obj.image = Image(u'PNG123Test')
>>> request = TestRequest(form={'field.image' : u''})
>>> widget = ImageWidget(field, request)
>>> widget._getFormInput() is obj.image
True

Now we want to delete the image. the forminput should be None now.

>>> request = TestRequest(form={'field.image' : u'',
...     'field.image.delete': u'true'})
>>> widget = ImageWidget(field, request)
>>> widget._getFormInput() is None
True
>>> print widget()
<div class="z3cImageWidget">
  <input type="file" name="field.image" id="field.image" /><br/>
  <input type="checkbox" name="field.image.delete" value="true" />delete image
</div>

The Widget Namespace

The widget namespace provides a way to traverse to the widgets of a formlib form.

>>> from z3c.widget.namespace.namespace import WidgetHandler

Let us define a form to test this behaviour.

>>> from zope.formlib import form
>>> from zope import interface, schema
>>> class IMyContent(interface.Interface):
...     title = schema.TextLine(title=u'Title')
>>> class MyContent(object):
...     interface.implements(IMyContent)
...     title=None
>>> content = MyContent()
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> class MyForm(form.EditForm):
...     form_fields = form.Fields(IMyContent)
>>> view = MyForm(content,request)
>>> handler = WidgetHandler(view,request)
>>> handler.traverse('title',None)
<zope.formlib.textwidgets.TextWidget object at ...>

Optional Dropdown Widgets

The Optional Dropdown Widget simulates the common desktop widget of a combo box, which can also receive a custom entry.

Before we can start, we have to do a little bit of setup:

>>> import zope.component
>>> import zope.schema
>>> import zope.app.form.browser
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> zope.component.provideAdapter(
...     zope.app.form.browser.TextWidget,
...     (zope.schema.interfaces.ITextLine, IBrowserRequest),
...     zope.app.form.interfaces.IInputWidget)

First we have to create a field and a request:

>>> from z3c.schema.optchoice import OptionalChoice
>>> optchoice = OptionalChoice(
...     __name__='occupation',
...     title=u'Occupation',
...     description=u'The Occupation',
...     values=(u'Programmer', u'Designer', u'Project Manager'),
...     value_type=zope.schema.TextLine())
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()

Now we can initialize widget.

>>> class Content(object):
...     occupation = None
>>> content = Content()
>>> boundOptChoice = optchoice.bind(content)
>>> from z3c.widget.optdropdown import OptionalDropdownWidget
>>> widget = OptionalDropdownWidget(boundOptChoice, request)

Let’s make sure that all fields have the correct value:

>>> widget.name
'field.occupation'
>>> widget.label
u'Occupation'
>>> widget.hint
u'The Occupation'
>>> widget.visible
True
>>> widget.required
True

The constructor should have also created 2 widgets:

>>> widget.customWidget
<zope.formlib.textwidgets.TextWidget object at ...>
>>> widget.dropdownWidget
<zope.formlib.itemswidgets.DropdownWidget object at ...>

setRenderedValue(value) Method

The first method is setRenderedValue(). The widget has two use cases, based on the type of value. If the value is a custom value, it will send the information to the custom widget:

>>> print widget.customWidget()
<... value="" />
>>> 'selected=""' in widget.dropdownWidget()
False
>>> widget.setRenderedValue(u'Scientist')
>>> print widget.customWidget()
<... value="Scientist" />
>>> 'selected=""' in widget.dropdownWidget()
False

After resetting the widget passing in one of the choices in the vocabulary, the value should be displayed in the dropdown:

>>> widget.setRenderedValue(u'Designer')
>>> print widget.customWidget()
<... value="" />
>>> print widget.dropdownWidget()
<div>
...
<option selected="selected" value="Designer">Designer</option>
...
</div>

setPrefix(prefix) Method

The prefix determines the name of the widget and the sub-widgets.

>>> widget.name
'field.occupation'
>>> widget.dropdownWidget.name
'field.occupation.occupation'
>>> widget.customWidget.name
'field.occupation.custom'
>>> widget.setPrefix('test.')
>>> widget.name
'test.occupation'
>>> widget.dropdownWidget.name
'test.occupation.occupation'
>>> widget.customWidget.name
'test.occupation.custom'

getInputValue() Method

This method returns a value based on the input; the data is assumed to be valid. In our case that means, if we entered a custom value, it is returned:

>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.getInputValue()
u'Teacher'

On the other hand, if we selected a choice from the vocabulary, it should be returned:

>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Designer'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.getInputValue()
u'Designer'

applyChanges(content) Method

This method applies the new value to the passed content. However, it must be smart enough to detect whether the values really changed.

>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.applyChanges(content)
True
>>> content.occupation
u'Teacher'
>>> widget.applyChanges(content)
False
>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Designer'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.applyChanges(content)
True
>>> content.occupation
u'Designer'
>>> widget.applyChanges(content)
False

hasInput() Method

This mehtod checks for any input, but does not validate it. In our case this means that either a choice has been selected or the the custom value has been entered.

>>> request = TestRequest()
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher\nBad Stuff'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasInput()
True
>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Waitress'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasInput()
True

hasValidInput() Method

Additionally to checking for any input, this method also checks whether the input is valid:

>>> request = TestRequest()
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Waitress'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Designer'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasValidInput()
True
>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher\nBad Stuff'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.hasValidInput()
True

hidden() Method

This method is implemented by simply concatenating the two widget’s hidden output:

>>> request = TestRequest()
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.setRenderedValue(u'Designer')
>>> print widget.hidden()
<input class="hiddenType" id="field.occupation.occupation"
       name="field.occupation.occupation" type="hidden" value="Designer"  />
<input class="hiddenType" id="field.occupation.custom"
       name="field.occupation.custom" type="hidden" value=""  />
>>> widget.setRenderedValue(u'Teacher')
>>> print widget.hidden()
<input class="hiddenType" id="field.occupation.occupation"
       name="field.occupation.occupation" type="hidden" value=""  />
<input class="hiddenType" id="field.occupation.custom"
       name="field.occupation.custom" type="hidden" value="Teacher"  />

error() Method

Again, we have our two cases. If an error occured in the dropdown, it is reported:

>>> from zope.app.form.interfaces import IWidgetInputError
>>> from zope.app.form.browser.exception import WidgetInputErrorView
>>> from zope.app.form.browser.interfaces import IWidgetInputErrorView
>>> zope.component.provideAdapter(
...     WidgetInputErrorView,
...     (IWidgetInputError, IBrowserRequest), IWidgetInputErrorView)
>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Designer'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.getInputValue()
u'Designer'
>>> widget.error()
''
>>> request = TestRequest(form={
...     'field.occupation.occupation': u'Waitress'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
ConversionError: (u'Invalid value', InvalidValue("token u'Waitress' not found in vocabulary"))
>>> widget.error()
u'<span class="error">Invalid value</span>'

Otherwise the custom widget’s errors are reported:

>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.getInputValue()
u'Teacher'
>>> widget.error()
''
>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher\nBad Stuff'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('custom', u'', ConstraintNotSatisfied(u'Teacher\nBad Stuff'))
>>> widget.error()
u'<span class="error">Constraint not satisfied</span>'

__call__() Method

This method renders the widget using the sub-widgets. It simply adds the two widgets’ output placing the connector between them:

>>> request = TestRequest(form={
...     'field.occupation.custom': u'Teacher'})
>>> widget = OptionalDropdownWidget(boundOptChoice, request)
>>> widget.connector
u'<br />\n'
>>> print widget()
<div>
<div class="value">
<select id="field.occupation.occupation"
        name="field.occupation.occupation" size="1" >
<option selected="selected" value="">(nothing selected)</option>
<option value="Programmer">Programmer</option>
<option value="Designer">Designer</option>
<option value="Project Manager">Project Manager</option>
</select>
</div>
<input name="field.occupation.occupation-empty-marker" type="hidden"
       value="1" />
</div><br />
<input class="textType" id="field.occupation.custom"
       name="field.occupation.custom" size="20" type="text" value="Teacher" />

SequenceTable Widget

This package provides a Sequence Widget just as zope.app.form.browser.sequencewidget. The main difference is that it places the subobject’s fields horizontally in a table. That means a kind of voucher-item forms are piece of cake to do.

There is also a widget (SequenceTableJSWidget) which does the add/remove item in the browser with javascript. The trick is to embed an invisible template of an empty row in the HTML, add that each time a new row is required.

Drawbacks of JS:
  • Validation is done ONLY when the complete form is submitted to the server.

  • Submitting the form and using the Back button of the browser does not work.

WARNING!

The subobject MUST have subwidgets. That is usually the case if the subobject is based on zope.schema.Object.

TODO

Tests. Some are there, some are copied from z.a.form.browser and need fix.

SSN Widget

The social security number widget can be used as a custom widget for text line fields, enforcing a particular layout.

First we have to create a field and a request:

>>> import datetime
>>> import zope.schema
>>> field = zope.schema.TextLine(
...     title=u'SSN',
...     description=u'Social Security Number',
...     required=True)
>>> field.__name__ = 'field'
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()

Now we can initialize widget.

>>> from z3c.widget.ssn.browser import SSNWidget
>>> widget = SSNWidget(field, request)

Let’s make sure that all fields have the correct value:

>>> widget.name
'field.field'
>>> widget.label
u'SSN'
>>> widget.hint
u'Social Security Number'
>>> widget.visible
True
>>> widget.required
True

The constructor should have also created 3 sub-widgets:

>>> widget.widgets['first']
<zope.formlib.textwidgets.TextWidget object at ...>
>>> widget.widgets['second']
<zope.formlib.textwidgets.TextWidget object at ...>
>>> widget.widgets['third']
<zope.formlib.textwidgets.TextWidget object at ...>

setRenderedValue(value) Method

The first method is setRenderedValue(). The widget has two use cases, based on the type of value:

>>> widget = SSNWidget(field, request)
>>> widget.setRenderedValue(u'123-45-6789')
>>> print widget()
<input class="textType" id="field.field.first" name="field.field.first"
       size="3" type="text" value="123"  />&nbsp;&mdash;&nbsp;
<input class="textType" id="field.field.second" name="field.field.second"
       size="2" type="text" value="45"  />&nbsp;&mdash;&nbsp;
<input class="textType" id="field.field.third" name="field.field.third"
       size="4" type="text" value="6789"  />

setPrefix(prefix) Method

The prefix determines the name of the widget and all its sub-widgets.

>>> widget.name
'field.field'
>>> widget.widgets['first'].name
'field.field.first'
>>> widget.widgets['second'].name
'field.field.second'
>>> widget.widgets['third'].name
'field.field.third'
>>> widget.setPrefix('test.')
>>> widget.name
'test.field'
>>> widget.widgets['first'].name
'test.field.first'
>>> widget.widgets['second'].name
'test.field.second'
>>> widget.widgets['third'].name
'test.field.third'

If the prefix does not end in a dot, one is added:

>>> widget.setPrefix('test')
>>> widget.name
'test.field'
>>> widget.widgets['first'].name
'test.field.first'
>>> widget.widgets['second'].name
'test.field.second'
>>> widget.widgets['third'].name
'test.field.third'

getInputValue() Method

This method returns the full SSN string:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> value = widget.getInputValue()
>>> value
u'123-45-6789'

If a set of values does not produce a valid string, a value error is raised:

>>> request = TestRequest(form={
...     'field.field.first': '1234',
...     'field.field.second': '56',
...     'field.field.third': '7890'})
>>> widget = SSNWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('first', u'Frst three digits', ConstraintNotSatisfied(u'1234'))
>>> widget._error.__class__
<class 'zope.formlib.interfaces.WidgetInputError'>

applyChanges(content) Method

This method applies the new SSN to the passed content. However, it must be smart enough to detect whether the values really changed.

>>> class Content(object):
...     field = None
>>> content = Content()
>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.applyChanges(content)
True
>>> content.field
u'123-45-6789'
>>> widget.applyChanges(content)
False

hasInput() Method

This method checks for any input, but does not validate it.

>>> request = TestRequest()
>>> widget = SSNWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123'})
>>> widget = SSNWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.second': '45'})
>>> widget = SSNWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.hasInput()
True

hasValidInput() Method

Additionally to checking for any input, this method also checks whether the input is valid:

>>> request = TestRequest()
>>> widget = SSNWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123'})
>>> widget = SSNWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.second': '45'})
>>> widget = SSNWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.hasValidInput()
True

hidden() Method

This method is renders the output as hidden fields:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> print widget.hidden()
<input class="hiddenType" id="field.field.first" name="field.field.first"
       type="hidden" value="123" />
<input class="hiddenType" id="field.field.second" name="field.field.second"
       type="hidden" value="45"  />
<input class="hiddenType" id="field.field.third" name="field.field.third"
       type="hidden" value="6789"  />

error() Method

Let’s test some bad data and check the error handling.

The third field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '678'})
>>> widget = SSNWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('third', u'Third four digits', ConstraintNotSatisfied(u'678'))
>>> print widget.error()
<span class="error">Constraint not satisfied</span>

The second field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '4-',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('second', u'Second two digits', ConstraintNotSatisfied(u'4-'))
>>> print widget.error()
<span class="error">Constraint not satisfied</span>

The first field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.first': 'xxx',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('first', u'Frst three digits', ConstraintNotSatisfied(u'xxx'))
>>> print widget.error()
<span class="error">Constraint not satisfied</span>

No error occurred:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> widget.getInputValue()
u'123-45-6789'
>>> widget.error()
''

__call__() Method

This method renders the widget using the sub-widgets. Let’s see the output:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45',
...     'field.field.third': '6789'})
>>> widget = SSNWidget(field, request)
>>> print widget()
<input class="textType" id="field.field.first" name="field.field.first"
       size="3" type="text" value="123"  />&nbsp;&mdash;&nbsp;
<input class="textType" id="field.field.second" name="field.field.second"
       size="2" type="text" value="45"  />&nbsp;&mdash;&nbsp;
<input class="textType" id="field.field.third" name="field.field.third"
       size="4" type="text" value="6789"  />

HTML-Editor Widget TinyMCE

This package provides a WYSIWYG-Editor-Widget for HTML-Content, by using the greate TinyMCE editor (see: http://tinymce.moxiecode.com/).

US Phone Widget

The US phone number widget can be used as a custom widget for text line fields, enforcing a particular layout.

First we have to create a field and a request:

>>> import datetime
>>> import zope.schema
>>> field = zope.schema.TextLine(
...     title=u'Phone',
...     description=u'Phone Number',
...     required=True)
>>> field.__name__ = 'field'
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()

Now we can initialize widget.

>>> from z3c.widget.usphone.browser import PhoneWidget
>>> widget = PhoneWidget(field, request)

Let’s make sure that all fields have the correct value:

>>> widget.name
'field.field'
>>> widget.label
u'Phone'
>>> widget.hint
u'Phone Number'
>>> widget.visible
True
>>> widget.required
True

The constructor should have also created 3 sub-widgets:

>>> widget.widgets['first']
<zope.formlib.textwidgets.TextWidget object at ...>
>>> widget.widgets['second']
<zope.formlib.textwidgets.TextWidget object at ...>
>>> widget.widgets['third']
<zope.formlib.textwidgets.TextWidget object at ...>

setRenderedValue(value) Method

The first method is setRenderedValue(). The widget has two use cases, based on the type of value:

>>> widget = PhoneWidget(field, request)
>>> widget.setRenderedValue(u'123-456-7890')
>>> print widget()
(<input class="textType" id="field.field.first" name="field.field.first"
        size="3" type="text" value="123"  />)&nbsp;
<input class="textType" id="field.field.second" name="field.field.second"
       size="3" type="text" value="456"  />&nbsp;&mdash;&nbsp;
<input class="textType" id="field.field.third" name="field.field.third"
       size="4" type="text" value="7890"  />

setPrefix(prefix) Method

The prefix determines the name of the widget and all its sub-widgets.

>>> widget.name
'field.field'
>>> widget.widgets['first'].name
'field.field.first'
>>> widget.widgets['second'].name
'field.field.second'
>>> widget.widgets['third'].name
'field.field.third'
>>> widget.setPrefix('test.')
>>> widget.name
'test.field'
>>> widget.widgets['first'].name
'test.field.first'
>>> widget.widgets['second'].name
'test.field.second'
>>> widget.widgets['third'].name
'test.field.third'

If the prefix does not end in a dot, one is added:

>>> widget.setPrefix('test')
>>> widget.name
'test.field'
>>> widget.widgets['first'].name
'test.field.first'
>>> widget.widgets['second'].name
'test.field.second'
>>> widget.widgets['third'].name
'test.field.third'

getInputValue() Method

This method returns the full phone string:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> value = widget.getInputValue()
>>> value
u'123-456-7890'

If a set of values does not produce a valid string, a value error is raised:

>>> request = TestRequest(form={
...     'field.field.first': '1234',
...     'field.field.second': '56',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('first', u'Area Code', ConstraintNotSatisfied(u'1234'))
>>> widget._error.__class__
<class 'zope.formlib.interfaces.WidgetInputError'>

applyChanges(content) Method

This method applies the new phone number to the passed content. However, it must be smart enough to detect whether the values really changed.

>>> class Content(object):
...     field = None
>>> content = Content()
>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.applyChanges(content)
True
>>> content.field
u'123-456-7890'
>>> widget.applyChanges(content)
False

hasInput() Method

This method checks for any input, but does not validate it.

>>> request = TestRequest()
>>> widget = PhoneWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.second': '456'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasInput()
True

hasValidInput() Method

Additionally to checking for any input, this method also checks whether the input is valid:

>>> request = TestRequest()
>>> widget = PhoneWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.second': '456'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasValidInput()
False
>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.hasValidInput()
True

hidden() Method

This method is renders the output as hidden fields:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> print widget.hidden()
<input class="hiddenType" id="field.field.first" name="field.field.first"
       type="hidden" value="123" />
<input class="hiddenType" id="field.field.second" name="field.field.second"
       type="hidden" value="456"  />
<input class="hiddenType" id="field.field.third" name="field.field.third"
       type="hidden" value="7890"  />

error() Method

Let’s test some bad data and check the error handling.

The third field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '78901'})
>>> widget = PhoneWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('third', u'Four Digits', ConstraintNotSatisfied(u'78901'))
>>> print widget.error()
<span class="error">Constraint not satisfied</span>

The second field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '45-',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('second', u'Three Digits', ConstraintNotSatisfied(u'45-'))
>>> print widget.error()
<span class="error">Constraint not satisfied</span>

The first field contains an invalid value:

>>> request = TestRequest(form={
...     'field.field.first': 'xxx',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.getInputValue()
Traceback (most recent call last):
...
WidgetInputError: ('first', u'Area Code', ConstraintNotSatisfied(u'xxx'))
>>> print widget.error()
<span class="error">Constraint not satisfied</span>

No error occurred:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> widget.getInputValue()
u'123-456-7890'
>>> widget.error()
''

__call__() Method

This method renders the widget using the sub-widgets. Let’s see the output:

>>> request = TestRequest(form={
...     'field.field.first': '123',
...     'field.field.second': '456',
...     'field.field.third': '7890'})
>>> widget = PhoneWidget(field, request)
>>> print widget()
(<input class="textType" id="field.field.first" name="field.field.first"
        size="3" type="text" value="123"  />)&nbsp;
<input class="textType" id="field.field.second" name="field.field.second"
       size="3" type="text" value="456"  />&nbsp;&mdash;&nbsp;
<input class="textType" id="field.field.third" name="field.field.third"
       size="4" type="text" value="7890"  />

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

z3c.widget-0.3.0.tar.gz (1.3 MB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page