[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