Commit 8b8f9f00 authored by Nicolas Delaby's avatar Nicolas Delaby

Merge Formulator and FormulatorPatch from ERP5Form


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@26872 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent de1888a7
Formulator Credits
Martijn Faassen (faassen@vet.uu.nl) -- Main developer, design and
implementation.
Many thanks go to:
Kit Blake (kitblake at v2.nl) -- UI help and design help.
Yury Don (yura at vpcit.ru) -- contributed EmailField and FloatField,
design and implementation help.
Stephan Richter (srichter at iuveno-net.de) -- contributed LinkField and
FileField. Contributed PatternChecker
module used by PatternField. Other
design and implementation help.
Nicola Larosa (nico at tekNico.net) -- feedback and bugfixes.
Magnus Heino (magus.heino at rivermen.se) -- feedback and bugfixes.
Joel Burton (jburton at scw.org) -- feedback and bugfixes.
Ulrich Eck (ueck at net-labs.de) -- much help and patience with the
TALES tab.
Dirk Datzert (Dirk.Datzert at rasselstein-hoesch.de) -- feedback and bugfixes.
Max Petrich (petrich.max at kis-solution.de) -- feedback and bugfixes.
Matt Behrens (matt.behrens at kohler.com) -- feedback and bugfixes.
Nikolay Kim (fafhrd at datacom.kz) -- code inspiration for
XMLToForm/FormToXML.
Godefroid Chapelle (gotcha at swing.be) -- Bugfixes.
Alan Runyan (runyaga at runyaga.com) -- Fix to email regular expression.
Sascha Welter (welter at network-ag.com) -- Extensive help with email
regular expression.
Clemens Klein-Robbenhaar (robbenhaar at espresto.com) -- Many bugfixes
and feature
additions.
Christian Zagrodnick (cz at gocept.com) -- Unicode awareness fixes and
XML entry form.
Iutin Vyacheslav (iutin at whirix.com) -- am/pm feature for DateTime
fields.
Kapil Thangavelu (k_vertigo at objectrealms.net) -- Enabled ':record' rendering.
Pierre-Julien Grizel (grizel at ingeniweb.com) -- ProductForm.
Sbastien Robin (seb at nexedi.com) -- more consistent ordering in XML
serialization.
Guido Wesdorp (guido at infrae.com) -- Added extra_item attribute on
compound fields.
Yura Petrov (ypetrov at naumen.ru) -- Various FSForm related
improvements.
Vladimir Voznesensky (vovic at smtp.ru) -- Enabling/disabling of fields.
Special thanks also goes to Rik Hoekstra.
Also a thank you to those few valiant souls who suffered through the
bugs of ZFormulator, the previous implementation. Let's hope this
one's better!
"""
This module contains some magic glue to make it seem as if we
can refer to field classes before they've been defined, through the
'fields' class.
This way, they can be used to create properties on fields.
When the field classes have been defined, get_field()
can be used on FieldProperty objects to get an
actual field object.
"""
from FieldRegistry import FieldRegistry
class DummyFieldFactory:
def __getattr__(self, name):
return DummyField(name)
fields = DummyFieldFactory()
class DummyField:
def __init__(self, desired_meta_class):
self.desired_meta_class = desired_meta_class
def __call__(self, id, **kw):
self.id = id
self.kw = kw
return self
def get_value(self, name):
return self.kw.get(name, "")
def get_real_field(self):
"""Get an actual field for this property.
"""
return apply(FieldRegistry.get_field_class(self.desired_meta_class),
(self.id,), self.kw)
"""Exception Classes for Formulator"""
# These classes are placed here so that they can be imported into TTW Python
# scripts. To do so, add the following line to your Py script:
# from Products.Formulator.Errors import ValidationError, FormValidationError
from Products.PythonScripts.Utility import allow_class
class FormValidationError(Exception):
def __init__(self, errors, result):
Exception.__init__(self,"Form Validation Error")
self.errors = errors
self.result = result
allow_class(FormValidationError)
class ValidationError(Exception):
def __init__(self, error_key, field):
Exception.__init__(self, error_key)
self.error_key = error_key
self.field_id = field.id
self.field = field
self.error_text = field.get_error_message(error_key)
allow_class(ValidationError)
class FieldDisabledError(AttributeError):
def __init__(self, error_key, field):
AttributeError.__init__(self, error_key)
self.field_id = field.id
self.field = field
allow_class(FieldDisabledError)
import Globals
from AccessControl import ClassSecurityInfo
try:
import Products.FileSystemSite
except ImportError:
# use CMF product
from Products.CMFCore.CMFCorePermissions import View
from Products.CMFCore.FSObject import FSObject
from Products.CMFCore.DirectoryView import registerFileExtension,\
registerMetaType, expandpath
else:
# use FileSystemSite product
from Products.FileSystemSite.Permissions import View
from Products.FileSystemSite.FSObject import FSObject
from Products.FileSystemSite.DirectoryView import registerFileExtension,\
registerMetaType, expandpath
from Products.Formulator.Form import ZMIForm
from Products.Formulator.XMLToForm import XMLToForm
class FSForm(FSObject, ZMIForm):
"""FSForm."""
meta_type = 'Filesystem Formulator Form'
manage_options = (
(
{'label':'Customize', 'action':'manage_main'},
{'label':'Test', 'action':'formTest'},
)
)
_updateFromFS = FSObject._updateFromFS
security = ClassSecurityInfo()
security.declareObjectProtected(View)
def __init__(self, id, filepath, fullname=None, properties=None):
FSObject.__init__(self, id, filepath, fullname, properties)
def _createZODBClone(self):
# not implemented yet
return None
def _readFile(self, reparse):
file = open(expandpath(self._filepath), 'rb')
try:
data = file.read()
finally:
file.close()
# update the form with the xml data
try:
XMLToForm(data, self)
except:
# bare except here, but I hope this is ok, as the
# exception should be reraised
# (except if the LOG raises another one ...
# should we be more paranoid here?)
import zLOG
zLOG.LOG(
'Formulator.FSForm', zLOG.ERROR,
'error reading form from file ' +
expandpath(self._filepath))
raise
#### The following is mainly taken from Form.py ACCESSORS section ###
## def get_field_ids(self):
## self._updateFromFS()
## return ZMIForm.get_field_ids(self)
## def get_fields_in_group(self, group):
## self._updateFromFS()
## return ZMIForm.get_fields_in_group(self, group)
## def has_field(self, id):
## self._updateFromFS()
## return ZMIForm.has_field(self, id)
## def get_field(self, id):
## self._updateFromFS()
## return ZMIForm.get_field(self, id)
## def get_groups(self):
## self._updateFromFS()
## return ZMIForm.get_groups(self)
## def get_form_encoding(self):
## self._updateFromFS()
## return ZMIForm.get_form_encoding(self)
## def header(self):
## self._updateFromFS()
## return ZMIForm.header(self)
## def get_xml(self):
## self._updateFromFS()
## return ZMIForm.get_xml(self)
## def all_meta_types(self):
## self._updateFromFS()
## return ZMIForm.all_meta_types(self)
## security.declareProtected('View management screens', 'get_group_rows')
## def get_group_rows(self):
## self._updateFromFS()
## return ZMIForm.get_group_rows(self)
Globals.InitializeClass(FSForm)
registerFileExtension('form', FSForm)
registerMetaType('FSForm', FSForm)
This diff is collapsed.
from Globals import DTMLFile
from HelpSys import HelpTopic
class FieldHelpTopic(HelpTopic.HelpTopic):
"""A special help topic for fields.
"""
meta_type = 'Help Topic'
def __init__(self, id, title, field_class,
permissions=None, categories=None):
self.id = id
self.title = title
self.field_class = field_class
if permissions is not None:
self.permissions = permissions
if categories is not None:
self.categories = categories
index_html = DTMLFile('dtml/FieldHelpTopic', globals())
def SearchableText(self):
"""Full text of the Help Topic, for indexing purposes."""
return "" # return self.index_html()
def get_groups(self):
"""Get form groups of this field.
"""
return self.field_class.form.get_groups()
def get_fields_in_group(self, group):
"""Get the fields in the group.
"""
return self.field_class.form.get_fields_in_group(group)
import os
import OFS
from Globals import ImageFile
from FieldHelpTopic import FieldHelpTopic
class FieldRegistry:
"""A registry of fields, maintaining a dictionary with
the meta_type of the field classes as key and the field class as
values. Updates the Form as necessary as well.
"""
def __init__(self):
"""Initializer of FieldRegistry.
"""
self._fields = {}
def get_field_class(self, fieldname):
"""Get a certain field class by its name (meta_type)
fieldname -- the name of the field to get from the registry
"""
return self._fields[fieldname]
def get_field_classes(self):
"""Return all fields.
"""
return self._fields
def registerField(self, field_class, icon=None):
"""Register field with Formulator.
field_class -- the class of the field to be registered
icon -- optional filename of the icon
"""
# put it in registry dictionary
self._fields[field_class.meta_type] = field_class
# set up dummy fields in field's form
initializeFieldForm(field_class)
# set up the icon if a filename is supplied
if icon:
setupIcon(field_class, icon, 'Formulator')
def registerFieldHelp(self, *args, **kw):
"""XXX: this is a quick fix to avoid bloating the ZODB.
Proper fix should only add FieldHelp when it's missing.
"""
pass
def initializeFields(self):
"""Initialize all field classes in field forms to use actual field
objects so we can finally eat our own dogfood.
"""
# for each field, realize fields in form
# this is finally possible as all field classes are now
# fully defined.
for field_class in self._fields.values():
field_class.form._realize_fields()
field_class.override_form._realize_fields()
field_class.tales_form._realize_fields()
# initialize registry as a singleton
FieldRegistry = FieldRegistry()
def initializeFieldForm(field_class):
"""Initialize the properties (fields and values) on a particular
field class. Also add the tales and override methods.
"""
from Form import BasicForm
from DummyField import fields
form = BasicForm()
override_form = BasicForm()
tales_form = BasicForm()
for field in getPropertyFields(field_class.widget):
form.add_field(field, "widget")
tales_field = fields.TALESField(field.id,
title=field.get_value('title'),
description="",
default="",
display_width=40,
required=0)
tales_form.add_field(tales_field, "widget")
method_field = fields.MethodField(field.id,
title=field.get_value("title"),
description="",
default="",
required=0)
override_form.add_field(method_field, "widget")
for field in getPropertyFields(field_class.validator):
form.add_field(field, "validator")
tales_field = fields.TALESField(field.id,
title=field.get_value('title'),
description="",
default="",
display_with=40,
required=0)
tales_form.add_field(tales_field, "validator")
method_field = fields.MethodField(field.id,
title=field.get_value("title"),
description="",
default="",
required=0)
override_form.add_field(method_field, "validator")
field_class.form = form
field_class.override_form = override_form
field_class.tales_form = tales_form
def getPropertyFields(obj):
"""Get property fields from a particular widget/validator.
"""
fields = []
for property_name in obj.property_names:
fields.append(getattr(obj, property_name))
return fields
def setupIcon(klass, icon, repository):
"""Load icon into Zope image object and put it in Zope's
repository for use by the ZMI, for a particular class.
klass -- the class of the field we're adding
icon -- the icon
"""
# set up misc_ respository if not existing yet
if not hasattr(OFS.misc_.misc_, repository):
setattr(OFS.misc_.misc_,
repository,
OFS.misc_.Misc_(repository, {}))
# get name of icon in the misc_ directory
icon_name = os.path.split(icon)[1]
# set up image object from icon file
icon_image = ImageFile(icon, globals())
icon_image.__roles__ = None
# put icon image object in misc_/Formulator/
getattr(OFS.misc_.misc_, repository)[icon_name] = icon_image
# set icon attribute in field_class to point to this image obj
setattr(klass, 'icon', 'misc_/%s/%s' %
(repository, icon_name))
This diff is collapsed.
from StringIO import StringIO
from cgi import escape
import types
#def write(s):
# if type(s) == type(u''):
# print "Unicode:", repr(s)
def formToXML(form, prologue=1):
"""Takes a formulator form and serializes it to an XML representation.
"""
f = StringIO()
write = f.write
if prologue:
write('<?xml version="1.0"?>\n\n')
write('<form>\n')
# export form settings
for field in form.settings_form.get_fields(include_disabled=1):
id = field.id
value = getattr(form, id)
if id == 'title':
value = escape(value)
if id == 'unicode_mode':
if value:
value = 'true'
else:
value = 'false'
write(' <%s>%s</%s>\n' % (id, value, id))
# export form groups
write(' <groups>\n')
for group in form.get_groups(include_empty=1):
write(' <group>\n')
write(' <title>%s</title>\n' % escape(group))
write(' <fields>\n\n')
for field in form.get_fields_in_group(group, include_disabled=1):
write(' <field><id>%s</id> <type>%s</type>\n' % (field.id, field.meta_type))
write(' <values>\n')
items = field.values.items()
items.sort()
for key, value in items:
if value is None:
continue
if value==True: # XXX Patch
value = 1 # XXX Patch
if value==False: # XXX Patch
value = 0 # XXX Patch
if callable(value): # XXX Patch
write(' <%s type="method">%s</%s>\n' % # XXX Patch
(key, escape(str(value.method_name)), key)) # XXX Patch
elif type(value) == type(1.1):
write(' <%s type="float">%s</%s>\n' % (key, escape(str(value)), key))
elif type(value) == type(1):
write(' <%s type="int">%s</%s>\n' % (key, escape(str(value)), key))
elif type(value) == type([]):
write(' <%s type="list">%s</%s>\n' % (key, escape(str(value)), key))
else:
if type(value) not in (types.StringType, types.UnicodeType):
value = str(value)
write(' <%s>%s</%s>\n' % (key, escape(value), key))
write(' </values>\n')
write(' <tales>\n')
items = field.tales.items()
items.sort()
for key, value in items:
if value:
write(' <%s>%s</%s>\n' % (key, escape(str(value._text)), key))
write(' </tales>\n')
write(' <messages>\n')
for message_key in field.get_error_names():
write(' <message name="%s">%s</message>\n' %
(escape(message_key), escape(field.get_error_message(message_key))))
write(' </messages>\n')
write(' </field>\n')
write(' </fields>\n')
write(' </group>\n')
write(' </groups>\n')
write('</form>')
if form.unicode_mode:
return f.getvalue().encode('UTF-8')
else:
return unicode(f.getvalue(), form.stored_encoding).encode('UTF-8')
This diff is collapsed.
# include some helper fields which are in their own files
from MethodField import MethodField
from ListTextAreaField import ListTextAreaField
from TALESField import TALESField
Installing Formulator
Requirements
Formulator should work with Zope versions 2.6 or higher:
http://www.zope.org/Products/Zope
For reading in forms as XML you need to have minidom installed;
this should come with a normal python 2.1 distribution. This is
not required to use Formulator, however.
Upgrading
to 1.6.0 from earlier versions
There should be no problems.
to 1.4.2 from earlier versions
There should be no problems.
to 1.4.1 from earlier versions
There should be no problems.
to 1.4.0 from earlier versions
There should be no problems.
to 1.3.1 from earlier versions
There should be no problems (see note for 0.9.2 though in the
unusual case you're upgrading from that..this is the last time
I'll mention it :).
to 1.3.0 from earlier versions
There should be no problems, but see the note if you're
upgrading from 0.9.2 or below (but I'd be surprised if you
were!).
to 1.2.0 from earlier versions
There should be no problems, but see the note if you're upgrading
from version 0.9.2 or below.
to 1.1.0 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below however, please see the upgrading note for 0.9.3. Do note
that the Override tab is scheduled to be phased out eventually in
favor of the TALES tab. This will take a while yet, though.
to 1.0.9 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below however, please see the upgrading note for 0.9.3. Do note
that the Override tab is scheduled to be phased out eventually in
favor of the TALES tab. This will take a while yet, though.
to 1.0.1 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below, please see the upgrading note for 0.9.3.
to 1.0 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below, please see the upgrading note for 0.9.3.
to 0.9.5 from earlier versions
There should be no problems in upgrading from 0.9.4 or 0.9.3.
If you're upgrading from 0.9.2 or below, see the upgrading note
for 0.9.3.
to 0.9.4 from earlier versions
There should be no problems in upgrading from 0.9.3.
If you're upgrading from 0.9.2 or below, see the upgrading
note for 0.9.3.
to 0.9.3 from earlier versions
'items_method' in ListField is gone; you'll have to adjust make
your forms use 'items' in the override tab now instead. Sorry
about that, it *was* marked experimental. :)
There should be no other problems in upgrading.
to 0.9.2 from earlier versions
There should be no significant upgrade problems; your forms
should still work. RangedIntegerFields should show up as
IntegerFields, which subsume their functionality.
to 0.9.1 from earlier versions
There should be no significant upgrade problems; your forms
should still work.
Quickstart
Formulator follows the normal Zope filesystem product installation
procedure; just unpack the tarball to your products directory and
restart Zope.
Now the same at a more leisurely pace.
Unpacking
Formulator comes as a 'Formulator-x.x.tgz' file, where 'x.x'
stands for the Formulator version number. On Unix, you can use::
tar xvzf Formulator-x.x.tgz
to unpack the file. On Windows you can use your favorite archiving
software, such as WinZip.
This will create a Formulator directory.
Installing the Product
Move this directory to your Zope's Products directory. Normally
this is 'yourzope/lib/python/Products'.
Now restart your Zope.
Verifying Installation
If all went well, Formulator should now be visible in Zope in the
Products screen ('/Control_Panel/Products'). In a Zope folder, you
should now see a 'Formulator Form' in your 'Add' list. You should
be able to add a form to a folder now.
Copyright (c) 2001, 2002, 2003 Infrae. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Neither the name of Infrae nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import string
from DummyField import fields
import Widget, Validator
from Field import ZMIField
class ListTextAreaWidget(Widget.TextAreaWidget):
default = fields.ListTextAreaField('default',
title='Default',
default=[],
required=0)
def render(self, field, key, value, REQUEST, render_prefix=None):
if value is None:
value = field.get_value('default')
lines = []
for element_text, element_value in value:
lines.append("%s | %s" % (element_text, element_value))
return Widget.TextAreaWidget.render(self, field, key,
string.join(lines, '\n'),
REQUEST)
ListTextAreaWidgetInstance = ListTextAreaWidget()
class ListLinesValidator(Validator.LinesValidator):
"""A validator that can deal with lines that have a | separator
in them to split between text and value of list items.
"""
def validate(self, field, key, REQUEST):
value = Validator.LinesValidator.validate(self, field, key, REQUEST)
result = []
for line in value:
elements = string.split(line, "|")
if len(elements) >= 2:
text, value = elements[:2]
else:
text = line
value = line
text = string.strip(text)
value = string.strip(value)
result.append((text, value))
return result
ListLinesValidatorInstance = ListLinesValidator()
class ListTextAreaField(ZMIField):
meta_type = "ListTextAreaField"
# field only has internal use
internal_field = 1
widget = ListTextAreaWidgetInstance
validator = ListLinesValidatorInstance
import string
from DummyField import fields
import Widget, Validator
from Globals import Persistent
import Acquisition
from Field import ZMIField
from AccessControl import getSecurityManager
class MethodWidget(Widget.TextWidget):
default = fields.MethodField('default',
title='Default',
default="",
required=0)
def render(self, field, key, value, REQUEST, render_prefix=None):
if value == None:
method_name = field.get_value('default')
else:
if value != "":
method_name = value.method_name
else:
method_name = ""
return Widget.TextWidget.render(self, field, key, method_name, REQUEST)
MethodWidgetInstance = MethodWidget()
class Method(Persistent, Acquisition.Implicit):
"""A method object; calls method name in acquisition context.
"""
def __init__(self, method_name):
self.method_name = method_name
def __call__(self, *arg, **kw):
# get method from acquisition path
method = getattr(self, self.method_name)
# check if we have 'View' permission for this method
# (raises error if not)
getSecurityManager().checkPermission('View', method)
# okay, execute it with supplied arguments
return apply(method, arg, kw)
class BoundMethod(Method):
"""A bound method calls a method on a particular object.
Should be used internally only.
"""
def __init__(self, object, method_name):
BoundMethod.inheritedAttribute('__init__')(self, method_name)
self.object = object
def __call__(self, *arg, **kw):
method = getattr(self.object, self.method_name)
return apply(method, arg, kw)
class MethodValidator(Validator.StringBaseValidator):
def validate(self, field, key, REQUEST):
value = Validator.StringBaseValidator.validate(self, field, key,
REQUEST)
if value == "" and not field.get_value('required'):
return value
return Method(value)
MethodValidatorInstance = MethodValidator()
class MethodField(ZMIField):
meta_type = 'MethodField'
internal_field = 1
widget = MethodWidgetInstance
validator = MethodValidatorInstance
import re
# Symbols that are used to represent groups of characters
NUMBERSYMBOL = 'd' # 0-9
CHARSYMBOL = 'e' # a-zA-Z
NUMCHARSYMBOL = 'f' # a-zA-Z0-9
# List of characters, that are special to Regex. Listing them here and
# therefore escaping them will help making the Validator secure.
# NOTE: Please do not add '*', since it is used to determine inifinite
# long char symbol rows. (See examples at the of the file.)
DANGEROUSCHARS = '\\()+?.$'
class PatternChecker:
"""
This class defines a basic user friendly checker and processor of
string values according to pattern.
It can verify whether a string value fits a certain pattern of
digits and letters and possible special characters.
"""
# a dictionary that converts an array of symbols to regex expressions
symbol_regex_dict = {NUMBERSYMBOL : '([0-9]{%i,%s})',
CHARSYMBOL : '([a-zA-Z]{%i,%s})',
NUMCHARSYMBOL : '([0-9a-zA-Z]{%i,%s})'}
def _escape(self, match_object):
"""Escape a single character.
"""
return '\\' + match_object.group(0)
def _escape_special_characters(self, s):
"""Escape the characters that have a special meaning in regex.
"""
return re.sub('[' + DANGEROUSCHARS + ']', self._escape, s)
def _unescape_special_characters(self, s):
"""Reverse the escaping, so that the final string is as close as
possible to the original one.
"""
return re.sub('\\\\', '', s)
def _replace_symbol_by_regex(self, match_object):
"""Replace the character symbol with their respective regex.
"""
length = len(match_object.group(0))
# Yikes, what a hack! But I could not come up with something better.
if match_object.group(0)[-1] == '*':
min = length - 1
max = ''
else:
min = length
max = str(min)
return self.symbol_regex_dict[match_object.group(0)[0]] %(min, max)
def make_regex_from_pattern(self, pattern):
"""Replaces all symbol occurences and creates a complete regex
string.
"""
regex = self._escape_special_characters(pattern)
for symbol in [NUMBERSYMBOL, CHARSYMBOL, NUMCHARSYMBOL]:
regex = re.sub(symbol+'{1,}\*?', self._replace_symbol_by_regex, regex)
return '^ *' + regex + ' *$'
def construct_value_from_match(self, result, pattern):
"""After we validated the string, we put it back together; this is
good, since we can easily clean up the data this way.
"""
value = self._escape_special_characters(pattern)
_symbols = '['+NUMBERSYMBOL + CHARSYMBOL + NUMCHARSYMBOL + ']'
re_obj = re.compile(_symbols+'{1,}\*?')
for res in result.groups():
match = re_obj.search(value)
value = value[:match.start()] + res + value[match.end():]
return value
def clean_value(self, value):
"""Clean up unnecessary white characters.
"""
# same as string.strip, but since I am using re everywhere here,
# why not use it now too?
value = re.sub('^\s*', '', value)
value = re.sub('\s*$', '', value)
# make out of several white spaces, one whitespace...
value = re.sub(' *', ' ', value)
return value
def validate_value(self, patterns, value):
"""Validate method that manges the entire validation process.
The validator goes through each pattern and
tries to get a match to the value (second parameter). At the end, the
first pattern of the list is taken to construct the value again; this
ensures data cleansing and a common data look.
"""
value = self.clean_value(value)
result = None
for pattern in patterns:
regex = self.make_regex_from_pattern(pattern)
re_obj = re.compile(regex)
result = re_obj.search(value)
if result:
break
if not result:
return None
value = self.construct_value_from_match(result, patterns[0])
return self._unescape_special_characters(value)
if __name__ == '__main__':
val = PatternChecker()
# American long ZIP
print val.validate_value(['ddddd-dddd'], '34567-1298')
print val.validate_value(['ddddd-dddd'], ' 34567-1298 \t ')
# American phone number
print val.validate_value(['(ddd) ddd-dddd', 'ddd-ddd-dddd',
'ddd ddd-dddd'],
'(345) 678-1298')
print val.validate_value(['(ddd) ddd-dddd', 'ddd-ddd-dddd',
'ddd ddd-dddd'],
'345-678-1298')
# American money
print val.validate_value(['$ d*.dd'], '$ 1345345.00')
# German money
print val.validate_value(['d*.dd DM'], '267.98 DM')
# German license plate
print val.validate_value(['eee ee-ddd'], 'OSL HR-683')
# German phone number (international)
print val.validate_value(['+49 (d*) d*'], '+49 (3574) 7253')
print val.validate_value(['+49 (d*) d*'], '+49 (3574) 7253')
"""
ProductForm.py
This file is an adaptation from part of Plone's FormTool.py tool.
It provides a wrapping around Formulator.BasicForm, allowing it
to be created inside a product but used outside it.
"""
import string
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
import FormValidationError, BasicForm
import StandardFields
class ProductForm(BasicForm):
"""Wraps Formulator.BasicForm and provides some convenience methods that
make BasicForms easier to work with from external methods."""
security = ClassSecurityInfo()
security.declareObjectPublic()
security.declareProtected('View', 'get_field')
def get_field(self, id):
"""Get a field of a certain id, wrapping in context of self
"""
return self.fields[id].__of__(self)
security.declarePublic('addField')
def addField(self, field_id, fieldType, group=None, **kwargs):
"""
Adds a Formulator Field to the wrapped BasicForm.
fieldType: An abbreviation for the Field type.
'String' generates a StringField, 'Int' generates an IntField, etc.
Uses a StringField if no suitable Field type is found.
field_id: Name of the variable in question. Note that Formulator adds
'field_' to variable names, so you will need to refer to the variable
foo as field_foo in form page templates.
group: Formulator group for the field.
Additional arguments: addField passes all other arguments on to the
new Field object. In addition, it allows you to modify the
Field's error messages by passing in arguments of the form
name_of_message = 'New error message'
See Formulator.StandardFields for details.
"""
if fieldType[-5:]!='Field':
fieldType = fieldType+'Field'
formulatorFieldClass = None
if hasattr(StandardFields, fieldType):
formulatorFieldClass = getattr(StandardFields, fieldType)
else:
formulatorFieldClass = getattr(StandardFields, 'StringField')
# pass a title parameter to the Field
kwargs['title'] = field_id
fieldObject = apply(formulatorFieldClass, (field_id, ), kwargs)
# alter Field error messages
# Note: This messes with Formulator innards and may break in the future.
# Unfortunately, Formulator doesn't do this already in Field.__init__
# and there isn't a Python-oriented method for altering message values
# so at present it's the only option.
for arg in kwargs.keys():
if fieldObject.message_values.has_key(arg):
fieldObject.message_values[arg] = kwargs[arg]
# Add the new Field to the wrapped BasicForm object
BasicForm.add_field(self, fieldObject, group)
security.declarePublic('validate')
def validate(self, REQUEST, errors=None):
"""
Executes the validator for each field in the wrapped BasicForm.add_field
Returns the results in a dictionary.
"""
if errors is None:
errors = REQUEST.get('errors', {})
# This is a bit of a hack to make some of Formulator's quirks
# transparent to developers. Formulator expects form fields to be
# prefixed by 'field_' in the request. To remove this restriction,
# we mangle the REQUEST, renaming keys from key to 'field_' + key
# before handing off to Formulator's validators. We will undo the
# mangling afterwards.
for field in self.get_fields():
key = field.id
value = REQUEST.get(key)
if value:
# get rid of the old key
try:
del REQUEST[key]
except:
pass
# move the old value to 'field_' + key
# if there is already a value at 'field_' + key,
# move it to 'field_field_' + key, and repeat
# to prevent key collisions
newKey = 'field_' + key
newValue = REQUEST.get(newKey)
while newValue:
REQUEST[newKey] = value
newKey = 'field_' + newKey
value = newValue
newValue = REQUEST.get(newKey)
REQUEST[newKey] = value
try:
result=self.validate_all(REQUEST)
except FormValidationError, e:
for error in e.errors:
errors[error.field.get_value('title')]=error.error_text
# unmangle the REQUEST
for field in self.get_fields():
key = field.id
value = 1
while value:
key = 'field_' + key
value = REQUEST.get(key)
if value:
REQUEST[key[6:]] = value
try:
del REQUEST[key]
except:
pass
return errors
InitializeClass(ProductForm)
Formulator
Formulator is a tool to help with the creation and validation of web
forms. Form fields are stored as objects in Zope, in a special Form
folder.
Features
* manage form fields through the Zope management interface.
* manage field look & feel as well as validation and processing
behavior.
* automatic field validation.
* determine field order and group fields together.
* easy extensibility with new field types.
* online help.
* serialization of form to XML and back.
Installation and Requirements
See INSTALL.txt for more information on installing Formulator.
Information
Formulator comes with online help, so click on 'Help!' in the Zope
management screens. If you want your brain to explode, read the
'How Formulator Eats its Own Dogfood' help topic.
Information is also available at the Formulator web site:
http://www.zope.org/Members/faassen/Formulator
There are also instructions to join the Formulator mailing list there.
Discussion about Formulator should preferably happen on the mailing list
first, though you can always mail me as well. But please consider the
list if you have questions or suggestions.
Even more info can be found by reading the source. :)
from Form import BasicForm
from Field import ZMIField
from DummyField import fields
from MethodField import BoundMethod
from DateTime import DateTime
import Validator, Widget
import OFS
class StringField(ZMIField):
meta_type = "StringField"
widget = Widget.TextWidgetInstance
validator = Validator.StringValidatorInstance
class PasswordField(ZMIField):
meta_type = "PasswordField"
widget = Widget.PasswordWidgetInstance
validator = Validator.StringValidatorInstance
class EmailField(ZMIField):
meta_type = "EmailField"
widget = Widget.TextWidgetInstance
validator = Validator.EmailValidatorInstance
class PatternField(ZMIField):
meta_type = "PatternField"
widget = Widget.TextWidgetInstance
validator = Validator.PatternValidatorInstance
class CheckBoxField(ZMIField):
meta_type = "CheckBoxField"
widget = Widget.CheckBoxWidgetInstance
validator = Validator.BooleanValidatorInstance
class IntegerField(ZMIField):
meta_type = "IntegerField"
widget = Widget.IntegerWidgetInstance
validator = Validator.IntegerValidatorInstance
class RangedIntegerField(ZMIField):
meta_type = "RangedIntegerField"
# this field is not addable anymore and deprecated. For
# backwards compatibility it's a clone of IntegerField,
# though it may go away in the future.
internal_field = 1
widget = Widget.TextWidgetInstance
validator = Validator.IntegerValidatorInstance
class FloatField(ZMIField):
meta_type = "FloatField"
widget = Widget.FloatWidgetInstance
validator = Validator.FloatValidatorInstance
class TextAreaField(ZMIField):
meta_type = "TextAreaField"
widget = Widget.TextAreaWidgetInstance
validator = Validator.TextValidatorInstance
class RawTextAreaField(ZMIField):
meta_type = "RawTextAreaField"
widget = Widget.TextAreaWidgetInstance
validator = Validator.StringValidatorInstance
class ListField(ZMIField):
meta_type = "ListField"
widget = Widget.ListWidgetInstance
validator = Validator.SelectionValidatorInstance
class MultiListField(ZMIField):
meta_type = "MultiListField"
widget = Widget.MultiListWidgetInstance
validator = Validator.MultiSelectionValidatorInstance
class LinesField(ZMIField):
meta_type = "LinesField"
widget = Widget.LinesTextAreaWidgetInstance
validator = Validator.LinesValidatorInstance
class RadioField(ZMIField):
meta_type = "RadioField"
widget = Widget.RadioWidgetInstance
validator = Validator.SelectionValidatorInstance
class MultiCheckBoxField(ZMIField):
meta_type = "MultiCheckBoxField"
widget = Widget.MultiCheckBoxWidgetInstance
validator = Validator.MultiSelectionValidatorInstance
class FileField(ZMIField):
meta_type = "FileField"
widget = Widget.FileWidgetInstance
validator = Validator.FileValidatorInstance
class LinkField(ZMIField):
meta_type = "LinkField"
widget = Widget.LinkWidgetInstance
validator = Validator.LinkValidatorInstance
class LabelField(ZMIField):
"""Just a label, doesn't really validate.
"""
meta_type = "LabelField"
widget = Widget.LabelWidgetInstance
validator = Validator.SuppressValidatorInstance
class DateTimeField(ZMIField):
meta_type = "DateTimeField"
widget = Widget.DateTimeWidgetInstance
validator = Validator.DateTimeValidatorInstance
def __init__(self, id, **kw):
# icky but necessary...
apply(ZMIField.__init__, (self, id), kw)
input_style = self.get_value('input_style')
if input_style == 'text':
self.sub_form = create_datetime_text_sub_form()
elif input_style == 'list':
self.sub_form = create_datetime_list_sub_form()
else:
assert 0, "Unknown input_style '%s'" % input_style
def on_value_input_style_changed(self, value):
if value == 'text':
self.sub_form = create_datetime_text_sub_form()
elif value == 'list':
self.sub_form = create_datetime_list_sub_form()
year_field = self.sub_form.get_field('year', include_disabled=1)
year_field.overrides['items'] = BoundMethod(self,
'override_year_items')
else:
assert 0, "Unknown input_style."
self.on_value_css_class_changed(self.values['css_class'])
def on_value_css_class_changed(self, value):
for field in self.sub_form.get_fields():
field.values['css_class'] = value
field._p_changed = 1
def override_year_items(self):
"""The method gets called to get the right amount of years.
"""
start_datetime = self.get_value('start_datetime')
end_datetime = self.get_value('end_datetime')
current_year = DateTime().year()
if start_datetime:
first_year = start_datetime.year()
else:
first_year = current_year
if end_datetime:
last_year = end_datetime.year() + 1
else:
last_year = first_year + 11
return create_items(first_year, last_year, digits=4)
def _get_user_input_value(self, key, REQUEST):
"""
Try to get a value of the field from the REQUEST
"""
if REQUEST.form['subfield_%s_%s' % (key, 'year')]:
return None
gmt_timezones = [('GMT%s' %zone, 'GMT%s' %zone,) for zone in range(-12, 0)]\
+ [('GMT', 'GMT',),] \
+ [('GMT+%s' %zone, 'GMT+%s' %zone,) for zone in range(1, 13)]
def create_datetime_text_sub_form():
sub_form = BasicForm()
year = IntegerField('year',
title="Year",
required=0,
display_width=4,
display_maxwidth=4,
max_length=4)
month = IntegerField('month',
title="Month",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
day = IntegerField('day',
title="Day",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
sub_form.add_group("date")
sub_form.add_fields([year, month, day], "date")
hour = IntegerField('hour',
title="Hour",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
minute = IntegerField('minute',
title="Minute",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
ampm = StringField('ampm',
title="am/pm",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
timezone = ListField('timezone',
title = "Timezone",
required = 0,
default = 'GMT',
items = gmt_timezones,
size = 1)
sub_form.add_fields([hour, minute, ampm, timezone], "time")
return sub_form
def create_datetime_list_sub_form():
""" Patch Products.Formulator.StandardFields so we can add timezone subfield """
sub_form = BasicForm()
year = ListField('year',
title="Year",
required=0,
default="",
items=create_items(2000, 2010, digits=4),
size=1)
month = ListField('month',
title="Month",
required=0,
default="",
items=create_items(1, 13, digits=2),
size=1)
day = ListField('day',
title="Day",
required=0,
default="",
items=create_items(1, 32, digits=2),
size=1)
sub_form.add_group("date")
sub_form.add_fields([year, month, day], "date")
hour = IntegerField('hour',
title="Hour",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
minute = IntegerField('minute',
title="Minute",
required=0,
display_width=2,
display_maxwidth=2,
max_length=2)
ampm = ListField('ampm',
title="am/pm",
required=0,
default="am",
items=[("am","am"),
("pm","pm")],
size=1)
timezone = ListField('timezone',
title = "Timezone",
required = 0,
default = 'GMT',
items = gmt_timezones,
size = 1)
sub_form.add_group("time")
sub_form.add_fields([hour, minute, ampm, timezone], "time")
return sub_form
def create_items(start, end, digits=0):
result = [("-", "")]
if digits:
format_string = "%0" + str(digits) + "d"
else:
format_string = "%s"
for i in range(start, end):
s = format_string % i
result.append((s, s))
return result
import string
from DummyField import fields
import Widget, Validator
from Globals import Persistent
import Acquisition
from Field import ZMIField
from AccessControl import getSecurityManager
class TALESWidget(Widget.TextWidget):
default = fields.MethodField('default',
title='Default',
default="",
required=0)
def render(self, field, key, value, REQUEST, render_prefix=None):
if value == None:
text = field.get_value('default')
else:
if value != "":
text = value._text
else:
text = ""
return Widget.TextWidget.render(self, field, key, text, REQUEST)
def render_view(self, field, value, REQUEST=None, render_prefix=None):
"""
Render TALES as read only
"""
if value == None:
text = field.get_value('default', REQUEST=REQUEST)
else:
if value != "":
text = value._text
else:
text = ""
return text
TALESWidgetInstance = TALESWidget()
class TALESNotAvailable(Exception):
pass
try:
# try to import getEngine from TALES
from Products.PageTemplates.Expressions import getEngine
class TALESMethod(Persistent, Acquisition.Implicit):
"""A method object; calls method name in acquisition context.
"""
def __init__(self, text):
self._text = text
def __call__(self, **kw):
expr = getattr(self, '_v_expr', None)
if expr is None:
self._v_expr = expr = getEngine().compile(self._text)
return getEngine().getContext(kw).evaluate(expr)
# check if we have 'View' permission for this method
# (raises error if not)
# getSecurityManager().checkPermission('View', method)
TALES_AVAILABLE = 1
except ImportError:
# cannot import TALES, so supply dummy TALESMethod
class TALESMethod(Persistent, Acquisition.Implicit):
"""A dummy method in case TALES is not available.
"""
def __init__(self, text):
self._text = text
def __call__(self, **kw):
raise TALESNotAvailable
TALES_AVAILABLE = 0
class TALESValidator(Validator.StringBaseValidator):
def validate(self, field, key, REQUEST):
value = Validator.StringBaseValidator.validate(self, field, key,
REQUEST)
if value == "" and not field.get_value('required'):
return value
return TALESMethod(value)
TALESValidatorInstance = TALESValidator()
class TALESField(ZMIField):
meta_type = 'TALESField'
internal_field = 1
widget = TALESWidgetInstance
validator = TALESValidatorInstance
Formulator TODO
- When using a BasicForm a field cannot get to the form's
get_form_encoding method (where the ZMIForm uses acquisistion
to achieve this).
- Make composite fields work well as hidden fields.
- Add combobox field
- Add various button fields
- Investigate duration and time only field.
- internationalisation (or error messages first)
- HTML filtering field?
- Add more unit tests.
This diff is collapsed.
This diff is collapsed.
from xml.dom.minidom import parse, parseString, Node
# an extremely simple system for loading in XML into objects
class Object:
pass
class XMLObject:
def __init__(self):
self.elements = Object()
self.first = Object()
self.attributes = {}
self.text = ''
def getElementNames(self):
return [element for element in dir(self.elements)
if not element.startswith('__')]
def getAttributes(self):
return self.attributes
def elementToObject(parent, node):
# create an object to represent element node
object = XMLObject()
# make object attributes off node attributes
for key, value in node.attributes.items():
object.attributes[key] = value
# make lists of child elements (or ignore them)
for child in node.childNodes:
nodeToObject(object, child)
# add ourselves to parent node
name = str(node.nodeName)
l = getattr(parent.elements, name, [])
l.append(object)
setattr(parent.elements, name, l)
def attributeToObject(parent, node):
# should never be called
pass
def textToObject(parent, node):
# add this text to parents text content
parent.text += node.data
def processingInstructionToObject(parent, node):
# don't do anything with these
pass
def commentToObject(parent, node):
# don't do anything with these
pass
def documentToObject(parent, node):
elementToObject(parent, node.documentElement)
def documentTypeToObject(parent, node):
# don't do anything with these
pass
_map = {
Node.ELEMENT_NODE: elementToObject,
Node.ATTRIBUTE_NODE: attributeToObject,
Node.TEXT_NODE: textToObject,
# Node.CDATA_SECTION_NODE:
# Node.ENTITY_NODE:
Node.PROCESSING_INSTRUCTION_NODE: processingInstructionToObject,
Node.COMMENT_NODE: commentToObject,
Node.DOCUMENT_NODE: documentToObject,
Node.DOCUMENT_TYPE_NODE: documentTypeToObject,
# Node.NOTATION_NODE:
}
def nodeToObject(parent, node):
_map[node.nodeType](parent, node)
def simplify_single_entries(object):
for name in object.getElementNames():
l = getattr(object.elements, name)
# set the first subelement (in case it's just one, this is easy)
setattr(object.first, name, l[0])
# now do the same for rest
for element in l:
simplify_single_entries(element)
def XMLToObjectsFromFile(path):
return XMLToObjects(parse(path))
def XMLToObjectsFromString(s):
return XMLToObjects(parseString(s))
def XMLToObjects(document):
object = XMLObject()
documentToObject(object, document)
document.unlink()
simplify_single_entries(object)
return object
import XMLObjects
from Products.Formulator.TALESField import TALESMethod
from Products.Formulator.MethodField import Method
def XMLToForm(s, form, override_encoding=None):
"""Takes an xml string and changes formulator form accordingly.
Heavily inspired by code from Nikolay Kim.
If override_encoding is set, form data is read assuming given
encoding instead of the one in the XML data itself. The form will
have to be modified afterwards to this stored_encoding itself.
"""
top = XMLObjects.XMLToObjectsFromString(s)
# wipe out groups
form.groups = {'Default':[]}
form.group_list = ['Default']
if override_encoding is None:
try:
unicode_mode = top.first.form.first.unicode_mode.text
except AttributeError:
unicode_mode = 'false'
# retrieve encoding information from XML
if unicode_mode == 'true':
# just use unicode strings being read in
encoding = None
else:
# store strings as specified encoding
try:
encoding = top.first.form.first.stored_encoding.text
except AttributeError:
encoding = 'ISO-8859-1'
else:
if override_encoding == 'unicode':
encoding = None
else:
encoding = override_encoding
# get the settings
settings = [field.id for field in form.settings_form.get_fields()]
for setting in settings:
value = getattr(top.first.form.first, setting, None)
if value is None:
continue
if setting == 'unicode_mode':
v = value.text == 'true'
elif setting == 'row_length':
v = int(value.text)
else:
v = encode(value.text, encoding)
setattr(form, setting, v)
# create groups
has_default = 0
for group in top.first.form.first.groups.elements.group:
# get group title and create group
group_title = encode(group.first.title.text, encoding)
if group_title == 'Default':
has_default = 1
form.add_group(group_title)
# create fields in group
if not hasattr(group.first.fields.elements, 'field'):
# empty <fields> element
continue
for entry in group.first.fields.elements.field:
id = str(encode(entry.first.id.text, encoding))
meta_type = encode(entry.first.type.text, encoding)
try:
form._delObject(id)
except (KeyError, AttributeError):
pass
form.manage_addField(id, '', meta_type)
field = form._getOb(id)
if group_title != 'Default':
form.move_field_group([id], 'Default', group_title)
# set values
values = entry.first.values
for name in values.getElementNames():
value = getattr(values.first, name)
if value.attributes.get('type') == 'float':
field.values[name] = float(value.text)
elif value.attributes.get('type') == 'int':
field.values[name] = int(value.text)
elif value.attributes.get('type') == 'method': # XXX Patch
field.values[name] = Method(value.text) # XXX Patch
elif value.attributes.get('type') == 'list':
# XXX bare eval here (this may be a security leak ?)
field.values[name] = eval(
encode(value.text, encoding))
else:
field.values[name] = encode(value.text, encoding)
# special hack for the DateTimeField
if field.meta_type=='DateTimeField':
field.on_value_input_style_changed(
field.get_value('input_style'))
# set tales
tales = entry.first.tales
for name in tales.getElementNames():
field.tales[name] = TALESMethod(
encode(getattr(tales.first, name).text, encoding))
# set messages
if hasattr(entry.first, 'messages'):
messages = entry.first.messages
entries = getattr(messages.elements, 'message', [])
for entry in entries:
name = entry.attributes.get('name')
text = encode(entry.text, encoding)
field.message_values[name] = text
# for persistence machinery
field.values = field.values
field.tales = field.tales
field.message_values = field.message_values
# delete default group
if not has_default:
form.move_group_down('Default')
form.remove_group('Default')
def encode(text, encoding):
if encoding is None:
return text
else:
return text.encode(encoding)
from Globals import DTMLFile
import Form
import StandardFields, HelperFields
from FieldRegistry import FieldRegistry
import Errors
from Products.PythonScripts.Utility import allow_module
try:
import Products.FileSystemSite
except ImportError:
try:
import Products.CMFCore
except ImportError:
pass
else:
import FSForm
else:
import FSForm
# Allow Errors to be imported TTW
allow_module('Products.Formulator.Errors')
def initialize(context):
"""Initialize the Formulator product.
"""
# register field classes
FieldRegistry.registerField(StandardFields.StringField,
'www/StringField.gif')
FieldRegistry.registerField(StandardFields.CheckBoxField,
'www/CheckBoxField.gif')
FieldRegistry.registerField(StandardFields.IntegerField,
'www/IntegerField.gif')
FieldRegistry.registerField(StandardFields.TextAreaField,
'www/TextAreaField.gif')
FieldRegistry.registerField(StandardFields.RawTextAreaField,
'www/TextAreaField.gif')
FieldRegistry.registerField(StandardFields.LinesField,
'www/LinesField.gif')
FieldRegistry.registerField(StandardFields.ListField,
'www/ListField.gif')
FieldRegistry.registerField(StandardFields.MultiListField,
'www/MultiListField.gif')
FieldRegistry.registerField(StandardFields.RadioField,
'www/RadioField.gif')
FieldRegistry.registerField(StandardFields.MultiCheckBoxField,
'www/MultiCheckBoxField.gif')
FieldRegistry.registerField(StandardFields.PasswordField,
'www/PasswordField.gif')
FieldRegistry.registerField(StandardFields.EmailField,
'www/EmailField.gif')
FieldRegistry.registerField(StandardFields.PatternField,
'www/PatternField.gif')
FieldRegistry.registerField(StandardFields.FloatField,
'www/FloatField.gif')
FieldRegistry.registerField(StandardFields.DateTimeField,
'www/DateTimeField.gif')
FieldRegistry.registerField(StandardFields.FileField,
'www/FileField.gif')
FieldRegistry.registerField(StandardFields.LinkField,
'www/LinkField.gif')
FieldRegistry.registerField(StandardFields.LabelField,
'www/LabelField.gif')
# some helper fields
FieldRegistry.registerField(HelperFields.ListTextAreaField)
FieldRegistry.registerField(HelperFields.MethodField)
FieldRegistry.registerField(HelperFields.TALESField)
# obsolete field (same as helper; useable but not addable)
FieldRegistry.registerField(StandardFields.RangedIntegerField,
'www/RangedIntegerField.gif')
# register help for the product
context.registerHelp()
# register field help for all fields
FieldRegistry.registerFieldHelp(context)
# register the form itself
context.registerClass(
Form.ZMIForm,
constructors = (Form.manage_addForm,
Form.manage_add),
icon = 'www/Form.gif')
# make Dummy Fields into real fields
FieldRegistry.initializeFields()
# do initialization of Form class to make fields addable
Form.initializeForm(FieldRegistry)
<dtml-var standard_html_header>
<h3>Formulator Field - <dtml-var id></h3>
<dtml-in get_groups>
<dtml-let group=sequence-item fields="get_fields_in_group(group)">
<dtml-if fields>
<h4><i><dtml-var "_.string.capitalize(group)"> properties</i></h4>
<dtml-in fields>
<dtml-let field=sequence-item>
<b><dtml-var "field.get_value('title')"> (<dtml-var "field.id">)</b>
<p><dtml-var "field.get_value('description')"></p>
</dtml-let>
</dtml-in>
</dtml-if>
</dtml-let>
</dtml-in>
<h4>More help</h4>
<p><a href="fieldEdit.txt">Field edit screen help</a></p>
<dtml-var standard_html_footer>
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add %s' % fieldname,
)">
<p class="form-help">
Add a <dtml-var fieldname> to the form.
</p>
<form action="manage_addField" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<input type="hidden" name="fieldname" value="&dtml-fieldname;">
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit_add"
value=" Add " />
<input class="form-element" type="submit" name="submit_add_and_edit"
value=" Add and Edit " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-let help_product="'Formulator'" help_topic=meta_type>
<dtml-var manage_tabs>
</dtml-let>
<p class="form-help">
Edit <dtml-var meta_type> properties here.
</p>
<form action="manage_edit" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<dtml-in "form.get_groups()">
<dtml-let group=sequence-item fields="form.get_fields_in_group(group)">
<dtml-if fields>
<tr>
<td colspan="3" class="form-title">
<dtml-var "_.string.capitalize(group)"> properties
</td>
</tr>
<dtml-var fieldListHeader>
<dtml-let current_field="this()">
<dtml-in fields>
<dtml-let field=sequence-item field_id="field.id"
value="current_field.get_orig_value(field_id)"
override="current_field.get_override(field_id)"
tales="current_field.get_tales(field_id)">
<tr>
<td align="left" valign="top">
<div class="form-label">
<dtml-if "tales or override">[</dtml-if><dtml-var "field.title()"><dtml-if "field.has_value('required') and field.get_value('required')">*</dtml-if><dtml-if "tales or override">]</dtml-if>
</div>
</td>
<td align="left" valign="top">
<dtml-var "field.render(value)">
</td>
<td><div class="form-element">
<dtml-var "field.meta_type">
</div></td>
</tr>
</dtml-let>
</dtml-in>
</dtml-let>
</dtml-if>
</dtml-let>
</dtml-in>
<tr>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes" />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<tr class="list-header">
<td align="left" valign="top">
<div class="form-label">
Name
</div>
</td>
<td align="left" valign="top">
<div class="form-label">
Value
</div>
</td>
<td align="left" valign="top">
<div class="form-label">
Field
</div>
</td>
</tr>
\ No newline at end of file
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
Edit <dtml-var meta_type> error messages here.
</p>
<form action="manage_messages" method="POST">
<table border="0">
<dtml-in "get_error_names()">
<dtml-let name=sequence-item value="get_error_message(name)">
<tr>
<td class="form-label"><dtml-var name></td>
<td><textarea name="&dtml-name;" cols="50" rows="4"><dtml-var value></textarea></td>
</tr>
</dtml-let>
</dtml-in>
<tr><td><input type="submit" value=" OK "></td></tr>
</table>
</form>
<dtml-var manage_page_footer>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Formulator Field - Messages
Description
Each field has a set of messages that can be displayed in case
validation fails. Standard messages are provided, but you can
change these messages for the particular field in this view. The
message text is the 'error_text' that will show up in case
validation fails.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Some mottos to inspire:
Formulator - Web forms, web forms well, and web forms only
Von Wiege bis zur Bahre, Formulare, Formulare! (with thanks to
Joachim Werner - it means "from crib to coffin, forms, forms!")
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment