[Checkins] SVN: z3c.widget/trunk/src/z3c/widget/sequence/ This
package provides a Sequence Widget just as
Adam Groszer
adamg at fw.hu
Sat Jan 13 05:24:49 EST 2007
Log message for revision 71996:
This package provides a Sequence Widget just as
zope.app.form.browser.sequencewidget.
The main difference is that it places the subobject's fields horizontally in
a table. That means a kind of voucher-item forms are piece of cake to do.
There is also a widget (SequenceTableJSWidget) which does the add/remove item
in the browser with javascript.
Changed:
A z3c.widget/trunk/src/z3c/widget/sequence/
A z3c.widget/trunk/src/z3c/widget/sequence/README.txt
A z3c.widget/trunk/src/z3c/widget/sequence/SETUP.cfg
A z3c.widget/trunk/src/z3c/widget/sequence/__init__.py
A z3c.widget/trunk/src/z3c/widget/sequence/configure.zcml
A z3c.widget/trunk/src/z3c/widget/sequence/sequencedisplaytablewidget.pt
A z3c.widget/trunk/src/z3c/widget/sequence/sequencetable.js
A z3c.widget/trunk/src/z3c/widget/sequence/sequencetablejswidget.pt
A z3c.widget/trunk/src/z3c/widget/sequence/sequencetablewidget.pt
A z3c.widget/trunk/src/z3c/widget/sequence/tests.py
A z3c.widget/trunk/src/z3c/widget/sequence/widget.py
A z3c.widget/trunk/src/z3c/widget/sequence/z3c.widget.sequence-configure.zcml
-=-
Added: z3c.widget/trunk/src/z3c/widget/sequence/README.txt
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/README.txt 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/README.txt 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,27 @@
+====================
+SequenceTable Widget
+====================
+
+This package provides a Sequence Widget just as
+zope.app.form.browser.sequencewidget.
+The main difference is that it places the subobject's fields horizontally in
+a table. That means a kind of voucher-item forms are piece of cake to do.
+
+There is also a widget (SequenceTableJSWidget) which does the add/remove item
+in the browser with javascript.
+The trick is to embed an invisible template of an empty row in the HTML,
+add that each time a new row is required.
+
+Drawbacks of JS:
+ * Validation is done ONLY when the complete form is submitted to the server.
+ * Submitting the form and using the Back button of the browser does not work.
+
+WARNING!
+~~~~~~~~
+The subobject MUST have subwidgets. That is usually the case if the subobject
+is based on zope.schema.Object.
+
+TODO
+~~~~
+Tests.
+Some are there, some are copied from z.a.form.browser and need fix.
\ No newline at end of file
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/README.txt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/SETUP.cfg
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/SETUP.cfg 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/SETUP.cfg 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+ z3c.widget.sequence-*.zcml
+</data-files>
\ No newline at end of file
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/SETUP.cfg
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/__init__.py
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/__init__.py 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/__init__.py 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1 @@
+# Make it a Python package
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/configure.zcml
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/configure.zcml 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/configure.zcml 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain="zope">
+
+ <resourceLibrary name="sequencetable">
+ <directory source="." include="sequencetable.js"/>
+ </resourceLibrary>
+
+</configure>
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/configure.zcml
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/sequencedisplaytablewidget.pt
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/sequencedisplaytablewidget.pt 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/sequencedisplaytablewidget.pt 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,26 @@
+<tal:defs define="message view/haveMessage">
+ <span tal:content="message"
+ tal:condition="message">
+ message</span>
+ <table class="sequencewidgettable"
+ tal:condition="not:message">
+ <thead tal:define="mainwidget view/mainWidget">
+ <th tal:repeat="widget mainwidget/subwidgets"
+ tal:content="widget/label">
+ Label
+ </th>
+ </thead>
+ <tbody>
+ <metal:block tal:repeat="mainwidget view/widgets">
+ <tr tal:define="oddrow repeat/mainwidget/odd"
+ tal:attributes="class python:oddrow and 'even' or 'odd'">
+ <td tal:repeat="widget mainwidget/subwidgets">
+ <div class="field" tal:content="structure widget">
+ <input type="text" style="width:100%"/>
+ </div>
+ </td>
+ </tr>
+ </metal:block>
+ </tbody>
+ </table>
+</tal:defs>
\ No newline at end of file
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/sequencedisplaytablewidget.pt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/sequencetable.js
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/sequencetable.js 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/sequencetable.js 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,105 @@
+function sequencetable_moveChildren(srcNode, destNode){
+ var count = 0;
+ while(srcNode.hasChildNodes()){
+ destNode.appendChild(srcNode.firstChild);
+ count++;
+ }
+ return count;
+}
+
+function sequencetable_copyChildren(srcNode, destNode){
+ var clonedNode = srcNode.cloneNode(true);
+ return sequencetable_moveChildren(clonedNode, destNode);
+}
+
+function sequencetable_addRow(sPrefix) {
+ var sTableID = sPrefix+".tbody";
+ var sRowID = sPrefix+"_row#_default_rowid_";
+ var tbodyElem = document.getElementById(sTableID);
+ var oRow = document.getElementById(sRowID);
+
+ var trElem, tdElem;
+ trElem = tbodyElem.insertRow(tbodyElem.rows.length);
+ var sHtml = new String(oRow.innerHTML);
+
+ sequencetable_copyChildren(oRow, trElem);
+
+ var oCount = document.getElementById(sPrefix+".count");
+ var nMaxLen = Number(document.getElementById(sPrefix+".max_length").value);
+ var sNewId = oCount.value;
+ oCount.value = String(Number(oCount.value)+1);
+
+ if (nMaxLen > 0){
+ if (Number(oCount.value) >= nMaxLen) {
+ var oAddButton = document.getElementById(sPrefix+".addbutton");
+ oAddButton.disabled = true;
+ }
+ }
+
+ var sNewtrId = oRow.id.replace(/_default_rowid_/g, sNewId);
+
+ trElem.id = oRow.id;
+
+ sequencetable_reName(trElem, "_default_rowid_", sNewId);
+
+ return false;
+}
+
+function sequencetable_replChild(startNode, oRegEx, sRepl){
+ try {
+ startNode.id = startNode.id.replace(oRegEx, sRepl);
+ } catch(E) {}
+ try {
+ startNode.name = startNode.name.replace(oRegEx, sRepl);
+ } catch(E) {}
+ //replacing in innerHTML breaks havoc
+ //try {
+ // var si = startNode.innerHTML;
+ // var aMatch = si.match(oRegEx);
+ // startNode.innerHTML = startNode.innerHTML.replace(oRegEx, sRepl);
+ //} catch(E) {}
+
+ for (var i = 0; i < startNode.childNodes.length; i++) {
+ sequencetable_replChild(startNode.childNodes[i], oRegEx, sRepl);
+ }
+}
+
+function sequencetable_reName(node, sOldID, sNewID){
+ var regexp = eval("/"+sOldID+"/g");
+ sequencetable_replChild(node, regexp, sNewID);
+}
+
+function sequencetable_delRow(oCell, sPrefix) {
+ var oCount = document.getElementById(sPrefix+".count");
+ var nCount = Number(oCount.value);
+ var nMinLen = Number(document.getElementById(sPrefix+".min_length").value);
+
+ if (nCount <= nMinLen) {
+ //alert("Cannot remove any more items!");
+ return false;
+ }
+
+ var node = oCell;
+ var regexp = eval("/"+sPrefix+"_row#(\\d+)/");
+ while (node.id.search(regexp) < 0) {
+ node = node.parentNode;
+ }
+ var oRow = node;
+ var sTableID = sPrefix+".tbody";
+ var tbodyElem = document.getElementById(sTableID);
+ var aMatch = node.id.match(/_row#(\d+)/);
+ var nToDel = Number(aMatch[1]);
+ oRow.parentNode.removeChild(oRow);
+
+ for (var i = nToDel+1; i < nCount; i++) {
+ sequencetable_reName(tbodyElem, sPrefix+"_row#"+i, sPrefix+"_row#"+(i-1));
+ sequencetable_reName(tbodyElem, sPrefix+"."+i+".", sPrefix+"."+(i-1)+".");
+ }
+
+ oCount.value = String(nCount-1);
+
+ var oAddButton = document.getElementById(sPrefix+".addbutton");
+ oAddButton.disabled = false;
+
+ return false;
+}
\ No newline at end of file
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/sequencetable.js
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/sequencetablejswidget.pt
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/sequencetablejswidget.pt 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/sequencetablejswidget.pt 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,179 @@
+<script type="text/JavaScript"
+ tal:condition="not:view/haveResourceLibrary">
+<!--
+function sequencetable_moveChildren(srcNode, destNode){
+ var count = 0;
+ while(srcNode.hasChildNodes()){
+ destNode.appendChild(srcNode.firstChild);
+ count++;
+ }
+ return count;
+}
+
+function sequencetable_copyChildren(srcNode, destNode){
+ var clonedNode = srcNode.cloneNode(true);
+ return sequencetable_moveChildren(clonedNode, destNode);
+}
+
+function sequencetable_addRow(sPrefix) {
+ var sTableID = sPrefix+".tbody";
+ var sRowID = sPrefix+"_row#_default_rowid_";
+ var tbodyElem = document.getElementById(sTableID);
+ var oRow = document.getElementById(sRowID);
+
+ var trElem, tdElem;
+ trElem = tbodyElem.insertRow(tbodyElem.rows.length);
+ var sHtml = new String(oRow.innerHTML);
+
+ sequencetable_copyChildren(oRow, trElem);
+
+ var oCount = document.getElementById(sPrefix+".count");
+ var nMaxLen = Number(document.getElementById(sPrefix+".max_length").value);
+ var sNewId = oCount.value;
+ oCount.value = String(Number(oCount.value)+1);
+
+ if (nMaxLen > 0){
+ if (Number(oCount.value) >= nMaxLen) {
+ var oAddButton = document.getElementById(sPrefix+".addbutton");
+ oAddButton.disabled = true;
+ }
+ }
+
+ var sNewtrId = oRow.id.replace(/_default_rowid_/g, sNewId);
+
+ trElem.id = oRow.id;
+
+ sequencetable_reName(trElem, "_default_rowid_", sNewId);
+
+ return false;
+}
+
+function sequencetable_replChild(startNode, oRegEx, sRepl){
+ try {
+ startNode.id = startNode.id.replace(oRegEx, sRepl);
+ } catch(E) {}
+ try {
+ startNode.name = startNode.name.replace(oRegEx, sRepl);
+ } catch(E) {}
+ //replacing in innerHTML breaks havoc
+ //try {
+ // var si = startNode.innerHTML;
+ // var aMatch = si.match(oRegEx);
+ // startNode.innerHTML = startNode.innerHTML.replace(oRegEx, sRepl);
+ //} catch(E) {}
+
+ for (var i = 0; i < startNode.childNodes.length; i++) {
+ sequencetable_replChild(startNode.childNodes[i], oRegEx, sRepl);
+ }
+}
+
+function sequencetable_reName(node, sOldID, sNewID){
+ var regexp = eval("/"+sOldID+"/g");
+ sequencetable_replChild(node, regexp, sNewID);
+}
+
+function sequencetable_delRow(oCell, sPrefix) {
+ var oCount = document.getElementById(sPrefix+".count");
+ var nCount = Number(oCount.value);
+ var nMinLen = Number(document.getElementById(sPrefix+".min_length").value);
+
+ if (nCount <= nMinLen) {
+ //alert("Cannot remove any more items!");
+ return false;
+ }
+
+ var node = oCell;
+ var regexp = eval("/"+sPrefix+"_row#(\\d+)/");
+ while (node.id.search(regexp) < 0) {
+ node = node.parentNode;
+ }
+ var oRow = node;
+ var sTableID = sPrefix+".tbody";
+ var tbodyElem = document.getElementById(sTableID);
+ var aMatch = node.id.match(/_row#(\d+)/);
+ var nToDel = Number(aMatch[1]);
+ oRow.parentNode.removeChild(oRow);
+
+ for (var i = nToDel+1; i < nCount; i++) {
+ sequencetable_reName(tbodyElem, sPrefix+"_row#"+i, sPrefix+"_row#"+(i-1));
+ sequencetable_reName(tbodyElem, sPrefix+"."+i+".", sPrefix+"."+(i-1)+".");
+ }
+
+ oCount.value = String(nCount-1);
+
+ var oAddButton = document.getElementById(sPrefix+".addbutton");
+ oAddButton.disabled = false;
+
+ return false;
+}
+//-->
+</script>
+
+<div style="display:none; height: 0px;">
+<table class="sequencewidgettable" style="visibility:hidden"
+ tal:define="emptywidget view/emptyWidget">
+ <thead>
+ <th>
+
+ </th>
+ <th tal:content="widget/label"
+ tal:repeat="widget emptywidget/subwidgets">
+ Label
+ </th>
+ </thead>
+ <tbody>
+ <tr tal:define="rowid string:${view/name}_row#_default_rowid_"
+ tal:attributes="id rowid">
+ <td>
+ <a href="#" onclick="return sequencetable_delRow(this,'${view/name}');return false"
+ tal:attributes="onclick string:return sequencetable_delRow(this,'${view/name}')">X</a>
+ </td>
+ <td tal:repeat="widget emptywidget/subwidgets">
+ <div class="field" tal:content="structure widget">
+ <input type="text" style="width:100%"/>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+</div>
+
+<input type="button" value="Add foo" id="${view/name}.addbutton"
+ tal:condition="view/need_add"
+ onClick="sequencetable_addRow('${view/name}')"
+ tal:attributes="onClick string:sequencetable_addRow('${view/name}');
+ value view/addButtonLabel;
+ id string:${view/name}.addbutton" />
+
+<table border="0" class="sequencewidgettable">
+ <thead tal:define="mainwidget view/mainWidget">
+ <th>
+
+ </th>
+ <th tal:content="widget/label"
+ tal:repeat="widget mainwidget/subwidgets">
+ Label
+ </th>
+ </thead>
+ <tbody id="${view/name}.tbody"
+ tal:attributes="id string:${view/name}.tbody">
+ <metal:block tal:repeat="mainwidget view/widgets">
+ <tr tal:define="rowid string:${view/name}_row#${repeat/mainwidget/index}"
+ tal:attributes="id rowid">
+ <td>
+ <a href="#" onclick="return sequencetable_delRow(this,'${view/name}')"
+ tal:attributes="onclick string:return sequencetable_delRow(this,'${view/name}')">X</a>
+ </td>
+ <td tal:repeat="widget mainwidget/subwidgets">
+ <span tal:define="error widget/error"
+ tal:replace="structure error" tal:condition="error" />
+ <div class="field" tal:content="structure widget">
+ <input type="text" style="width:100%"/>
+ </div>
+ </td>
+ </tr>
+ </metal:block>
+ </tbody>
+</table>
+
+<input tal:replace="structure view/marker" />
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/sequencetablejswidget.pt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/sequencetablewidget.pt
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/sequencetablewidget.pt 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/sequencetablewidget.pt 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,45 @@
+<table class="sequencewidgettable">
+ <thead tal:define="mainwidget view/mainWidget">
+ <th>
+
+ </th>
+ <metal:block tal:repeat="widget mainwidget/subwidgets">
+ <th tal:content="widget/label">
+ Label
+ </th>
+ </metal:block>
+ </thead>
+ <tbody>
+ <metal:block tal:repeat="mainwidget view/widgets">
+ <tr tal:define="oddrow repeat/mainwidget/odd"
+ tal:attributes="class python:oddrow and 'even' or 'odd'">
+ <td>
+ <input type="checkbox"
+ tal:attributes="
+ name string:${view/name}.remove_${repeat/mainwidget/index}"
+ tal:condition="view/need_delete" />
+ </td>
+ <td tal:repeat="widget mainwidget/subwidgets">
+ <span tal:define="error widget/error"
+ tal:replace="structure error" tal:condition="error" />
+ <div class="field" tal:content="structure widget">
+ <input type="text" style="width:100%"/>
+ </div>
+ </td>
+ </tr>
+ </metal:block>
+ <tr>
+ <td colspan="2">
+ <input type="submit" value="Remove selected items"
+ tal:condition="view/need_delete"
+ tal:attributes="name string:${view/name}.remove"
+ i18n:attributes="value remove-selected-items" />
+ <input type="submit" value="Add foo"
+ tal:condition="view/need_add"
+ tal:attributes="name string:${view/name}.add;
+ value view/addButtonLabel" />
+ </td>
+ </tr>
+ </tbody>
+</table>
+<input tal:replace="structure view/marker" />
\ No newline at end of file
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/sequencetablewidget.pt
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/tests.py
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/tests.py 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/tests.py 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,445 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Sequence Field Widget tests.
+
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+from zope.schema import Tuple, List, TextLine, Object
+from zope.schema.interfaces import ITextLine, ValidationError
+from zope.publisher.browser import TestRequest
+from zope.interface import Interface, implements
+from zope.interface.verify import verifyClass
+
+from zope.app import zapi
+from zope.app.testing import ztapi, setup
+from zope.app.form.browser import TextWidget, ObjectWidget, DisplayWidget
+#from zope.app.form.browser import TupleSequenceTableWidget, ListSequenceWidget
+#from zope.app.form.browser import SequenceDisplayWidget
+#from zope.app.form.browser import SequenceWidget
+from z3c.widget.sequence.widget import (SequenceDisplayTableWidget,
+ TupleSequenceTableWidget,
+ ListSequenceTableWidget,
+ TupleSequenceTableJSWidget,
+ ListSequenceTableJSWidget)
+from zope.app.form.interfaces import IDisplayWidget
+from zope.app.form.interfaces import IInputWidget, MissingInputError
+from zope.app.form.interfaces import IWidgetInputError, WidgetInputError
+from zope.app.form.browser.interfaces import IWidgetInputErrorView
+from zope.app.form import CustomWidgetFactory
+from zope.app.form.browser.exception import WidgetInputErrorView
+
+from zope.app.form.browser.tests.support import VerifyResults
+from zope.app.form.browser.tests.test_browserwidget import BrowserWidgetTest
+
+class IFoo(Interface):
+ fieldA = TextLine(title=u"Field A")
+ fieldB = TextLine(title=u"Field Boo")
+
+class SequenceWidgetTestHelper(object):
+
+ def setUpContent(self, desc=u'', title=u'Foo Title'):
+ class ITestContent(Interface):
+ foo = self._FieldFactory(
+ title=title,
+ description=desc,
+ )
+ class TestObject(object):
+ implements(ITestContent)
+
+ self.content = TestObject()
+ self.field = ITestContent['foo'].bind(self.content)
+ self.request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
+ self.request.form['field.foo'] = u'Foo Value'
+ self._widget = self._WidgetFactory(
+ self.field, self.field.value_type, self.request)
+
+ def setUp(self):
+ setup.placefulSetUp()
+ self.setUpContent()
+
+ def _FieldFactory(self, **kw):
+ kw.update({
+ '__name__': u'foo',
+ 'value_type': Object(__name__=u'bar',
+ schema=IFoo)})
+ return Tuple(**kw)
+
+
+class SequenceWidgetTest(SequenceWidgetTestHelper, BrowserWidgetTest):
+ """Documents and tests the tuple and list (sequence) widgets.
+
+ >>> verifyClass(IInputWidget, TupleSequenceTableWidget)
+ True
+ >>> verifyClass(IInputWidget, TupleSequenceTableJSWidget)
+ True
+ >>> verifyClass(IInputWidget, ListSequenceTableWidget)
+ True
+ >>> verifyClass(IInputWidget, ListSequenceTableJSWidget)
+ True
+ """
+
+ _WidgetFactory = TupleSequenceTableWidget
+
+ def testRender(self):
+ pass
+
+ def setUp(self):
+ super(SequenceWidgetTest, self).setUp()
+ ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
+ ztapi.browserViewProviding(IWidgetInputError, WidgetInputErrorView,
+ IWidgetInputErrorView)
+
+ def test_haveNoData(self):
+ self.failIf(self._widget.hasInput())
+
+ def test_hasInput(self):
+ self._widget.request.form['field.foo.count'] = u'0'
+ self.failUnless(self._widget.hasInput())
+
+ def test_customWidgetFactory(self):
+ """Verify that the widget can be constructed via the CustomWidgetFactory
+ (Issue #293)
+ """
+
+ value_type = TextLine(__name__=u'bar')
+ self.field = List( __name__=u'foo', value_type=value_type )
+ request = TestRequest()
+
+ # set up the custom widget factory and verify that it works
+ sw = CustomWidgetFactory(self._WidgetFactory)
+ widget = sw(self.field, request)
+ assert widget.subwidget is None
+ assert widget.context.value_type is value_type
+
+ # set up a variant that specifies the subwidget to use and verify it
+ class PollOption(object) : pass
+ ow = CustomWidgetFactory(ObjectWidget, PollOption)
+ sw = CustomWidgetFactory(self._WidgetFactory, subwidget=ow)
+ widget = sw(self.field, request)
+ assert widget.subwidget is ow
+ assert widget.context.value_type is value_type
+
+ def test_subwidget(self):
+ """This test verifies that the specified subwidget is not ignored.
+ (Issue #293)
+ """
+ self.field = List(__name__=u'foo',
+ value_type=TextLine(__name__=u'bar'))
+ request = TestRequest()
+
+ class PollOption(object) : pass
+ ow = CustomWidgetFactory(ObjectWidget, PollOption)
+ widget = self._WidgetFactory(
+ self.field, self.field.value_type, request, subwidget=ow)
+ assert widget.subwidget is ow
+
+ def test_list(self):
+ self.field = List(
+ __name__=u'foo',
+ value_type=TextLine(__name__=u'bar'))
+ request = TestRequest()
+ widget = ListSequenceTableWidget(
+ self.field, self.field.value_type, request)
+ self.failIf(widget.hasInput())
+ self.assertRaises(MissingInputError, widget.getInputValue)
+
+ request = TestRequest(form={'field.foo.add': u'Add bar',
+ 'field.foo.count': u'0'})
+ widget = ListSequenceTableWidget(
+ self.field, self.field.value_type, request)
+ self.assert_(widget.hasInput())
+ self.assertRaises(WidgetInputError, widget.getInputValue)
+
+ request = TestRequest(form={'field.foo.0.bar': u'Hello world!',
+ 'field.foo.count': u'1'})
+ widget = ListSequenceTableWidget(
+ self.field, self.field.value_type, request)
+ self.assert_(widget.hasInput())
+ self.assertEquals(widget.getInputValue(), [u'Hello world!'])
+
+ #def test_new(self):
+ # request = TestRequest()
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # self.failIf(widget.hasInput())
+ # self.assertRaises(MissingInputError, widget.getInputValue)
+ # check_list = ('input', 'name="field.foo.add"')
+ # self.verifyResult(widget(), check_list)
+ #
+ #def test_add(self):
+ # request = TestRequest(form={'field.foo.add': u'Add bar',
+ # 'field.foo.count': u'0'})
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # self.assert_(widget.hasInput())
+ # self.assertRaises(WidgetInputError, widget.getInputValue)
+ # check_list = (
+ # 'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
+ # 'submit', 'submit', 'field.foo.add'
+ # )
+ # self.verifyResult(widget(), check_list, inorder=True)
+ #
+ #def test_request(self):
+ # request = TestRequest(form={'field.foo.0.bar': u'Hello world!',
+ # 'field.foo.count': u'1'})
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # self.assert_(widget.hasInput())
+ # self.assertEquals(widget.getInputValue(), (u'Hello world!',))
+ #
+ #def test_existing(self):
+ # request = TestRequest()
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # widget.setRenderedValue((u'existing',))
+ # self.failIf(widget.hasInput())
+ # self.assertRaises(MissingInputError, widget.getInputValue)
+ # check_list = (
+ # 'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
+ # 'existing',
+ # 'submit', 'submit', 'field.foo.add',
+ # 'field.foo.count" value="1"',
+ # )
+ # self.verifyResult(widget(), check_list, inorder=True)
+ # widget.setRenderedValue((u'existing', u'second'))
+ # self.failIf(widget.hasInput())
+ # self.assertRaises(MissingInputError, widget.getInputValue)
+ # check_list = (
+ # 'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
+ # 'existing',
+ # 'checkbox', 'field.foo.remove_1', 'input', 'field.foo.1.bar',
+ # 'second',
+ # 'submit', 'submit', 'field.foo.add',
+ # 'field.foo.count" value="2"',
+ # )
+ # self.verifyResult(widget(), check_list, inorder=True)
+ #
+ #def test_remove(self):
+ # request = TestRequest(form={
+ # 'field.foo.remove_0': u'1',
+ # 'field.foo.0.bar': u'existing', 'field.foo.1.bar': u'second',
+ # 'field.foo.remove': u'Remove selected items',
+ # 'field.foo.count': u'2'})
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # widget.setRenderedValue((u'existing', u'second'))
+ # self.assertEquals(widget.getInputValue(), (u'second',))
+ # check_list = (
+ # 'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
+ # 'existing',
+ # 'checkbox', 'field.foo.remove_1', 'input', 'field.foo.1.bar',
+ # 'second',
+ # 'submit', 'submit', 'field.foo.add',
+ # 'field.foo.count" value="2"',
+ # )
+ # self.verifyResult(widget(), check_list, inorder=True)
+ #
+ #def test_min(self):
+ # request = TestRequest()
+ # self.field.min_length = 2
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # widget.setRenderedValue((u'existing',))
+ # self.assertRaises(MissingInputError, widget.getInputValue)
+ # check_list = (
+ # 'input', 'field.foo.0.bar', 'existing',
+ # 'input', 'field.foo.1.bar', 'value=""',
+ # 'submit', 'field.foo.add'
+ # )
+ # s = widget()
+ # self.verifyResult(s, check_list, inorder=True)
+ # self.assertEquals(s.find('checkbox'), -1)
+ #
+ #def test_max(self):
+ # request = TestRequest()
+ # self.field.max_length = 1
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # widget.setRenderedValue((u'existing',))
+ # self.assertRaises(MissingInputError, widget.getInputValue)
+ # s = widget()
+ # self.assertEquals(s.find('field.foo.add'), -1)
+ #
+ #def test_anonymousfield(self):
+ # self.field = Tuple(__name__=u'foo', value_type=TextLine())
+ # request = TestRequest()
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # widget.setRenderedValue((u'existing',))
+ # s = widget()
+ # check_list = (
+ # 'input', '"field.foo.0."', 'existing',
+ # 'submit', 'submit', 'field.foo.add'
+ # )
+ # s = widget()
+ # self.verifyResult(s, check_list, inorder=True)
+ #
+ #def test_usererror(self):
+ # self.field = Tuple(__name__=u'foo',
+ # value_type=TextLine(__name__='bar'))
+ # request = TestRequest(form={
+ # 'field.foo.0.bar': u'', 'field.foo.1.bar': u'nonempty',
+ # 'field.foo.count': u'2'})
+ # widget = TupleSequenceTableWidget(
+ # self.field, self.field.value_type, request)
+ # s = widget()
+ # # Rendering a widget should not raise errors!
+ # result = widget()
+ #
+ # data = widget._generateSequence()
+ # self.assertEquals(data, [None, u'nonempty'])
+ #
+ #def doctest_widgeterrors(self):
+ # """Test that errors on subwidgets appear
+ #
+ # >>> field = Tuple(__name__=u'foo',
+ # ... value_type=TextLine(__name__='bar'))
+ # >>> request = TestRequest(form={
+ # ... 'field.foo.0.bar': u'',
+ # ... 'field.foo.1.bar': u'nonempty',
+ # ... 'field.foo.count': u'2'})
+ # >>> widget = TupleSequenceTableWidget(field, field.value_type, request)
+ #
+ # If we render the widget, we see no errors:
+ #
+ # >>> print widget()
+ # <BLANKLINE>
+ # ...
+ # <tr>
+ # <td>
+ # <input class="editcheck" type="checkbox"
+ # name="field.foo.remove_0" />
+ # </td>
+ # <td>
+ # <input class="textType" id="field.foo.0.bar"
+ # name="field.foo.0.bar"
+ # size="20" type="text" value="" />
+ # </td>
+ # </tr>
+ # ...
+ #
+ # However, if we call getInputValue or hasValidInput, the
+ # errors on the widgets are preserved and displayed:
+ #
+ # >>> widget.hasValidInput()
+ # False
+ #
+ # >>> print widget()
+ # <BLANKLINE>
+ # ...
+ # <tr>
+ # <td>
+ # <input class="editcheck" type="checkbox"
+ # name="field.foo.remove_0" />
+ # </td>
+ # <td>
+ # <span class="error">Required input is missing.</span>
+ # <input class="textType" id="field.foo.0.bar"
+ # name="field.foo.0.bar"
+ # size="20" type="text" value="" />
+ # </td>
+ # </tr>
+ # ...
+ # """
+
+
+class SequenceDisplayWidgetTest(
+ VerifyResults, SequenceWidgetTestHelper, unittest.TestCase):
+
+ def _WidgetFactory(self, *args, **kw):
+ w = SequenceDisplayTableWidget(*args, **kw)
+ w.cssClass = "testwidget"
+ return w
+
+ def setUp(self):
+ self.setUpContent()
+ self.request = TestRequest()
+ self.widget = self._WidgetFactory(
+ self.field, self.field.value_type, self.request)
+ ztapi.browserViewProviding(ITextLine, DisplayWidget, IDisplayWidget)
+
+ #def test_render_empty(self):
+ # self.content.foo = ()
+ # self.assertEquals(self.widget(), '(no values)')
+ #
+ #def test_render_missing(self):
+ # self.content.foo = self.field.missing_value
+ # self.assertEquals(self.widget(), '(no value available)')
+ #
+ #def test_render_single(self):
+ # self.content.foo = (u'one value',)
+ # check_list = ['<ol', 'class=', 'testwidget',
+ # '<li', 'one value', '</li', '</ol']
+ # self.verifyResult(self.widget(), check_list, inorder=True)
+ #
+ #def test_render_multiple(self):
+ # self.content.foo = (u'one', u'two', u'three', u'four')
+ # check_list = ['<ol', 'class=', 'testwidget',
+ # '<li', 'one', '</li',
+ # '<li', 'two', '</li',
+ # '<li', 'three', '</li',
+ # '<li', 'four', '</li',
+ # '</ol']
+ # self.verifyResult(self.widget(), check_list, inorder=True)
+
+ #def test_render_alternate_cssClass(self):
+ # self.content.foo = (u'one value',)
+ # check_list = ['<ol', 'class=', 'altclass',
+ # '<li', 'one value', '</li', '</ol']
+ # self.widget.cssClass = 'altclass'
+ # self.verifyResult(self.widget(), check_list, inorder=True)
+
+ #def test_honors_subwidget(self):
+ # self.widget = self._WidgetFactory(
+ # self.field, self.field.value_type, self.request,
+ # subwidget=UppercaseDisplayWidget)
+ # self.content.foo = (u'first value', u'second value')
+ # check_list = ['<ol', 'class=', 'testwidget',
+ # '<li', 'FIRST VALUE', '</li',
+ # '<li', 'SECOND VALUE', '</li',
+ # '</ol']
+ # self.verifyResult(self.widget(), check_list, inorder=True)
+
+
+class UppercaseDisplayWidget(DisplayWidget):
+
+ def __call__(self):
+ return super(UppercaseDisplayWidget, self).__call__().upper()
+
+
+def setUp(test):
+ setup.placefulSetUp()
+ ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
+ ztapi.browserViewProviding(IWidgetInputError, WidgetInputErrorView,
+ IWidgetInputErrorView)
+
+
+def tearDown(test):
+ setup.placefulTearDown()
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(SequenceWidgetTest),
+ doctest.DocTestSuite(setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.ELLIPSIS
+ |doctest.NORMALIZE_WHITESPACE
+ |doctest.REPORT_NDIFF),
+ unittest.makeSuite(SequenceDisplayWidgetTest),
+ ))
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/tests.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/widget.py
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/widget.py 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/widget.py 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1,125 @@
+# -*- coding: UTF-8 -*-
+
+from zope.i18n import translate
+from zope.app import zapi
+from zope.app.form.interfaces import IDisplayWidget, IInputWidget
+
+from zope.app.form.browser import SequenceDisplayWidget, SequenceWidget
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+
+try:
+ from zc import resourcelibrary
+ haveResourceLibrary = True
+except ImportError:
+ haveResourceLibrary = False
+
+def _getSubWidget(sequenceWidget, prefix="%s."):
+ field = sequenceWidget.context.value_type
+ if sequenceWidget.subwidget is not None:
+ widget = sequenceWidget.subwidget(field, sequenceWidget.request)
+ else:
+ widget = zapi.getMultiAdapter((field, sequenceWidget.request),
+ IInputWidget)
+ widget.setPrefix(prefix % (sequenceWidget.name))
+ return widget
+
+class SequenceDisplayTableWidget(SequenceDisplayWidget):
+ template = ViewPageTemplateFile('sequencedisplaytablewidget.pt')
+
+ def __call__(self):
+ return self.template()
+
+ def mainWidget(self):
+ return _getSubWidget(self)
+
+ def haveMessage(self):
+ if self._renderedValueSet():
+ data = self._data
+ else:
+ data = self.context.get(self.context.context)
+
+ # deal with special cases:
+ if data == self.context.missing_value:
+ return translate(self._missingValueMessage, self.request)
+ data = list(data)
+ if not data:
+ return translate(self._emptySequenceMessage, self.request)
+ return None
+
+ def widgets(self):
+ # get the data to display:
+ if self._renderedValueSet():
+ data = self._data
+ else:
+ data = self.context.get(self.context.context)
+
+ # deal with special cases:
+ if data == self.context.missing_value:
+ return translate(self._missingValueMessage, self.request)
+ data = list(data)
+ if not data:
+ return translate(self._emptySequenceMessage, self.request)
+
+ widgets = []
+ for i, item in enumerate(data):
+ widget = self._getSubWidget(i)
+ widget.setRenderedValue(item)
+ widgets.append(widget)
+ return widgets
+
+class SequenceTableWidget(SequenceWidget):
+ template = ViewPageTemplateFile('sequencetablewidget.pt')
+
+ def mainWidget(self):
+ return _getSubWidget(self)
+
+class TupleSequenceTableWidget(SequenceTableWidget):
+ _type = tuple
+
+
+class ListSequenceTableWidget(SequenceTableWidget):
+ _type = list
+
+
+class SequenceTableJSWidget(SequenceWidget):
+ template = ViewPageTemplateFile('sequencetablejswidget.pt')
+ haveResourceLibrary = haveResourceLibrary
+
+ def mainWidget(self):
+ return _getSubWidget(self)
+
+ def emptyWidget(self):
+ widget = _getSubWidget(self, prefix="%s._default_rowid_")
+ widget.setRenderedValue(None)
+ return widget
+
+ def _getPresenceMarker(self, count=0):
+ maxval = self.context.max_length
+ if maxval:
+ maxval=str(maxval)
+ else:
+ maxval=''
+ minval = self.context.min_length
+ if minval:
+ minval=str(minval)
+ else:
+ minval=''
+ rv = ('<input type="hidden" name="%s.count" id="%s.count" value="%d" />'
+ % (self.name, self.name, count))
+ rv = rv+('<input type="hidden" name="%s.max_length" id="%s.max_length" value="%s" />'
+ % (self.name, self.name, maxval))
+ rv = rv+('<input type="hidden" name="%s.min_length" id="%s.min_length" value="%s" />'
+ % (self.name, self.name, minval))
+ return rv
+
+ def __call__(self, *args, **kw):
+ if haveResourceLibrary:
+ resourcelibrary.need('sequencetable')
+ return super(SequenceTableJSWidget, self).__call__(*args, **kw)
+
+class TupleSequenceTableJSWidget(SequenceTableJSWidget):
+ _type = tuple
+
+
+class ListSequenceTableJSWidget(SequenceTableJSWidget):
+ _type = list
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/widget.py
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
Added: z3c.widget/trunk/src/z3c/widget/sequence/z3c.widget.sequence-configure.zcml
===================================================================
--- z3c.widget/trunk/src/z3c/widget/sequence/z3c.widget.sequence-configure.zcml 2007-01-13 10:23:11 UTC (rev 71995)
+++ z3c.widget/trunk/src/z3c/widget/sequence/z3c.widget.sequence-configure.zcml 2007-01-13 10:24:47 UTC (rev 71996)
@@ -0,0 +1 @@
+<include package="z3c.widget.sequence"/>
\ No newline at end of file
Property changes on: z3c.widget/trunk/src/z3c/widget/sequence/z3c.widget.sequence-configure.zcml
___________________________________________________________________
Name: svn:keywords
+ Date Author Id Revision
Name: svn:eol-style
+ native
More information about the Checkins
mailing list