pax_global_header 0000666 0000000 0000000 00000000064 12770231644 0014520 g ustar 00root root 0000000 0000000 52 comment=504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/ 0000775 0000000 0000000 00000000000 12770231644 0023517 5 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/ 0000775 0000000 0000000 00000000000 12770231644 0025177 5 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/ 0000775 0000000 0000000 00000000000 12770231644 0027331 5 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/CREDITS.txt 0000664 0000000 0000000 00000005443 12770231644 0031175 0 ustar 00root root 0000000 0000000 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.
Sщbastien 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!
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/DummyField.py 0000664 0000000 0000000 00000001742 12770231644 0031746 0 ustar 00root root 0000000 0000000 """
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)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/Errors.py 0000664 0000000 0000000 00000002242 12770231644 0031157 0 ustar 00root root 0000000 0000000 """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, error_text=None):
Exception.__init__(self, error_key)
self.error_key = error_key
self.field_id = field.id
self.field = field
if error_text is not None:
self.error_text = error_text
else:
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)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/FSForm.py 0000664 0000000 0000000 00000006621 12770231644 0031044 0 ustar 00root root 0000000 0000000 from App.class_init import default__class_init__ as InitializeClass
from AccessControl import ClassSecurityInfo
try:
import Products.FileSystemSite
except ImportError:
# use CMF product
from Products.CMFCore.permissions import View
from Products.CMFCore.FSObject import FSObject
from Products.CMFCore.DirectoryView import registerFileExtension,\
registerMetaType
else:
# use FileSystemSite product
from Products.FileSystemSite.Permissions import View
from Products.FileSystemSite.FSObject import FSObject
from Products.FileSystemSite.DirectoryView import registerFileExtension,\
registerMetaType
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(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 ' +
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)
InitializeClass(FSForm)
registerFileExtension('form', FSForm)
registerMetaType('FSForm', FSForm)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/Field.py 0000664 0000000 0000000 00000067732 12770231644 0030745 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
from App.class_init import default__class_init__ as InitializeClass
import Acquisition
from Persistence import Persistent
from App.special_dtml import DTMLFile
from AccessControl import ClassSecurityInfo
import OFS
from Shared.DC.Scripts.Bindings import Bindings
from Errors import ValidationError
from Products.Formulator.Widget import MultiItemsWidget
from zLOG import LOG
from lxml import etree
class Field:
"""Base class of all fields.
A field is an object consisting of a widget and a validator.
"""
security = ClassSecurityInfo()
# this is a field
is_field = 1
# this is not an internal field (can be overridden by subclass)
internal_field = 0
# can alternatively render this field with Zope's :record syntax
# this will be the record's name
field_record = None
def __init__(self, id, **kw):
self.id = id
# initialize values of fields in form
self.initialize_values(kw)
# initialize tales expression for fields in form
self.initialize_tales()
# initialize overrides of fields in form
self.initialize_overrides()
# initialize message values with defaults
message_values = {}
for message_name in self.validator.message_names:
message_values[message_name] = getattr(self.validator,
message_name)
self.message_values = message_values
security.declareProtected('Change Formulator Fields', 'initialize_values')
def initialize_values(self, dict):
"""Initialize values for properties, defined by fields in
associated form.
"""
values = {}
for field in self.form.get_fields(include_disabled=1):
id = field.id
value = dict.get(id, field.get_value('default'))
values[id] = value
self.values = values
security.declareProtected('Change Formulator Fields',
'initialize_tales')
def initialize_tales(self):
"""Initialize tales expressions for properties (to nothing).
"""
tales = {}
for field in self.form.get_fields():
id = field.id
tales[id] = ""
self.tales = tales
security.declareProtected('Change Formulator Fields',
'initialize_overrides')
def initialize_overrides(self):
"""Initialize overrides for properties (to nothing).
"""
overrides = {}
for field in self.form.get_fields():
id = field.id
overrides[id] = ""
self.overrides = overrides
security.declareProtected('Access contents information', 'has_value')
def has_value(self, id):
"""Return true if the field defines such a value.
"""
if self.values.has_key(id) or self.form.has_field(id):
return 1
else:
return 0
security.declareProtected('Access contents information', 'get_orig_value')
def get_orig_value(self, id):
"""Get value for id; don't do any override calculation.
"""
if self.values.has_key(id):
return self.values[id]
else:
return self.form.get_field(id).get_value('default')
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""Get value for id.
Optionally pass keyword arguments that get passed to TALES
expression.
"""
tales_expr = self.tales.get(id, "")
if tales_expr:
# For some reason, path expressions expect 'here' and 'request'
# to exist, otherwise they seem to fail. python expressions
# don't seem to have this problem.
# add 'here' if not in kw
if not kw.has_key('here'):
kw['here'] = self.aq_parent
kw['request'] = self.REQUEST
value = tales_expr.__of__(self)(
field=self,
form=self.aq_parent, **kw)
else:
override = self.overrides.get(id, "")
if override:
# call wrapped method to get answer
value = override.__of__(self)()
else:
# get normal value
value = self.get_orig_value(id)
# if normal value is a callable itself, wrap it
if callable(value):
return value.__of__(self)
else:
return value
security.declareProtected('View management screens', 'get_override')
def get_override(self, id):
"""Get override method for id (not wrapped)."""
return self.overrides.get(id, "")
security.declareProtected('View management screens', 'get_tales')
def get_tales(self, id):
"""Get tales expression method for id."""
return self.tales.get(id, "")
security.declareProtected('Access contents information', 'is_required')
def is_required(self):
"""Check whether this field is required (utility function)
"""
return self.has_value('required') and self.get_value('required')
security.declareProtected('View management screens', 'get_error_names')
def get_error_names(self):
"""Get error messages.
"""
return self.validator.message_names
security.declareProtected('Access contents information',
'generate_field_key')
def generate_field_key(self, validation=0, key=None, key_prefix=None):
"""Generate the key Silva uses to render the field in the form.
"""
# Patched by JPS for ERP5 in order to
# dynamically change the name
if key_prefix is None:
key_prefix = 'field'
if key is not None:
return '%s_%s' % (key_prefix, key)
if self.field_record is None:
return '%s_%s' % (key_prefix, self.id)
elif validation:
return self.id
elif isinstance(self.widget, MultiItemsWidget):
return "%s.%s:record:list" % (self.field_record, self.id)
else:
return '%s.%s:record' % (self.field_record, self.id)
security.declareProtected('Access contents information',
'generate_subfield_key')
def generate_subfield_key(self, id, validation=0, key=None):
"""Generate the key Silva uses to render a sub field.
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
"""
if key is None: key = self.id
if self.field_record is None or validation:
return 'subfield_%s_%s' % (key, id)
return '%s.subfield_%s_%s:record' % (self.field_record, key, id)
security.declareProtected('View management screens', 'get_error_message')
def get_error_message(self, name):
try:
return self.message_values[name]
except KeyError:
if name in self.validator.message_names:
return getattr(self.validator, name)
else:
return "Unknown error: %s" % name
security.declarePrivate('_render_helper')
def _render_helper(self, key, value, REQUEST, render_prefix=None, editable=None):
value = self._get_default(key, value, REQUEST)
__traceback_info__ = ('key=%s value=%r' % (key, value))
if self.get_value('hidden', REQUEST=REQUEST):
return self.widget.render_hidden(self, key, value, REQUEST)
else:
if editable is None:
editable = self.get_value('editable', REQUEST=REQUEST)
if not editable:
return self.widget.render_view(self, value, REQUEST=REQUEST,
render_prefix=render_prefix)
else:
return self.widget.render(self, key, value, REQUEST,
render_prefix=render_prefix)
security.declarePrivate('_render_odt_helper')
def _render_odt_helper(self, key, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict, local_name):
value = self._get_default(key, value, REQUEST)
__traceback_info__ = ('key=%s value=%r' % (key, value))
if not self.get_value('editable', REQUEST=REQUEST):
return self.widget.render_odt_view(self, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict,
local_name)
else:
return self.widget.render_odt(self, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict,
local_name)
security.declarePrivate('_render_odt_variable_helper')
def _render_odt_variable_helper(self, key, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict, local_name):
value = self._get_default(key, value, REQUEST)
__traceback_info__ = ('key=%s value=%r' % (key, value))
return self.widget.render_odt_variable(self, value, as_string,
ooo_builder, REQUEST,
render_prefix, attr_dict,
local_name)
security.declarePrivate('_get_default')
def _get_default(self, key, value, REQUEST):
if value is not None:
return value
try:
value = REQUEST.form[key]
except (KeyError, AttributeError):
# fall back on default
return self.get_value('default')
# if we enter a string value while the field expects unicode,
# convert to unicode first
# this solves a problem when re-rendering a sticky form with
# values from request
if (self.has_value('unicode') and self.get_value('unicode') and
type(value) == type('')):
return unicode(value, self.get_form_encoding())
else:
return value
security.declarePrivate('_get_user_input_value')
def _get_user_input_value(self, key, REQUEST):
"""
Try to get a value of the field from the REQUEST
"""
return REQUEST.form[key]
security.declareProtected('View', 'render')
def render(self, value=None, REQUEST=None, key=None, render_prefix=None, key_prefix=None, editable=None):
"""Render the field widget.
value -- the value the field should have (for instance
from validation).
REQUEST -- REQUEST can contain raw (unvalidated) field
information. If value is None, REQUEST is searched
for this value.
editable -- if not None, this boolean can override the Editable property
of the rendered field
if value and REQUEST are both None, the 'default' property of
the field will be used for the value.
"""
return self._render_helper(
self.generate_field_key(key=key, key_prefix=key_prefix),
value,
REQUEST,
render_prefix=render_prefix,
editable=editable,
)
security.declareProtected('View', 'render_view')
def render_view(self, value=None, REQUEST=None, render_prefix=None):
"""Render value to be viewed.
"""
return self.widget.render_view(self, value, REQUEST=REQUEST)
security.declareProtected('View', 'render_pdf')
def render_pdf(self, value=None, REQUEST=None, key=None, **kw):
"""
render_pdf renders the field for reportlab
"""
return self.widget.render_pdf(self, value)
security.declareProtected('View', 'render_html')
def render_html(self, *args, **kw):
"""
render_html is used to as definition of render method in Formulator.
"""
return self.render(*args, **kw)
security.declareProtected('View', 'render_htmlgrid')
def render_htmlgrid(self, value=None, REQUEST=None, key=None, render_prefix=None, key_prefix=None):
"""
render_htmlgrid returns a list of tuple (title, html render)
"""
# What about CSS ? What about description ? What about error ?
widget_key = self.generate_field_key(key=key, key_prefix=key_prefix)
value = self._get_default(widget_key, value, REQUEST)
__traceback_info__ = ('key=%s value=%r' % (key, value))
return self.widget.render_htmlgrid(self, widget_key, value, REQUEST, render_prefix=render_prefix)
security.declareProtected('View', 'render_odf')
def render_odf(self, field=None, key=None, value=None, REQUEST=None,
render_format='ooo', render_prefix=None):
return self.widget.render_odf(self, key, value, REQUEST, render_format,
render_prefix)
security.declareProtected('View', 'render_odt')
def render_odt(self, key=None, value=None, as_string=True, ooo_builder=None,
REQUEST=None, render_prefix=None, attr_dict=None, local_name='p',
key_prefix=None):
field_key = self.generate_field_key(key=key, key_prefix=key_prefix)
return self._render_odt_helper(field_key, value, as_string,
ooo_builder, REQUEST, render_prefix,
attr_dict, local_name)
security.declareProtected('View', 'render_odt_variable')
def render_odt_variable(self, key=None, value=None, as_string=True,
ooo_builder=None, REQUEST=None, render_prefix=None, attr_dict=None,
local_name='variable-set', key_prefix=None):
field_key = self.generate_field_key(key=key, key_prefix=key_prefix)
return self._render_odt_variable_helper(field_key, value, as_string,
ooo_builder, REQUEST, render_prefix,
attr_dict, local_name)
security.declareProtected('View', 'render_odt_view')
def render_odt_view(self, value=None, as_string=True, ooo_builder=None,
REQUEST=None, render_prefix=None, attr_dict=None, local_name='p'):
"""Call read-only renderer
"""
return self.widget.render_odt_view(self, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict,
local_name)
security.declareProtected('View', 'render_odg')
def render_odg(self, key=None, value=None, as_string=True, ooo_builder=None,
REQUEST=None, render_prefix=None, attr_dict=None, local_name='p',
key_prefix=None):
widget_key = self.generate_field_key(key=key, key_prefix=key_prefix)
value = self._get_default(widget_key, value, REQUEST)
return self.widget.render_odg(self, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict,
local_name)
security.declareProtected('View', 'render_css')
def render_css(self, REQUEST=None):
"""
Generate css content which will be added inline.
XXX key parameter may be needed.
"""
return self.widget.render_css(self, REQUEST)
security.declareProtected('View', 'get_css_list')
def get_css_list(self, REQUEST=None):
"""
Returns list of css sheets needed by the field
to be included in global css imports
"""
return self.widget.get_css_list(self, REQUEST)
security.declareProtected('View', 'get_javascript_list')
def get_javascript_list(self, REQUEST=None):
"""
Returns list of javascript needed by the field
to be included in global js imports
"""
return self.widget.get_javascript_list(self, REQUEST)
security.declareProtected('View', 'render_dict')
def render_dict(self, value=None, REQUEST=None, key=None, **kw):
"""
This is yet another field rendering. It is designed to allow code to
understand field's value data by providing its type and format when
applicable.
"""
return self.widget.render_dict(self, value)
security.declareProtected('View', 'render_from_request')
def render_from_request(self, REQUEST, key_prefix=None):
"""Convenience method; render the field widget from REQUEST
(unvalidated data), or default if no raw data is found.
"""
return self._render_helper(self.generate_field_key(key_prefix=key_prefix), None, REQUEST)
security.declareProtected('View', 'render_sub_field')
def render_sub_field(self, id, value=None, REQUEST=None, key=None, render_prefix=None):
"""Render a sub field, as part of complete rendering of widget in
a form. Works like render() but for sub field.
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
"""
return self.sub_form.get_field(id)._render_helper(
self.generate_subfield_key(id, key=key), value, REQUEST, render_prefix)
security.declareProtected('View', 'render_sub_field_from_request')
def render_sub_field_from_request(self, id, REQUEST):
"""Convenience method; render the field widget from REQUEST
(unvalidated data), or default if no raw data is found.
"""
return self.sub_form.get_field(id)._render_helper(
self.generate_subfield_key(id), None, REQUEST)
security.declarePrivate('_validate_helper')
def _validate_helper(self, key, REQUEST):
value = self.validator.validate(self, key, REQUEST)
# now call external validator after all other validation
external_validator = self.get_value('external_validator')
if external_validator and not external_validator(value, REQUEST):
self.validator.raise_error('external_validator_failed', self)
return value
security.declareProtected('View', 'validate')
def validate(self, REQUEST, key_prefix=None):
"""Validate/transform the field.
"""
return self._validate_helper(
self.generate_field_key(validation=1, key_prefix=key_prefix), REQUEST)
security.declareProtected('View', 'need_validate')
def need_validate(self, REQUEST, key_prefix=None):
"""Return true if validation is needed for this field.
"""
return self.validator.need_validate(
self, self.generate_field_key(validation=1, key_prefix=key_prefix), REQUEST)
security.declareProtected('View', 'validate_sub_field')
def validate_sub_field(self, id, REQUEST, key=None):
"""Validates a subfield (as part of field validation).
"""
return self.sub_form.get_field(id)._validate_helper(
self.generate_subfield_key(id, validation=1, key=key), REQUEST)
def PrincipiaSearchSource(self):
from Products.Formulator import MethodField
from Products.Formulator import TALESField
def getSearchSource(obj):
obj_type = type(obj)
if obj_type is MethodField.Method:
return obj.method_name
elif obj_type is TALESField.TALESMethod:
return obj._text
elif obj_type is unicode:
return obj.encode('utf-8')
return str(obj)
return ' '.join(map(getSearchSource,
(self.values.values()+self.tales.values()+
self.overrides.values())))
InitializeClass(Field)
class ZMIField(
Acquisition.Implicit,
Persistent,
OFS.SimpleItem.Item,
Field,
):
"""Base class for a field implemented as a Python (file) product.
"""
security = ClassSecurityInfo()
security.declareObjectProtected('View')
# the various tabs of a field
manage_options = (
{'label':'Edit', 'action':'manage_main',
'help':('Formulator', 'fieldEdit.txt')},
{'label':'TALES', 'action':'manage_talesForm',
'help':('Formulator', 'fieldTales.txt')},
{'label':'Override', 'action':'manage_overrideForm',
'help':('Formulator', 'fieldOverride.txt')},
{'label':'Messages', 'action':'manage_messagesForm',
'help':('Formulator', 'fieldMessages.txt')},
{'label':'Test', 'action':'fieldTest',
'help':('Formulator', 'fieldTest.txt')},
) + OFS.SimpleItem.SimpleItem.manage_options
security.declareProtected('View', 'title')
def title(self):
"""The title of this field."""
return self.get_value('title')
# display edit screen as main management screen
security.declareProtected('View management screens', 'manage_main')
manage_main = DTMLFile('dtml/fieldEdit', globals())
security.declareProtected('Change Formulator Fields', 'manage_edit')
def manage_edit(self, REQUEST):
"""Submit Field edit form.
"""
try:
# validate the form and get results
result = self.form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_main(self,REQUEST,
manage_tabs_message=message)
else:
raise
self._edit(result)
if REQUEST:
message="Content changed."
return self.manage_main(self,REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Fields', 'manage_edit_xmlrpc')
def manage_edit_xmlrpc(self, map):
"""Edit Field Properties through XMLRPC
"""
# BEWARE: there is no validation on the values passed through the map
self._edit(map)
def _edit(self, result):
# first check for any changes
values = self.values
# if we are in unicode mode, convert result to unicode
# acquire get_unicode_mode and get_stored_encoding from form..
if self.get_unicode_mode():
new_result = {}
for key, value in result.items():
if type(value) == type(''):
# in unicode mode, Formulator UI always uses UTF-8
value = unicode(value, 'UTF-8')
new_result[key] = value
result = new_result
changed = []
for key, value in result.items():
# store keys for which we want to notify change
if not values.has_key(key) or values[key] != value:
changed.append(key)
# now do actual update of values
values.update(result)
self.values = values
# finally notify field of all changed values if necessary
for key in changed:
method_name = "on_value_%s_changed" % key
if hasattr(self, method_name):
getattr(self, method_name)(values[key])
security.declarePrivate('manage_beforeDelete')
def manage_beforeDelete(self, item, container):
"""Remove name from list if object is deleted.
"""
# update group info in form
if hasattr(item.aq_explicit, 'is_field'):
container.field_removed(item.id)
security.declarePrivate('manage_afterAdd')
def manage_afterAdd(self, item, container):
"""What happens when we add a field.
"""
# update group info in form
if hasattr(item.aq_explicit, 'is_field'):
container.field_added(item.id)
# methods screen
security.declareProtected('View management screens',
'manage_overrideForm')
manage_overrideForm = DTMLFile('dtml/fieldOverride', globals())
security.declareProtected('Change Formulator Forms', 'manage_override')
def manage_override(self, REQUEST):
"""Change override methods.
"""
try:
# validate the form and get results
result = self.override_form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_overrideForm(self,REQUEST,
manage_tabs_message=message)
else:
raise
# update overrides of field with results
if not hasattr(self, "overrides"):
self.overrides = result
else:
self.overrides.update(result)
self.overrides = self.overrides
if REQUEST:
message="Content changed."
return self.manage_overrideForm(self,REQUEST,
manage_tabs_message=message)
# tales screen
security.declareProtected('View management screens',
'manage_talesForm')
manage_talesForm = DTMLFile('dtml/fieldTales', globals())
security.declareProtected('Change Formulator Forms', 'manage_tales')
def manage_tales(self, REQUEST):
"""Change TALES expressions.
"""
try:
# validate the form and get results
result = self.tales_form.validate(REQUEST)
except ValidationError, err:
if REQUEST:
message = "Error: %s - %s" % (err.field.get_value('title'),
err.error_text)
return self.manage_talesForm(self,REQUEST,
manage_tabs_message=message)
else:
raise
self._edit_tales(result)
if REQUEST:
message="Content changed."
return self.manage_talesForm(self, REQUEST,
manage_tabs_message=message)
def _edit_tales(self, result):
if not hasattr(self, 'tales'):
self.tales = result
else:
self.tales.update(result)
self.tales = self.tales
security.declareProtected('Change Formulator Forms', 'manage_tales_xmlrpc')
def manage_tales_xmlrpc(self, map):
"""Change TALES expressions through XMLRPC.
"""
# BEWARE: there is no validation on the values passed through the map
from TALESField import TALESMethod
result = {}
for key, value in map.items():
result[key] = TALESMethod(value)
self._edit_tales(result)
# display test screen
security.declareProtected('View management screens', 'fieldTest')
fieldTest = DTMLFile('dtml/fieldTest', globals())
# messages screen
security.declareProtected('View management screens', 'manage_messagesForm')
manage_messagesForm = DTMLFile('dtml/fieldMessages', globals())
# field list header
security.declareProtected('View management screens', 'fieldListHeader')
fieldListHeader = DTMLFile('dtml/fieldListHeader', globals())
# field description display
security.declareProtected('View management screens', 'fieldDescription')
fieldDescription = DTMLFile('dtml/fieldDescription', globals())
security.declareProtected('Change Formulator Fields', 'manage_messages')
def manage_messages(self, REQUEST):
"""Change message texts.
"""
messages = self.message_values
unicode_mode = self.get_unicode_mode()
for message_key in self.get_error_names():
message = REQUEST[message_key]
if unicode_mode:
message = unicode(message, 'UTF-8')
messages[message_key] = message
self.message_values = messages
if REQUEST:
message="Content changed."
return self.manage_messagesForm(self,REQUEST,
manage_tabs_message=message)
security.declareProtected('View', 'index_html')
def index_html(self, REQUEST):
"""Render this field.
"""
return self.render(REQUEST=REQUEST)
security.declareProtected('Access contents information', '__getitem__')
def __getitem__(self, key):
return self.get_value(key)
security.declareProtected('View management screens', 'isTALESAvailable')
def isTALESAvailable(self):
"""Return true only if TALES is available.
"""
try:
from Products.PageTemplates.Expressions import getEngine
return 1
except ImportError:
return 0
def getTemplateField(self):
return self
getRecursiveTemplateField = getTemplateField
InitializeClass(ZMIField)
PythonField = ZMIField # NOTE: for backwards compatibility
class ZClassField(Field):
"""Base class for a field implemented as a ZClass.
"""
pass
FieldHelpTopic.py 0000664 0000000 0000000 00000002003 12770231644 0032452 0 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator from App.special_dtml 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)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/FieldRegistry.py0000664 0000000 0000000 00000012331 12770231644 0032457 0 ustar 00root root 0000000 0000000 import os
import OFS
from App.ImageFile 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))
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/Form.py 0000664 0000000 0000000 00000115215 12770231644 0030613 0 ustar 00root root 0000000 0000000 import AccessControl
import OFS
from App.class_init import default__class_init__ as InitializeClass
from Acquisition import aq_base
from App.special_dtml import DTMLFile
from Persistence import Persistent
from AccessControl import ClassSecurityInfo
from OFS.role import RoleManager
from OFS.ObjectManager import ObjectManager
from OFS.PropertyManager import PropertyManager
from OFS.SimpleItem import Item
import Acquisition
from urllib import quote
import os
import string
from StringIO import StringIO
from Errors import ValidationError, FormValidationError, FieldDisabledError
from FieldRegistry import FieldRegistry
from Widget import render_tag
from DummyField import fields
from FormToXML import formToXML
from XMLToForm import XMLToForm
from ComputedAttribute import ComputedAttribute
# FIXME: manage_renameObject hack needs these imports
from Acquisition import aq_base
from App.Dialogs import MessageDialog
from OFS.CopySupport import CopyError, eNotSupported
import sys
class Form:
"""Form base class.
"""
security = ClassSecurityInfo()
# need to make settings form upgrade
encoding = 'UTF-8'
stored_encoding = 'ISO-8859-1'
unicode_mode = False
# CONSTRUCTORS
def __init__(self, action, method, enctype, name,
encoding, stored_encoding, unicode_mode):
"""Initialize form.
"""
# make groups dict with entry for default group
self.groups = {"Default": []}
# the display order of the groups
self.group_list = ["Default"]
# form submit info
self.name = name # for use by javascript
self.action = action
self.method = method
self.enctype = enctype
self.encoding = encoding
self.stored_encoding = stored_encoding
self.unicode_mode = unicode_mode
# MANIPULATORS
security.declareProtected('Change Formulator Forms', 'field_added')
def field_added(self, field_id, group=None):
"""A field was added to the form.
"""
# get indicated group or the first group if none was indicated
group = group or self.group_list[0]
# add it to the indicated group (create group if nonexistent)
groups = self.groups
field_list = groups.get(group, [])
field_list.append(field_id)
groups[group] = field_list
if group not in self.group_list:
self.group_list.append(group)
self.group_list = self.group_list
self.groups = groups
security.declareProtected('Change Formulator Forms', 'field_removed')
def field_removed(self, field_id):
"""A field was removed from the form.
"""
for field_list in self.groups.values():
if field_id in field_list:
field_list.remove(field_id)
break # should be done as soon as we found it once
self.groups = self.groups
security.declareProtected('Change Formulator Forms', 'move_field_up')
def move_field_up(self, field_id, group):
groups = self.groups
field_list = groups[group]
i = field_list.index(field_id)
if i == 0:
return False # can't move further up, so we're done
# swap fields, moving i up
field_list[i], field_list[i - 1] = field_list[i - 1], field_list[i]
self.groups = groups
return True
security.declareProtected('Change Formulator Forms', 'move_field_down')
def move_field_down(self, field_id, group):
groups = self.groups
field_list = groups[group]
i = field_list.index(field_id)
if i == len(field_list) - 1:
return False # can't move further down, so we're done
# swap fields, moving i down
field_list[i], field_list[i + 1] = field_list[i + 1], field_list[i]
self.groups = groups
return True
security.declareProtected('Change Formulator Forms', 'move_field_group')
def move_field_group(self, field_ids, from_group, to_group):
"""Moves a fields from one group to the other.
"""
if len(field_ids) == 0 or from_group == to_group:
return False
groups = self.groups
from_list = groups[from_group]
to_list = groups[to_group]
for field in self.get_fields_in_group(from_group, include_disabled=1)[:]:
if field.id in field_ids:
from_list.remove(field.id)
to_list.append(field.id)
self.groups = groups
return True
security.declareProtected('Change Formulator Forms', 'add_group')
def add_group(self, group):
"""Add a new group.
"""
groups = self.groups
if groups.has_key(group):
return False # group already exists (NOTE: should we raise instead?)
groups[group] = []
# add the group to the bottom of the list of groups
self.group_list.append(group)
self.group_list = self.group_list
self.groups = groups
return True
security.declareProtected('Change Formulator Forms', 'remove_group')
def remove_group(self, group):
"""Remove a group.
"""
groups = self.groups
if group == self.group_list[0]:
return False # can't remove first group
if not groups.has_key(group):
return False # group does not exist (NOTE: should we raise instead?)
# move whatever is in the group now to the end of the first group
groups[self.group_list[0]].extend(groups[group])
# now remove the key
del groups[group]
# remove it from the group order list as well
self.group_list.remove(group)
self.group_list = self.group_list
self.groups = groups
return True
security.declareProtected('Change Formulator Forms', 'rename_group')
def rename_group(self, group, name):
"""Rename a group.
"""
group_list = self.group_list
groups = self.groups
if not groups.has_key(group):
return False # can't rename unexisting group
if groups.has_key(name):
return False # can't rename into existing name
i = group_list.index(group)
group_list[i] = name
groups[name] = groups[group]
del groups[group]
self.group_list = group_list
self.groups = groups
return True
security.declareProtected('Change Formulator Forms', 'move_group_up')
def move_group_up(self, group):
"""Move a group up in the group list.
"""
group_list = self.group_list
i = group_list.index(group)
if i == 0:
return False # can't move further up, so we're done
# swap groups, moving i up
group_list[i], group_list[i - 1] = group_list[i - 1], group_list[i]
self.group_list = group_list
return True
security.declareProtected('Change Formulator Forms', 'move_group_down')
def move_group_down(self, group):
"""Move a group down in the group list.
"""
group_list = self.group_list
i = group_list.index(group)
if i == len(group_list) - 1:
return False # can't move further up, so we're done
# swap groups, moving i down
group_list[i], group_list[i + 1] = group_list[i + 1], group_list[i]
self.group_list = group_list
return True
# ACCESSORS
security.declareProtected('View', 'get_fields')
def get_fields(self, include_disabled=False):
"""Get all fields for all groups (in the display order).
"""
result = []
for group in self.get_groups(include_empty=True):
result.extend(self.get_fields_in_group(group, include_disabled))
return result
security.declareProtected('View', 'get_field_ids')
def get_field_ids(self, include_disabled=False):
"""Get all the ids of the fields in the form.
"""
result = []
for field in self.get_fields(include_disabled):
result.append(field.id)
return result
security.declareProtected('View', 'get_fields_in_group')
def get_fields_in_group(self, group, include_disabled=False):
"""Get all fields in a group (in the display order).
"""
result = []
for field_id in self.groups.get(group, []):
try:
field = self.get_field(field_id, include_disabled)
except FieldDisabledError:
pass
else:
result.append(field)
return result
security.declareProtected('View', 'has_field')
def has_field(self, id, include_disabled):
"""Check whether the form has a field of a certain id.
"""
# define in subclass
pass
security.declareProtected('View', 'get_field')
def get_field(self, id):
"""Get a field of a certain id.
"""
# define in subclass
pass
security.declareProtected('View', 'get_groups')
def get_groups(self, include_empty=False):
"""Get a list of all groups, in display order.
If include_empty is false, suppress groups that do not have
enabled fields.
"""
if include_empty:
return self.group_list
return [group for group in self.group_list
if self.get_fields_in_group(group)]
security.declareProtected('View', 'get_form_encoding')
def get_form_encoding(self):
"""Get the encoding the form is in. Should be the same as the
encoding of the page, if specified, for unicode to work. Default
is 'UTF-8'.
"""
return getattr(self, 'encoding', 'UTF-8')
security.declareProtected('View', 'get_stored_encoding')
def get_stored_encoding(self):
"""Get the encoding of the stored field properties.
"""
return getattr(self, 'stored_encoding', 'ISO-8859-1')
security.declareProtected('View', 'get_unicode_mode')
def get_unicode_mode(self):
"""Get unicode mode information.
"""
return getattr(self, 'unicode_mode', False)
security.declareProtected('View', 'render')
def render(self, dict=None, REQUEST=None):
"""Render form in a default way.
"""
dict = dict or {}
result = StringIO()
w = result.write
w(self.header())
for group in self.get_groups():
w('
%s
\n' % group)
w('
\n')
for field in self.get_fields_in_group(group):
if dict.has_key(field.id):
value = dict[field.id]
else:
value = None
w('
\n')
if not field.get_value('hidden'):
w('
%s
\n' % field.get_value('title'))
else:
w('
')
w('
%s
\n' % field.render(value, REQUEST))
w('
\n')
w('
\n')
w('\n')
w(self.footer())
return result.getvalue()
security.declareProtected('View', 'render_view')
def render_view(self, dict=None):
"""Render contents (default simplistic way).
"""
dict = dict or {}
result = StringIO()
w = result.write
for group in self.get_groups():
w('
%s
\n' % group)
w('
\n')
for field in self.get_fields_in_group(group):
if dict.has_key(field.id):
value = dict[field.id]
else:
value = None
w('
\n')
w('
%s
\n' % field.get_value('title'))
w('
%s
\n' % field.render_view(value))
w('
\n')
w('
\n')
return result.getvalue()
security.declareProtected('View', 'validate')
def validate(self, REQUEST):
"""Validate all enabled fields in this form. Stop validating and
pass up ValidationError if any occurs.
"""
result = {}
for field in self.get_fields():
# skip any fields we don't need to validate
if not field.need_validate(REQUEST):
continue
value = field.validate(REQUEST)
# store under id
result[field.id] = value
# store as alternate name as well if necessary
alternate_name = field.get_value('alternate_name')
if alternate_name:
result[alternate_name] = value
return result
security.declareProtected('View', 'validate_to_request')
def validate_to_request(self, REQUEST):
"""Validation, stop validating as soon as error.
"""
result = self.validate(REQUEST)
for key, value in result.items():
REQUEST.set(key, value)
return result
security.declareProtected('View', 'validate_all')
def validate_all(self, REQUEST):
"""Validate all enabled fields in this form, catch any ValidationErrors
if they occur and raise a FormValidationError in the end if any
Validation Errors occured.
"""
result = {}
errors = []
for field in self.get_fields():
# skip any field we don't need to validate
if not field.need_validate(REQUEST):
continue
try:
value = field.validate(REQUEST)
# store under id
result[field.id] = value
# store as alternate name as well if necessary
alternate_name = field.get_value('alternate_name')
if alternate_name:
result[alternate_name] = value
except ValidationError, err:
errors.append(err)
if len(errors) > 0:
raise FormValidationError(errors, result)
return result
security.declareProtected('View', 'validate_all_to_request')
def validate_all_to_request(self, REQUEST, key_prefix=None):
"""Validation, continue validating all fields, catch errors.
Everything that could be validated will be added to REQUEST.
"""
try:
result = self.validate_all(REQUEST, key_prefix=key_prefix)
except FormValidationError, e:
# put whatever result we have in REQUEST
for key, value in e.result.items():
REQUEST.set(key, value)
# reraise exception
raise
for key, value in result.items():
REQUEST.set(key, value)
return result
security.declareProtected('View', 'session_store')
def session_store(self, session, REQUEST):
"""Store form data in REQUEST into session.
"""
data = session.getSessionData()
for field in self.get_fields():
id = field.id
data.set(id, REQUEST[id])
security.declareProtected('View', 'session_retrieve')
def session_retrieve(self, session, REQUEST):
"""Retrieve form data from session into REQUEST.
"""
data = session.getSessionData()
for field in self.get_fields():
id = field.id
REQUEST.set(id, data.get(id))
security.declareProtected('View', 'header')
def header(self):
"""Starting form tag.
"""
# FIXME: backwards compatibility; name attr may not be present
if not hasattr(self, "name"):
self.name = ""
name = self.name
if self.enctype is not "":
if name:
return render_tag("form",
name=name,
action=self.action,
method=self.method,
enctype=self.enctype) + ">"
else:
return render_tag("form",
action=self.action,
method=self.method,
enctype=self.enctype) + ">"
else:
if name:
return render_tag("form",
name=name,
action=self.action,
method=self.method) + ">"
else:
return render_tag("form",
action=self.action,
method=self.method) + ">"
security.declareProtected('View', 'footer')
def footer(self):
"""Closing form tag.
"""
return ""
security.declareProtected('Change Formulator Forms', 'get_xml')
def get_xml(self):
"""Get this form in XML serialization.
"""
return formToXML(self)
security.declareProtected('Change Formulator Forms', 'set_xml')
def set_xml(self, xml, override_encoding=None):
"""change form according to xml"""
XMLToForm(xml, self, override_encoding)
def _management_page_charset(self):
if not self.unicode_mode:
return self.stored_encoding
else:
return 'UTF-8'
security.declareProtected('Access contents information',
'management_page_charset')
management_page_charset = ComputedAttribute(_management_page_charset)
security.declareProtected('View', 'set_encoding_header')
def set_encoding_header(self):
"""Set the encoding in the RESPONSE object.
This can be used to make sure a page is in the same encoding the
textual form contents is in.
"""
if not self.unicode_mode:
encoding = self.stored_encoding
else:
encoding = 'UTF-8'
self.REQUEST.RESPONSE.setHeader(
'Content-Type',
'text/html;charset=%s' % encoding)
InitializeClass(Form)
class BasicForm(Persistent, Acquisition.Implicit, Form):
"""A form that manages its own fields, not using ObjectManager.
Can contain dummy fields defined by DummyField.
"""
security = ClassSecurityInfo()
def __init__(self, action="", method="POST", enctype="", name="",
encoding="UTF-8", stored_encoding='ISO-8859-1',
unicode_mode=False):
BasicForm.inheritedAttribute('__init__')(
self, action, method, enctype,
name, encoding, stored_encoding, unicode_mode)
self.title = 'Basic Form' # XXX to please FormToXML..
self.fields = {}
security.declareProtected('Change Formulator Forms', 'add_field')
def add_field(self, field, group=None):
"""Add a field to the form to a certain group.
"""
# update group info
self.field_added(field.id, group)
# add field to list
self.fields[field.id] = field
self.fields = self.fields
security.declareProtected('Change Formulator Forms', 'add_fields')
def add_fields(self, fields, group=None):
"""Add a number of fields to the form at once (in a group).
"""
for field in fields:
self.add_field(field, group)
security.declareProtected('Change Formulator Forms', 'remove_field')
def remove_field(self, field):
"""Remove field from form.
"""
# update group info
self.field_removed(field.id)
# remove field from list
del self.fields[field.id]
self.fields = self.fields
security.declareProtected('View', 'has_field')
def has_field(self, id, include_disabled=False):
"""Check whether the form has a field of a certain id.
If disabled fields are not included, pretend they're not there.
"""
field = self.fields.get(id, None)
if field is None:
return False
return include_disabled or field.get_value('enabled')
security.declareProtected('View', 'get_field')
def get_field(self, id, include_disabled=False):
"""get a field of a certain id."""
field = self.fields[id]
if include_disabled or field.get_value('enabled'):
return field
raise FieldDisabledError("Field %s is disabled" % id, field)
def _realize_fields(self):
"""Make the fields in this form actual fields, not just dummy fields.
"""
for field in self.get_fields(include_disabled=True):
if hasattr(field, 'get_real_field'):
field = field.get_real_field()
self.fields[field.id] = field
self.fields = self.fields
InitializeClass(BasicForm)
def create_settings_form():
"""Create settings form for ZMIForm.
"""
form = BasicForm('manage_settings')
title = fields.StringField('title',
title="Title",
required=False,
default="")
row_length = fields.IntegerField('row_length',
title='Number of groups in row (in order tab)',
required=True,
default=4)
name = fields.StringField('name',
title="Form name",
required=False,
default="")
action = fields.StringField('action',
title='Form action',
required=False,
default="")
method = fields.ListField('method',
title='Form method',
items=[('POST', 'POST'),
('GET', 'GET')],
required=True,
size=1,
default='POST')
enctype = fields.ListField('enctype',
title='Form enctype',
items=[('No enctype', ""),
('application/x-www-form-urlencoded',
'application/x-www-form-urlencoded'),
('multipart/form-data',
'multipart/form-data')],
required=False,
size=1,
default=None)
encoding = fields.StringField('encoding',
title='Encoding of pages the form is in',
default="UTF-8",
required=True)
stored_encoding = fields.StringField('stored_encoding',
title='Encoding of form properties',
default='ISO-8859-1',
required=True)
unicode_mode = fields.CheckBoxField('unicode_mode',
title='Form properties are unicode',
default=False,
required=True)
form.add_fields([title, row_length, name, action, method,
enctype, encoding, stored_encoding, unicode_mode])
return form
class ZMIForm(ObjectManager, PropertyManager, RoleManager, Item, Form):
"""
A Formulator Form, fields are managed by ObjectManager.
"""
meta_type = "Formulator Form"
security = ClassSecurityInfo()
# should be helpful with ZClasses, but not sure why I
# had it in here as a comment in the first place..
security.declareObjectProtected('View')
# the tabs we want to show
manage_options = (
(
{'label':'Contents', 'action':'manage_main',
'help':('Formulator', 'formContents.txt')},
{'label':'Test', 'action':'formTest',
'help':('Formulator', 'formTest.txt')},
{'label':'Order', 'action':'formOrder',
'help':('Formulator', 'formOrder.txt')},
{'label':'Settings', 'action':'formSettings',
'help':('Formulator', 'formSettings.txt')},
{'label':'XML', 'action':'formXML',
'help':('Formulator', 'formXML.txt')},
) +
PropertyManager.manage_options +
RoleManager.manage_options +
Item.manage_options
)
def __init__(self, id, title, unicode_mode=False):
"""Initialize form.
id -- id of form
title -- the title of the form
"""
ZMIForm.inheritedAttribute('__init__')(self, "", "POST", "", id,
'UTF-8', 'ISO-8859-1',
unicode_mode)
self.id = id
self.title = title
self.row_length = 4
security.declarePublic('all_meta_types')
def all_meta_types(self):
"""Get all meta types addable to this field. The ZMI uses
this method (original defined in ObjectManager).
"""
return self._meta_types
def manage_renameObject(self, id, new_id, REQUEST=None):
"""Rename a particular sub-object, the *old* way.
FIXME: hack that could be removed once Zope 2.4.x
goes back to a useful semantics..."""
try: self._checkId(new_id)
except: raise CopyError, MessageDialog(
title='Invalid Id',
message=sys.exc_info()[1],
action ='manage_main')
ob=self._getOb(id)
if not ob.cb_isMoveable():
raise CopyError, eNotSupported % id
self._verifyObjectPaste(ob)
try: ob._notifyOfCopyTo(self, op=1)
except: raise CopyError, MessageDialog(
title='Rename Error',
message=sys.exc_info()[1],
action ='manage_main')
self._delObject(id)
ob = aq_base(ob)
ob._setId(new_id)
# Note - because a rename always keeps the same context, we
# can just leave the ownership info unchanged.
self._setObject(new_id, ob, set_owner=False)
if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=True)
return None
#security.declareProtected('View', 'get_fields_raw')
#def get_fields_raw(self):
# """Get all fields, in arbitrary order.
# """
# return filter(lambda obj: hasattr(obj.aq_explicit, 'is_field'),
# self.objectValues())
security.declareProtected('View', 'has_field')
def has_field(self, id, include_disabled=False):
"""Check whether the form has a field of a certain id.
"""
field = self._getOb(id, None)
if field is None or not hasattr(aq_base(field), 'is_field'):
return False
return include_disabled or field.get_value('enabled')
security.declareProtected('View', 'get_field')
def get_field(self, id, include_disabled=False):
"""Get a field of a certain id
"""
field = self._getOb(id, None)
if field is None or not hasattr(aq_base(field), 'is_field'):
raise AttributeError, "No field %s" % id
if include_disabled or field.get_value('enabled'):
return field
raise FieldDisabledError("Field %s disabled" % id, field)
security.declareProtected('Change Formulator Forms', 'manage_addField')
def manage_addField(self, id, title, fieldname, REQUEST=None):
"""Add a new field to the form.
id -- the id of the field to add
title -- the title of the field to add; this will be used in
displays of the field on forms
fieldname -- the name of the field (meta_type) to add
Result -- empty string
"""
title = string.strip(title)
if not title:
title = id # title is always required, use id if not provided
# get the field class we want to add
field_class = FieldRegistry.get_field_class(fieldname)
# create field instance
field = field_class(id, title=title, description="")
# add the field to the form
id = self._setObject(id, field)
# respond to add_and_edit button if necessary
add_and_edit(self, id, REQUEST)
return ''
security.declareProtected('View management screens', 'formTest')
formTest = DTMLFile('dtml/formTest', globals())
settings_form = create_settings_form()
security.declareProtected('View management screens', 'formSettings')
formSettings = DTMLFile('dtml/formSettings', globals())
security.declareProtected('View management screens', 'formOrder')
formOrder = DTMLFile('dtml/formOrder', globals())
security.declareProtected('View management screens', 'formXML')
formXML = DTMLFile('dtml/formXML', globals())
security.declareProtected('Change Formulator Forms', 'manage_editXML')
def manage_editXML(self, form_data, REQUEST):
"""Change form using XML.
"""
self.set_xml(form_data)
return self.formXML(self, REQUEST,
manage_tabs_message="Changed form")
security.declareProtected('Change Formulator Forms', 'manage_settings')
def manage_settings(self, REQUEST):
"""Change settings in settings screen.
"""
try:
result = self.settings_form.validate_all(REQUEST)
except FormValidationError, e:
message = "Validation error(s). " + string.join(
map(lambda error: "%s: %s" % (error.field.get_value('title'),
error.error_text), e.errors), " ")
return self.formSettings(self, REQUEST,
manage_tabs_message=message)
# if we need to switch encoding, get xml representation before setting
if result['unicode_mode'] != self.unicode_mode:
xml = self.get_xml()
# now set the form settings
# convert XML to or from unicode mode if necessary
unicode_message = None
if result['unicode_mode'] != self.unicode_mode:
# get XML (using current stored_encoding)
xml = self.get_xml()
# now save XML data again using specified encoding
if result['unicode_mode']:
encoding = 'unicode'
unicode_message = "Converted to unicode."
else:
encoding = result['stored_encoding']
unicode_message = ("Converted from unicode to %s encoding" %
encoding)
self.set_xml(xml, encoding)
# now set the form settings
for key, value in result.items():
setattr(self, key, value)
message="Settings changed."
if unicode_message is not None:
message = message + ' ' + unicode_message
return self.formSettings(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms', 'manage_refresh')
def manage_refresh(self, REQUEST):
"""Refresh internal data structures of this form.
FIXME: this doesn't work right now
"""
# self.update_groups()
REQUEST.RESPONSE.redirect('manage_main')
security.declarePrivate('_get_field_ids')
def _get_field_ids(self, group, REQUEST):
"""Get the checked field_ids that we're operating on
"""
field_ids = []
for field in self.get_fields_in_group(group, include_disabled=True):
if REQUEST.form.has_key(field.id):
field_ids.append(field.id)
return field_ids
security.declareProtected('View management screens',
'get_group_rows')
def get_group_rows(self):
"""Get the groups in rows (for the order screen).
"""
row_length = self.row_length
groups = self.get_groups(include_empty=True)
# get the amount of rows
rows = len(groups) / row_length
# if we would have extra groups not in a row, add a row
if len(groups) % self.row_length != 0:
rows = rows + 1
# now create a list of group lists and return it
result = []
for i in range(rows):
start = i * row_length
result.append(groups[start: start + row_length])
return result
security.declareProtected('View', 'get_largest_group_length')
def get_largest_group_length(self):
"""Get the largest group length available; necessary for
'order' screen user interface.
"""
max = 0
for group in self.get_groups(include_empty=True):
fields = self.get_fields_in_group(group)
if len(fields) > max:
max = len(fields)
return max
security.declareProtected('Change Formulator Forms',
'manage_move_field_up')
def manage_move_field_up(self, group, REQUEST):
"""Moves up a field in a group.
"""
field_ids = self._get_field_ids(group, REQUEST)
if (len(field_ids) == 1 and
self.move_field_up(field_ids[0], group)):
message = "Field %s moved up." % field_ids[0]
else:
message = "Can't move field up."
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_move_field_down')
def manage_move_field_down(self, group, REQUEST):
"""Moves down a field in a group.
"""
field_ids = self._get_field_ids(group, REQUEST)
if (len(field_ids) == 1 and
self.move_field_down(field_ids[0], group)):
message = "Field %s moved down." % field_ids[0]
else:
message = "Can't move field down."
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_move_group')
def manage_move_group(self, group, to_group, REQUEST):
"""Moves fields to a different group.
"""
field_ids = self._get_field_ids(group, REQUEST)
if (to_group != 'Move to:' and
self.move_field_group(field_ids, group, to_group)):
fields = string.join(field_ids, ", ")
message = "Fields %s transferred from %s to %s." % (fields,
group,
to_group)
else:
message = "Can't transfer fields."
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_add_group')
def manage_add_group(self, new_group, REQUEST):
"""Adds a new group.
"""
group = string.strip(new_group)
if (group and group != 'Select group' and
self.add_group(group)):
message = "Group %s created." % (group)
else:
message = "Can't create group."
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_remove_group')
def manage_remove_group(self, group, REQUEST):
"""Removes group.
"""
if self.remove_group(group):
message = "Group %s removed." % (group)
else:
message = "Can't remove group."
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_rename_group')
def manage_rename_group(self, group, REQUEST):
"""Renames group.
"""
if REQUEST.has_key('new_name'):
new_name = string.strip(REQUEST['new_name'])
if self.rename_group(group, new_name):
message = "Group %s renamed to %s." % (group, new_name)
else:
message = "Can't rename group."
else:
message = "No new name supplied."
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_move_group_up')
def manage_move_group_up(self, group, REQUEST):
"""Move a group up.
"""
if self.move_group_up(group):
message = "Group %s moved up." % group
else:
message = "Can't move group %s up" % group
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
security.declareProtected('Change Formulator Forms',
'manage_move_group_down')
def manage_move_group_down(self, group, REQUEST):
"""Move a group down.
"""
if self.move_group_down(group):
message = "Group %s moved down." % group
else:
message = "Can't move group %s down" % group
return self.formOrder(self, REQUEST,
manage_tabs_message=message)
PythonForm = ZMIForm # NOTE: backwards compatibility
InitializeClass(ZMIForm)
manage_addForm = DTMLFile("dtml/formAdd", globals())
def manage_add(self, id, title="", unicode_mode=False, REQUEST=None):
"""Add form to folder.
id -- the id of the new form to add
title -- the title of the form to add
Result -- empty string
"""
# add actual object
id = self._setObject(id, ZMIForm(id, title, unicode_mode))
# respond to the add_and_edit button if necessary
add_and_edit(self, id, REQUEST)
return ''
def add_and_edit(self, id, REQUEST):
"""Helper method to point to the object's management screen if
'Add and Edit' button is pressed.
id -- id of the object we just added
"""
if REQUEST is None:
return
try:
u = self.DestinationURL()
except:
u = REQUEST['URL1']
if hasattr(REQUEST, 'submit_add_and_edit'):
u = "%s/%s" % (u, quote(id))
REQUEST.RESPONSE.redirect(u+'/manage_main')
def initializeForm(field_registry):
"""Sets up ZMIForm with fields from field_registry.
"""
form_class = ZMIForm
meta_types = []
for meta_type, field in field_registry.get_field_classes().items():
# don't set up in form if this is a field for internal use only
if field.internal_field:
continue
# set up individual add dictionaries for meta_types
dict = { 'name': field.meta_type,
'action':
'manage_addProduct/Formulator/manage_add%sForm' % meta_type }
meta_types.append(dict)
# set up add method
setattr(form_class,
'manage_add%sForm' % meta_type,
DTMLFile('dtml/fieldAdd', globals(), fieldname=meta_type))
# set up meta_types that can be added to form
form_class._meta_types = tuple(meta_types)
# set up settings form
form_class.settings_form._realize_fields()
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/FormToXML.py 0000664 0000000 0000000 00000007202 12770231644 0031473 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
from cgi import escape
from lxml import etree
from lxml.etree import Element, SubElement, CDATA
from lxml.builder import E
def formToXML(form, prologue=1):
"""Takes a formulator form and serializes it to an XML representation.
"""
form_as_xml = Element('form')
encoding = form.stored_encoding or 'utf-8'
# export form settings
for field in form.settings_form.get_fields(include_disabled=1):
id = field.id
value = getattr(form, id)
if id == 'unicode_mode':
if value:
value = 'true'
else:
value = 'false'
sub_element = SubElement(form_as_xml, id)
sub_element.text = escape(str(value)).decode(encoding)
groups = SubElement(form_as_xml, 'groups')
# export form groups
for group in form.get_groups(include_empty=1):
group_element = SubElement(groups, 'group')
group_element.append(E.title(group))
fields = SubElement(group_element, 'fields')
for field in form.get_fields_in_group(group, include_disabled=1):
field_element = E.field(
E.id(str(field.id)),
E.type(str(field.meta_type))
)
fields.append(field_element)
values_element = SubElement(field_element, 'values')
items = field.values.items()
items.sort()
for key, value in items:
if value is None:
continue
if value is True: # XXX Patch
value = 1 # XXX Patch
if value is False: # XXX Patch
value = 0 # XXX Patch
if callable(value): # XXX Patch
value_element = SubElement(values_element, key, type='method')
elif isinstance(value, float):
value_element = SubElement(values_element, key, type='float')
elif isinstance(value, int):
value_element = SubElement(values_element, key, type='int')
elif isinstance(value, list):
value_element = SubElement(values_element, key, type='list')
else:
if not isinstance(value, (str, unicode)):
value = str(value)
value_element = SubElement(values_element, key)
value_element.text = escape(str(value)).decode(encoding)
tales_element = SubElement(field_element, 'tales')
items = field.tales.items()
items.sort()
for key, value in items:
if value:
tale_element = SubElement(tales_element, key)
tale_element.text = escape(str(value._text)).decode(encoding)
messages = SubElement(field_element, 'messages')
for message_key in field.get_error_names():
message_element = SubElement(messages, 'message', name=message_key)
message_element.text = escape(field.get_error_message(message_key)).decode(encoding)
# Special attribute for ProxyFields *delegated_list*
delegated_list = getattr(field, 'delegated_list', [])
if delegated_list:
delegated_list_element = SubElement(field_element, 'delegated_list')
delegated_list.sort()
[SubElement(delegated_list_element, delegated) for delegated in delegated_list]
form_as_string = etree.tostring(form_as_xml, encoding='utf-8',
xml_declaration=True, pretty_print=True)
if form.unicode_mode:
return etree.tostring(form_as_xml, encoding='utf-8',
xml_declaration=True, pretty_print=True)
else:
return etree.tostring(form_as_xml, encoding=form.stored_encoding,
xml_declaration=True, pretty_print=True)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/HISTORY.txt 0000664 0000000 0000000 00000032644 12770231644 0031244 0 ustar 00root root 0000000 0000000 Formulator changes
1.6.1
Bugs Fixed
- Adding Fields to empty Groups had not been possible
- ZMI "Order" tab of an empty form did raise an exception
1.6.0
Features Added
- FileSystemSite/DirectoryView improvements:
* XML filesystem representation of Formulator forms can now
also be used with CMF (if FileSystemSite is not installed).
* FSForm gets automatically registered with the directory
view system if CMF or FileSystemSite is installed.
- Infrastructure for Validators not to get taken into account in
validation procedures (need_validate).
- A new label field. Doesn't participate in validation. It shows
its text as a label in the form.
- Unicode mode. A form can now be put in 'unicode mode', which
means it stores all its textual data as unicode strings. This
allows for easier integration with Zope systems that use unicode
internally, such as Silva.
- Disabling of fields. A field can now be disabled from being
displayed or validated by unchecking the 'Enabled' validator
property. This can be done dynamically as well using TALES
overrides.
Bugs Fixed
- The css_class value of a DateTime field had been ignored. It
is now properly passed down to its subfields, so all subfield
elements are rendered with the given css_class value.
1.5.0
Features Added
- Added ProductForm, which provides a wrapping around
Formulator.BasicForm, allowing it to be created inside a
product but used outside it.
- Allow turning off of XML prologue section.
- Optimization of TALESMethod by caching compiled expression.
This speeds SilvaMetadata indexing up by a lot if a fallback
on default is made, especially in the case of Python
expressions, as it avoids lots of compilation overhead.
- Extra attribute defined for list/multicheckbox/radio fields
called 'extra_item', which allows setting HTML attributes to
individual list item/checkbox/radio button.
Bugs Fixed
- XML serialization should be more consistent now; field properties
are now ordered by name upon serialization.
- Allow XML export of BasicForm.
1.4.2
Bugs Fixed
- Sticky forms should now work correctly in the presence of unicode.
Encoded data is automatically converted to unicode if the information
is pulled from the REQUEST form.
1.4.1
Bugs Fixed
- It was not possible to make DateTime fields not required when
'allow_empty_time' was enabled. Fixed.
1.4.0
Features Added
- Added limited ability to output unicode for selected
fields. Only works properly in Zope 2.6.x, and the HTML pages
these forms are in need an output encoding set (such as
UTF-8, which is also Formulator's default encoding). If
'unicode' checkbox is checked Formulator will try to interpret
its input in the Form's encoding (default is UTF-8). It will
also try to display its values in that encoding. Note that
only field values and items currently work with unicode -- the
rest of the textual properties of a field are still stored as
8-bits. If you make sure that these properties are encoded as
UTF-8 (or whatever encoding you choose for the form) things
should be okay, however.
- Can now also change forms using XML (not just view it).
- DateTime fields can now optionally input AM/PM.
- DateTime fields can now optionally be set to allow time to
be left empty.
- 'whitespace_preserve' option on string type fields. If turned on,
whitespace will not be automatically stripped and will count as
input.
- 'render_view' method on fields to render the value outside a
widget.
- Added some code support used by SilvaMetadata to enable rendering
of fields with Zope's ':record' syntax.
Bugs Fixed
- Fixed a Python2.2 compatibility bug in XMLObjects.py
- DateTimeField now picks up default values from REQUEST
properly if necessary.
- XML representation of the LinkField "check_timeout" value
messed the type="float" attribute.
- Additional unit tests.
1.3.1 (2002/12/20)
Features Added
- Error messages can now be included in the XML serialization.
- Ability to encode lists as a special type in values.
Bugs Fixed
- Some more proper encodings.
- Handle case where group has no field.
- Handle DateTime field better.
1.3.0 (2002/11/26)
Features Added
- FormToXML and XMLToForm modules have functions to serialize
(most of) form to XML and read it in again (over an existing
form).
- New XML tab for forms which shows the XML serialization (no
saving option yet).
- FSForm.py uses XML serialization to provide a formulator form
version for FileSystemSite. It does not get imported by
default.
Bugs Fixed
- The email validator has an improved regular expression.
- Fix error that occured when trying to render DateTimeField as
hidden.
1.2.0 (2002/03/06)
Features Added
- Changes to exception infrastructure so errors can now be
imported and caught in a through the web Python script. Example::
from Products.Formulator.Errors import ValidationError, FormValidationError
- added __getitem__ to Field so instead of using get_value() you can
also do this in Python: form.field['title'], and in ZPT you can
use this in path expressions: form/field/title
- made a start with Formulator unit tests; some validators get
automatically tested now.
Bugs Fixed
- Removed dependencies of the name of 'Add and Edit' button to make
internationalization of the management interface easier.
- added permission to make ZClasses work a bit better (but they
still don't cooperate well with Formulator, I think. I don't use
ZClasses, so I hope to hear from this from ZClass users)
- Form's properties tab now visible and form tabs stopped
misbehaving.
- Lists and such should handle multiple items with the same value
a bit better, selecting only one.
- the LinkField now checks site-internal links better.
1.1.0 (2001/10/26)
Bugs Fixed
- Fixed bug in form settings tab.
- the LinkField now checks site-internal links better.
1.0.9 (2001/10/05)
Features Added
- New TALES tab for fields as a more powerful Override tab;
PageTemplates needs to be installed to make it work.
- added 'name' attribute for forms. When the form header is
rendered, name will be an attribute. This can be used to
control forms with Javascript.
Bugs Fixed
- More compliance with Zope product guidelines; moved dtml
files from www dir to dtml dir.
- Fixed a bug in that form titles would not work. Forms now have
titles, and you can change them in the settings tab. (Formulator
does not use the title property internally though)
1.0.1 (2001/07/27)
Bugs Fixed
- Fixed bug with renaming groups. Previously, renamed groups were not
properly stored in the ZODB.
- Made MultiSelectionValidator (used by MultiListField among others)
deal better with integer values.
- Hacked around CopySupport changes in Zope 2.4.0; renames work
again now.
1.0 (2001/07/10)
Features Added
- New field: RawTextAreaField. A textarea field that doesn't
do a lot of processing on the text input.
- Checked in BSD license text.
Bugs Fixed
- Fixed minor bug in year handling of DateTimeField.
- Now hidden fields also take text from 'extra' property.
- Fixed bug in MultiItemsWidget; would not deal with only a
single item being selected.
0.9.5 (2001/06/27)
Features Added
- Added FileField (with browse button). Can be used to upload
files if form is set to multipart/form-data.
- Added LinkField for URLs.
- Made ListField and RadioField more tolerant of integer
(and possibly other) values, not only strings.
- Made ListField and RadioField happy to deal with non-tuples too in the
items list. In this case, the item text and value will be identical.
- Refactored ListWidget and RadioWidget so they share code; they both
inherit from SingleItemsWidget now.
- Added LinesField to submit a list of lines in a textarea.
- Added MultiListField and MultiCheckBoxField, both use new
MultiItemsWidget and MultiSelectionValidator.
- Added EXPERIMENTAL PatternField.
0.9.4 (2001/06/20)
Features Added
- Added API docs for Form, BasicForm and ZMIForm.
- Renamed the confusingly named PythonForm and PythonField to
ZMIForm and ZMIField, as they are used from the Zope Management
Interface and not from Python.
- Added render() method to form for basic form rendering.
- Added Formulator HOWTO document.
Bugs Fixed
- Removed some validation code that wasn't in use anymore (items_method).
- Removed 'has_field_id' in Form as this duplicated
the functionality of 'has_field'.
- Turned in Python sources to for XHTML compliance.
- Tweaked radiobutton; text is now closer to the button itself,
different buttons are further apart.
0.9.3 (2001/06/12)
Features Added
- added RadioField for simple display of radio buttons.
- added action, method and enctype property to form settings.
These are displayed using the special form.header() and form.footer()
methods.
- added override tab to allow all properties to be overridden by
method calls instead. 'items_method' in ListField went
away.
- added ability to display DateTimeFields using drop down lists
instead of text input. Added some other bells and whistles to
DateTimeField. Changed some of the inner workings of composite
fields; component fields are now unique per field instance
instead of shared between them.
- is_required() utility method on field to check whether a field
is required.
- some internal features, such the ability to have a method
called as soon as a property has changed.
Bugs Fixed
- Fixed typos in security assertions.
- use REQUEST.form instead of REQUEST where possible.
- display month and day with initial zero in DateTimeField.
- Fixed bug in validate_all_to_request(); what can be validated
will now be added to REQUEST if possible, even if a
FormValidationError is raised.
0.9.2 (2001/05/23)
Features Added
- Ability to rename groups, including the first 'Default' group.
- Improved support for sticky forms; form.render() can now
take an optional second argument, REQUEST, which can come
from a previous form submit. Even unvalidated fields will
then be sticky.
- fields can call an extra optional external validation
function (such as a Python script).
- New alternate name property: the alternate name is added to
the result dictionary or REQUEST object after validation. This
can be useful to support field names which wouldn't be valid
field names, which can occur in some locales.
- New extra property; can be used to add extra attributes to
a HTML tag.
- Some IntegerField properties can now be left empty if
no value is required, instead of having to set them to 0.
- Merged functionality of RangedIntegerField into IntegerField.
RangedIntegerField is not addable anymore, though supported
as a clone of IntegerField for backwards compatibility. Leaving
'start' and 'end' empty in the new IntegerField will mean those
checks will not be performed.
Bugs Fixed
- Added more missing security declarations.
- html_quote added in various places to make fields display
various HTML entities the right way.
0.9.1 (2001/05/13)
Features Added
- Widgets now have a 'hidden' property. If set, the widget is
drawn as a 'hidden' field. 'hidden' fields do get validated
normally, however.
- Changed API of Widget and Validator slightly; render() and
validate() methods now take an extra 'key' argument indicating
the name the field should have in the form. This is necessarily
to handle sub fields of composite fields.
- Added EmailField and FloatField.
- Added some infrastructure to support 'composite fields'; fields
composed out of multiple sub fields.
- Added DateTimeField, the first example of a composite field
(field made of other fields).
Bugs Fixed
- General code cleanups; removed some unused methods.
- Fixed security assertion for validate_all_to_request() method.
- MethodFields now check whether they have 'View' permission to
execute listed Python Script or DTML Method.
- RangedInteger is now < end, instead of <=, compatible with the
documentation.
0.9 (2001/04/30)
Initial Release
- Initial public release of Formulator.
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/HelperFields.py 0000664 0000000 0000000 00000000262 12770231644 0032251 0 ustar 00root root 0000000 0000000 # include some helper fields which are in their own files
from MethodField import MethodField
from ListTextAreaField import ListTextAreaField
from TALESField import TALESField
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/INSTALL.txt 0000664 0000000 0000000 00000010032 12770231644 0031174 0 ustar 00root root 0000000 0000000 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.
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/LICENSE.txt 0000664 0000000 0000000 00000002734 12770231644 0031162 0 ustar 00root root 0000000 0000000 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.
ListTextAreaField.py 0000664 0000000 0000000 00000003456 12770231644 0033151 0 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator 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
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/MethodField.py 0000664 0000000 0000000 00000004500 12770231644 0032066 0 ustar 00root root 0000000 0000000 import string
from DummyField import fields
import Widget, Validator
from Persistence 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
PatternChecker.py 0000664 0000000 0000000 00000012163 12770231644 0032531 0 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator 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')
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/ProductForm.py 0000664 0000000 0000000 00000012002 12770231644 0032142 0 ustar 00root root 0000000 0000000 """
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 App.class_init import default__class_init__ as 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)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/README.txt 0000664 0000000 0000000 00000002342 12770231644 0031030 0 ustar 00root root 0000000 0000000 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. :)
StandardFields.py 0000664 0000000 0000000 00000021722 12770231644 0032517 0 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator 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_timezone_style_changed(self, value):
if value:
input_style = self.get_value('input_style')
self.on_value_input_style_changed(input_style)
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
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 = Widget.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 = Widget.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
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/TALESField.py 0000664 0000000 0000000 00000005310 12770231644 0031516 0 ustar 00root root 0000000 0000000 import string
from DummyField import fields
import Widget, Validator
from Persistence 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
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/TODO.txt 0000664 0000000 0000000 00000000660 12770231644 0030641 0 ustar 00root root 0000000 0000000 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.
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/Validator.py 0000664 0000000 0000000 00000103110 12770231644 0031624 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
import re
import PatternChecker
from DummyField import fields
from DateTime import DateTime
from threading import Thread
from urllib import urlopen
from urlparse import urljoin
from Errors import ValidationError
from DateTime.DateTime import DateError, TimeError
import unicodedata
class ValidatorBase:
"""Even more minimalistic base class for validators.
"""
property_names = ['enabled','editable']
message_names = []
enabled = fields.CheckBoxField('enabled',
title="Enabled",
description=(
"If a field is not enabled, it will considered to be not "
"in the form during rendering or validation. Be careful "
"when you change this state dynamically (in the TALES tab): "
"a user could submit a field that since got disabled, or "
"get a validation error as a field suddenly got enabled that "
"wasn't there when the form was drawn."),
default=1)
editable = fields.CheckBoxField('editable',
title="Editable",
description=(
"If a field is not editable, then the user can only see"
"the value. This allows to drawn very different forms depending"
"on use permissions."),
default=1)
def raise_error(self, error_key, field):
raise ValidationError(error_key, field)
def validate(self, field, key, REQUEST):
pass # override in subclass
def need_validate(self, field, key, REQUEST):
"""Default behavior is always validation.
"""
return 1
class Validator(ValidatorBase):
"""Validates input and possibly transforms it to output.
"""
property_names = ValidatorBase.property_names + ['external_validator']
external_validator = fields.MethodField('external_validator',
title="External Validator",
description=(
"When a method name is supplied, this method will be "
"called each time this field is being validated. All other "
"validation code is called first, however. The value (result of "
"previous validation) and the REQUEST object will be passed as "
"arguments to this method. Your method should return true if the "
"validation succeeded. Anything else will cause "
"'external_validator_failed' to be raised."),
default="",
required=0)
message_names = ValidatorBase.message_names + ['external_validator_failed']
external_validator_failed = "The input failed the external validator."
class StringBaseValidator(Validator):
"""Simple string validator.
"""
property_names = Validator.property_names + ['required', 'whitespace_preserve']
required = fields.CheckBoxField('required',
title='Required',
description=(
"Checked if the field is required; the user has to fill in some "
"data."),
default=0)
whitespace_preserve = fields.CheckBoxField('whitespace_preserve',
title="Preserve whitespace",
description=(
"Checked if the field preserves whitespace. This means even "
"just whitespace input is considered to be data."),
default=0)
message_names = Validator.message_names + ['required_not_found']
required_not_found = 'Input is required but no input given.'
def validate(self, field, key, REQUEST):
# We had to add this patch for hidden fields of type "list"
value = REQUEST.get(key, REQUEST.get('default_%s' % (key, )))
if value is None:
if field.get_value('required'):
raise Exception, 'Required field %s has not been transmitted. Check that all required fields are in visible groups.' % (repr(field.id), )
else:
raise KeyError, 'Field %s is not present in request object.' % (repr(field.id), )
if isinstance(value, str):
if field.has_value('whitespace_preserve'):
if not field.get_value('whitespace_preserve'):
value = value.strip()
else:
# XXX Compatibility: use to prevent KeyError exception from get_value
value = value.strip()
if field.get_value('required') and value == "":
self.raise_error('required_not_found', field)
return value
class StringValidator(StringBaseValidator):
property_names = StringBaseValidator.property_names +\
['unicode', 'max_length', 'truncate']
unicode = fields.CheckBoxField('unicode',
title='Unicode',
description=(
"Checked if the field delivers a unicode string instead of an "
"8-bit string."),
default=0)
max_length = fields.IntegerField('max_length',
title='Maximum length',
description=(
"The maximum amount of characters that can be entered in this "
"field. If set to 0 or is left empty, there is no maximum. "
"Note that this is server side validation."),
default="",
required=0)
truncate = fields.CheckBoxField('truncate',
title='Truncate',
description=(
"If checked, truncate the field if it receives more input than is "
"allowed. The normal behavior in this case is to raise a validation "
"error, but the text can be silently truncated instead."),
default=0)
message_names = StringBaseValidator.message_names +\
['too_long']
too_long = 'Too much input was given.'
def validate(self, field, key, REQUEST):
value = StringBaseValidator.validate(self, field, key, REQUEST)
if field.get_value('unicode'):
# use acquisition to get encoding of form
value = unicode(value, field.get_form_encoding())
max_length = field.get_value('max_length') or 0
truncate = field.get_value('truncate')
if max_length > 0 and len(value) > max_length:
if truncate:
value = value[:max_length]
else:
self.raise_error('too_long', field)
return value
StringValidatorInstance = StringValidator()
class EmailValidator(StringValidator):
message_names = StringValidator.message_names + ['not_email']
not_email = 'You did not enter an email address.'
# This regex allows for a simple username or a username in a
# multi-dropbox (%). The host part has to be a normal fully
# qualified domain name, allowing for 6 characters (.museum) as a
# TLD. No bang paths (uucp), no dotted-ip-addresses, no angle
# brackets around the address (we assume these would be added by
# some custom script if needed), and of course no characters that
# don't belong in an e-mail address.
pattern = re.compile('^[0-9a-zA-Z_\'&.%+-]+@([0-9a-zA-Z]([0-9a-zA-Z-]*[0-9a-zA-Z])?\.)+[a-zA-Z]{2,6}$')
def validate(self, field, key, REQUEST):
value = StringValidator.validate(self, field, key, REQUEST)
if value == "" and not field.get_value('required'):
return value
if self.pattern.search(value.lower()) == None:
self.raise_error('not_email', field)
return value
EmailValidatorInstance = EmailValidator()
class PatternValidator(StringValidator):
# does the real work
checker = PatternChecker.PatternChecker()
property_names = StringValidator.property_names +\
['pattern']
pattern = fields.StringField('pattern',
title="Pattern",
required=1,
default="",
description=(
"The pattern the value should conform to. Patterns are "
"composed of digits ('d'), alphabetic characters ('e') and "
"alphanumeric characters ('f'). Any other character in the pattern "
"should appear literally in the value in that place. Internal "
"whitespace is checked as well but may be included in any amount. "
"Example: 'dddd ee' is a Dutch zipcode (postcode). "
"NOTE: currently experimental and details may change!")
)
message_names = StringValidator.message_names +\
['pattern_not_matched']
pattern_not_matched = "The entered value did not match the pattern."
def validate(self, field, key, REQUEST):
value = StringValidator.validate(self, field, key, REQUEST)
if value == "" and not field.get_value('required'):
return value
value = self.checker.validate_value([field.get_value('pattern')],
value)
if value is None:
self.raise_error('pattern_not_matched', field)
return value
PatternValidatorInstance = PatternValidator()
class BooleanValidator(Validator):
property_names = Validator.property_names + ['required']
required = fields.CheckBoxField('required',
title='Required',
description=(
"Checked if the field is required; the user has to check."),
default=0)
message_names = Validator.message_names + ['required_not_found']
required_not_found = 'This field is mandatory.'
def validate(self, field, key, REQUEST):
result = REQUEST.get(key, REQUEST.get('default_%s' % key))
if result is None:
raise KeyError('Field %r is not present in request object.' % field.id)
# XXX If the checkbox is hidden, Widget_render_hidden is used instead of
# CheckBoxWidget_render, and ':int' suffix is missing.
value = result and result != '0' and 1 or 0
if not value and field.get_value('required'):
self.raise_error('required_not_found', field)
return value
BooleanValidatorInstance = BooleanValidator()
class IntegerValidator(StringBaseValidator):
property_names = StringBaseValidator.property_names +\
['start', 'end']
start = fields.IntegerField('start',
title='Start',
description=(
"The integer entered by the user must be larger than or equal to "
"this value. If left empty, there is no minimum."),
default="",
required=0)
end = fields.IntegerField('end',
title='End',
description=(
"The integer entered by the user must be smaller than this "
"value. If left empty, there is no maximum."),
default="",
required=0)
message_names = StringBaseValidator.message_names +\
['not_integer', 'integer_out_of_range']
not_integer = 'You did not enter an integer.'
integer_out_of_range = 'The integer you entered was out of range.'
def validate(self, field, key, REQUEST):
value = StringBaseValidator.validate(self, field, key, REQUEST)
# we need to add this check again
if value == "" and not field.get_value('required'):
return value
value = normalizeFullWidthNumber(value)
try:
if value.find(' ')>0:
value = value.replace(' ','')
value = int(value)
except ValueError:
self.raise_error('not_integer', field)
start = field.get_value('start')
end = field.get_value('end')
if start != "" and value < start:
self.raise_error('integer_out_of_range', field)
if end != "" and value >= end:
self.raise_error('integer_out_of_range', field)
return value
IntegerValidatorInstance = IntegerValidator()
class FloatValidator(StringBaseValidator):
message_names = StringBaseValidator.message_names + ['not_float',
'too_large_precision']
not_float = "You did not enter a floating point number."
too_large_precision = "The number you input has too large precision."
def _validatePrecision(self, field, value, decimal_point, input_style):
""" Validate the consistency among the precision and the user inputs """
if not field.has_value('precision'):
return value
precision = field.get_value('precision')
if precision == '' or precision is None:
# need to validate when the precision is 0
return value
index = value.find(decimal_point)
if index < 0:
return value
input_precision_length = len(value[index+1:])
if input_precision_length > int(precision):
self.raise_error('too_large_precision', field)
return value
def validate(self, field, key, REQUEST):
value = StringBaseValidator.validate(self, field, key, REQUEST)
if value == "" and not field.get_value('required'):
return value
value = normalizeFullWidthNumber(value)
input_style = field.get_value('input_style')
decimal_separator = ''
decimal_point = '.'
if input_style == "-1234.5":
decimal_point = '.'
elif input_style == '-1 234.5':
decimal_separator = ' '
decimal_point = '.'
elif input_style == '-1 234,5':
decimal_separator = ' '
decimal_point = ','
elif input_style == '-1.234,5':
decimal_separator = '.'
decimal_point = ','
elif input_style == '-1,234.5':
decimal_separator = ','
decimal_point = '.'
value = value.replace(decimal_separator,'')
input_style = field.get_value('input_style')
if value.find(decimal_point) >= 0:
value = value.replace(decimal_point, '.')
if value.find('%') >= 0:
value = value.replace('%', '')
value = self._validatePrecision(field, value, decimal_point, input_style)
try:
value = float(value)
if input_style.find('%')>=0:
value = value / 100
except ValueError:
self.raise_error('not_float', field)
return value
FloatValidatorInstance = FloatValidator()
class LinesValidator(StringBaseValidator):
property_names = StringBaseValidator.property_names +\
['unicode', 'max_lines', 'max_linelength', 'max_length']
unicode = fields.CheckBoxField('unicode',
title='Unicode',
description=(
"Checked if the field delivers a unicode string instead of an "
"8-bit string."),
default=0)
max_lines = fields.IntegerField('max_lines',
title='Maximum lines',
description=(
"The maximum amount of lines a user can enter. If set to 0, "
"or is left empty, there is no maximum."),
default="",
required=0)
max_linelength = fields.IntegerField('max_linelength',
title="Maximum length of line",
description=(
"The maximum length of a line. If set to 0 or is left empty, there "
"is no maximum."),
default="",
required=0)
max_length = fields.IntegerField('max_length',
title="Maximum length (in characters)",
description=(
"The maximum total length in characters that the user may enter. "
"If set to 0 or is left empty, there is no maximum."),
default="",
required=0)
message_names = StringBaseValidator.message_names +\
['too_many_lines', 'line_too_long', 'too_long']
too_many_lines = 'You entered too many lines.'
line_too_long = 'A line was too long.'
too_long = 'You entered too many characters.'
def validate(self, field, key, REQUEST):
value = StringBaseValidator.validate(self, field, key, REQUEST)
# Added as a patch for hidden values
if isinstance(value, (list, tuple)):
value = '\n'.join(value)
# we need to add this check again
if value == "" and not field.get_value('required'):
return []
if field.get_value('unicode'):
value = unicode(value, field.get_form_encoding())
# check whether the entire input is too long
max_length = field.get_value('max_length') or 0
if max_length and len(value) > max_length:
self.raise_error('too_long', field)
# split input into separate lines
lines = value.replace('\r\n', '\n').split('\n')
# check whether we have too many lines
max_lines = field.get_value('max_lines') or 0
if max_lines and len(lines) > max_lines:
self.raise_error('too_many_lines', field)
# strip extraneous data from lines and check whether each line is
# short enough
max_linelength = field.get_value('max_linelength') or 0
result = []
whitespace_preserve = field.get_value('whitespace_preserve')
for line in lines:
if not whitespace_preserve:
line = line.strip()
if max_linelength and len(line) > max_linelength:
self.raise_error('line_too_long', field)
result.append(line)
return result
LinesValidatorInstance = LinesValidator()
class TextValidator(LinesValidator):
def validate(self, field, key, REQUEST):
value = LinesValidator.validate(self, field, key, REQUEST)
# we need to add this check again
if value == [] and not field.get_value('required'):
return ""
# join everything into string again with \n and return
return "\n".join(value)
TextValidatorInstance = TextValidator()
class SelectionValidator(StringBaseValidator):
property_names = StringBaseValidator.property_names +\
['unicode']
unicode = fields.CheckBoxField('unicode',
title='Unicode',
description=(
"Checked if the field delivers a unicode string instead of an "
"8-bit string."),
default=0)
message_names = StringBaseValidator.message_names +\
['unknown_selection']
unknown_selection = 'You selected an item that was not in the list.'
def validate(self, field, key, REQUEST):
value = StringBaseValidator.validate(self, field, key, REQUEST)
if value == "" and not field.get_value('required'):
return value
# get the text and the value from the list of items
for item in list(field.get_value('items', cell=getattr(REQUEST,'cell',None))) + [field.get_value('default', cell=getattr(REQUEST,'cell',None))]:
try:
item_text, item_value = item
except (ValueError, TypeError):
item_text = item
item_value = item
# check if the value is equal to the string/unicode version of
# item_value; if that's the case, we can return the *original*
# value in the list (not the submitted value). This way, integers
# will remain integers.
# XXX it is impossible with the UI currently to fill in unicode
# items, but it's possible to do it with the TALES tab
if field.get_value('unicode') and isinstance(item_value, unicode):
str_value = item_value.encode(field.get_form_encoding())
else:
str_value = str(item_value)
if str_value == value:
return item_value
# if we didn't find the value, return error
self.raise_error('unknown_selection', field)
SelectionValidatorInstance = SelectionValidator()
class MultiSelectionValidator(Validator):
property_names = Validator.property_names + ['required', 'unicode']
required = fields.CheckBoxField('required',
title='Required',
description=(
"Checked if the field is required; the user has to fill in some "
"data."),
default=1)
unicode = fields.CheckBoxField('unicode',
title='Unicode',
description=(
"Checked if the field delivers a unicode string instead of an "
"8-bit string."),
default=0)
message_names = Validator.message_names + ['required_not_found',
'unknown_selection']
required_not_found = 'Input is required but no input given.'
unknown_selection = 'You selected an item that was not in the list.'
def validate(self, field, key, REQUEST):
if REQUEST.get('default_%s' % (key, )) is None:
raise KeyError, 'Field %s is not present in request object (marker field default_%s not found).' % (repr(field.id), key)
values = REQUEST.get(key, [])
# NOTE: a hack to deal with single item selections
if not isinstance(values, list):
# put whatever we got in a list
values = [values]
# if we selected nothing and entry is required, give error, otherwise
# give entry list
if len(values) == 0:
if field.get_value('required'):
self.raise_error('required_not_found', field)
else:
return values
# convert everything to unicode if necessary
if field.get_value('unicode'):
values = [unicode(value, field.get_form_encoding())
for value in values]
# create a dictionary of possible values
value_dict = {}
for item in field.get_value('items', cell=getattr(REQUEST,'cell',None)): # Patch by JPS for Listbox
try:
item_text, item_value = item
except ValueError:
item_text = item
item_value = item
value_dict[item_value] = 0
default_value = field.get_value('default', cell=getattr(REQUEST,'cell',None))
if isinstance(default_value, (list, tuple)):
for v in default_value:
value_dict[v] = 0
else:
value_dict[default_value] = 0
# check whether all values are in dictionary
result = []
for value in values:
# FIXME: hack to accept int values as well
try:
int_value = int(value)
except ValueError:
int_value = None
if int_value is not None and value_dict.has_key(int_value):
result.append(int_value)
continue
if value_dict.has_key(value):
result.append(value)
continue
self.raise_error('unknown_selection', field)
# everything checks out
return result
MultiSelectionValidatorInstance = MultiSelectionValidator()
class FileValidator(Validator):
required = fields.CheckBoxField('required',
title='Required',
description=(
"Checked if the field is required; the "
"user has to fill in some data."),
default=0)
property_names = Validator.property_names + ['required']
message_names = Validator.message_names + ['required_not_found']
required_not_found = 'Input is required but no input given.'
def validate(self, field, key, REQUEST):
value = REQUEST.get(key, None)
if field.get_value('required') and value in (None, ''):
self.raise_error('required_not_found', field)
return value
FileValidatorInstance = FileValidator()
class LinkHelper:
"""A helper class to check if links are openable.
"""
status = 0
def __init__(self, link):
self.link = link
def open(self):
try:
urlopen(self.link)
except:
# all errors will definitely result in a failure
pass
else:
# FIXME: would like to check for 404 errors and such?
self.status = 1
class LinkValidator(StringValidator):
property_names = StringValidator.property_names +\
['check_link', 'check_timeout', 'link_type']
check_link = fields.CheckBoxField('check_link',
title='Check Link',
description=(
"Check whether the link is not broken."),
default=0)
check_timeout = fields.FloatField('check_timeout',
title='Check Timeout',
description=(
"Maximum amount of seconds to check link. Required"),
default=7.0,
required=1)
link_type = fields.ListField('link_type',
title='Type of Link',
default="external",
size=1,
items=[('External Link', 'external'),
('Internal Link', 'internal'),
('Relative Link', 'relative')],
description=(
"Define the type of the link. Required."),
required=1)
message_names = StringValidator.message_names + ['not_link']
not_link = 'The specified link is broken.'
def validate(self, field, key, REQUEST):
value = StringValidator.validate(self, field, key, REQUEST)
if value == "" and not field.get_value('required'):
return value
link_type = field.get_value('link_type')
if link_type == 'internal':
value = urljoin(REQUEST['BASE0'], value)
elif link_type == 'relative':
value = urljoin(REQUEST['URL1'], value)
# otherwise must be external
# FIXME: should try regular expression to do some more checking here?
# if we don't need to check the link, we're done now
if not field.get_value('check_link'):
return value
# resolve internal links using Zope's resolve_url
if link_type in ['internal', 'relative']:
try:
REQUEST.resolve_url(value)
except:
self.raise_error('not_link', field)
# check whether we can open the link
link = LinkHelper(value)
thread = Thread(target=link.open)
thread.start()
thread.join(field.get_value('check_timeout'))
del thread
if not link.status:
self.raise_error('not_link', field)
return value
LinkValidatorInstance = LinkValidator()
class DateTimeValidator(Validator):
"""
Added support for key in every call to validate_sub_field
"""
property_names = Validator.property_names + ['required',
'start_datetime',
'end_datetime',
'allow_empty_time']
required = fields.CheckBoxField('required',
title='Required',
description=(
"Checked if the field is required; the user has to enter something "
"in the field."),
default=1)
start_datetime = fields.DateTimeField('start_datetime',
title="Start datetime",
description=(
"The date and time entered must be later than or equal to "
"this date/time. If left empty, no check is performed."),
default=None,
input_style="text",
required=0)
end_datetime = fields.DateTimeField('end_datetime',
title="End datetime",
description=(
"The date and time entered must be earlier than "
"this date/time. If left empty, no check is performed."),
default=None,
input_style="text",
required=0)
allow_empty_time = fields.CheckBoxField('allow_empty_time',
title="Allow empty time",
description=(
"Allow time to be left empty. Time will default to midnight "
"on that date."),
default=0)
message_names = Validator.message_names + ['required_not_found',
'not_datetime',
'datetime_out_of_range']
required_not_found = 'Input is required but no input given.'
not_datetime = 'You did not enter a valid date and time.'
datetime_out_of_range = 'The date and time you entered were out of range.'
def validate(self, field, key, REQUEST):
try:
year = field.validate_sub_field('year', REQUEST, key=key)
month = field.validate_sub_field('month', REQUEST, key=key)
if field.get_value('hide_day'):
day = 1
else:
day = field.validate_sub_field('day', REQUEST, key=key)
if field.get_value('date_only'):
hour = 0
minute = 0
elif field.get_value('allow_empty_time'):
hour = field.validate_sub_field('hour', REQUEST, key=key)
minute = field.validate_sub_field('minute', REQUEST, key=key)
if hour == '' and minute == '':
hour = 0
minute = 0
elif hour == '' or minute == '':
raise ValidationError('not_datetime', field)
else:
hour = field.validate_sub_field('hour', REQUEST, key=key)
minute = field.validate_sub_field('minute', REQUEST, key=key)
except ValidationError:
self.raise_error('not_datetime', field)
# handling of completely empty sub fields
if ((year == '' and month == '') and
(field.get_value('hide_day') or day == '') and
(field.get_value('date_only') or (hour == '' and minute == '')
or (hour == 0 and minute == 0))):
if field.get_value('required'):
self.raise_error('required_not_found', field)
else:
# field is not required, return None for no entry
return None
# handling of partially empty sub fields; invalid datetime
if ((year == '' or month == '') or
(not field.get_value('hide_day') and day == '') or
(not field.get_value('date_only') and
(hour == '' or minute == ''))):
self.raise_error('not_datetime', field)
if field.get_value('ampm_time_style'):
ampm = field.validate_sub_field('ampm', REQUEST, key=key)
if field.get_value('allow_empty_time'):
if ampm == '':
ampm = 'am'
hour = int(hour)
# handling not am or pm
# handling hour > 12
if ((ampm != 'am') and (ampm != 'pm')) or (hour > 12):
self.raise_error('not_datetime', field)
if (ampm == 'pm') and (hour == 0):
self.raise_error('not_datetime', field)
elif ampm == 'pm' and hour < 12:
hour += 12
# handle possible timezone input
timezone = ''
if field.get_value('timezone_style'):
timezone = field.validate_sub_field('timezone', REQUEST, key=key)
try:
# handling of hidden day, which can be first or last day of the month:
if field.get_value('hidden_day_is_last_day'):
if int(month) == 12:
tmp_year = int(year) + 1
tmp_month = 1
else:
tmp_year = int(year)
tmp_month = int(month) + 1
tmp_day = DateTime(tmp_year, tmp_month, 1, hour, minute)
result = tmp_day - 1
else:
result = DateTime(int(year),
int(month),
int(day),
hour,
minute)
year = result.year()
result = DateTime('%s/%s/%s %s:%s %s' % (year,
int(month),
int(day),
hour,
minute, timezone))
# ugh, a host of string based exceptions (not since Zope 2.7)
except ('DateTimeError', 'Invalid Date Components', 'TimeError',
DateError, TimeError) :
self.raise_error('not_datetime', field)
# check if things are within range
start_datetime = field.get_value('start_datetime')
if (start_datetime not in (None, '') and
result < start_datetime):
self.raise_error('datetime_out_of_range', field)
end_datetime = field.get_value('end_datetime')
if (end_datetime not in (None, '') and
result >= end_datetime):
self.raise_error('datetime_out_of_range', field)
return result
DateTimeValidatorInstance = DateTimeValidator()
class SuppressValidator(ValidatorBase):
"""A validator that is actually not used.
"""
def need_validate(self, field, key, REQUEST):
"""Don't ever validate; suppress result in output.
"""
return 0
SuppressValidatorInstance = SuppressValidator()
fullwidth_minus_character_list = (
u'\u2010',
u'\u2011',
u'\u2012',
u'\u2013',
u'\u2014',
u'\u2015',
u'\u2212',
u'\u30fc',
u'\uff0d',
)
def normalizeFullWidthNumber(value):
try:
value = unicodedata.normalize('NFKD', value.decode('UTF8'))
if value[0] in fullwidth_minus_character_list:
value = u'-' + value[1:]
value = value.encode('ASCII', 'ignore')
except UnicodeDecodeError:
pass
return value
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/Widget.py 0000664 0000000 0000000 00000237226 12770231644 0031142 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
import string
from DummyField import fields
from DocumentTemplate.DT_Util import html_quote
from DateTime import DateTime, Timezones
from cgi import escape
import types
from DocumentTemplate.ustr import ustr
from urlparse import urljoin
from lxml import etree
from lxml.etree import Element, SubElement
from lxml.builder import ElementMaker
import re
DRAW_URI = 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'
TEXT_URI = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
FORM_URI = 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'
OFFICE_URI = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
STYLE_URI = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'
NSMAP = {
'draw': DRAW_URI,
'text': TEXT_URI,
'form': FORM_URI,
'office': OFFICE_URI,
'style': STYLE_URI,
}
EForm = ElementMaker(namespace=FORM_URI, nsmap=NSMAP)
RE_OOO_ESCAPE = re.compile(r'([\n\t])?([^\n\t]*)')
class OOoEscaper:
"""Replacement function to use inside re.sub expression.
This function replace \t by
\n by
The parent node is passed to the constructor.
"""
def __init__(self, parent_node):
self.parent_node = parent_node
def __call__(self, match_object):
match_value = match_object.group(1)
if match_value is None:
self.parent_node.text = match_object.group(2)
elif match_value == '\n':
line_break = SubElement(self.parent_node, '{%s}%s' % (TEXT_URI, 'line-break'))
line_break.tail = match_object.group(2)
elif match_value == '\t':
line_break = SubElement(self.parent_node, '{%s}%s' % (TEXT_URI, 'tab'))
line_break.tail = match_object.group(2)
class Widget:
"""A field widget that knows how to display itself as HTML.
"""
property_names = ['title', 'description',
'default', 'css_class', 'alternate_name',
'hidden']
title = fields.StringField('title',
title='Title',
description=(
"The title of this field. This is the title of the field that "
"will appear in the form when it is displayed. Required."),
default="",
required=1)
description = fields.TextAreaField('description',
title='Description',
description=(
"Description of this field. The description property can be "
"used to add a short description of what a field does; such as "
"this one."),
default="",
width="20", height="3",
required=0)
css_class = fields.StringField('css_class',
title='CSS class',
description=(
"The CSS class of the field. This can be used to style your "
"formulator fields using cascading style sheets. Not required."),
default="",
required=0)
alternate_name = fields.StringField('alternate_name',
title='Alternate name',
description=(
"An alternative name for this field. This name will show up in "
"the result dictionary when doing validation, and in the REQUEST "
"if validation goes to request. This can be used to support names "
"that cannot be used as Zope ids."),
default="",
required=0)
hidden = fields.CheckBoxField('hidden',
title="Hidden",
description=(
"This field will be on the form, but as a hidden field. The "
"contents of the hidden field will be the default value. "
"Hidden fields are not visible but will be validated."),
default=0)
# NOTE: for ordering reasons (we want extra at the end),
# this isn't in the base class property_names list, but
# instead will be referred to by the subclasses.
extra = fields.StringField('extra',
title='Extra',
description=(
"A string containing extra HTML code for attributes. This "
"string will be literally included in the rendered field."
"This property can be useful if you want "
"to add an onClick attribute to use with JavaScript, for instance."),
default="",
required=0)
def render(self, field, key, value, REQUEST):
"""Renders this widget as HTML using property values in field.
"""
return "[widget]"
def render_hidden(self, field, key, value, REQUEST, render_prefix=None):
"""Renders this widget as a hidden field.
"""
try:
extra = field.get_value('extra')
except KeyError:
# In case extra is not defined as in DateTimeWidget
extra = ''
result = ''
# We must adapt the rendering to the type of the value
# in order to get the correct type back
if isinstance(value, (tuple, list)):
for v in value:
result += render_element("input",
type="hidden",
name="%s:list" % key,
value=v,
extra=extra)
else:
result = render_element("input",
type="hidden",
name=key,
value=value,
extra=extra)
return result
def render_view(self, field, value, REQUEST=None, render_prefix=None):
"""Renders this widget for public viewing.
"""
# default implementation
if value is None:
return ''
return value
render_pdf = render_view
def render_html(self, *args, **kw):
return self.render(*args, **kw)
def render_htmlgrid(self, field, key, value, REQUEST, render_prefix=None):
"""
render_htmlgrid returns a list of tuple (title, html render)
"""
# XXX Calling _render_helper on the field is not optimized
return ((field.get_value('title'),
field._render_helper(key, value, REQUEST, render_prefix=render_prefix)),)
def render_css(self, field, REQUEST):
"""
Default render css for widget - to be overwritten in field classes.
Should return valid css code as string.
The value returned by this method will be used as inline style for a field.
"""
pass
def get_css_list(self, field, REQUEST):
"""
Return CSS needed by the widget - to be overwritten in field classes.
Should return a list of CSS file names.
These names will be appended to global css_list and included in a rendered page.
"""
return []
def get_javascript_list(self, field, REQUEST):
"""
Return JS needed by the widget - to be overwritten in field classes.
Should return a list of javascript file names.
These names will be appended to global js_list and included in a rendered page.
"""
return []
def render_dict(self, field, value):
"""
This is yet another field rendering. It is designed to allow code to
understand field's value data by providing its type and format when
applicable.
"""
return None
def render_odt(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
"""This render dedicated to render fields inside OOo document
(eg. editable mode)
"""
# XXX By default fallback to render_odt_view
return self.render_odt_view(field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name)
def render_odt_view(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name, escape=False):
"""
Return a field value rendered in odt format as read-only mode.
- as_string return value as string or as xml object
- attr_dict can be used for additional attributes (like style).
- ooo_builder wrapper of ODF zipped archive usefull to insert images
- local_name local-name of the node returned by this render
"""
if attr_dict is None:
attr_dict = {}
if isinstance(value, str):
#required by lxml
value = value.decode('utf-8')
if value is None:
value = ''
text_node = Element('{%s}%s' % (TEXT_URI, local_name), nsmap=NSMAP)
if escape:
RE_OOO_ESCAPE.sub(OOoEscaper(text_node), value)
else:
text_node.text = value
text_node.attrib.update(attr_dict)
if as_string:
return etree.tostring(text_node)
return text_node
def render_odt_variable(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
"""
Return a field value rendered in odt format as read-only mode.
- as_string return value as string or as xml object
- attr_dict can be used for additional attributes (like style).
- ooo_builder wrapper of ODF zipped archive usefull to insert images
- local_name local-name of the node returned by this render
"""
if attr_dict is None:
attr_dict = {}
attr_dict['{%s}value-type' % OFFICE_URI] = 'string'
if isinstance(value, str):
#required by lxml
value = value.decode('utf-8')
if value is None:
value = ''
text_node = Element('{%s}%s' % (TEXT_URI, local_name), nsmap=NSMAP)
text_node.text = value
text_node.attrib.update(attr_dict)
if as_string:
return etree.tostring(text_node)
return text_node
def render_odg(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
"""This render dedicated to render fields inside OOo document
(eg. editable mode)
"""
# XXX By default fallback to render_odg_view
return self.render_odg_view(field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name)
def render_odg_view(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
"""
Default render odg for widget - to be overwritten in field classes.
Return a field node rendered in odg format.
- as_string return value as string or as xml object
- attr_dict can be used for additional attributes (like style).
- ooo_builder wrapper of ODF zipped archive usefull to insert images
- local_name local-name of the node returned by this render
my_string_field value
"""
if attr_dict is None:
attr_dict = {}
if isinstance(value, str):
#required by lxml
value = value.decode('utf-8')
if value is None:
value = ''
draw_frame_tag_name = '{%s}%s' % (DRAW_URI, 'frame')
draw_frame_node = Element(draw_frame_tag_name, nsmap=NSMAP)
draw_frame_attribute_list = attr_dict.get(draw_frame_tag_name)
if draw_frame_attribute_list:
draw_frame_node.attrib.update(draw_frame_attribute_list[0])
draw_tag_name = '{%s}%s' % (DRAW_URI, 'text-box')
draw_node = Element(draw_tag_name, nsmap=NSMAP)
draw_tag_attribute_list = attr_dict.get(draw_tag_name)
if draw_tag_attribute_list:
draw_node.attrib.update(draw_tag_attribute_list[0])
text_p_tag_name = '{%s}%s' % (TEXT_URI, 'p')
text_p_node = Element(text_p_tag_name, nsmap=NSMAP)
text_p_attribute_list = attr_dict.get(text_p_tag_name)
if text_p_attribute_list:
text_p_node.attrib.update(text_p_attribute_list[0])
text_span_tag_name = '{%s}%s' % (TEXT_URI, 'span')
text_span_node = Element(text_span_tag_name, nsmap=NSMAP)
text_span_attribute_list = attr_dict.get(text_span_tag_name)
if text_span_attribute_list:
text_span_node.attrib.update(text_span_attribute_list[0])
text_p_node.append(text_span_node)
draw_node.append(text_p_node)
draw_frame_node.append(draw_node)
RE_OOO_ESCAPE.sub(OOoEscaper(text_span_node), value)
if as_string:
return etree.tostring(draw_frame_node)
return draw_frame_node
class TextWidget(Widget):
"""Text widget
"""
property_names = Widget.property_names +\
['display_width', 'display_maxwidth', 'input_type', 'extra']
default = fields.StringField('default',
title='Default',
description=(
"You can place text here that will be used as the default "
"value of the field, unless the programmer supplies an override "
"when the form is being generated."),
default="",
required=0)
display_width = fields.IntegerField('display_width',
title='Display width',
description=(
"The width in characters. Required."),
default=20,
required=1)
display_maxwidth = fields.IntegerField('display_maxwidth',
title='Maximum input',
description=(
"The maximum input in characters that the widget will allow. "
"Required. If set to 0 or is left empty, there is no maximum. "
"Note that is client side behavior only."),
default="",
required=0)
input_type = fields.StringField('input_type',
title='Input type',
description=(
"The type of the input field like 'color', 'date', 'email' etc."
"Note input types, not supported by old web browsers, will behave "
"as input type text."),
default="text",
required=0)
def render(self, field, key, value, REQUEST, render_prefix=None):
"""Render text input field.
"""
display_maxwidth = field.get_value('display_maxwidth') or 0
input_type = field.get_value('input_type') or 'text'
if display_maxwidth > 0:
return render_element("input",
type=input_type,
name=key,
css_class=field.get_value('css_class'),
value=value,
size=field.get_value('display_width'),
maxlength=display_maxwidth,
extra=field.get_value('extra'))
else:
return render_element("input",
type=input_type,
name=key,
css_class=field.get_value('css_class'),
value=value,
size=field.get_value('display_width'),
extra=field.get_value('extra'))
def render_view(self, field, value, REQUEST=None, render_prefix=None):
"""Render text as non-editable.
This renderer is designed to be type error resistant.
in we get a non string value. It does escape the result
and produces clean xhtml.
Patch the render_view of TextField to enclose the value within html tags if css class defined
"""
if value is None:
return ''
if isinstance(value, types.ListType) or isinstance(value, types.TupleType):
old_value = value
else:
old_value = [str(value)]
value = []
for line in old_value:
value.append(escape(line))
value = ' '.join(value)
extra = field.get_value('extra')
if extra not in (None, ''):
value = "
" % (extra, value)
css_class = field.get_value('css_class')
if css_class not in ('', None):
return "%s" % (css_class, value)
return value
def render_pdf(self, field, value, render_prefix=None):
"""Render the field as PDF."""
return self.format_value(field, value)
def render_dict(self, field, value, render_prefix=None):
"""
This is yet another field rendering. It is designed to allow code to
understand field's value data by providing its type and format when
applicable.
It returns a dict with 3 keys:
type : Text representation of value's type.
format: Type-dependant-formated formating information.
This only describes the field format settings, not the actual
format of provided value.
query : Passthrough of given value.
"""
if not value:
return None
precision = field.get_value('precision')
format = '0'
if precision:
format = '0.'
# in 'format', the only important thing is the number of decimal places,
# so we add some places until we reach the precision defined on the
# field.
for x in xrange(0, precision):
format += '0'
if isinstance(value, unicode):
value = value.encode(field.get_form_encoding())
return {'query': value,
'format': format,
'type': 'float'}
def render_odt_view(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
if attr_dict is None:
attr_dict = {}
text_node = Element('{%s}%s' % (TEXT_URI, local_name), nsmap=NSMAP)
text_node.text = self.format_value(field, value).decode('utf-8')
text_node.attrib.update(attr_dict)
if as_string:
return etree.tostring(text_node)
return text_node
def render_odt_variable(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
"""
Return a field value rendered in odt format as read-only mode.
- as_string return value as string or as xml object
- attr_dict can be used for additional attributes (like style).
- ooo_builder wrapper of ODF zipped archive usefull to insert images
- local_name local-name of the node returned by this render
"""
if attr_dict is None:
attr_dict = {}
attr_dict['{%s}value-type' % OFFICE_URI] = 'float'
if isinstance(value, str):
#required by lxml
value = value.decode('utf-8')
text_node = Element('{%s}%s' % (TEXT_URI, local_name), nsmap=NSMAP)
text_node.text = str(value)
attr_dict['{%s}value' % OFFICE_URI] = str(value)
text_node.attrib.update(attr_dict)
if as_string:
return etree.tostring(text_node)
return text_node
def render_odg(self, field, value, as_string, ooo_builder, REQUEST,
render_prefix, attr_dict, local_name):
if attr_dict is None:
attr_dict = {}
value = field.render_pdf(value)
return Widget.render_odg(self, field, value, as_string, ooo_builder,
REQUEST, render_prefix, attr_dict, local_name)
FloatWidgetInstance = FloatWidget()
class LinkWidget(TextWidget):
def render_view(self, field, value, REQUEST=None, render_prefix=None):
"""Render link.
"""
link_type = field.get_value('link_type', REQUEST=REQUEST)
if REQUEST is None:
# stop relying on get_request bein patched in Globals
REQUEST = field.REQUEST
if link_type == 'internal':
value = urljoin(REQUEST['BASE0'], value)
elif link_type == 'relative':
value = urljoin(REQUEST['URL1'], value)
return '%s' % (value,
field.get_value('title', cell=getattr(REQUEST,'cell',None)))
LinkWidgetInstance = LinkWidget()
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/XMLObjects.py 0000664 0000000 0000000 00000005321 12770231644 0031656 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
from xml.dom.minidom import parse, parseString, Node
from xml.sax.saxutils import unescape
# 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 += unescape(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
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/XMLToForm.py 0000664 0000000 0000000 00000012422 12770231644 0031473 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
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 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
# set delegated_list, mean ProxyField
is_proxy_field = False
if hasattr(entry.first, 'delegated_list'):
delegated_list_element = entry.first.delegated_list
delegated_list = delegated_list_element.getElementNames()
field.delegated_list = delegated_list
is_proxy_field = True
# for persistence machinery
field.values = field.values
field.tales = field.tales
field.message_values = field.message_values
if is_proxy_field:
field.delegated_list = field.delegated_list
# 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)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/__init__.py 0000664 0000000 0000000 00000006707 12770231644 0031454 0 ustar 00root root 0000000 0000000 import Form
import StandardFields, HelperFields
from FieldRegistry import FieldRegistry
import Errors
from Products.PythonScripts.Utility import allow_module
try:
try:
import Products.FileSystemSite
except ImportError:
import Products.CMFCore
except ImportError:
pass
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)
erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/dtml/ 0000775 0000000 0000000 00000000000 12770231644 0030271 5 ustar 00root root 0000000 0000000 FieldHelpTopic.dtml 0000664 0000000 0000000 00000001135 12770231644 0033727 0 ustar 00root root 0000000 0000000 erp5-504ef76e2f8fd67bcbcf6ea5cc131e67451cd57e-product-Formulator/product/Formulator/dtml