[CMF-checkins] SVN: CMF/branches/yuppie-formlib/CMFDefault/formlib/
- added some base classes and utilities for using
zope.formlib in CMF
Yvo Schubbe
y.2006_ at wcm-solutions.de
Thu Nov 2 13:08:20 EST 2006
Log message for revision 71029:
- added some base classes and utilities for using zope.formlib in CMF
Changed:
A CMF/branches/yuppie-formlib/CMFDefault/formlib/
A CMF/branches/yuppie-formlib/CMFDefault/formlib/__init__.py
A CMF/branches/yuppie-formlib/CMFDefault/formlib/editform.pt
A CMF/branches/yuppie-formlib/CMFDefault/formlib/form.py
A CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.py
A CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.txt
A CMF/branches/yuppie-formlib/CMFDefault/formlib/tests.py
A CMF/branches/yuppie-formlib/CMFDefault/formlib/viewform.pt
A CMF/branches/yuppie-formlib/CMFDefault/formlib/vocabulary.py
-=-
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/__init__.py
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/__init__.py 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/__init__.py 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""CMFDefault formlib support.
+
+$Id$
+"""
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/editform.pt
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/editform.pt 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/editform.pt 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,38 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<ul class="errors" tal:condition="view/errors">
+ <li tal:repeat="error view/error_views"><tal:span
+ tal:replace="structure error" /></li>
+</ul>
+
+<h1 tal:content="view/label">EDIT: OBJECT</h1>
+
+<form class="form" action="." method="post" enctype="multipart/form-data"
+ tal:attributes="action request/ACTUAL_URL">
+<div class="widgets">
+ <tal:loop tal:repeat="widget view/widgets"
+><div class="widget"
+ tal:define="split widget/split|nothing; hint widget/hint|nothing"
+ tal:attributes="class python: split and 'widget split' or 'widget'">
+ <label tal:attributes="for widget/name; title python: hint or None"
+ ><span class="required" tal:condition="widget/required">*</span
+ ><tal:span tal:content="widget/label" /></label>
+ <div class="field">
+ <div class="data"><tal:span tal:replace="structure widget" /></div
+ ><tal:case tal:condition="widget/error"
+ tal:replace="structure widget/error" /></div>
+ </div></tal:loop>
+</div>
+<div class="clear"></div>
+
+<div class="buttons">
+ <tal:loop tal:repeat="action view/actions"
+ tal:replace="structure action/render" />
+</div>
+</form>
+</metal:slot>
+
+</body>
+</html>
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/editform.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/form.py
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/form.py 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/form.py 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,166 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Formlib form base classes.
+
+$Id$
+"""
+
+from datetime import datetime
+from sets import Set
+
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from Products.Five.formlib.formbase import PageDisplayForm
+from Products.Five.formlib.formbase import PageForm
+from Products.PluginIndexes.DateIndex.DateIndex import Local
+from zope.formlib import form
+from zope.i18n.interfaces import IUserPreferredLanguages
+from zope.i18n.locales import LoadLocaleError
+from zope.i18n.locales import locales
+from ZTUtils import make_query
+
+from Products.CMFDefault.utils import Message as _
+from Products.CMFDefault.utils import translate
+from Products.CMFDefault.browser.utils import ViewBase
+
+
+# from zope.publisher.http.HTTPRequest
+def getLocale(request):
+ envadapter = IUserPreferredLanguages(request, None)
+ if envadapter is None:
+ return None
+
+ langs = envadapter.getPreferredLanguages()
+ for httplang in langs:
+ parts = (httplang.split('-') + [None, None])[:3]
+ try:
+ return locales.getLocale(*parts)
+ except LoadLocaleError:
+ # Just try the next combination
+ pass
+ else:
+ # No combination gave us an existing locale, so use the default,
+ # which is guaranteed to exist
+ return locales.getLocale(None, None, None)
+
+
+class EditFormBase(PageForm, ViewBase):
+
+ template = ViewPageTemplateFile('editform.pt')
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+ self.request.locale = getLocale(request)
+
+ def _setRedirect(self, provider_id, action_path, keys=''):
+ provider = self._getTool(provider_id)
+ try:
+ target = provider.getActionInfo(action_path, self.context)['url']
+ except ValueError:
+ target = self._getPortalURL()
+
+ kw = {}
+ if self.status:
+ message = translate(self.status, self.context)
+ if isinstance(message, unicode):
+ message = message.encode(self._getBrowserCharset())
+ kw['portal_status_message'] = message
+ for k in keys.split(','):
+ k = k.strip()
+ v = self.request.form.get(k, None)
+ if v:
+ kw[k] = v
+
+ query = kw and ( '?%s' % make_query(kw) ) or ''
+ self.request.RESPONSE.redirect( '%s%s' % (target, query) )
+
+ return ''
+
+ def handle_failure(self, action, data, errors):
+ if self.status:
+ message = translate(self.status, self.context)
+ self.request.other['portal_status_message'] = message
+
+
+class ContentEditFormBase(EditFormBase):
+
+ actions = form.Actions(
+ form.Action(
+ name='change',
+ label=_(u'Change'),
+ validator='handle_validate',
+ success='handle_change_success',
+ failure='handle_failure'),
+ form.Action(
+ name='change_and_view',
+ label=_(u'Change and View'),
+ validator='handle_validate',
+ success='handle_change_and_view_success',
+ failure='handle_failure'))
+
+ description = u''
+
+ def setUpWidgets(self, ignore_request=False):
+ self.adapters = {}
+ self.widgets = form.setUpEditWidgets(
+ self.form_fields, self.prefix, self.context, self.request,
+ adapters=self.adapters, ignore_request=ignore_request
+ )
+
+ @property
+ def label(self):
+ obj_type = translate(self.context.Type(), self.context)
+ return _(u'Edit ${obj_type}', mapping={'obj_type': obj_type})
+
+ def handle_validate(self, action, data):
+ if self.context.wl_isLocked():
+ self.status = _(u'This resource is locked via webDAV.')
+ return ()
+
+ def _handle_success(self, action, data):
+ # normalize set and datetime
+ for k, v in data.iteritems():
+ if isinstance(v, Set):
+ data[k] = set(v)
+ elif isinstance(v, datetime) and v.tzname() is None:
+ data[k] = v.replace(tzinfo=Local)
+ if form.applyChanges(self.context, self.form_fields, data,
+ self.adapters):
+ self.context.reindexObject()
+ obj_type = translate(self.context.Type(), self.context)
+ self.status = _(u'${obj_type} changed.',
+ mapping={'obj_type': obj_type})
+ else:
+ self.status = _(u'Nothing to change.')
+
+ def handle_change_success(self, action, data):
+ self._handle_success(action, data)
+ return self._setRedirect('portal_types', 'object/edit')
+
+ def handle_change_and_view_success(self, action, data):
+ self._handle_success(action, data)
+ return self._setRedirect('portal_types', 'object/view')
+
+
+class DisplayFormBase(PageDisplayForm, ViewBase):
+
+ template = ViewPageTemplateFile('viewform.pt')
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+ self.request.locale = getLocale(request)
+
+ @property
+ def label(self):
+ return self.context.Type()
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/form.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.py
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.py 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.py 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,125 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Formlib schema adapter base classes.
+
+$Id$
+"""
+
+from datetime import datetime
+
+from DateTime.DateTime import DateTime
+from Products.PluginIndexes.DateIndex.DateIndex import Local
+from zope.interface import implements
+from zope.schema import BytesLine
+from zope.schema.interfaces import IBytesLine
+
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.utils import checkEmailAddress
+
+
+class SchemaAdapterBase(object):
+
+ def __init__(self, context):
+ self.context = context
+ ptool = getToolByName(context, 'portal_properties')
+ self.encoding = ptool.getProperty('default_charset', None)
+
+
+_marker = object()
+
+class ProxyFieldProperty(object):
+
+ """Computed attributes based on schema fields.
+
+ Based on zope.schema.fieldproperty.FieldProperty.
+ """
+
+ def __init__(self, field, get_name=None, set_name=None):
+ if get_name is None:
+ get_name = field.__name__
+
+ self._field = field
+ self._get_name = get_name
+ self._set_name = set_name
+
+ def __get__(self, inst, klass):
+ if inst is None:
+ return self
+
+ attribute = getattr(inst.context, self._get_name, _marker)
+ if attribute is _marker:
+ field = self._field.bind(inst)
+ attribute = getattr(field, 'default', _marker)
+ if attribute is _marker:
+ raise AttributeError(self._field.__name__)
+ elif callable(attribute):
+ attribute = attribute()
+
+ if isinstance(attribute, str) and inst.encoding:
+ return attribute.decode(inst.encoding)
+ elif isinstance(attribute, DateTime):
+ return datetime.fromtimestamp(attribute.timeTime(), Local)
+ elif isinstance(attribute, (tuple, list)):
+ if inst.encoding:
+ attribute = [ isinstance(v, str)
+ and v.decode(inst.encoding) or v
+ for v in attribute ]
+ return set(attribute)
+ return attribute
+
+ def __set__(self, inst, value):
+ field = self._field.bind(inst)
+ field.validate(value)
+ if field.readonly:
+ raise ValueError(self._field.__name__, 'field is readonly')
+ if isinstance(value, unicode) and inst.encoding:
+ value = value.encode(inst.encoding)
+ elif isinstance(value, datetime):
+ value = DateTime(*value.astimezone(Local).timetuple()[:6])
+ elif isinstance(value, set):
+ if inst.encoding:
+ value = [ isinstance(v, unicode)
+ and v.encode(inst.encoding) or v
+ for v in value ]
+ value = tuple(value)
+ if self._set_name:
+ getattr(inst.context, self._set_name)(value)
+ elif inst.context.hasProperty(self._get_name):
+ inst.context._updateProperty(self._get_name, value)
+ else:
+ setattr(inst.context, self._get_name, value)
+
+ def __getattr__(self, name):
+ return getattr(self._field, name)
+
+
+class IEmailLine(IBytesLine):
+
+ """A field containing an email address.
+ """
+
+
+class EmailLine(BytesLine):
+
+ """Email schema field.
+ """
+
+ implements(IEmailLine)
+
+ default = ''
+ missing_value = ''
+
+ def _validate(self, value):
+ super(EmailLine, self)._validate(value)
+ checkEmailAddress(value)
+ return True
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.txt
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.txt 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.txt 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,76 @@
+Utilities
+
+
+ Testing ProxyFieldProperty:
+
+ Setting up content and adapter::
+
+ >>> from OFS.PropertyManager import PropertyManager
+ >>> class FooContent(PropertyManager):
+ ... _properties=({'id':'foo_prop', 'type': 'string'},)
+ ... foo_text = ''
+ ... foo_datetime = None
+ ... foo_set = ()
+ ... foo_prop = ''
+
+ >>> from zope.interface import Interface
+ >>> from zope import schema
+ >>> class IFooContentView(Interface):
+ ... foo_text = schema.Text()
+ ... foo_datetime = schema.Datetime()
+ ... foo_set = schema.Set()
+ ... foo_prop = schema.Text()
+
+ >>> from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+ >>> class FooContentAdapter(object):
+ ...
+ ... foo_text = ProxyFieldProperty(IFooContentView['foo_text'])
+ ... foo_datetime = ProxyFieldProperty(IFooContentView['foo_datetime'])
+ ... foo_set = ProxyFieldProperty(IFooContentView['foo_set'])
+ ... foo_prop = ProxyFieldProperty(IFooContentView['foo_prop'])
+ ...
+ ... def __init__(self, context):
+ ... self.context = context
+ ... self.encoding = 'utf-8'
+
+ >>> content = FooContent()
+ >>> adapter = FooContentAdapter(content)
+
+ unicode is mapped to str::
+
+ >>> foo_text = u'foo'
+ >>> adapter.foo_text = foo_text
+ >>> content.foo_text
+ 'foo'
+ >>> adapter.foo_text == foo_text
+ True
+
+ datetime is mapped to DateTime::
+
+ >>> from datetime import datetime
+ >>> from DateTime.DateTime import DateTime
+ >>> from Products.PluginIndexes.DateIndex.DateIndex import Local
+ >>> foo_datetime = datetime(2002, 2, 2, 2, 2, 2, tzinfo=Local)
+ >>> adapter.foo_datetime = foo_datetime
+ >>> isinstance(content.foo_datetime, DateTime)
+ True
+ >>> adapter.foo_datetime == foo_datetime
+ True
+
+ set is mapped to tuple::
+
+ >>> foo_set = set([3, 1, 4])
+ >>> adapter.foo_set = foo_set
+ >>> content.foo_set
+ (1, 3, 4)
+ >>> adapter.foo_set == foo_set
+ True
+
+ PropertyManager properties use _setProperty::
+
+ >>> foo_prop = u'foo'
+ >>> adapter.foo_prop = foo_text
+ >>> content.foo_prop
+ 'foo'
+ >>> adapter.foo_prop == foo_prop
+ True
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/schema.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/tests.py
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/tests.py 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/tests.py 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""CMFDefault formlib tests.
+
+$Id$
+"""
+
+import unittest
+import Testing
+from zope.testing import doctest
+
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('schema.txt',
+ optionflags=doctest.NORMALIZE_WHITESPACE),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/viewform.pt
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/viewform.pt 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/viewform.pt 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,22 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<h1 tal:content="view/label">TYPE</h1>
+
+<div class="form">
+<div class="widgets">
+ <tal:loop tal:repeat="widget view/widgets"
+><div class="widget" tal:define="split widget/split|nothing"
+ tal:attributes="class python: split and 'widget split' or 'widget'">
+ <label tal:content="widget/label">FIELD TITLE</label>
+ <div class="field">
+ <div class="data"><tal:span tal:replace="structure widget" /></div></div>
+ </div></tal:loop>
+</div>
+<div class="clear" />
+</div>
+</metal:slot>
+
+</body>
+</html>
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/viewform.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: CMF/branches/yuppie-formlib/CMFDefault/formlib/vocabulary.py
===================================================================
--- CMF/branches/yuppie-formlib/CMFDefault/formlib/vocabulary.py 2006-11-02 17:38:30 UTC (rev 71028)
+++ CMF/branches/yuppie-formlib/CMFDefault/formlib/vocabulary.py 2006-11-02 18:08:20 UTC (rev 71029)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Formlib schema vocabulary base classes.
+
+$Id$
+"""
+
+from zope.schema.vocabulary import SimpleVocabulary
+
+
+class SimpleVocabulary(SimpleVocabulary):
+
+ def fromTitleItems(cls, items, *interfaces):
+ """Construct a vocabulary from a list of (token, value, title) tuples.
+ """
+ terms = [ cls.createTerm(value, token, title)
+ for (token, value, title) in items ]
+ return cls(terms, *interfaces)
+
+ fromTitleItems = classmethod(fromTitleItems)
Property changes on: CMF/branches/yuppie-formlib/CMFDefault/formlib/vocabulary.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
More information about the CMF-checkins
mailing list