[Zope3-checkins] CVS: Zope3/src/zope/app/tree - CHANGES.txt:1.1
MAINTAINER.txt:1.1 README.txt:1.1 TODO.txt:1.1
__init__.py:1.1 adapters.py:1.1 configure.zcml:1.1
filters.py:1.1 interfaces.py:1.1 node.py:1.1 utils.py:1.1
version.txt:1.1
Philipp von Weitershausen
philikon at philikon.de
Thu Feb 19 15:43:06 EST 2004
Update of /cvs-repository/Zope3/src/zope/app/tree
In directory cvs.zope.org:/tmp/cvs-serv12688
Added Files:
CHANGES.txt MAINTAINER.txt README.txt TODO.txt __init__.py
adapters.py configure.zcml filters.py interfaces.py node.py
utils.py version.txt
Log Message:
Bring over zope.products.statictree to zope.app.
=== Added File Zope3/src/zope/app/tree/CHANGES.txt ===
CHANGES
=======
v1.2 (2004-02-19) -- 'Scruffy'
- Moved to zope.app.tree
- It is now called 'ZopeTree' again. Hoorray!
- Refactored browser stuff into its own browser subpackage.
- Separated the handling of tree state from the cookie functionality
to provide a base class for views based on other tree state sources.
v1.1 (2004-02-16) -- 'Zapp'
- Added support for displaying lines in a tree (Stephan Richter)
+ Changes in Node.getFlatDict() to provide more data. Removed
'depth' from node info, but added 'row-state' and
'last-level-node'. Changed interface and test accordingly.
+ Updated templates for StaticTree skin and example. Note that
third party templates from 1.0.x will not work anymore and must be
updated as well!
v1.0.1 (2004-01-16) -- 'Nibbler'
- Added last remaining pieces for unit tests
- Updated documentation
- Rounded some rough edges in the skin
- Integrated it into the Zope3 distribution below the zope.products
package
v1.0 (2003-11-24) -- 'Lur'
- Ported to Zope 3
- Renamed it to 'statictree'
- Much more unit tests
- Added filter functionality
- Provided sample implementations as well as an alternate
rotterdam-like skin using the static tree
=== Added File Zope3/src/zope/app/tree/MAINTAINER.txt ===
This package is maintained by:
Philipp "philiKON" von Weitershausen, philikon at philikon.de
=== Added File Zope3/src/zope/app/tree/README.txt ===
ZopeTree
========
What is ZopeTree?
-----------------
ZopeTree is a port of Philipp's Zope2 product ZopeTree. ZopeTree was
meant to be a light-weight and easy-to-use static tree implementation,
mainly designed for use in ZPTs. It was originally written because
Zope2's ZTUtils.Tree was found to be too complicated and inflexible.
The ZTUtils package has not been ported to Zope3. Parts of it, like
batching, have found their way into Zope3, though. Only support for
static tree generation is not in the core.
How to use it
-------------
Using the skin
--------------
ZopeTree comes with a pre-defined skin, StaticTree. It looks just
like Zope3's default skin, Rotterdam, except that it displays a static
tree in the navigation box instead of the Javascript/XML based dynamic
tree.
Using predefined views on objects
---------------------------------
ZopeTree comes with several predefined views:
- cookie_tree: simple view using cookies for tree state storage.
- folder_cookie_tree: same as above, however only showing folders.
- site_cookie_tree: same as above, with the nearest site as root node.
- root_cookie_tree: same as above, with the root container as root
node.
The example page template(s) in the 'example' directory give an idea
how to use these views for HTML templating.
Customization
-------------
The best way to customize ZopeTree is to define your own view for
objects (usually *). If you want to use the cookie functionality,
simply extend the cookie browser view::
from zope.app.tree.filters import OnlyInterfacesFilter
from zope.app.tree.browser.cookie import CookieTreeView
class BendableStaticTreeView(StaticTreeView):
def bendablesTree(self):
# tree with only IBendables, but also show the folder
# they're in
filter = OnlyInterfacesFilter(IBendable, IFolder)
return self.cookieTree(filter)
You can also write your own filters. All you have to do is implement
the IObjectFindFilter interface (which is trivial)::
from zope.interface import implements
from zope.app.interfaces.find import IObjectFindFilter
class BendableFilter:
implements(IObjectFindFilter)
def matches(self, obj)
# only allow bendable objects
return obj.isBendable()
License and Copyright
---------------------
This product is released under the terms of the Zope Public License
(ZPL) v2.0. See the 'ZopePublicLicense.txt' file at the root of your
Zope distribution.
Copyright (c) 2003 Philipp "philiKON" von Weitershausen
Copyright (c) 2004 Zope Corporation and Contributors
Credits
-------
Thanks to ZopeMag (http://zopemag.com) for sponsoring development of
the original ZopeTree product.
Thanks to Runyaga LLC (http://runyaga.com) for sponsoring the Zope3
port.
=== Added File Zope3/src/zope/app/tree/TODO.txt ===
TODO
for v1.3
- allow sorting: would work similar to filter
- add XML representation so it's compatible with rotterdam's
xmltree.js, but stateful
=== Added File Zope3/src/zope/app/tree/__init__.py ===
# make this directory a package
=== Added File Zope3/src/zope/app/tree/adapters.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Object adapters
This module contains adapters necessary to use common objects with
statictree. The most prominent ones are those for ILocation and
IContainer. We also provide adapters for any object, so we don't end
up with ComponentLookupErrors whenever encounter unknown
objects. Explicit is better than implement EXCEPT WHEN IT'S NOT...
$Id: adapters.py,v 1.1 2004/02/19 20:43:04 philikon Exp $
"""
from zope.interface import Interface, implements
from zope.component.exceptions import ComponentLookupError
from zope.app import zapi
from zope.app.interfaces.location import ILocation
from zope.app.interfaces.container import IReadContainer
from zope.app.interfaces.services.service import ISite
from interfaces import IUniqueId, IChildObjects
__metaclass__ = type
class StubUniqueId:
implements(IUniqueId)
__used_for__ = Interface
def __init__(self, context):
self.context = context
def getId(self):
# this does not work for persistent objects
return str(id(self.context))
class StubChildObjects:
implements(IChildObjects)
__used_for__ = Interface
def __init__(self, context):
pass
def hasChildren(self):
return False
def getChildObjects(self):
return ()
class LocationUniqueId:
implements(IUniqueId)
__used_for__ = ILocation
def __init__(self, context):
self.context = context
def getId(self):
context = self.context
if not context.__name__:
# always try to be unique
return str(id(context))
parents = [context.__name__]
parents += [parent.__name__ for parent in zapi.getParents(context)
if parent.__name__]
return '\\'.join(parents)
class ContainerChildObjects:
implements(IChildObjects)
__used_for__ = IReadContainer
def __init__(self, context):
self.context = context
def hasChildren(self):
return bool(len(self.context))
def getChildObjects(self):
return self.context.values()
class ContainerSiteChildObjects(ContainerChildObjects):
"""Adapter for read containers which are sites as well. The site
manager will be treated as just another child object.
"""
__used_for__ = ISite
def hasChildren(self):
if super(ContainerSiteChildObjects, self).hasChildren():
return True
try:
self.context.getSiteManager()
return True
except ComponentLookupError:
return False
def getChildObjects(self):
values = super(ContainerSiteChildObjects, self).getChildObjects()
try:
return [self.context.getSiteManager()] + list(values)
except ComponentLookupError:
return values
=== Added File Zope3/src/zope/app/tree/configure.zcml ===
<configure
xmlns="http://namespaces.zope.org/zope"
i18n_domain="zope"
>
<class class=".node.Node">
<allow interface=".interfaces.INode" />
</class>
<utility
provides=".interfaces.ITreeStateEncoder"
factory=".utils.TreeStateEncoder"
/>
<!-- stub adapters -->
<adapter
provides=".interfaces.IUniqueId"
for="*"
factory=".adapters.StubUniqueId"
/>
<adapter
provides=".interfaces.IChildObjects"
for="*"
factory=".adapters.StubChildObjects"
/>
<!-- adapters for zope.app.container machinery -->
<adapter
provides=".interfaces.IUniqueId"
for="zope.app.interfaces.location.ILocation"
factory=".adapters.LocationUniqueId"
/>
<adapter
provides=".interfaces.IChildObjects"
for="zope.app.interfaces.container.IReadContainer"
factory=".adapters.ContainerChildObjects"
/>
<adapter
provides=".interfaces.IChildObjects"
for="zope.app.interfaces.services.service.ISite"
factory=".adapters.ContainerSiteChildObjects"
/>
<include package=".browser" />
</configure>
=== Added File Zope3/src/zope/app/tree/filters.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Filters
Child objects can be filtered out by certain criteria which are
defined by a filter. Writing your own filter should be very easy. All
you have to implement is the IObjectFindFilter interface from the
zope.app.interfaces.find package. Already existing filters for the
find machinery may be used with statictree just as well.
Since commonly needed, this module provides two filters that filter by
interface.
$Id: filters.py,v 1.1 2004/02/19 20:43:04 philikon Exp $
"""
from zope.interface import implements
from zope.app.interfaces.find import IObjectFindFilter
__metaclass__ = type
class OnlyInterfacesFilter:
"""Only match objects that implement one of the given interfaces.
"""
implements(IObjectFindFilter)
only_interfaces = True
def __init__(self, *filterby):
self.ifaces = filterby
def matches(self, obj):
ifaces = self.ifaces
for iface in ifaces:
if iface.isImplementedBy(obj):
return self.only_interfaces
return not self.only_interfaces
class AllButInterfacesFilter(OnlyInterfacesFilter):
"""Match only objects that do not implement one of the given
interfaces.
"""
only_interfaces = False
=== Added File Zope3/src/zope/app/tree/interfaces.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Static tree interfaces
$Id: interfaces.py,v 1.1 2004/02/19 20:43:04 philikon Exp $
"""
from zope.interface import Interface, Attribute
from zope.schema import Bool, Int
class IUniqueId(Interface):
"""Interface that promises to return a unique id within a
tree.
Problem: How are implementing objects (most probably adapters)
supposed to know, whether their id is unique in the context? Well,
they just have to be damn sure that they are unique.
"""
def getId():
"""Return a string containing a unique id within a tree
"""
class IChildObjects(Interface):
"""Interface providing methods to retrieve child objects so they
can be wrapped in tree nodes.
"""
def hasChildren():
"""Return true if child objects are available
"""
def getChildObjects():
"""Return a sequence of child objects
"""
class INode(IUniqueId, IChildObjects):
"""A node in the tree
"""
context = Attribute("""
The object that is being wrapped.
""")
depth = Int(
title=u"Depth",
description=u"The positional depth of this node in the tree.",
)
expanded = Bool(
title=u"Expanded",
description=u"True if this node is expanded.",
)
def expand(recursive=False):
"""Expand this node.
'recursive' can be set to True to expand all child nodes as
well
"""
def collapse():
"""Collapse this node.
"""
def getChildNodes():
"""Return a sequence of children nodes if the node is expanded.
"""
def getFlatNodes():
"""Return a flat list of nodes in the tree. Children of
expanded nodes are shown.
"""
def getFlatDicts():
"""Return information of all nodes in a flat tuple and the maximum
depth.
The tuple consists of node information dictionaries. Each directionary
has the following keys:
- 'node': This is the node itself.
- 'tree-state': A hash value that uniquely identifies the expansion
state of the node.
- 'row-state': When representing the node in a GUI it is necessary
to know whether the levels higher up are opened or not. We use
this information to decide whether we should or should not draw a
vertical line in the tree.
The 'row-state' value is simply a list of 'True' and
'False'. 'True' signifies that a level is open and more elements
of this level are expected further down.
- 'last-level-node': A boolean that signifies whether a node is the
last node of its level.
This method is intended for output formats that cannot handle nested
values easily. An example here are Page Templates.
"""
class ITreeStateEncoder(Interface):
"""This utility can encode and decode the ids of expanded nodes
"""
def encodeTreeState(expanded_nodes):
"""Encode the tree expansion information in 'expanded_nodes'.
"""
def decodeTreeState(tree_state):
"""Decode the tree expansion information 'tree_state'.
"""
=== Added File Zope3/src/zope/app/tree/node.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""A node in the treee
$Id: node.py,v 1.1 2004/02/19 20:43:04 philikon Exp $
"""
from zope.interface import implements
from zope.app import zapi
from interfaces import INode, IUniqueId, IChildObjects, \
ITreeStateEncoder
__metaclass__ = type
class Node:
"""A tree node
This object represents a node in the tree. It wraps the actual
object and provides the INode interface to be relied on. In that
way, it works similar to an adapter.
This implementation is designed to be as lazy as
possible. Especially, it will only create child nodes when
necessary.
"""
implements(INode)
__slots__ = (
'context', 'expanded', 'filter', '_id', '_expanded_nodes',
'_child_nodes', '_child_objects_adapter',
)
def __init__(self, context, expanded_nodes=[], filter=None):
self.context = context
self.expanded = False
self.filter = filter
self._expanded_nodes = expanded_nodes
self._id = id = zapi.getAdapter(context, IUniqueId).getId()
if id in expanded_nodes:
self.expand()
def _create_child_nodes(self):
"""Create child nodes and save the result so we don't have
to create that sequence every time"""
nodes = []
for obj in self.getChildObjects():
node = Node(obj, self._expanded_nodes, self.filter)
nodes.append(node)
self._child_nodes = nodes
def _get_child_objects_adapter(self):
"""Lazily create the child objects adapter"""
if not hasattr(self, '_child_objects_adapter'):
self._child_objects_adapter = zapi.getAdapter(
self.context, IChildObjects)
return self._child_objects_adapter
def expand(self, recursive=False):
"""See zope.app.tree.interfaces.INode"""
self.expanded = True
if recursive:
for node in self.getChildNodes():
node.expand(True)
def collapse(self):
"""See zope.app.tree.interfaces.INode"""
self.expanded = False
def getId(self):
"""See zope.app.tree.interfaces.INode"""
return self._id
def hasChildren(self):
"""See the zope.app.tree.interfaces.INode"""
# we could actually test for the length of the result of
# getChildObjects(), but we need to watch performance
return self._get_child_objects_adapter().hasChildren()
def getChildObjects(self):
"""See the zope.app.tree.interfaces.INode"""
filter = self.filter
children = self._get_child_objects_adapter().getChildObjects()
if filter:
return [child for child in children if filter.matches(child)]
return children
def getChildNodes(self):
"""See zope.app.tree.interfaces.INode"""
if not self.expanded:
return []
if not hasattr(self, '_child_nodes'):
# children nodes are not created until they are explicitly
# requested through this method
self._create_child_nodes()
return self._child_nodes[:]
def getFlatNodes(self):
"""See zope.app.tree.interfaces.INode"""
nodes = []
for node in self.getChildNodes():
nodes.append(node)
nodes += node.getFlatNodes()
return nodes
def getFlatDicts(self, maxdepth=0, row_state=None):
"""See zope.app.tree.interfaces.INode"""
nodes = []
if row_state is None:
row_state = []
encoder = zapi.getUtility(self.context, ITreeStateEncoder)
if self.hasChildren() and len(row_state) > maxdepth:
maxdepth = len(row_state)
childNodes = self.getChildNodes()
for node in childNodes:
id = node.getId()
expanded_nodes = self._expanded_nodes[:]
if id in self._expanded_nodes:
# if the node is already expanded, the toggle would
# collapse it
expanded_nodes.remove(id)
row_state.append(not node is childNodes[-1])
else:
# if it isn't expanded, the toggle would expand it
expanded_nodes += [id]
row_state.append(False)
flatdict = {
'node': node,
'tree-state': encoder.encodeTreeState(expanded_nodes),
'row-state': row_state[:-1],
'last-level-node': node is childNodes[-1],
}
nodes.append(flatdict)
child_nodes, maxdepth = node.getFlatDicts(maxdepth, row_state)
nodes += child_nodes
row_state.pop()
return nodes, maxdepth
=== Added File Zope3/src/zope/app/tree/utils.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Static tree utilities
$Id: utils.py,v 1.1 2004/02/19 20:43:04 philikon Exp $
"""
import zlib
from zope.interface import implements
from interfaces import ITreeStateEncoder
class TreeStateEncoder:
"""Encodes tree state
>>> expanded_nodes = ['a', 'c', 'foobar']
>>> encoder = TreeStateEncoder()
>>> encoded = encoder.encodeTreeState(expanded_nodes)
>>> decoded = encoder.decodeTreeState(encoded)
>>> decoded == expanded_nodes
True
"""
implements(ITreeStateEncoder)
# note that this implementation relies on the node ids not
# containing colons
def encodeTreeState(self, expanded_nodes):
tree_state = ":".join(expanded_nodes)
tree_state = zlib.compress(tree_state)
return b2a(tree_state)
def decodeTreeState(self, tree_state):
tree_state = a2b(tree_state)
tree_state = zlib.decompress(tree_state)
return tree_state.split(":")
#
# The following code has been taken unchanged from Zope2's
# ZTUtils.Tree module
#
from binascii import b2a_base64, a2b_base64
from string import translate, maketrans
a2u_map = maketrans('+/=', '-._')
u2a_map = maketrans('-._', '+/=')
def b2a(s):
'''Encode a value as a cookie- and url-safe string.
Encoded string use only alpahnumeric characters, and "._-".
'''
s = str(s)
if len(s) <= 57:
return translate(b2a_base64(s)[:-1], a2u_map)
frags = []
for i in range(0, len(s), 57):
frags.append(b2a_base64(s[i:i + 57])[:-1])
return translate(''.join(frags), a2u_map)
def a2b(s):
'''Decode a b2a-encoded string.'''
s = translate(s, u2a_map)
if len(s) <= 76:
return a2b_base64(s)
frags = []
for i in range(0, len(s), 76):
frags.append(a2b_base64(s[i:i + 76]))
return ''.join(frags)
=== Added File Zope3/src/zope/app/tree/version.txt ===
statictree 1.1
More information about the Zope3-Checkins
mailing list