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

Merge Formulator and FormulatorPatch from ERP5Form


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@26872 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent de1888a7
Formulator Credits
Martijn Faassen (faassen@vet.uu.nl) -- Main developer, design and
implementation.
Many thanks go to:
Kit Blake (kitblake at v2.nl) -- UI help and design help.
Yury Don (yura at vpcit.ru) -- contributed EmailField and FloatField,
design and implementation help.
Stephan Richter (srichter at iuveno-net.de) -- contributed LinkField and
FileField. Contributed PatternChecker
module used by PatternField. Other
design and implementation help.
Nicola Larosa (nico at tekNico.net) -- feedback and bugfixes.
Magnus Heino (magus.heino at rivermen.se) -- feedback and bugfixes.
Joel Burton (jburton at scw.org) -- feedback and bugfixes.
Ulrich Eck (ueck at net-labs.de) -- much help and patience with the
TALES tab.
Dirk Datzert (Dirk.Datzert at rasselstein-hoesch.de) -- feedback and bugfixes.
Max Petrich (petrich.max at kis-solution.de) -- feedback and bugfixes.
Matt Behrens (matt.behrens at kohler.com) -- feedback and bugfixes.
Nikolay Kim (fafhrd at datacom.kz) -- code inspiration for
XMLToForm/FormToXML.
Godefroid Chapelle (gotcha at swing.be) -- Bugfixes.
Alan Runyan (runyaga at runyaga.com) -- Fix to email regular expression.
Sascha Welter (welter at network-ag.com) -- Extensive help with email
regular expression.
Clemens Klein-Robbenhaar (robbenhaar at espresto.com) -- Many bugfixes
and feature
additions.
Christian Zagrodnick (cz at gocept.com) -- Unicode awareness fixes and
XML entry form.
Iutin Vyacheslav (iutin at whirix.com) -- am/pm feature for DateTime
fields.
Kapil Thangavelu (k_vertigo at objectrealms.net) -- Enabled ':record' rendering.
Pierre-Julien Grizel (grizel at ingeniweb.com) -- ProductForm.
Sbastien Robin (seb at nexedi.com) -- more consistent ordering in XML
serialization.
Guido Wesdorp (guido at infrae.com) -- Added extra_item attribute on
compound fields.
Yura Petrov (ypetrov at naumen.ru) -- Various FSForm related
improvements.
Vladimir Voznesensky (vovic at smtp.ru) -- Enabling/disabling of fields.
Special thanks also goes to Rik Hoekstra.
Also a thank you to those few valiant souls who suffered through the
bugs of ZFormulator, the previous implementation. Let's hope this
one's better!
"""
This module contains some magic glue to make it seem as if we
can refer to field classes before they've been defined, through the
'fields' class.
This way, they can be used to create properties on fields.
When the field classes have been defined, get_field()
can be used on FieldProperty objects to get an
actual field object.
"""
from FieldRegistry import FieldRegistry
class DummyFieldFactory:
def __getattr__(self, name):
return DummyField(name)
fields = DummyFieldFactory()
class DummyField:
def __init__(self, desired_meta_class):
self.desired_meta_class = desired_meta_class
def __call__(self, id, **kw):
self.id = id
self.kw = kw
return self
def get_value(self, name):
return self.kw.get(name, "")
def get_real_field(self):
"""Get an actual field for this property.
"""
return apply(FieldRegistry.get_field_class(self.desired_meta_class),
(self.id,), self.kw)
"""Exception Classes for Formulator"""
# These classes are placed here so that they can be imported into TTW Python
# scripts. To do so, add the following line to your Py script:
# from Products.Formulator.Errors import ValidationError, FormValidationError
from Products.PythonScripts.Utility import allow_class
class FormValidationError(Exception):
def __init__(self, errors, result):
Exception.__init__(self,"Form Validation Error")
self.errors = errors
self.result = result
allow_class(FormValidationError)
class ValidationError(Exception):
def __init__(self, error_key, field):
Exception.__init__(self, error_key)
self.error_key = error_key
self.field_id = field.id
self.field = field
self.error_text = field.get_error_message(error_key)
allow_class(ValidationError)
class FieldDisabledError(AttributeError):
def __init__(self, error_key, field):
AttributeError.__init__(self, error_key)
self.field_id = field.id
self.field = field
allow_class(FieldDisabledError)
import Globals
from AccessControl import ClassSecurityInfo
try:
import Products.FileSystemSite
except ImportError:
# use CMF product
from Products.CMFCore.CMFCorePermissions import View
from Products.CMFCore.FSObject import FSObject
from Products.CMFCore.DirectoryView import registerFileExtension,\
registerMetaType, expandpath
else:
# use FileSystemSite product
from Products.FileSystemSite.Permissions import View
from Products.FileSystemSite.FSObject import FSObject
from Products.FileSystemSite.DirectoryView import registerFileExtension,\
registerMetaType, expandpath
from Products.Formulator.Form import ZMIForm
from Products.Formulator.XMLToForm import XMLToForm
class FSForm(FSObject, ZMIForm):
"""FSForm."""
meta_type = 'Filesystem Formulator Form'
manage_options = (
(
{'label':'Customize', 'action':'manage_main'},
{'label':'Test', 'action':'formTest'},
)
)
_updateFromFS = FSObject._updateFromFS
security = ClassSecurityInfo()
security.declareObjectProtected(View)
def __init__(self, id, filepath, fullname=None, properties=None):
FSObject.__init__(self, id, filepath, fullname, properties)
def _createZODBClone(self):
# not implemented yet
return None
def _readFile(self, reparse):
file = open(expandpath(self._filepath), 'rb')
try:
data = file.read()
finally:
file.close()
# update the form with the xml data
try:
XMLToForm(data, self)
except:
# bare except here, but I hope this is ok, as the
# exception should be reraised
# (except if the LOG raises another one ...
# should we be more paranoid here?)
import zLOG
zLOG.LOG(
'Formulator.FSForm', zLOG.ERROR,
'error reading form from file ' +
expandpath(self._filepath))
raise
#### The following is mainly taken from Form.py ACCESSORS section ###
## def get_field_ids(self):
## self._updateFromFS()
## return ZMIForm.get_field_ids(self)
## def get_fields_in_group(self, group):
## self._updateFromFS()
## return ZMIForm.get_fields_in_group(self, group)
## def has_field(self, id):
## self._updateFromFS()
## return ZMIForm.has_field(self, id)
## def get_field(self, id):
## self._updateFromFS()
## return ZMIForm.get_field(self, id)
## def get_groups(self):
## self._updateFromFS()
## return ZMIForm.get_groups(self)
## def get_form_encoding(self):
## self._updateFromFS()
## return ZMIForm.get_form_encoding(self)
## def header(self):
## self._updateFromFS()
## return ZMIForm.header(self)
## def get_xml(self):
## self._updateFromFS()
## return ZMIForm.get_xml(self)
## def all_meta_types(self):
## self._updateFromFS()
## return ZMIForm.all_meta_types(self)
## security.declareProtected('View management screens', 'get_group_rows')
## def get_group_rows(self):
## self._updateFromFS()
## return ZMIForm.get_group_rows(self)
Globals.InitializeClass(FSForm)
registerFileExtension('form', FSForm)
registerMetaType('FSForm', FSForm)
This diff is collapsed.
from Globals import DTMLFile
from HelpSys import HelpTopic
class FieldHelpTopic(HelpTopic.HelpTopic):
"""A special help topic for fields.
"""
meta_type = 'Help Topic'
def __init__(self, id, title, field_class,
permissions=None, categories=None):
self.id = id
self.title = title
self.field_class = field_class
if permissions is not None:
self.permissions = permissions
if categories is not None:
self.categories = categories
index_html = DTMLFile('dtml/FieldHelpTopic', globals())
def SearchableText(self):
"""Full text of the Help Topic, for indexing purposes."""
return "" # return self.index_html()
def get_groups(self):
"""Get form groups of this field.
"""
return self.field_class.form.get_groups()
def get_fields_in_group(self, group):
"""Get the fields in the group.
"""
return self.field_class.form.get_fields_in_group(group)
import os
import OFS
from Globals import ImageFile
from FieldHelpTopic import FieldHelpTopic
class FieldRegistry:
"""A registry of fields, maintaining a dictionary with
the meta_type of the field classes as key and the field class as
values. Updates the Form as necessary as well.
"""
def __init__(self):
"""Initializer of FieldRegistry.
"""
self._fields = {}
def get_field_class(self, fieldname):
"""Get a certain field class by its name (meta_type)
fieldname -- the name of the field to get from the registry
"""
return self._fields[fieldname]
def get_field_classes(self):
"""Return all fields.
"""
return self._fields
def registerField(self, field_class, icon=None):
"""Register field with Formulator.
field_class -- the class of the field to be registered
icon -- optional filename of the icon
"""
# put it in registry dictionary
self._fields[field_class.meta_type] = field_class
# set up dummy fields in field's form
initializeFieldForm(field_class)
# set up the icon if a filename is supplied
if icon:
setupIcon(field_class, icon, 'Formulator')
def registerFieldHelp(self, *args, **kw):
"""XXX: this is a quick fix to avoid bloating the ZODB.
Proper fix should only add FieldHelp when it's missing.
"""
pass
def initializeFields(self):
"""Initialize all field classes in field forms to use actual field
objects so we can finally eat our own dogfood.
"""
# for each field, realize fields in form
# this is finally possible as all field classes are now
# fully defined.
for field_class in self._fields.values():
field_class.form._realize_fields()
field_class.override_form._realize_fields()
field_class.tales_form._realize_fields()
# initialize registry as a singleton
FieldRegistry = FieldRegistry()
def initializeFieldForm(field_class):
"""Initialize the properties (fields and values) on a particular
field class. Also add the tales and override methods.
"""
from Form import BasicForm
from DummyField import fields
form = BasicForm()
override_form = BasicForm()
tales_form = BasicForm()
for field in getPropertyFields(field_class.widget):
form.add_field(field, "widget")
tales_field = fields.TALESField(field.id,
title=field.get_value('title'),
description="",
default="",
display_width=40,
required=0)
tales_form.add_field(tales_field, "widget")
method_field = fields.MethodField(field.id,
title=field.get_value("title"),
description="",
default="",
required=0)
override_form.add_field(method_field, "widget")
for field in getPropertyFields(field_class.validator):
form.add_field(field, "validator")
tales_field = fields.TALESField(field.id,
title=field.get_value('title'),
description="",
default="",
display_with=40,
required=0)
tales_form.add_field(tales_field, "validator")
method_field = fields.MethodField(field.id,
title=field.get_value("title"),
description="",
default="",
required=0)
override_form.add_field(method_field, "validator")
field_class.form = form
field_class.override_form = override_form
field_class.tales_form = tales_form
def getPropertyFields(obj):
"""Get property fields from a particular widget/validator.
"""
fields = []
for property_name in obj.property_names:
fields.append(getattr(obj, property_name))
return fields
def setupIcon(klass, icon, repository):
"""Load icon into Zope image object and put it in Zope's
repository for use by the ZMI, for a particular class.
klass -- the class of the field we're adding
icon -- the icon
"""
# set up misc_ respository if not existing yet
if not hasattr(OFS.misc_.misc_, repository):
setattr(OFS.misc_.misc_,
repository,
OFS.misc_.Misc_(repository, {}))
# get name of icon in the misc_ directory
icon_name = os.path.split(icon)[1]
# set up image object from icon file
icon_image = ImageFile(icon, globals())
icon_image.__roles__ = None
# put icon image object in misc_/Formulator/
getattr(OFS.misc_.misc_, repository)[icon_name] = icon_image
# set icon attribute in field_class to point to this image obj
setattr(klass, 'icon', 'misc_/%s/%s' %
(repository, icon_name))
This diff is collapsed.
from StringIO import StringIO
from cgi import escape
import types
#def write(s):
# if type(s) == type(u''):
# print "Unicode:", repr(s)
def formToXML(form, prologue=1):
"""Takes a formulator form and serializes it to an XML representation.
"""
f = StringIO()
write = f.write
if prologue:
write('<?xml version="1.0"?>\n\n')
write('<form>\n')
# export form settings
for field in form.settings_form.get_fields(include_disabled=1):
id = field.id
value = getattr(form, id)
if id == 'title':
value = escape(value)
if id == 'unicode_mode':
if value:
value = 'true'
else:
value = 'false'
write(' <%s>%s</%s>\n' % (id, value, id))
# export form groups
write(' <groups>\n')
for group in form.get_groups(include_empty=1):
write(' <group>\n')
write(' <title>%s</title>\n' % escape(group))
write(' <fields>\n\n')
for field in form.get_fields_in_group(group, include_disabled=1):
write(' <field><id>%s</id> <type>%s</type>\n' % (field.id, field.meta_type))
write(' <values>\n')
items = field.values.items()
items.sort()
for key, value in items:
if value is None:
continue
if value==True: # XXX Patch
value = 1 # XXX Patch
if value==False: # XXX Patch
value = 0 # XXX Patch
if callable(value): # XXX Patch
write(' <%s type="method">%s</%s>\n' % # XXX Patch
(key, escape(str(value.method_name)), key)) # XXX Patch
elif type(value) == type(1.1):
write(' <%s type="float">%s</%s>\n' % (key, escape(str(value)), key))
elif type(value) == type(1):
write(' <%s type="int">%s</%s>\n' % (key, escape(str(value)), key))
elif type(value) == type([]):
write(' <%s type="list">%s</%s>\n' % (key, escape(str(value)), key))
else:
if type(value) not in (types.StringType, types.UnicodeType):
value = str(value)
write(' <%s>%s</%s>\n' % (key, escape(value), key))
write(' </values>\n')
write(' <tales>\n')
items = field.tales.items()
items.sort()
for key, value in items:
if value:
write(' <%s>%s</%s>\n' % (key, escape(str(value._text)), key))
write(' </tales>\n')
write(' <messages>\n')
for message_key in field.get_error_names():
write(' <message name="%s">%s</message>\n' %
(escape(message_key), escape(field.get_error_message(message_key))))
write(' </messages>\n')
write(' </field>\n')
write(' </fields>\n')
write(' </group>\n')
write(' </groups>\n')
write('</form>')
if form.unicode_mode:
return f.getvalue().encode('UTF-8')
else:
return unicode(f.getvalue(), form.stored_encoding).encode('UTF-8')
This diff is collapsed.
# include some helper fields which are in their own files
from MethodField import MethodField
from ListTextAreaField import ListTextAreaField
from TALESField import TALESField
Installing Formulator
Requirements
Formulator should work with Zope versions 2.6 or higher:
http://www.zope.org/Products/Zope
For reading in forms as XML you need to have minidom installed;
this should come with a normal python 2.1 distribution. This is
not required to use Formulator, however.
Upgrading
to 1.6.0 from earlier versions
There should be no problems.
to 1.4.2 from earlier versions
There should be no problems.
to 1.4.1 from earlier versions
There should be no problems.
to 1.4.0 from earlier versions
There should be no problems.
to 1.3.1 from earlier versions
There should be no problems (see note for 0.9.2 though in the
unusual case you're upgrading from that..this is the last time
I'll mention it :).
to 1.3.0 from earlier versions
There should be no problems, but see the note if you're
upgrading from 0.9.2 or below (but I'd be surprised if you
were!).
to 1.2.0 from earlier versions
There should be no problems, but see the note if you're upgrading
from version 0.9.2 or below.
to 1.1.0 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below however, please see the upgrading note for 0.9.3. Do note
that the Override tab is scheduled to be phased out eventually in
favor of the TALES tab. This will take a while yet, though.
to 1.0.9 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below however, please see the upgrading note for 0.9.3. Do note
that the Override tab is scheduled to be phased out eventually in
favor of the TALES tab. This will take a while yet, though.
to 1.0.1 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below, please see the upgrading note for 0.9.3.
to 1.0 from earlier versions
There should be no problems. If you're upgrading from 0.9.2 or
below, please see the upgrading note for 0.9.3.
to 0.9.5 from earlier versions
There should be no problems in upgrading from 0.9.4 or 0.9.3.
If you're upgrading from 0.9.2 or below, see the upgrading note
for 0.9.3.
to 0.9.4 from earlier versions
There should be no problems in upgrading from 0.9.3.
If you're upgrading from 0.9.2 or below, see the upgrading
note for 0.9.3.
to 0.9.3 from earlier versions
'items_method' in ListField is gone; you'll have to adjust make
your forms use 'items' in the override tab now instead. Sorry
about that, it *was* marked experimental. :)
There should be no other problems in upgrading.
to 0.9.2 from earlier versions
There should be no significant upgrade problems; your forms
should still work. RangedIntegerFields should show up as
IntegerFields, which subsume their functionality.
to 0.9.1 from earlier versions
There should be no significant upgrade problems; your forms
should still work.
Quickstart
Formulator follows the normal Zope filesystem product installation
procedure; just unpack the tarball to your products directory and
restart Zope.
Now the same at a more leisurely pace.
Unpacking
Formulator comes as a 'Formulator-x.x.tgz' file, where 'x.x'
stands for the Formulator version number. On Unix, you can use::
tar xvzf Formulator-x.x.tgz
to unpack the file. On Windows you can use your favorite archiving
software, such as WinZip.
This will create a Formulator directory.
Installing the Product
Move this directory to your Zope's Products directory. Normally
this is 'yourzope/lib/python/Products'.
Now restart your Zope.
Verifying Installation
If all went well, Formulator should now be visible in Zope in the
Products screen ('/Control_Panel/Products'). In a Zope folder, you
should now see a 'Formulator Form' in your 'Add' list. You should
be able to add a form to a folder now.
Copyright (c) 2001, 2002, 2003 Infrae. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Neither the name of Infrae nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.