diff --git a/browser/liquid/liquid4.css b/browser/liquid/liquid4.css
index 74b690c..7a3d071 100644
--- a/browser/liquid/liquid4.css
+++ b/browser/liquid/liquid4.css
@@ -22,14 +22,14 @@ body {
#header,#footer {width:100%}
#menu,#content,#sub-section {float:left}
#menu {width:20%}
-#content {width:60%}
-#sub-section {width:19.9%}
+#content {width:67%}
+#sub-section {width:12.9%}
#footer {clear:left}
/* more general stuff */
.box {
- margin: 12px 15px 8px 10px;
+ margin: 12px 12px 8px 10px;
border-left: none;
}
diff --git a/container/DEPENDENCIES.cfg b/container/DEPENDENCIES.cfg
new file mode 100644
index 0000000..a309abd
--- /dev/null
+++ b/container/DEPENDENCIES.cfg
@@ -0,0 +1,4 @@
+zope.app
+zope.component
+zope.interface
+persistent
diff --git a/container/README.txt b/container/README.txt
new file mode 100644
index 0000000..3892541
--- /dev/null
+++ b/container/README.txt
@@ -0,0 +1,48 @@
+Ordered Containers
+==================
+
+ ($Id$)
+
+ >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
+ >>> site = placefulSetUp(True)
+ >>> from zope.interface import implements
+
+Let's add an ordered container and place some objects in it:
+
+ >>> from zope.app.container.interfaces import IOrderedContainer
+ >>> import zope.app.container.ordered
+ >>> class OrderedContainer(zope.app.container.ordered.OrderedContainer):
+ ... implements(IOrderedContainer)
+
+ >>> c1 = OrderedContainer()
+ >>> site['c1'] = c1
+ >>> c1['sub1'] = OrderedContainer()
+ >>> c1['sub2'] = OrderedContainer()
+ >>> c1['sub3'] = OrderedContainer()
+ >>> c1['sub4'] = OrderedContainer()
+ >>> c1.keys()
+ ['sub1', 'sub2', 'sub3', 'sub4']
+
+A special management view provides methods for moving objects down, up,
+to the bottom, and to the top
+
+ >>> from cybertools.container.ordered import OrderedContainerView
+ >>> from zope.publisher.browser import TestRequest
+ >>> view = OrderedContainerView(c1, TestRequest())
+ >>> view.moveToBottom(('sub3',))
+ >>> c1.keys()
+ ['sub1', 'sub2', 'sub4', 'sub3']
+ >>> view.moveUp(('sub4',), 1)
+ >>> c1.keys()
+ ['sub1', 'sub4', 'sub2', 'sub3']
+ >>> view.moveToTop(('sub2',))
+ >>> c1.keys()
+ ['sub2', 'sub1', 'sub4', 'sub3']
+ >>> view.moveDown(('sub2',), 2)
+ >>> c1.keys()
+ ['sub1', 'sub4', 'sub2', 'sub3']
+
+The end...
+==========
+
+ >>> placefulTearDown()
diff --git a/container/SETUP.cfg b/container/SETUP.cfg
new file mode 100644
index 0000000..2e893b1
--- /dev/null
+++ b/container/SETUP.cfg
@@ -0,0 +1,5 @@
+# Tell zpkg how to install the ZCML slugs.
+
+
+ cybertools.container-*.zcml
+
diff --git a/container/__init__.py b/container/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/container/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/container/configure.zcml b/container/configure.zcml
new file mode 100644
index 0000000..d97817e
--- /dev/null
+++ b/container/configure.zcml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/container/contents.pt b/container/contents.pt
new file mode 100644
index 0000000..505f090
--- /dev/null
+++ b/container/contents.pt
@@ -0,0 +1,216 @@
+
+
+
+
+
diff --git a/container/cybertools.container-configure.zcml b/container/cybertools.container-configure.zcml
new file mode 100644
index 0000000..b9894b5
--- /dev/null
+++ b/container/cybertools.container-configure.zcml
@@ -0,0 +1 @@
+
diff --git a/container/interfaces.py b/container/interfaces.py
new file mode 100644
index 0000000..a21e778
--- /dev/null
+++ b/container/interfaces.py
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+interface definitions for ordered containers.
+
+$Id$
+"""
+
+from zope.interface import Interface, Attribute
+
+
diff --git a/container/ordered.py b/container/ordered.py
new file mode 100644
index 0000000..6144d70
--- /dev/null
+++ b/container/ordered.py
@@ -0,0 +1,95 @@
+#
+# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+Ordered container implementation.
+
+$Id$
+"""
+
+from zope.app import zapi
+from zope.app.container.ordered import OrderedContainer as BaseOrderedContainer
+from zope.cachedescriptors.property import Lazy
+from zope.app.container.browser.contents import JustContents
+
+
+class OrderedContainerView(JustContents):
+ """ A view providing the necessary methods for moving sub-objects
+ within an ordered container.
+ """
+
+ @Lazy
+ def url(self):
+ return zapi.absoluteURL(self.context, self.request)
+
+ @Lazy
+ def orderable(self):
+ return len(self.context) > 1
+
+ def checkMoveAction(self):
+ request = self.request
+ for var in request:
+ if var.startswith('move_'):
+ params = []
+ if 'delta' in request:
+ params.append('delta=' + request['delta'])
+ if 'ids' in request:
+ for id in request['ids']:
+ params.append('ids:list=' + id)
+ request.response.redirect('%s/%s?%s'
+ % (self.url, var, '&'.join(params)))
+ return True
+ return False
+
+ def moveDown(self, ids=[], delta=1):
+ self.context.updateOrder(
+ moveByDelta(self.context.keys(), ids, int(delta)))
+ self.request.response.redirect(self.url + '/contents.html')
+
+ def moveUp(self, ids=[], delta=1):
+ self.context.updateOrder(
+ moveByDelta(self.context.keys(), ids, -int(delta)))
+ self.request.response.redirect(self.url + '/contents.html')
+
+ def moveToBottom(self, ids=[]):
+ self.context.updateOrder(
+ moveByDelta(self.context.keys(), ids, len(self.context)))
+ self.request.response.redirect(self.url + '/contents.html')
+
+ def moveToTop(self, ids=[]):
+ self.context.updateOrder(
+ moveByDelta(self.context.keys(), ids, -len(self.context)))
+ self.request.response.redirect(self.url + '/contents.html')
+
+
+def moveByDelta(objs, toMove, delta):
+ """ Return the list given by objs resorted in a way that the elements
+ of toMove (which must be in the objs list) have been moved by delta.
+ """
+ result = [obj for obj in objs if obj not in toMove]
+ if delta < 0:
+ objs = list(reversed(objs))
+ result.reverse()
+ toMove = sorted(toMove, lambda x,y: cmp(objs.index(x), objs.index(y)))
+ for element in toMove:
+ newPos = min(len(result), objs.index(element) + abs(delta))
+ result.insert(newPos, element)
+ if delta < 0:
+ result.reverse()
+ return result
+
diff --git a/container/tests.py b/container/tests.py
new file mode 100755
index 0000000..7956cd4
--- /dev/null
+++ b/container/tests.py
@@ -0,0 +1,13 @@
+# $Id$
+
+import unittest
+from zope.testing.doctestunit import DocFileSuite
+
+
+def test_suite():
+ return unittest.TestSuite((
+ DocFileSuite('README.txt'),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')