diff --git a/CHANGES.txt b/CHANGES.txt
index 36fc8a2..7d8329e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -5,4 +5,7 @@ CHANGES
Version 0.5.0 (unreleased)
-------------------------
+- implemented ITreeItems adapter which is responsible for list all items listed
+ in tree
+
- Initial Release
diff --git a/src/z3c/jsontree/base.py b/src/z3c/jsontree/base.py
index 5dbb6a8..e398a6d 100644
--- a/src/z3c/jsontree/base.py
+++ b/src/z3c/jsontree/base.py
@@ -20,12 +20,9 @@
import zope.interface
import zope.component
from zope.traversing import api
-from zope.security.interfaces import Unauthorized
-from zope.security.interfaces import Forbidden
from zope.traversing.browser import absoluteURL
from zope.traversing.namespace import getResource
from zope.contentprovider.interfaces import IContentProvider
-from zope.app.container.interfaces import IReadContainer
from zope.app.component import hooks
from z3c.template.template import getPageTemplate
@@ -42,10 +39,54 @@
from z3c.jsontree.interfaces import STATE_EXPANDED
from z3c.jsontree.interfaces import STATE_COLLAPSED
from z3c.jsontree.interfaces import STATE_STATIC
+from z3c.jsontree import subitem
from z3c.jsontree import util
-class TreeBase(object):
+class IdGenerator(object):
+ """This mixin class generates Object Ids based on the the objects path.
+
+ Note: The objects must be traversable by it's path. You can implement a
+ a custom path traverse concept in the getObjectByPath it you need to use
+ another traverse concept.
+
+ This ids must conform the w3c recommendation described in:
+ http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-name
+ """
+
+ def getId(self, item):
+ """Returns the DOM id for a given object.
+
+ Note: we encode the upper case letters because the Dom element id are
+ not case sensitive in HTML. We prefix each upper case letter with ':'.
+ """
+ path = api.getPath(item)
+ newPath = u''
+ for letter in path:
+ if letter in string.uppercase:
+ newPath += ':' + letter
+ else:
+ newPath += letter
+
+ # we use a dot as a root representation, this avoids to get the same id
+ # for the ul and the first li tag
+ if newPath == '/':
+ newPath = '.'
+ # add additinal dot which separates the tree id and the path, is used
+ # for get the tree id out of the string in the javascript using
+ # ids = id.split("."); treeId = ids[0];
+ id = self.z3cJSONTreeId +'.'+ newPath
+ # convert '/' path separator to marker '::', because the path '/'' is
+ # not allowed as DOM id. See also:
+ # http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-name
+ return id.replace('/', '::')
+
+ def id(self):
+ """Returns the DOM id for a given context."""
+ return self.getId(self.context)
+
+
+class TreeBase(subitem.SubItemMixin):
"""Tree iterator base implementation."""
root = None
@@ -78,20 +119,6 @@ def __init__(self, context, request):
self.context = context
self.request = request
- def getSubItems(self, item):
- items = []
- append = items.append
- if IReadContainer.providedBy(item):
- keys = list(item.keys())
- else:
- keys = []
- for name in keys:
- # Only include items we can traverse to
- subItem = api.traverse(item, name, None)
- if subItem is not None:
- append(subItem)
- return items
-
def getIconURL(self, item, request, name='icon'):
return util.getIconURL(item, request, name=name)
@@ -99,16 +126,6 @@ def getParents(self):
root = self.getRoot()
return util.getParentsFromContextToObject(self.context, root)
- def hasSubItems(self, item):
- res = False
- if IReadContainer.providedBy(item):
- try:
- if len(item) > 0:
- res = True
- except(Unauthorized, Forbidden):
- pass
- return res
-
def update(self):
"""Returns HTML code for representing a
tag tree with the
siblings and parents of an object.
@@ -120,6 +137,10 @@ def update(self):
If we access the tree via a virtual host, the root is adjusted to
the right root object.
+ The childTags get used in every implementation of this package. The
+ items in the childTags can get rendered with the python renderer which
+ uses inline code or with the template based renderer which is probably
+ slower then the python renderer becaue of it's tal usage.
"""
childTags = None
stackItem = self.context
@@ -129,16 +150,14 @@ def update(self):
tagList = []
append = tagList.append
- for subItem in self.getSubItems(item):
- if self.hasSubItems(subItem):
-
+ for name, subItem, hasSubItems in self.getSubItems(item):
+ if hasSubItems:
if subItem == stackItem:
- append(self.renderUL(subItem, childTags))
+ append(self.renderUL(name, subItem, childTags))
else:
- append(self.renderUL(subItem))
-
+ append(self.renderUL(name, subItem))
else:
- append(self.renderLI(subItem))
+ append(self.renderLI(name, subItem))
childTags = ' '.join(tagList)
stackItem = item
@@ -146,6 +165,88 @@ def update(self):
self.childTags = childTags
+class ProviderBase(object):
+ """Base class for tag element provider."""
+
+ template = getPageTemplate()
+
+ root = None
+ state = None
+ name = None
+ childTags = None
+ iconName = 'icon'
+
+ z3cJSONTreeId = JSON_TREE_ID
+ z3cJSONTreeName = JSON_TREE_ID
+ z3cJSONTreeClass = JSON_TREE_ID
+
+ viewName = JSON_TREE_VIEW_NAME
+
+ # LI tag CSS names
+ collapsedCSSName = JSON_LI_CSS_COLLAPSED
+ expandedCSSName = JSON_LI_CSS_EXPANDED
+ staticCSSName = JSON_LI_CSS_STATIC
+
+ # toggle icon names
+ collapsedIconName = JSON_TOGGLE_ICON_COLLAPSED
+ expandedIconNamen = JSON_TOGGLE_ICON_EXPANDED
+ staticIconName = JSON_TOGGLE_ICON_STATIC
+
+ def __init__(self, context, request, view):
+ self.context = context
+ self.request = request
+ self.view = view
+
+ @property
+ def className(self):
+ if self.state == STATE_COLLAPSED:
+ return self.collapsedCSSName
+ elif self.state == STATE_EXPANDED:
+ return self.expandedCSSName
+ else:
+ return self.staticCSSName
+
+ @property
+ def toggleIcon(self):
+ """Returns a toggle icon including settings for json url."""
+ if self.state == STATE_COLLAPSED:
+ iconName = self.collapsedIconName
+ elif self.state == STATE_EXPANDED:
+ iconName = self.expandedIconNamen
+ else:
+ iconName = self.staticIconName
+ icon = zope.component.getMultiAdapter((self.context, self.request),
+ name=iconName)
+ resource = getResource(icon.context, icon.rname, self.request)
+ src = resource()
+ longDescURL = absoluteURL(self.context, self.request)
+ return ('' % (src, icon.width,
+ icon.height, longDescURL))
+
+ def icon(self):
+ """Returns a toggle icon including settings for json url."""
+ icon = zope.component.queryMultiAdapter((self.context, self.request),
+ name=self.iconName)
+ if icon is not None:
+ resource = getResource(icon.context, icon.rname, self.request)
+ src = resource()
+ longDescURL = absoluteURL(self.context, self.request)
+ return ('' % (src, icon.width, icon.height))
+ return u''
+
+ @property
+ def url(self):
+ return absoluteURL(self.context, self.request) +'/'+ self.viewName
+
+ def update(self):
+ pass
+
+ def render(self):
+ return self.template()
+
+
class PythonRenderer(object):
"""Mixin class for template less tree generation.
@@ -184,8 +285,7 @@ def getToggleIcon(self, item, state):
'border="0" longDesc="%s" />' % (src, icon.width,
icon.height, longDescURL))
- def renderLI(self, item):
- name = api.getName(item)
+ def renderLI(self, name, item):
url = absoluteURL(item, self.request) +'/'+ self.viewName
iconURL = self.getIconURL(item, self.request)
id = self.getId(item)
@@ -200,9 +300,8 @@ def renderLI(self, item):
res += ''
return res
- def renderUL(self, item, childTags=None):
+ def renderUL(self, name, item, childTags=None):
"""Renders - tag with already rendered child tags."""
- name = api.getName(item)
url = absoluteURL(item, self.request) +'/'+ self.viewName
iconURL = self.getIconURL(item, self.request)
id = self.getId(item)
@@ -281,15 +380,16 @@ class TemplateRenderer(object):
ulProviderName = 'ul'
treeProviderName = 'tree'
- def renderLI(self, item):
+ def renderLI(self, name, item):
provider = zope.component.getMultiAdapter(
(item, self.request, self), IContentProvider,
self.liProviderName)
+ provider.name = name
provider.state = STATE_STATIC
provider.update()
return provider.render()
- def renderUL(self, item, childTags=None):
+ def renderUL(self, name, item, childTags=None):
"""Renders
- tag with already rendered child tags."""
if item == self.context:
state = STATE_COLLAPSED
@@ -302,6 +402,7 @@ def renderUL(self, item, childTags=None):
provider = zope.component.getMultiAdapter(
(item, self.request, self), IContentProvider,
self.ulProviderName)
+ provider.name = name
provider.childTags = childTags
provider.state = state
provider.update()
@@ -325,132 +426,3 @@ def tree(self):
provider.state = state
provider.update()
return provider.render()
-
-
-class IdGenerator(object):
- """This mixin class generates Object Ids based on the the objects path.
-
- Note: The objects must be traversable by it's path. You can implement a
- a custom path traverse concept in the getObjectByPath it you need to use
- another traverse concept.
-
- This ids must conform the w3c recommendation described in:
- http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-name
- """
-
- def getId(self, item):
- """Returns the DOM id for a given object.
-
- Note: we encode the upper case letters because the Dom element id are
- not case sensitive in HTML. We prefix each upper case letter with ':'.
- """
- path = api.getPath(item)
- newPath = u''
- for letter in path:
- if letter in string.uppercase:
- newPath += ':' + letter
- else:
- newPath += letter
-
- # we use a dot as a root representation, this avoids to get the same id
- # for the ul and the first li tag
- if newPath == '/':
- newPath = '.'
- # add additinal dot which separates the tree id and the path, is used
- # for get the tree id out of the string in the javascript using
- # ids = id.split("."); treeId = ids[0];
- id = self.z3cJSONTreeId +'.'+ newPath
- # convert '/' path separator to marker '::', because the path '/'' is
- # not allowed as DOM id. See also:
- # http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-name
- return id.replace('/', '::')
-
- def id(self):
- """Returns the DOM id for a given context."""
- return self.getId(self.context)
-
-
-class ProviderBase(object):
- """Base class for element provider."""
-
- template = getPageTemplate()
-
- root = None
- state = None
- childTags = None
- iconName = 'icon'
-
- z3cJSONTreeId = JSON_TREE_ID
- z3cJSONTreeName = JSON_TREE_ID
- z3cJSONTreeClass = JSON_TREE_ID
-
- viewName = JSON_TREE_VIEW_NAME
-
- # LI tag CSS names
- collapsedCSSName = JSON_LI_CSS_COLLAPSED
- expandedCSSName = JSON_LI_CSS_EXPANDED
- staticCSSName = JSON_LI_CSS_STATIC
-
- # toggle icon names
- collapsedIconName = JSON_TOGGLE_ICON_COLLAPSED
- expandedIconNamen = JSON_TOGGLE_ICON_EXPANDED
- staticIconName = JSON_TOGGLE_ICON_STATIC
-
- def __init__(self, context, request, view):
- self.context = context
- self.request = request
- self.view = view
-
- @property
- def className(self):
- if self.state == STATE_COLLAPSED:
- return self.collapsedCSSName
- elif self.state == STATE_EXPANDED:
- return self.expandedCSSName
- else:
- return self.staticCSSName
-
- @property
- def toggleIcon(self):
- """Returns a toggle icon including settings for json url."""
- if self.state == STATE_COLLAPSED:
- iconName = self.collapsedIconName
- elif self.state == STATE_EXPANDED:
- iconName = self.expandedIconNamen
- else:
- iconName = self.staticIconName
- icon = zope.component.getMultiAdapter((self.context, self.request),
- name=iconName)
- resource = getResource(icon.context, icon.rname, self.request)
- src = resource()
- longDescURL = absoluteURL(self.context, self.request)
- return ('' % (src, icon.width,
- icon.height, longDescURL))
-
- def icon(self):
- """Returns a toggle icon including settings for json url."""
- icon = zope.component.queryMultiAdapter((self.context, self.request),
- name=self.iconName)
- if icon is not None:
- resource = getResource(icon.context, icon.rname, self.request)
- src = resource()
- longDescURL = absoluteURL(self.context, self.request)
- return ('' % (src, icon.width, icon.height))
- return u''
-
- @property
- def name(self):
- return api.getName(self.context) or u'[top]'
-
- @property
- def url(self):
- return absoluteURL(self.context, self.request) +'/'+ self.viewName
-
- def update(self):
- pass
-
- def render(self):
- return self.template()
-
diff --git a/src/z3c/jsontree/browser/tests/output/tree_3.txt b/src/z3c/jsontree/browser/tests/output/tree_3.txt
new file mode 100644
index 0000000..bf78cba
--- /dev/null
+++ b/src/z3c/jsontree/browser/tests/output/tree_3.txt
@@ -0,0 +1 @@
+
diff --git a/src/z3c/jsontree/browser/tests/test_view.py b/src/z3c/jsontree/browser/tests/test_view.py
index cc93907..64a7404 100644
--- a/src/z3c/jsontree/browser/tests/test_view.py
+++ b/src/z3c/jsontree/browser/tests/test_view.py
@@ -25,6 +25,7 @@
from zope.pagetemplate.tests.util import check_xml
from zope.publisher.browser import TestRequest
from zope.app.component import testing
+from zope.app.folder import Folder
from z3c.testing import TestCase
from z3c.jsontree.browser.tests import util
@@ -80,6 +81,19 @@ def test_simple_tree_view_2(self):
ultree = view.tree
check_xml(ultree, util.read_output('tree_2.txt'))
+ def test_simple_tree_view_3(self):
+ """This test includes cyrillic letters and maxItems."""
+ context = self.rootFolder['folder1']
+ for i in range(55):
+ context[str(i)] = Folder()
+
+ subFolder = context['1']
+ request = TestRequest()
+ view = SimpleJSONTreeView(subFolder, request)
+ view.update()
+ ultree = view.tree
+ check_xml(ultree, util.read_output('tree_3.txt'))
+
def test_generic_tree_view_1(self):
context = self.rootFolder['folder1']
request = TestRequest()
@@ -97,6 +111,19 @@ def test_generic_tree_view_2(self):
ultree = view.tree
check_xml(ultree, util.read_output('tree_2.txt'))
+ def test_generic_tree_view_3(self):
+ """This test includes cyrillic letters and maxItems."""
+ context = self.rootFolder['folder1']
+ for i in range(55):
+ context[str(i)] = Folder()
+
+ subFolder = context['1']
+ request = TestRequest()
+ view = GenericJSONTreeView(subFolder, request)
+ view.update()
+ ultree = view.tree
+ check_xml(ultree, util.read_output('tree_3.txt'))
+
def test_suite():
return unittest.TestSuite((
diff --git a/src/z3c/jsontree/browser/tree.py b/src/z3c/jsontree/browser/tree.py
index 7bbcb97..410c72a 100644
--- a/src/z3c/jsontree/browser/tree.py
+++ b/src/z3c/jsontree/browser/tree.py
@@ -20,6 +20,7 @@
import zope.interface
import zope.component
from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.http import IHTTPRequest
from zope.viewlet.interfaces import IViewlet
from z3c.jsontree import interfaces
@@ -55,7 +56,7 @@ class LITagProvider(base.ProviderBase, base.IdGenerator):
"""LI tag content provider."""
zope.interface.implements(interfaces.ILITagProvider)
- zope.component.adapts(zope.interface.Interface, IBrowserRequest,
+ zope.component.adapts(zope.interface.Interface, IHTTPRequest,
interfaces.ITemplateRenderer)
@@ -63,13 +64,15 @@ class ULTagProvider(base.ProviderBase, base.IdGenerator):
"""UL tag contet provider."""
zope.interface.implements(interfaces.IULTagProvider)
- zope.component.adapts(zope.interface.Interface, IBrowserRequest,
+ zope.component.adapts(zope.interface.Interface, IHTTPRequest,
interfaces.ITemplateRenderer)
class TreeProvider(base.ProviderBase, base.IdGenerator):
"""UL tag contet provider."""
+ name = u'[top]'
+
zope.interface.implements(interfaces.ITreeProvider)
zope.component.adapts(zope.interface.Interface, IBrowserRequest,
interfaces.ITemplateRenderer)
diff --git a/src/z3c/jsontree/browser/z3c.jsontree.css b/src/z3c/jsontree/browser/z3c.jsontree.css
index 826d8ae..d8a55e2 100644
--- a/src/z3c/jsontree/browser/z3c.jsontree.css
+++ b/src/z3c/jsontree/browser/z3c.jsontree.css
@@ -1,4 +1,4 @@
-
+/*---[ z3c.jsontree styles ]-------------------------------------------------*/
ul.z3cJSONTree {
list-style-type: none;
margin: 0px;
diff --git a/src/z3c/jsontree/configure.zcml b/src/z3c/jsontree/configure.zcml
index 8ef7c54..aa6b5f3 100644
--- a/src/z3c/jsontree/configure.zcml
+++ b/src/z3c/jsontree/configure.zcml
@@ -3,16 +3,31 @@
xmlns:z3c="http://namespaces.zope.org/z3c"
i18n_domain="z3c">
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/z3c/jsontree/interfaces.py b/src/z3c/jsontree/interfaces.py
index c1b901d..30a2b28 100644
--- a/src/z3c/jsontree/interfaces.py
+++ b/src/z3c/jsontree/interfaces.py
@@ -36,6 +36,20 @@
STATE_STATIC = 'static'
+class ITreeItems(zope.interface.Interface):
+ """Knows the items listed in tree for the given context."""
+
+ def __init__(context, provider):
+ """Adapts the context and the request.
+
+ This allows to use different adapters for different layers on the same
+ context.
+ """
+
+ subItems = zope.interface.Attribute(
+ """List of (name, item, hasSubItems) tuple.""")
+
+
class IElementProvider(IContentProvider):
state = zope.interface.Attribute(
@@ -103,10 +117,10 @@ class ITreeProvider(IElementProvider):
class ITreeRenderer(zope.interface.Interface):
"""Knows how to render elements fo the tree items."""
- def renderLI(item):
+ def renderLI(name, item):
"""Renders
- tags."""
- def renderUL(item, childTags=None):
+ def renderUL(name, item, childTags=None):
"""Renders
- tag including rendered child tags."""
tree = zope.interface.Attribute(
diff --git a/src/z3c/jsontree/jsonrpc.py b/src/z3c/jsontree/jsonrpc.py
index c451f4f..5f38045 100644
--- a/src/z3c/jsontree/jsonrpc.py
+++ b/src/z3c/jsontree/jsonrpc.py
@@ -16,61 +16,44 @@
"""
__docformat__ = 'restructuredtext'
-from zope.traversing import api
-from zope.security.interfaces import Unauthorized
-from zope.security.interfaces import Forbidden
from zope.traversing.browser import absoluteURL
-from zope.app.container.interfaces import IReadContainer
from z3c.jsonrpc.publisher import MethodPublisher
from z3c.jsontree.interfaces import JSON_TREE_ID
from z3c.jsontree.interfaces import JSON_TREE_VIEW_NAME
from z3c.jsontree import base
+from z3c.jsontree import subitem
from z3c.jsontree import util
-class JSONTreeItems(MethodPublisher, base.IdGenerator):
+class NoneJSONTreeItems(MethodPublisher, base.IdGenerator):
+ """Returns no items.
+
+ Default JSON adapter for all objects.
+ """
+
+ def loadJSONTreeItems(self, id):
+ """Returns child information for the object with the given path in a
+ JSON format."""
+ return {'treeChilds': {'id':id, 'childs':[]}}
+
+
+class JSONTreeItems(subitem.SubItemMixin, MethodPublisher,
+ base.IdGenerator):
"""Returns the data of the childs from the path for the json tree.
This is a simple implementation which uses the traversal concept.
If you need to lookup items for other conponents then containers,
e.g. for a widget, you need to implement your own child loader class.
+
+ This is the default JSON adapter for IReadContainer.
"""
viewName = JSON_TREE_VIEW_NAME
z3cJSONTreeId = JSON_TREE_ID
linkHandler = ''
- @property
- def subItems(self):
- """Returns a list of items for the given context."""
- items = []
- keys = []
- append = items.append
- if IReadContainer.providedBy(self.context):
- try:
- keys = list(self.context.keys())
- except(Unauthorized, Forbidden):
- return items
- for name in keys:
- # Only include items we can traverse to
- subItem = api.traverse(self.context, name, None)
- if subItem is not None:
- append(subItem)
- return items
-
- def hasSubItems(self, item):
- """Returns a True or False if the sub item has subitems."""
- res = False
- if IReadContainer.providedBy(item):
- try:
- if len(item) > 0:
- res = True
- except(Unauthorized, Forbidden):
- pass
- return res
-
def getIconURL(self, item, request, name='icon'):
return util.getIconURL(item, request, name=name)
@@ -81,9 +64,9 @@ def loadJSONTreeItems(self, id):
childs = []
append = childs.append
- for subItem in self.subItems:
+ for name, subItem, hasSubItems in self.getSubItems(self.context):
- if self.hasSubItems(subItem):
+ if hasSubItems:
hasChilds = True
else:
hasChilds = False
@@ -91,7 +74,7 @@ def loadJSONTreeItems(self, id):
info = {}
url = absoluteURL(subItem, self.request)
info['id'] = self.getId(subItem)
- info['content'] = api.getName(subItem)
+ info['content'] = name
info['url'] = url +'/'+ self.viewName
info['iconURL'] = util.getIconURL(subItem, self.request)
info['linkHandler'] = self.linkHandler
diff --git a/src/z3c/jsontree/jsonrpc.zcml b/src/z3c/jsontree/jsonrpc.zcml
new file mode 100644
index 0000000..81e2821
--- /dev/null
+++ b/src/z3c/jsontree/jsonrpc.zcml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/z3c/jsontree/subitem.py b/src/z3c/jsontree/subitem.py
new file mode 100644
index 0000000..47ab282
--- /dev/null
+++ b/src/z3c/jsontree/subitem.py
@@ -0,0 +1,115 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = 'restructuredtext'
+
+import zope.interface
+import zope.component
+import zope.i18nmessageid
+import zope.i18n
+from zope.security.interfaces import Unauthorized
+from zope.security.interfaces import Forbidden
+from zope.traversing import api
+from zope.app.container.interfaces import IReadContainer
+
+from z3c.jsontree import interfaces
+
+_ = zope.i18nmessageid.MessageFactory('z3c')
+
+
+class SubItemMixin(object):
+ """Base class for sub item lookup."""
+
+ def getSubItems(self, item):
+ """Delegate call to ITreeItems adapter."""
+ adapter = zope.component.getMultiAdapter((item, self.request),
+ interfaces.ITreeItems)
+ return adapter.subItems
+
+
+class TreeItemsMixin(object):
+ """Base class for ITreeItems adapter."""
+
+ zope.interface.implements(interfaces.ITreeItems)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+
+class NoneTreeItems(TreeItemsMixin):
+ """Returns no items.
+
+ Default tree item adapter for all objects.
+ """
+
+ @property
+ def subItems(self):
+ return []
+
+
+class ContainerTreeItems(TreeItemsMixin):
+ """Knows the items listed in tree for the given context.
+
+ This is the default tree item adapter for IReadContainer.
+ """
+
+ maxItems = 50
+
+ def _hasSubItems(self, item):
+ """This method allows us to decide if a sub item has items from the
+ point of view of the context."""
+ res = False
+ if IReadContainer.providedBy(item):
+ try:
+ if len(item) > 0:
+ res = True
+ except(Unauthorized, Forbidden):
+ pass
+ return res
+
+ @property
+ def subItems(self):
+ """Collect all tree items for the given context."""
+ items = []
+ keys = []
+ append = items.append
+ if IReadContainer.providedBy(self.context):
+ try:
+ keys = list(self.context.keys())
+ except(Unauthorized, Forbidden):
+ return items
+ else:
+ return items
+ counter = 1
+ for name in keys:
+ # Only include items we can traverse to
+ subItem = api.traverse(self.context, name, None)
+ if subItem is not None:
+ append((api.getName(subItem), subItem,
+ self._hasSubItems(subItem)))
+ counter += 1
+ if counter == self.maxItems:
+ # add context which should support a item listing view with
+ # batch
+ lenght = len(keys) - self.maxItems
+ default = '[%s more items...]' % lenght
+ name = zope.i18n.translate(
+ _('[${lenght} more items...]', mapping={'lenght':lenght}),
+ context=self.request, default=default)
+ append((name, self.context, False))
+ break
+ return items
diff --git a/src/z3c/jsontree/tests.py b/src/z3c/jsontree/tests.py
index 0ec5822..9a8c776 100644
--- a/src/z3c/jsontree/tests.py
+++ b/src/z3c/jsontree/tests.py
@@ -22,14 +22,18 @@
import zope.component
from zope.testing import doctest
from zope.publisher.browser import TestRequest
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.app.container.interfaces import IReadContainer
from zope.app.testing import setup
import z3c.testing
import z3c.jsonrpc.testing
+from z3c.jsonrpc.interfaces import IJSONRPCRequest
from z3c.jsontree import browser
from z3c.template.zcml import TemplateFactory
from z3c.template.interfaces import IContentTemplate
from z3c.jsontree import interfaces
+from z3c.jsontree import subitem
from z3c.jsontree.browser import tree
@@ -65,28 +69,44 @@ def __call__(self, context, request):
return self
+def setUpAdapters():
+ # register icon resource
+ zope.component.provideAdapter(ResourceStub,
+ name='z3cJSONTreeCollapsedIcon')
+ zope.component.provideAdapter(ResourceStub,
+ name='z3cJSONTreeExpandedIcon')
+ zope.component.provideAdapter(ResourceStub,
+ name='z3cJSONTreeStaticIcon')
+
+ # register icon
+ zope.component.provideAdapter(IconStub('z3cJSONTreeCollapsedIcon'),
+ (zope.interface.Interface, zope.interface.Interface),
+ provides=zope.interface.Interface, name='z3cJSONTreeCollapsedIcon')
+ zope.component.provideAdapter(IconStub('z3cJSONTreeExpandedIcon'),
+ (zope.interface.Interface, zope.interface.Interface),
+ provides=zope.interface.Interface, name='z3cJSONTreeExpandedIcon')
+ zope.component.provideAdapter(IconStub('z3cJSONTreeStaticIcon'),
+ (zope.interface.Interface, zope.interface.Interface),
+ provides=zope.interface.Interface, name='z3cJSONTreeStaticIcon')
+
+ # setup adapters
+ zope.component.provideAdapter(subitem.NoneTreeItems,
+ (zope.interface.Interface, IBrowserRequest))
+ zope.component.provideAdapter(subitem.ContainerTreeItems,
+ (IReadContainer, IBrowserRequest))
+ zope.component.provideAdapter(subitem.NoneTreeItems,
+ (zope.interface.Interface, IJSONRPCRequest))
+ zope.component.provideAdapter(subitem.ContainerTreeItems,
+ (IReadContainer, IJSONRPCRequest))
+
+
class TestSimpleJSONTree(z3c.testing.InterfaceBaseTest):
def setUp(test):
setup.placefulSetUp(True)
- # register icon resource
- zope.component.provideAdapter(ResourceStub,
- name='z3cJSONTreeCollapsedIcon')
- zope.component.provideAdapter(ResourceStub,
- name='z3cJSONTreeExpandedIcon')
- zope.component.provideAdapter(ResourceStub,
- name='z3cJSONTreeStaticIcon')
-
- # register icon
- zope.component.provideAdapter(IconStub('z3cJSONTreeCollapsedIcon'),
- (zope.interface.Interface, zope.interface.Interface),
- provides=zope.interface.Interface, name='z3cJSONTreeCollapsedIcon')
- zope.component.provideAdapter(IconStub('z3cJSONTreeExpandedIcon'),
- (zope.interface.Interface, zope.interface.Interface),
- provides=zope.interface.Interface, name='z3cJSONTreeExpandedIcon')
- zope.component.provideAdapter(IconStub('z3cJSONTreeStaticIcon'),
- (zope.interface.Interface, zope.interface.Interface),
- provides=zope.interface.Interface, name='z3cJSONTreeStaticIcon')
+
+ # setup adapters
+ setUpAdapters()
def tearDown(test):
setup.placefulTearDown()
@@ -105,24 +125,9 @@ class TestGenericJSONTree(z3c.testing.InterfaceBaseTest):
def setUp(test):
setup.placefulSetUp(True)
- # register icon resource
- zope.component.provideAdapter(ResourceStub,
- name='z3cJSONTreeCollapsedIcon')
- zope.component.provideAdapter(ResourceStub,
- name='z3cJSONTreeExpandedIcon')
- zope.component.provideAdapter(ResourceStub,
- name='z3cJSONTreeStaticIcon')
-
- # register icon
- zope.component.provideAdapter(IconStub('z3cJSONTreeCollapsedIcon'),
- (zope.interface.Interface, zope.interface.Interface),
- provides=zope.interface.Interface, name='z3cJSONTreeCollapsedIcon')
- zope.component.provideAdapter(IconStub('z3cJSONTreeExpandedIcon'),
- (zope.interface.Interface, zope.interface.Interface),
- provides=zope.interface.Interface, name='z3cJSONTreeExpandedIcon')
- zope.component.provideAdapter(IconStub('z3cJSONTreeStaticIcon'),
- (zope.interface.Interface, zope.interface.Interface),
- provides=zope.interface.Interface, name='z3cJSONTreeStaticIcon')
+
+ # setup adapters
+ setUpAdapters()
# register tree content provider
zope.component.provideAdapter(tree.TreeProvider, name='tree')
@@ -149,11 +154,15 @@ def getTestPos(self):
return (None, TestRequest())
+def setUp(test):
+ # setup adapters
+ setUpAdapters()
+
+
def test_suite():
return unittest.TestSuite((
z3c.jsonrpc.testing.FunctionalDocFileSuite('README.txt',
- setUp=z3c.jsonrpc.testing.setUp,
- tearDown=z3c.jsonrpc.testing.tearDown,
+ setUp=setUp,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
unittest.makeSuite(TestSimpleJSONTree),