[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>
+      &nbsp;
+    </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>
+      &nbsp;
+    </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>
+      &nbsp;
+    </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