[Checkins] SVN: z3c.sessionwidget/trunk/src/ This is a widget that
interfaces with a session to manage the input
Stephan Richter
srichter at cosmos.phy.tufts.edu
Tue Sep 19 13:01:35 EDT 2006
Log message for revision 70231:
This is a widget that interfaces with a session to manage the input
data. This is ideal for any objects that require multiple steps to
assemble themselves, such as files, complex lists, etc. I will provide
an image widget based on this code in a moment.
--This line, and those below, will
be ignored--
A .
Changed:
A z3c.sessionwidget/trunk/src/README.txt
A z3c.sessionwidget/trunk/src/SETUP.cfg
A z3c.sessionwidget/trunk/src/__init__.py
A z3c.sessionwidget/trunk/src/configure.zcml
A z3c.sessionwidget/trunk/src/interfaces.py
A z3c.sessionwidget/trunk/src/namespace.py
A z3c.sessionwidget/trunk/src/tests.py
A z3c.sessionwidget/trunk/src/widget.py
A z3c.sessionwidget/trunk/src/z3c.sessionwidget-configure.zcml
-=-
Added: z3c.sessionwidget/trunk/src/README.txt
===================================================================
--- z3c.sessionwidget/trunk/src/README.txt 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/README.txt 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,158 @@
+========================
+The Session Input Widget
+========================
+
+Sometimes fields do not describe just simple data types, but complex data
+structures. In those scenarios, widgets of the field do not represent a simple
+text input or a small set of input elements, but more complex
+combinations. Until now, those scenarios were solved using either sub-forms or
+a fairly complicated widget for the object field.
+
+While a sub-form is an acceptable solution, it often writes the data to the
+object before the form's save button is pressed. This is, for example, the
+case when uploading images and displaying them right away as visual
+feedback. The object widget has the same problem when constructing sequences
+qith complex sub-items. In general it is up to the developer to decide when to
+present a new form or use the widget, but at least s/he should have the
+choice.
+
+To solve this problem, this package provides a widget that is only responsible
+for relaying the data of interest to a session. A sub-form or any other
+componentcan then use this session data and provide new data, if desired. The
+session data will also be publically available via a URL, so that the objects
+can also be displayed, such as images. This has the advantage that the data is
+*not* stored on the content until the overall form is submitted.
+
+The session has two data fields:
+
+1. data --> The data object of interest as it will be stored in the content
+ object.
+
+2. changed --> A boolean saying whether the data has changed.
+
+Usually, sub-forms work with those two data fields and update the new data. To
+demonstrate this we first need to create a schema for the object and the
+content containing the object,
+
+ >>> import zope.interface
+ >>> import zope.schema
+
+ >>> class IObject(zope.interface.Interface):
+ ... '''An object in the content.'''
+
+ >>> class IContent(zope.interface.Interface):
+ ... obj = zope.schema.Object(
+ ... title=u'Object',
+ ... schema=IObject)
+
+implement those interfaces,
+
+ >>> class Object(object):
+ ... zope.interface.implements(IObject)
+ ... def __init__(self, id):
+ ... self.id = id
+ ... def __repr__(self):
+ ... return '<%s %s>' %(self.__class__.__name__, self.id)
+
+ >>> class Content(object):
+ ... zope.interface.implements(IContent)
+ ... obj = zope.schema.fieldproperty.FieldProperty(IContent['obj'])
+
+and finally instantiate the content:
+
+ >>> content = Content()
+
+After creating a request,
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+
+we can now initialize the widget:
+
+ >>> from z3c.sessionwidget import widget
+ >>> objWidget = widget.SessionInputWidget(IContent['obj'], request)
+
+The widget can directly access the session:
+
+ >>> objWidget.session
+ <zope.app.session.session.SessionPkgData object at ...>
+ >>> objWidget.session.get('data', 'nothing')
+ 'nothing'
+ >>> objWidget.session.get('changed', 'nothing')
+ 'nothing'
+
+Initially, the widget has no data:
+
+ >>> objWidget.hasInput()
+ False
+
+Once we set the rendered value, we have soem input:
+
+ >>> objWidget.setRenderedValue(Object('1'))
+ >>> objWidget.hasInput()
+ True
+
+Of course, you can now retrieve that value:
+
+ >>> objWidget.getInputValue()
+ <Object 1>
+
+Let's now say that some arbitrary form creates a new object.
+
+ >>> objWidget.session['data'] = Object('2')
+
+This form is also responsible for setting the changed flag:
+
+ >>> objWidget.session['changed'] = True
+
+Now the input value is different:
+
+ >>> objWidget.getInputValue()
+ <Object 2>
+
+Also, setting the rendered value is now ineffective:
+
+ >>> objWidget.setRenderedValue(Object('1'))
+ >>> objWidget.getInputValue()
+ <Object 2>
+
+When applying the changes, the method only looks at the changed flag to decide
+whether the data changed:
+
+ >>> content.obj
+
+ >>> objWidget.applyChanges(content)
+ True
+
+ >>> content.obj
+ <Object 2>
+
+Note that calling thos method also resets the session:
+
+ >>> objWidget.session['data']
+ >>> objWidget.session['changed']
+
+Note that hidden always renders empty:
+
+ >>> objWidget.hidden()
+ ''
+
+Let's now set a new object value again, but changing the changed flag. No
+changes will be applied:
+
+ >>> objWidget.session['data'] = Object('3')
+ >>> objWidget.session['changed'] = False
+
+ >>> content.obj
+ <Object 2>
+ >>> objWidget.applyChanges(content)
+ False
+ >>> content.obj
+ <Object 2>
+
+
+TO DO:
+------
+
+The id identifying the session data is not unique enough; we need some better
+mechanism for this later.
Property changes on: z3c.sessionwidget/trunk/src/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.sessionwidget/trunk/src/SETUP.cfg
===================================================================
--- z3c.sessionwidget/trunk/src/SETUP.cfg 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/SETUP.cfg 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+ z3c.sessionwidget-*.zcml
+</data-files>
Added: z3c.sessionwidget/trunk/src/__init__.py
===================================================================
--- z3c.sessionwidget/trunk/src/__init__.py 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/__init__.py 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,3 @@
+# Make a package.
+
+from z3c.sessionwidget.widget import SessionInputWidget
Property changes on: z3c.sessionwidget/trunk/src/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.sessionwidget/trunk/src/configure.zcml
===================================================================
--- z3c.sessionwidget/trunk/src/configure.zcml 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/configure.zcml 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,19 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope">
+
+ <view
+ name="session" type="*"
+ provides="zope.traversing.interfaces.ITraversable" for="*"
+ factory=".namespace.sessionNamespace"
+ />
+
+ <view
+ for="zope.app.session.session.SessionPkgData"
+ type="zope.publisher.interfaces.browser.IBrowserRequest"
+ provides="zope.publisher.interfaces.browser.IBrowserPublisher"
+ factory="zope.app.container.traversal.ItemTraverser"
+ permission="zope.Public"
+ allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
+ />
+
+</configure>
Property changes on: z3c.sessionwidget/trunk/src/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.sessionwidget/trunk/src/interfaces.py
===================================================================
--- z3c.sessionwidget/trunk/src/interfaces.py 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/interfaces.py 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Session Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+from zope.app.form import interfaces
+
+class ISessionWidget(interfaces.IWidget):
+ """A widget that communicates its data via a session."""
+
+ session = zope.interface.Attribute(
+ '''The session object that holds all information necessary for the
+ session widget to implement the IWidget API. Two fields are declared:
+
+ ``data`` -- Holds the data object that is to be stored. It can be any
+ arbitrary Python object.
+
+ ``changed`` -- a flag (boolean) describing whether the object has been
+ changed since the widget was originally initialized.
+ ''')
Property changes on: z3c.sessionwidget/trunk/src/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.sessionwidget/trunk/src/namespace.py
===================================================================
--- z3c.sessionwidget/trunk/src/namespace.py 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/namespace.py 2006-09-19 17:01:34 UTC (rev 70231)
@@ -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.
+#
+##############################################################################
+"""Session Namespace
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.app.session.interfaces import ISession
+from z3c.sessionwidget import widget
+
+class sessionNamespace(object):
+ """Used to traverse to a session."""
+
+ def __init__(self, ob, request=None):
+ self.context = ob
+ self.request = request
+
+ def traverse(self, name, ignore):
+ return ISession(self.request)[name]
Property changes on: z3c.sessionwidget/trunk/src/namespace.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.sessionwidget/trunk/src/tests.py
===================================================================
--- z3c.sessionwidget/trunk/src/tests.py 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/tests.py 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,55 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Image Widgets Test
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import doctest
+import unittest
+import zope.component
+from zope.app.session import interfaces
+from zope.app.session import session
+from zope.app.testing import placelesssetup
+from zope.app.session.http import CookieClientIdManager
+from zope.publisher.interfaces import IRequest
+from zope.testing.doctestunit import DocFileSuite
+
+
+def setUp(test):
+ placelesssetup.setUp()
+ zope.component.provideAdapter(
+ session.ClientId, (IRequest,), interfaces.IClientId)
+ zope.component.provideAdapter(
+ session.Session, (IRequest,), interfaces.ISession)
+ zope.component.provideUtility(
+ CookieClientIdManager(), interfaces.IClientIdManager)
+ zope.component.provideUtility(
+ session.PersistentSessionDataContainer(),
+ interfaces.ISessionDataContainer)
+
+def tearDown(test):
+ placelesssetup.tearDown()
+
+
+def test_suite():
+ return unittest.TestSuite((
+ DocFileSuite('README.txt',
+ setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.sessionwidget/trunk/src/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.sessionwidget/trunk/src/widget.py
===================================================================
--- z3c.sessionwidget/trunk/src/widget.py 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/widget.py 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Session Widget Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.location
+from zope.app import form
+from zope.app.form.browser import widget
+from zope.app.session.interfaces import ISession
+from zope.app.session.session import SessionPkgData
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.sessionwidget import interfaces
+
+SESSION_KEY = 'z3c.sessionwidget.SessionInputWidget'
+
+class SessionInputWidget(widget.BrowserWidget, form.InputWidget):
+ """Session Input Widget"""
+ zope.interface.implements(interfaces.ISessionWidget)
+
+ @property
+ def session(self):
+ """Get the session containing all data relevant for this widget."""
+ return ISession(self.request)[SESSION_KEY].setdefault(
+ self.name, SessionPkgData())
+
+ def setRenderedValue(self, value):
+ """See zope.app.form.interfaces.IWidget"""
+ if not self.session.get('changed', False):
+ self.session['data'] = value
+
+ def hidden(self):
+ """See zope.app.form.browser.interfaces.IBrowserWidget"""
+ # Since the data is stored in a session, no data has to be passed here.
+ return ''
+
+ def hasInput(self):
+ """See zope.app.form.interfaces.IInputWidget"""
+ missing = self.context.missing_value
+ return self.session.get('data', missing) is not missing
+
+ def getInputValue(self):
+ """See zope.app.form.interfaces.IInputWidget"""
+ self._error = None
+ field = self.context
+
+ # form input is required, otherwise raise an error
+ if not self.hasInput():
+ raise MissingInputError(self.name, self.label, None)
+
+ # Get the value from the session
+ value = self.session['data']
+
+ # allow missing values only for non-required fields
+ if value == field.missing_value and not field.required:
+ return value
+
+ # value must be valid per the field constraints
+ try:
+ field.validate(value)
+ except ValidationError, v:
+ self._error = WidgetInputError(
+ self.context.__name__, self.label, v)
+ raise self._error
+
+ return value
+
+ def applyChanges(self, content):
+ """See zope.app.form.interfaces.IInputWidget"""
+ field = self.context
+ value = self.getInputValue()
+ # Look into the session to see whether the data changed
+ if self.session['changed']:
+ zope.location.locate(value, content, field.getName())
+ field.set(content, value)
+ changed = True
+ else:
+ changed = False
+ # Cleanup the session, so that everything is reset for next time
+ self.session['changed'] = None
+ self.session['data'] = None
+ # Now return the result
+ return changed
+
+ def __call__(self):
+ """See zope.app.form.browser.interfaces.IBrowserWidget"""
+ form = zope.component.getMultiAdapter(
+ (self, self.request), zope.interface.Interface,
+ 'SessionInputWidget.form')
+ form.update()
+ return form.render()
Property changes on: z3c.sessionwidget/trunk/src/widget.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.sessionwidget/trunk/src/z3c.sessionwidget-configure.zcml
===================================================================
--- z3c.sessionwidget/trunk/src/z3c.sessionwidget-configure.zcml 2006-09-19 16:59:18 UTC (rev 70230)
+++ z3c.sessionwidget/trunk/src/z3c.sessionwidget-configure.zcml 2006-09-19 17:01:34 UTC (rev 70231)
@@ -0,0 +1 @@
+<include package="z3c.sessionwidget" />
Property changes on: z3c.sessionwidget/trunk/src/z3c.sessionwidget-configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list