[Checkins] SVN: zope.browsermenu/trunk/ Initial import.

Dan Korostelev nadako at gmail.com
Mon Aug 24 10:27:27 EDT 2009


Log message for revision 103151:
  Initial import.

Changed:
  _U  zope.browsermenu/trunk/
  A   zope.browsermenu/trunk/CHANGES.txt
  A   zope.browsermenu/trunk/README.txt
  A   zope.browsermenu/trunk/bootstrap.py
  A   zope.browsermenu/trunk/buildout.cfg
  A   zope.browsermenu/trunk/setup.py
  A   zope.browsermenu/trunk/src/
  A   zope.browsermenu/trunk/src/zope/
  A   zope.browsermenu/trunk/src/zope/__init__.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/
  A   zope.browsermenu/trunk/src/zope/browsermenu/__init__.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/configure.zcml
  A   zope.browsermenu/trunk/src/zope/browsermenu/fields.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/interfaces.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/menu.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/menu.txt
  A   zope.browsermenu/trunk/src/zope/browsermenu/meta.zcml
  A   zope.browsermenu/trunk/src/zope/browsermenu/metaconfigure.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/metadirectives.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/__init__.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/addmenuitems.zcml
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/menus-permissions.zcml
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/menus.zcml
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/test_addMenuItem.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/test_directives.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/test_fields.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menu.py
  A   zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menudirectives.py

-=-

Property changes on: zope.browsermenu/trunk
___________________________________________________________________
Added: svn:ignore
   + bin
coverage
develop-eggs
parts
.installed.cfg


Added: zope.browsermenu/trunk/CHANGES.txt
===================================================================
--- zope.browsermenu/trunk/CHANGES.txt	                        (rev 0)
+++ zope.browsermenu/trunk/CHANGES.txt	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+3.9.0 (unreleased)
+==================
+
+Initial release. This package was splitted off zope.app.publisher.


Property changes on: zope.browsermenu/trunk/CHANGES.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/README.txt
===================================================================
--- zope.browsermenu/trunk/README.txt	                        (rev 0)
+++ zope.browsermenu/trunk/README.txt	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,10 @@
+========
+Overview
+========
+
+*This package is at present not reusable without depending on a large
+chunk of the Zope Toolkit and its assumptions. It is maintained by the*
+`Zope Toolkit project <http://docs.zope.org/zopetoolkit/>`_.
+
+This package provides an implementation of browser menus and ZCML directives
+for configuring them.


Property changes on: zope.browsermenu/trunk/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/bootstrap.py
===================================================================
--- zope.browsermenu/trunk/bootstrap.py	                        (rev 0)
+++ zope.browsermenu/trunk/bootstrap.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 73800 2007-03-27 16:16:42Z dobe $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: zope.browsermenu/trunk/bootstrap.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/buildout.cfg
===================================================================
--- zope.browsermenu/trunk/buildout.cfg	                        (rev 0)
+++ zope.browsermenu/trunk/buildout.cfg	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,22 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report pydev
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = zope.browsermenu [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = zope.browsermenu [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
+
+[pydev]
+recipe = pb.recipes.pydev
+eggs = zope.browsermenu

Added: zope.browsermenu/trunk/setup.py
===================================================================
--- zope.browsermenu/trunk/setup.py	                        (rev 0)
+++ zope.browsermenu/trunk/setup.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,60 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+"""zope.app.publisher setup
+"""
+from setuptools import setup, find_packages, Extension
+
+long_description = (open('README.txt').read() + '\n\n' +
+                    open('CHANGES.txt').read())
+
+setup(name='zope.browsermenu',
+      version = '3.9.0dev',
+      url='http://pypi.python.org/pypi/zope.browsermenu/',
+      author='Zope Corporation and Contributors',
+      author_email='zope-dev at zope.org',
+      classifiers = ['Environment :: Web Environment',
+                     'Intended Audience :: Developers',
+                     'License :: OSI Approved :: Zope Public License',
+                     'Programming Language :: Python',
+                     'Operating System :: OS Independent',
+                     'Topic :: Internet :: WWW/HTTP',
+                     'Framework :: Zope3',
+                     ],
+      description='Implementations and means for configuration of Zope 3-'
+                  'style views and resources.',
+      long_description=long_description,
+
+      packages=find_packages('src'),
+      package_dir={'': 'src'},
+
+      namespace_packages=['zope'],
+      include_package_data=True,
+      install_requires=['setuptools',
+                        'zope.component>=3.7.0',
+                        'zope.configuration',
+                        'zope.i18nmessageid',
+                        'zope.interface',
+                        'zope.pagetemplate>=3.5.0',
+                        'zope.publisher>=3.8.0',
+                        'zope.schema',
+                        'zope.security[untrustedpython]',
+                        'zope.traversing>3.7.0',
+                        'zope.browser',
+                        ],
+      extras_require={
+          'test': ['zope.testing'],
+          },
+
+      zip_safe = False,
+      )


Property changes on: zope.browsermenu/trunk/setup.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/__init__.py
===================================================================
--- zope.browsermenu/trunk/src/zope/__init__.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/__init__.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)


Property changes on: zope.browsermenu/trunk/src/zope/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/configure.zcml
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/configure.zcml	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/configure.zcml	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,14 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <interface interface=".interfaces.IMenuItemType" />
+
+  <view
+      for="*"
+      type="zope.publisher.interfaces.browser.IDefaultBrowserLayer"
+      name="view_get_menu"
+      factory=".menu.MenuAccessView"
+      allowed_interface=".interfaces.IMenuAccessView"
+      permission="zope.Public"
+      />
+
+</configure>


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/configure.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/src/zope/browsermenu/fields.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/fields.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/fields.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,109 @@
+#############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Browser-Presentation related Fields.
+
+$Id: fields.py 100240 2009-05-22 16:33:41Z faassen $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.component import queryUtility
+from zope.component.interfaces import ComponentLookupError
+from zope.configuration.exceptions import ConfigurationError
+from zope.configuration.fields import GlobalObject
+from zope.schema import ValidationError
+
+from zope.browsermenu.interfaces import IMenuItemType
+
+
+class MenuField(GlobalObject):
+    r"""This fields represents a menu (item type).
+
+    Besides being able to look up the menu by importing it, we also try
+    to look up the name in the site manager.
+
+    >>> from zope.interface import directlyProvides
+    >>> from zope.interface.interface import InterfaceClass
+
+    >>> menu1 = InterfaceClass('menu1', (),
+    ...                        __doc__='Menu Item Type: menu1',
+    ...                        __module__='zope.app.menus')
+    >>> directlyProvides(menu1, IMenuItemType)
+
+    >>> menus = None
+    >>> class Resolver(object):
+    ...     def resolve(self, path):
+    ...         if path.startswith('zope.app.menus') and \
+    ...             hasattr(menus, 'menu1') or \
+    ...             path == 'zope.browsermenu.menus.menu1':
+    ...             return menu1
+    ...         raise ConfigurationError('menu1')
+
+    >>> field = MenuField()
+    >>> field = field.bind(Resolver())
+
+    Test 1: Import the menu
+    -----------------------
+
+    >>> field.fromUnicode('zope.browsermenu.menus.menu1') is menu1
+    True
+
+    Test 2: We have a shortcut name. Import the menu from `zope.app.menus1`.
+    ------------------------------------------------------------------------
+
+    >>> from types import ModuleType as module
+    >>> import sys
+    >>> menus = module('menus')
+    >>> old = sys.modules.get('zope.app.menus', None)
+    >>> sys.modules['zope.app.menus'] = menus
+    >>> setattr(menus, 'menu1', menu1)
+
+    >>> field.fromUnicode('menu1') is menu1
+    True
+
+    >>> if old is not None:
+    ...     sys.modules['zope.app.menus'] = old
+
+    Test 3: Get the menu from the Site Manager
+    ------------------------------------------
+    
+    >>> from zope.component import provideUtility
+    >>> provideUtility(menu1, IMenuItemType, 'menu1')
+
+    >>> field.fromUnicode('menu1') is menu1
+    True
+    """
+
+    def fromUnicode(self, u):
+        name = str(u.strip())
+
+        try:
+            value = queryUtility(IMenuItemType, name)
+        except ComponentLookupError:
+            # The component architecture is not up and running.
+            pass
+        else: 
+            if value is not None:
+                self.validate(value)
+                return value
+
+        try:
+            value = self.context.resolve('zope.app.menus.'+name)
+        except ConfigurationError, v:
+            try:
+                value = self.context.resolve(name)
+            except ConfigurationError, v:
+                raise ValidationError(v)
+        
+        self.validate(value)
+        return value


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/fields.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/interfaces.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/interfaces.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/interfaces.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,161 @@
+##############################################################################
+#
+# Copyright (c) 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.
+#
+##############################################################################
+"""Browser-Specific Publisher interfaces
+
+$Id: browser.py 97455 2009-03-03 19:57:53Z nadako $
+"""
+from zope.i18nmessageid import ZopeMessageFactory as _
+from zope.interface import Interface, directlyProvides
+from zope.interface.interfaces import IInterface
+from zope.schema import TextLine, Text, URI, Int
+
+
+class IMenuItemType(IInterface):
+    """Menu item type
+
+    Menu item types are interfaces that define classes of
+    menu items.
+    """
+
+class AddMenu(Interface):
+    """Special menu for providing a list of addable objects."""
+
+directlyProvides(AddMenu, IMenuItemType)
+
+
+class IBrowserMenu(Interface):
+    """Menu
+
+    Menus are objects that can return a list of menu items they contain. How
+    they generate this list is up to them. Commonly, however, they will look
+    up adapters that provide the ``IBrowserMenuItem`` interface.
+    """
+
+    id = TextLine(
+        title=_("Menu Id"),
+        description=_("The id uniquely identifies this menu."),
+        required=True
+        )
+
+    title = TextLine(
+        title=_("Menu title"),
+        description=_("The title provides the basic label for the menu."),
+        required=False
+        )
+
+    description = Text(
+        title=_("Menu description"),
+        description=_("A description of the menu. This might be shown "
+                      "on menu pages or in pop-up help for menus."),
+        required=False
+        )
+
+    def getMenuItems(object, request):
+        """Return a TAL-friendly list of menu items.
+
+        The object (acts like the context) and request can be used to select
+        the items that are available.
+        """
+
+
+class IBrowserMenuItem(Interface):
+    """Menu type
+
+    An interface that defines a menu.
+    """
+
+    title = TextLine(
+        title=_("Menu item title"),
+        description=_("The title provides the basic label for the menu item."),
+        required=True
+        )
+
+    description = Text(
+        title=_("Menu item description"),
+        description=_("A description of the menu item. This might be shown "
+                      "on menu pages or in pop-up help for menu items."),
+        required=False
+        )
+
+    action = TextLine(
+        title=_("The URL to display if the item is selected"),
+        description=_("When a user selects a browser menu item, the URL"
+                      "given in the action is displayed. The action is "
+                      "usually given as a relative URL, relative to the "
+                      "object the menu item is for."),
+       required=True
+       )
+
+    order = Int(
+        title=_("Menu item ordering hint"),
+        description=_("This attribute provides a hint for menu item ordering."
+                      "Menu items will generally be sorted by the `for_`"
+                      "attribute and then by the order.")
+        )
+
+    filter_string = TextLine(
+        title=_("A condition for displaying the menu item"),
+        description=_("The condition is given as a TALES expression. The "
+                      "expression has access to the variables:\n"
+                      "\n"
+                      "context -- The object the menu is being displayed "
+                      "for\n"
+                      "\n"
+                      "request -- The browser request\n"
+                      "\n"
+                      "nothing -- None\n"
+                      "\n"
+                      "The menu item will not be displayed if there is a \n"
+                      "filter and the filter evaluates to a false value."),
+        required=False)
+
+    icon = URI(
+        title=_("Icon URI"),
+        description=_("URI of the icon representing this menu item"))
+       
+    def available():
+        """Test whether the menu item should be displayed
+        
+        A menu item might not be available for an object, for example
+        due to security limitations or constraints.
+        """
+
+class IBrowserSubMenuItem(IBrowserMenuItem):
+    """A menu item that points to a sub-menu."""
+
+    submenuId = TextLine(
+        title=_("Sub-Menu Id"),
+        description=_("The menu id of the menu that describes the "
+                      "sub-menu below this item."),
+        required=True)
+        
+    action = TextLine(
+        title=_("The URL to display if the item is selected"),
+        description=_("When a user selects a browser menu item, the URL "
+                      "given in the action is displayed. The action is "
+                      "usually given as a relative URL, relative to the "
+                      "object the menu item is for."),
+       required=False
+       )
+
+
+class IMenuAccessView(Interface):
+    """View that provides access to menus"""
+
+    def __getitem__(menu_id):
+        """Get menu information
+
+        Return a sequence of dictionaries with labels and
+        actions, where actions are relative URLs.
+        """


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/interfaces.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/menu.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/menu.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/menu.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,199 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Menu Registration code.
+
+$Id: menu.py 100372 2009-05-25 19:31:23Z tseaver $
+"""
+__docformat__ = "reStructuredText"
+import sys
+
+from zope.component import getAdapters, getUtility
+from zope.interface import Interface, implements, providedBy
+from zope.interface.interfaces import IInterface
+from zope.pagetemplate.engine import Engine
+from zope.publisher.browser import BrowserView
+from zope.security import canAccess, checkPermission
+from zope.security.interfaces import Forbidden, Unauthorized
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing.publicationtraverse import PublicationTraverser
+
+from zope.browsermenu.interfaces import IBrowserMenu, IMenuItemType
+from zope.browsermenu.interfaces import IBrowserMenuItem, IBrowserSubMenuItem
+from zope.browsermenu.interfaces import IMenuAccessView
+
+class BrowserMenu(object):
+    """Browser Menu"""
+    implements(IBrowserMenu)
+
+    def __init__(self, id, title=u'', description=u''):
+        self.id = id
+        self.title = title
+        self.description = description
+
+    def getMenuItemType(self):
+        return getUtility(IMenuItemType, self.id)
+
+    def getMenuItems(self, object, request):
+        """Return menu item entries in a TAL-friendly form."""
+
+        result = []
+        for name, item in getAdapters((object, request),
+                                      self.getMenuItemType()):
+            if item.available():
+                result.append(item)
+
+        # Now order the result. This is not as easy as it seems.
+        #
+        # (1) Look at the interfaces and put the more specific menu entries
+        #     to the front. 
+        # (2) Sort unambigious entries by order and then by title.
+        ifaces = list(providedBy(removeSecurityProxy(object)).__iro__)
+        max_key = len(ifaces)
+        def iface_index(item):
+            iface = item._for
+            if not iface:
+                iface = Interface
+            if IInterface.providedBy(iface):
+                return ifaces.index(iface)
+            if isinstance(removeSecurityProxy(object), item._for):
+                # directly specified for class, this goes first.
+                return -1
+            # no idea. This goes last.
+            return max_key
+        result = [(iface_index(item), item.order, item.title, item)
+                  for item in result]
+        result.sort()
+
+        result = [
+            {'title': title,
+             'description': item.description,
+             'action': item.action,
+             'selected': (item.selected() and u'selected') or u'',
+             'icon': item.icon,
+             'extra': item.extra,
+             'submenu': (IBrowserSubMenuItem.providedBy(item) and
+                         getMenu(item.submenuId, object, request)) or None}
+            for index, order, title, item in result]
+
+        return result
+
+
+class BrowserMenuItem(BrowserView):
+    """Browser Menu Item Class"""
+    implements(IBrowserMenuItem)
+
+    title = u''
+    description = u''
+    action = u''
+    extra = None
+    order = 0
+    permission = None
+    filter = None
+    icon = None
+    _for = Interface
+
+    def available(self):
+        # Make sure we have the permission needed to access the menu's action
+        if self.permission is not None:
+            # If we have an explicit permission, check that we
+            # can access it.
+            if not checkPermission(self.permission, self.context):
+                return False
+
+        elif self.action != u'':
+            # Otherwise, test access by attempting access
+            path = self.action
+            l = self.action.find('?')
+            if l >= 0:
+                path = self.action[:l]
+
+            traverser = PublicationTraverser()
+            try:
+                view = traverser.traverseRelativeURL(
+                    self.request, self.context, path)
+            except (Unauthorized, Forbidden, LookupError):
+                return False
+            else:
+                # we're assuming that view pages are callable
+                # this is a pretty sound assumption
+                if not canAccess(view, '__call__'):
+                    return False
+
+        # Make sure that we really want to see this menu item
+        if self.filter is not None:
+
+            try:
+                include = self.filter(Engine.getContext(
+                    context = self.context,
+                    nothing = None,
+                    request = self.request,
+                    modules = sys.modules,
+                    ))
+            except Unauthorized:
+                return False
+            else:
+                if not include:
+                    return False
+
+        return True
+
+    def selected(self):
+        request_url = self.request.getURL()
+
+        normalized_action = self.action
+        if self.action.startswith('@@'):
+            normalized_action = self.action[2:]
+
+        if request_url.endswith('/'+normalized_action):
+            return True
+        if request_url.endswith('/++view++'+normalized_action):
+            return True
+        if request_url.endswith('/@@'+normalized_action):
+            return True
+
+        return False
+
+
+class BrowserSubMenuItem(BrowserMenuItem):
+    """Browser Menu Item Base Class"""
+    implements(IBrowserSubMenuItem)
+
+    submenuId = None
+
+    def selected(self):
+        if self.action is u'':
+            return False
+        return super(BrowserSubMenuItem, self).selected()
+
+
+def getMenu(id, object, request):
+    """Return menu item entries in a TAL-friendly form."""
+    menu = getUtility(IBrowserMenu, id)
+    return menu.getMenuItems(object, request)
+
+
+def getFirstMenuItem(id, object, request):
+    """Get the first item of a menu."""
+    items = getMenu(id, object, request)
+    if items:
+        return items[0]
+    return None
+
+
+class MenuAccessView(BrowserView):
+    """A view allowing easy access to menus."""
+    implements(IMenuAccessView)
+
+    def __getitem__(self, menuId):
+        return getMenu(menuId, self.context, self.request)


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/menu.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/menu.txt
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/menu.txt	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/menu.txt	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,633 @@
+=============
+Browser Menus
+=============
+
+Browser menus are used to categorize browser actions, such as the views of a
+content component or the addable components of a container. In essence they
+provide the same functionality as menu bars in desktop application.
+
+  >>> from zope.browsermenu import menu, metaconfigure
+
+Menus are simple components that have an id, title and description. They also
+must provide a method called ``getMenuItems(object, request)`` that returns a
+TAL-friendly list of information dictionaries. We will see this in detail
+later. The default menu implementation, however, makes the menu be very
+transparent by identifying the menu through an interface. So let's define and
+register a simple edit menu:
+
+  >>> import zope.interface
+  >>> class EditMenu(zope.interface.Interface):
+  ...     """This is an edit menu."""
+
+  >>> from zope.browsermenu.interfaces import IMenuItemType
+  >>> zope.interface.directlyProvides(EditMenu, IMenuItemType)
+
+  >>> from zope.component import provideUtility
+  >>> provideUtility(EditMenu, IMenuItemType, 'edit')
+
+Now we have to create and register the menu itself:
+
+  >>> from zope.browsermenu.interfaces import IBrowserMenu
+  >>> provideUtility(
+  ...     menu.BrowserMenu('edit', u'Edit', u'Edit Menu'), IBrowserMenu, 'edit')
+
+Note that these steps seem like a lot of boilerplate, but all this work is
+commonly done for you via ZCML. An item in a menu is simply an adapter that
+provides. In the following section we will have a closer look at the browser
+menu item:
+
+``BrowserMenuItem`` class
+-------------------------
+
+The browser menu item represents an entry in the menu. Essentially, the menu
+item is a browser view of a content component. Thus we have to create a
+content component first:
+
+  >>> class IContent(zope.interface.Interface):
+  ...     pass
+
+  >>> from zope.publisher.interfaces.browser import IBrowserPublisher
+  >>> from zope.security.interfaces import Unauthorized, Forbidden
+
+  >>> class Content(object):
+  ...     zope.interface.implements(IContent, IBrowserPublisher)
+  ...
+  ...     def foo(self):
+  ...         pass
+  ...
+  ...     def browserDefault(self, r):
+  ...         return self, ()
+  ...
+  ...     def publishTraverse(self, request, name):
+  ...         if name.startswith('fb'):
+  ...             raise Forbidden, name
+  ...         if name.startswith('ua'):
+  ...             raise Unauthorized, name
+  ...         if name.startswith('le'):
+  ...             raise LookupError, name
+  ...         return self.foo
+
+We also implemented the ``IBrowserPublisher`` interface, because we want to
+make the object traversable, so that we can make availability checks later.
+
+Since the ``BrowserMenuItem`` is just a view, we can initiate it with an
+object and a request.
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> item = menu.BrowserMenuItem(Content(), TestRequest())
+
+Note that the menu item knows *nothing* about the menu itself. It purely
+depends on the adapter registration to determine in which menu it will
+appear. The advantage is that a menu item can be reused in several menus.
+
+Now we add a title, description, order and icon and see whether we can then
+access the value. Note that these assignments are always automatically done by
+the framework.
+
+  >>> item.title = u'Item 1'
+  >>> item.title
+  u'Item 1'
+
+  >>> item.description = u'This is Item 1.'
+  >>> item.description
+  u'This is Item 1.'
+
+  >>> item.order
+  0
+  >>> item.order = 1
+  >>> item.order
+  1
+
+  >>> item.icon is None
+  True
+  >>> item.icon = u'/@@/icon.png'
+  >>> item.icon
+  u'/@@/icon.png'
+
+Since there is no permission or view specified yet, the menu item should
+be available and not selected.
+
+  >>> item.available()
+  True
+  >>> item.selected()
+  False
+
+There are two ways to deny availability of a menu item: (1) the current
+user does not have the correct permission to access the action or the menu
+item itself, or (2) the filter returns ``False``, in which case the menu
+item should also not be shown. 
+
+  >>> from zope.security.interfaces import IPermission
+  >>> from zope.security.permission import Permission
+  >>> perm = Permission('perm', 'Permission')
+  >>> provideUtility(perm, IPermission, 'perm')
+
+  >>> class ParticipationStub(object):
+  ...     principal = 'principal'
+  ...     interaction = None
+
+
+In the first case, the permission of the menu item was explicitely
+specified. Make sure that the user needs this permission to make the menu
+item available.
+
+  >>> item.permission = perm
+
+Now, we are not setting any user. This means that the menu item should be
+available.
+
+  >>> from zope.security.management import newInteraction, endInteraction
+  >>> endInteraction()
+  >>> newInteraction()
+  >>> item.available()
+  True
+
+Now we specify a principal that does not have the specified permission.
+
+  >>> endInteraction()
+  >>> newInteraction(ParticipationStub())
+  >>> item.available()
+  False
+
+In the second case, the permission is not explicitely defined and the
+availability is determined by the permission required to access the
+action.
+
+  >>> item.permission = None
+
+  All views starting with 'fb' are forbidden, the ones with 'ua' are
+  unauthorized and all others are allowed.
+
+  >>> item.action = u'fb'
+  >>> item.available()
+  False
+  >>> item.action = u'ua'
+  >>> item.available()
+  False
+  >>> item.action = u'a'
+  >>> item.available()
+  True
+
+Also, sometimes a menu item might be registered for a view that does not
+exist. In those cases the traversal mechanism raises a `TraversalError`, which
+is a special type of `LookupError`. All actions starting with `le` should
+raise this error:
+
+  >>> item.action = u'le'
+  >>> item.available()
+  False
+
+Now let's test filtering. If the filter is specified, it is assumed to be
+a TALES obejct.
+
+  >>> from zope.pagetemplate.engine import Engine
+  >>> item.action = u'a'
+  >>> item.filter = Engine.compile('not:context')
+  >>> item.available()
+  False
+  >>> item.filter = Engine.compile('context')
+  >>> item.available()
+  True
+
+Finally, make sure that the menu item can be selected.
+
+  >>> item.request = TestRequest(SERVER_URL='http://127.0.0.1/@@view.html',
+  ...                            PATH_INFO='/@@view.html')
+
+  >>> item.selected()
+  False
+  >>> item.action = u'view.html'
+  >>> item.selected()
+  True
+  >>> item.action = u'@@view.html'
+  >>> item.selected()
+  True
+  >>> item.request = TestRequest(
+  ...     SERVER_URL='http://127.0.0.1/++view++view.html',
+  ...     PATH_INFO='/++view++view.html')
+  >>> item.selected()
+  True
+  >>> item.action = u'otherview.html'
+  >>> item.selected()
+  False
+
+
+``BrowserSubMenuItem`` class
+----------------------------
+
+The menu framework also allows for submenus. Submenus can be inserted by
+creating a special menu item that simply points to another menu to be
+inserted:
+
+  >>> item = menu.BrowserSubMenuItem(Content(), TestRequest())
+
+The framework will always set the sub-menu automatically (we do it
+manually here):
+
+  >>> class SaveOptions(zope.interface.Interface):
+  ...     "A sub-menu that describes available save options for the content."
+
+  >>> zope.interface.directlyProvides(SaveOptions, IMenuItemType)
+
+  >>> provideUtility(SaveOptions, IMenuItemType, 'save')
+  >>> provideUtility(menu.BrowserMenu('save', u'Save', u'Save Menu'),
+  ...                IBrowserMenu, 'save')
+
+Now we can assign the sub-menu id to the menu item: 
+
+  >>> item.submenuId = 'save'
+
+Also, the ``action`` attribute for the browser sub-menu item is optional,
+because you often do not want the item itself to represent something. The rest
+of the class is identical to the ``BrowserMenuItem`` class.
+
+
+Getting a Menu
+--------------
+
+Now that we know how the single menu item works, let's have a look at how menu
+items get put together to a menu. But let's first create some menu items and
+register them as adapters with the component architecture.
+
+Register the edit menu entries first. We use the menu item factory to create
+the items:
+
+  >>> from zope.component import provideAdapter
+  >>> from zope.publisher.interfaces.browser import IBrowserRequest
+
+  >>> undo = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Undo", 
+  ...                                 action="undo.html")
+  >>> provideAdapter(undo, (IContent, IBrowserRequest), EditMenu, 'undo')
+
+  >>> redo = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Redo",
+  ...                                 action="redo.html", icon="/@@/redo.png")
+  >>> provideAdapter(redo, (IContent, IBrowserRequest), EditMenu, 'redo')
+
+  >>> save = metaconfigure.MenuItemFactory(menu.BrowserSubMenuItem, title="Save", 
+  ...                                 submenuId='save', order=2)
+  >>> provideAdapter(save, (IContent, IBrowserRequest), EditMenu, 'save')
+
+And now the save options:
+
+  >>> saveas = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Save as", 
+  ...                                   action="saveas.html")
+  >>> provideAdapter(saveas, (IContent, IBrowserRequest), 
+  ...                SaveOptions, 'saveas')
+
+  >>> saveall = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Save all",
+  ...                                    action="saveall.html")
+  >>> provideAdapter(saveall, (IContent, IBrowserRequest), 
+  ...                SaveOptions, 'saveall')
+
+Note that we can also register menu items for classes:
+
+
+  >>> new = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="New",
+  ...                                 action="new.html", _for=Content)
+  >>> provideAdapter(new, (Content, IBrowserRequest), EditMenu, 'new')
+
+
+The utility that is used to generate the menu into a TAL-friendly
+data-structure is ``getMenu()``::
+
+  getMenu(menuId, object, request)
+
+where ``menuId`` is the id originally specified for the menu. Let's look up the
+menu now:
+
+  >>> pprint(menu.getMenu('edit', Content(), TestRequest()))
+  [{'action': 'new.html',
+    'description': u'',
+    'extra': None,
+    'icon': None,
+    'selected': u'',
+    'submenu': None,
+    'title': 'New'},
+  {'action': 'redo.html',
+    'description': u'',
+    'extra': None,
+    'icon': '/@@/redo.png',
+    'selected': u'',
+    'submenu': None,
+    'title': 'Redo'},
+   {'action': 'undo.html',
+    'description': u'',
+    'extra': None,
+    'icon': None,
+    'selected': u'',
+    'submenu': None,
+    'title': 'Undo'},
+   {'action': u'',
+    'description': u'',
+    'extra': None,
+    'icon': None,
+    'selected': u'',
+    'submenu': [{'action': 'saveall.html',
+                 'description': u'',
+                 'extra': None,
+                 'icon': None,
+                 'selected': u'',
+                 'submenu': None,
+                 'title': 'Save all'},
+                {'action': 'saveas.html',
+                 'description': u'',
+                 'extra': None,
+                 'icon': None,
+                 'selected': u'',
+                 'submenu': None,
+                 'title': 'Save as'}],
+    'title': 'Save'}]
+
+
+Custom ``IBrowserMenu`` Implementations
+---------------------------------------
+
+Until now we have only seen how to use the default menu implementation. Much
+of the above boilerplate was necessary just to support custom menus. But what
+could custom menus do? Sometimes menu items are dynamically generated based on
+a certain state of the object the menu is for. For example, you might want to
+show all items in a folder-like component. So first let's create this
+folder-like component:
+
+  >>> class Folderish(Content):
+  ...     names = ['README.txt', 'logo.png', 'script.py']
+
+Now we create a menu using the names to create a menu:
+
+  >>> from zope.browsermenu.interfaces import IBrowserMenu
+
+  >>> class Items(object):
+  ...     zope.interface.implements(IBrowserMenu)
+  ...  
+  ...     def __init__(self, id, title=u'', description=u''):
+  ...         self.id = id
+  ...         self.title = title
+  ...         self.description = description
+  ...     
+  ...     def getMenuItems(self, object, request):
+  ...         return [{'title': name,
+  ...                  'description': None,
+  ...                  'action': name + '/manage',
+  ...                  'selected': u'',
+  ...                  'icon': None,
+  ...                  'extra': {},
+  ...                  'submenu': None}
+  ...                 for name in object.names]
+
+and register it:
+
+  >>> provideUtility(Items('items', u'Items', u'Items Menu'),
+  ...                IBrowserMenu, 'items')
+
+We can now get the menu items using the previously introduced API:
+
+  >>> pprint(menu.getMenu('items', Folderish(), TestRequest()))
+  [{'action': 'README.txt/manage',
+    'description': None,
+    'extra': {},
+    'icon': None,
+    'selected': u'',
+    'submenu': None,
+    'title': 'README.txt'},
+   {'action': 'logo.png/manage',
+    'description': None,
+    'extra': {},
+    'icon': None,
+    'selected': u'',
+    'submenu': None,
+    'title': 'logo.png'},
+   {'action': 'script.py/manage',
+    'description': None,
+    'extra': {},
+    'icon': None,
+    'selected': u'',
+    'submenu': None,
+    'title': 'script.py'}]
+
+
+``MenuItemFactory`` class
+-------------------------
+
+As you have seen above already, we have used the menu item factory to generate
+adapter factories for menu items. The factory needs a particular
+``IBrowserMenuItem`` class to instantiate. Here is an example using a dummy
+menu item class:
+  
+  >>> class DummyBrowserMenuItem(object):
+  ...     "a dummy factory for menu items"
+  ...     def __init__(self, context, request):
+  ...         self.context = context
+  ...         self.request = request
+  
+To instantiate this class, pass the factory and the other arguments as keyword
+arguments (every key in the arguments should map to an attribute of the menu
+item class). We use dummy values for this example.
+  
+  >>> factory = metaconfigure.MenuItemFactory(
+  ...     DummyBrowserMenuItem, title='Title', description='Description', 
+  ...     icon='Icon', action='Action', filter='Filter', 
+  ...     permission='zope.Public', extra='Extra', order='Order', _for='For')
+  >>> factory.factory is DummyBrowserMenuItem
+  True
+  
+The "zope.Public" permission needs to be translated to ``CheckerPublic``.
+  
+  >>> from zope.security.checker import CheckerPublic
+  >>> factory.kwargs['permission'] is CheckerPublic
+  True
+  
+Call the factory with context and request to return the instance.  We continue
+to use dummy values.
+  
+  >>> item = factory('Context', 'Request')
+  
+The returned value should be an instance of the ``DummyBrowserMenuItem``, and
+have all of the values we initially set on the factory.
+  
+  >>> isinstance(item, DummyBrowserMenuItem)
+  True
+  >>> item.context
+  'Context'
+  >>> item.request
+  'Request'
+  >>> item.title
+  'Title'
+  >>> item.description
+  'Description'
+  >>> item.icon
+  'Icon'
+  >>> item.action
+  'Action'
+  >>> item.filter
+  'Filter'
+  >>> item.permission is CheckerPublic
+  True
+  >>> item.extra
+  'Extra'
+  >>> item.order
+  'Order'
+  >>> item._for
+  'For'
+  
+If you pass a permission other than ``zope.Public`` to the
+``MenuItemFactory``, it should pass through unmodified.
+  
+  >>> factory = metaconfigure.MenuItemFactory(
+  ...     DummyBrowserMenuItem, title='Title', description='Description', 
+  ...     icon='Icon', action='Action', filter='Filter', 
+  ...     permission='another.Permission', extra='Extra', order='Order', 
+  ...     _for='For_')
+  >>> factory.kwargs['permission']
+  'another.Permission'
+
+
+Directive Handlers
+------------------
+
+``menu`` Directive Handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Provides a new menu (item type).
+
+  >>> class Context(object):
+  ...     info = u'doc'
+  ...     def __init__(self): 
+  ...         self.actions = []
+  ...
+  ...     def action(self, **kw): 
+  ...         self.actions.append(kw)
+
+Possibility 1: The Old Way
+++++++++++++++++++++++++++
+  
+  >>> context = Context()
+  >>> metaconfigure.menuDirective(context, u'menu1', title=u'Menu 1')
+  >>> iface = context.actions[0]['args'][1]
+  >>> iface.getName()
+  u'menu1'
+
+  >>> import sys
+  >>> hasattr(sys.modules['zope.app.menus'], 'menu1')
+  True
+
+  >>> del sys.modules['zope.app.menus'].menu1
+
+Possibility 2: Just specify an interface
+++++++++++++++++++++++++++++++++++++++++
+
+  >>> class menu1(zope.interface.Interface):
+  ...     pass
+
+  >>> context = Context()
+  >>> metaconfigure.menuDirective(context, interface=menu1)
+  >>> context.actions[0]['args'][1] is menu1
+  True
+
+Possibility 3: Specify an interface and an id
++++++++++++++++++++++++++++++++++++++++++++++
+
+  >>> context = Context()
+  >>> metaconfigure.menuDirective(context, id='menu1', interface=menu1)
+
+  >>> pprint([action['discriminator'] for action in context.actions])
+  [('browser', 'MenuItemType', '__builtin__.menu1'),
+   ('interface', '__builtin__.menu1'),
+   ('browser', 'MenuItemType', 'menu1'),
+   ('utility',
+    <InterfaceClass zope.browsermenu.interfaces.IBrowserMenu>,
+    'menu1'),
+   None]
+   
+Here are some disallowed configurations.
+
+  >>> context = Context()
+  >>> metaconfigure.menuDirective(context)
+  Traceback (most recent call last):
+  ...
+  ConfigurationError: You must specify the 'id' or 'interface' attribute.
+
+  >>> metaconfigure.menuDirective(context, title='Menu 1')
+  Traceback (most recent call last):
+  ...
+  ConfigurationError: You must specify the 'id' or 'interface' attribute.
+
+
+``menuItems`` Directive Handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Register several menu items for a particular menu.
+
+  >>> class TestMenuItemType(zope.interface.Interface):
+  ...     pass
+
+  >>> class ITest(zope.interface.Interface): 
+  ...     pass
+
+  >>> context = Context()
+  >>> items = metaconfigure.menuItemsDirective(context, TestMenuItemType, ITest)
+  >>> context.actions
+  []
+  >>> items.menuItem(context, u'view.html', 'View')
+  >>> items.subMenuItem(context, SaveOptions, 'Save')
+
+  >>> disc = [action['discriminator'] for action in context.actions]
+  >>> disc.sort()
+  >>> pprint(disc[-2:])
+  [('adapter',
+    (<InterfaceClass __builtin__.ITest>,
+     <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+    <InterfaceClass __builtin__.TestMenuItemType>,
+    'Save'),
+   ('adapter',
+    (<InterfaceClass __builtin__.ITest>,
+     <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+    <InterfaceClass __builtin__.TestMenuItemType>,
+    'View')]
+
+Custom menu item classes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+We can register menu items and sub menu items with custom classes instead
+of ones used by default. For that, we need to create an implementation
+of IBrowserMenuItem or IBrowserSubMenuItem.
+
+  >>> context = Context()
+  >>> items = metaconfigure.menuItemsDirective(context, TestMenuItemType, ITest)
+  >>> context.actions
+  []
+
+Let's create a custom menu item class that inherits standard BrowserMenuItem:
+
+  >>> class MyMenuItem(menu.BrowserMenuItem):
+  ...    pass
+
+  >>> items.menuItem(context, u'view.html', 'View', item_class=MyMenuItem)
+
+Also create a custom sub menu item class inheriting standard BrowserSubMenuItem:
+
+  >>> class MySubMenuItem(menu.BrowserSubMenuItem):
+  ...    pass
+
+  >>> items.subMenuItem(context, SaveOptions, 'Save', item_class=MySubMenuItem)
+
+  >>> actions = sorted(context.actions, key=lambda a:a['discriminator'])
+  >>> factories = [action['args'][1] for action in actions][-2:]
+
+  >>> factories[0].factory is MySubMenuItem
+  True
+
+  >>> factories[1].factory is MyMenuItem
+  True
+
+These directive will fail if you provide an item_class that does not
+implement IBrowserMenuItem/IBrowserSubMenuItem:
+
+  >>> items.menuItem(context, u'fail', 'Failed', item_class=object)
+  Traceback (most recent call last):
+  ...
+  ValueError: Item class (<type 'object'>) must implement IBrowserMenuItem
+
+  >>> items.subMenuItem(context, SaveOptions, 'Failed', item_class=object)
+  Traceback (most recent call last):
+  ...
+  ValueError: Item class (<type 'object'>) must implement IBrowserSubMenuItem


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/menu.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/src/zope/browsermenu/meta.zcml
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/meta.zcml	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/meta.zcml	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,49 @@
+<configure xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/browser">
+
+    <meta:directive
+        name="menu"
+        schema=".metadirectives.IMenuDirective"
+        handler=".metaconfigure.menuDirective"
+        />
+
+    <meta:complexDirective
+        name="menuItems"
+        schema=".metadirectives.IMenuItemsDirective"
+        handler=".metaconfigure.menuItemsDirective"
+        >
+
+      <meta:subdirective
+          name="menuItem"
+          schema=".metadirectives.IMenuItemSubdirective"
+          />
+
+      <meta:subdirective
+          name="subMenuItem"
+          schema=".metadirectives.ISubMenuItemSubdirective"
+          />
+
+    </meta:complexDirective>
+
+    <meta:directive
+        name="menuItem"
+        schema=".metadirectives.IMenuItemDirective"
+        handler=".metaconfigure.menuItemDirective"
+        />
+
+    <meta:directive
+        name="subMenuItem"
+        schema=".metadirectives.ISubMenuItemDirective"
+        handler=".metaconfigure.subMenuItemDirective"
+        />
+
+    <meta:directive
+        name="addMenuItem"
+        schema=".metadirectives.IAddMenuItemDirective"
+        handler=".metaconfigure.addMenuItem"
+        />
+
+  </meta:directives>
+
+</configure>


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/meta.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/src/zope/browsermenu/metaconfigure.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/metaconfigure.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/metaconfigure.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,306 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Menu Directives Configuration Handlers
+
+$Id: menumeta.py 100372 2009-05-25 19:31:23Z tseaver $
+"""
+from zope.browser.interfaces import IAdding
+from zope.component import getGlobalSiteManager, getUtility
+from zope.component.interface import provideInterface
+from zope.component.zcml import adapter, proxify, utility
+from zope.configuration.exceptions import ConfigurationError
+from zope.interface import Interface
+from zope.interface.interface import InterfaceClass
+from zope.pagetemplate.engine import Engine
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.security.checker import InterfaceChecker, CheckerPublic
+from zope.security.metaconfigure import ClassDirective
+
+from zope.browsermenu.menu import BrowserMenu, BrowserMenuItem, BrowserSubMenuItem
+from zope.browsermenu.interfaces import IBrowserMenu, IMenuItemType
+from zope.browsermenu.interfaces import IBrowserMenuItem, IBrowserSubMenuItem
+from zope.browsermenu.interfaces import AddMenu
+
+# Create special modules that contain all menu item types
+from types import ModuleType as module
+import sys
+try:
+    import zope.app
+except ImportError: # we doesn't always have zope.app now
+    sys.modules['zope.app'] = module('app')
+menus = module('menus')
+sys.modules['zope.app.menus'] = menus
+
+
+_order_counter = {}
+
+
+def menuDirective(_context, id=None, class_=BrowserMenu, interface=None,
+                  title=u'', description=u''):
+    """Registers a new browser menu."""
+    if id is None and interface is None:
+        raise ConfigurationError(
+            "You must specify the 'id' or 'interface' attribute.")
+
+    if interface is None:
+        if id in dir(menus):
+            # reuse existing interfaces for the id, without this we are not
+            # able to override menus.
+            interface = getattr(menus, id)
+        else:
+            interface = InterfaceClass(id, (),
+                                       __doc__='Menu Item Type: %s' %id,
+                                       __module__='zope.app.menus')
+            # Add the menu item type to the `menus` module.
+            # Note: We have to do this immediately, so that directives using the
+            # MenuField can find the menu item type.
+            setattr(menus, id, interface)
+        path = 'zope.app.menus.' + id
+    else:
+        path = interface.__module__ + '.' + interface.getName()
+
+        # If an id was specified, make this menu available under this id.
+        # Note that the menu will be still available under its path, since it
+        # is an adapter, and the `MenuField` can resolve paths as well.
+        if id is None:
+            id = path
+        else:
+            # Make the interface available in the `zope.app.menus` module, so
+            # that other directives can find the interface under the name
+            # before the CA is setup.
+            _context.action(
+                discriminator = ('browser', 'MenuItemType', path),
+                callable = provideInterface,
+                args = (path, interface, IMenuItemType, _context.info)
+                )
+            setattr(menus, id, interface)
+
+    # Register the layer interface as an interface
+    _context.action(
+        discriminator = ('interface', path),
+        callable = provideInterface,
+        args = (path, interface),
+        kw = {'info': _context.info}
+        )
+
+    # Register the menu item type interface as an IMenuItemType
+    _context.action(
+        discriminator = ('browser', 'MenuItemType', id),
+        callable = provideInterface,
+        args = (id, interface, IMenuItemType, _context.info)
+        )
+
+    # Register the menu as a utility
+    utility(_context, IBrowserMenu, class_(id, title, description), name=id)
+
+
+def menuItemDirective(_context, menu, for_,
+                      action, title, description=u'', icon=None, filter=None,
+                      permission=None, layer=IDefaultBrowserLayer, extra=None,
+                      order=0, item_class=None):
+    """Register a single menu item."""
+    return menuItemsDirective(_context, menu, for_, layer).menuItem(
+        _context, action, title, description, icon, filter,
+        permission, extra, order, item_class)
+
+
+def subMenuItemDirective(_context, menu, for_, title, submenu,
+                         action=u'', description=u'', icon=None, filter=None,
+                         permission=None, layer=IDefaultBrowserLayer,
+                         extra=None, order=0, item_class=None):
+    """Register a single sub-menu menu item."""
+    return menuItemsDirective(_context, menu, for_, layer).subMenuItem(
+        _context, submenu, title, description, action, icon, filter,
+        permission, extra, order, item_class)
+
+
+class MenuItemFactory(object):
+    """generic factory for menu items."""
+
+    def __init__(self, factory, **kwargs):
+        self.factory = factory
+        if 'permission' in kwargs and kwargs['permission'] == 'zope.Public':
+            kwargs['permission'] = CheckerPublic
+        self.kwargs = kwargs
+
+    def __call__(self, context, request):
+        item = self.factory(context, request)
+
+        for key, value in self.kwargs.items():
+            setattr(item, key, value)
+
+        if item.permission is not None:
+            checker = InterfaceChecker(IBrowserMenuItem, item.permission)
+            item = proxify(item, checker)
+
+        return item
+
+
+class menuItemsDirective(object):
+    """Register several menu items for a particular menu."""
+
+    menuItemClass = BrowserMenuItem
+    subMenuItemClass = BrowserSubMenuItem
+
+    def __init__(self, _context, menu, for_, layer=IDefaultBrowserLayer,
+                 permission=None):
+        self.for_ = for_
+        self.menuItemType = menu
+        self.layer = layer
+        self.permission = permission
+
+    def menuItem(self, _context, action, title, description=u'',
+                 icon=None, filter=None, permission=None, extra=None,
+                 order=0, item_class=None):
+
+        if filter is not None:
+            filter = Engine.compile(filter)
+
+        if permission is None:
+            permission = self.permission
+
+        if order == 0:
+            order = _order_counter.get(self.for_, 1)
+            _order_counter[self.for_] = order + 1
+
+        if item_class is None:
+            item_class = self.menuItemClass
+
+        if not IBrowserMenuItem.implementedBy(item_class):
+            raise ValueError("Item class (%s) must implement IBrowserMenuItem" % item_class)
+
+        factory = MenuItemFactory(
+            item_class,
+            title=title, description=description, icon=icon, action=action,
+            filter=filter, permission=permission, extra=extra, order=order,
+            _for=self.for_)
+        adapter(_context, (factory,), self.menuItemType,
+                (self.for_, self.layer), name=title)
+
+    def subMenuItem(self, _context, submenu, title, description=u'',
+                    action=u'', icon=None, filter=None, permission=None,
+                    extra=None, order=0, item_class=None):
+
+        if filter is not None:
+            filter = Engine.compile(filter)
+
+        if permission is None:
+            permission = self.permission
+
+        if order == 0:
+            order = _order_counter.get(self.for_, 1)
+            _order_counter[self.for_] = order + 1
+
+        if item_class is None:
+            item_class = self.subMenuItemClass
+
+        if not IBrowserSubMenuItem.implementedBy(item_class):
+            raise ValueError("Item class (%s) must implement IBrowserSubMenuItem" % item_class)
+
+        factory = MenuItemFactory(
+            item_class,
+            title=title, description=description, icon=icon, action=action,
+            filter=filter, permission=permission, extra=extra, order=order,
+            _for=self.for_, submenuId=submenu)
+        adapter(_context, (factory,), self.menuItemType,
+                (self.for_, self.layer), name=title)
+
+    def __call__(self, _context):
+        # Nothing to do.
+        pass
+
+def _checkViewFor(for_=None, layer=None, view_name=None):
+    """Check if there is a view of that name registered for IAdding
+    and IBrowserRequest. If not raise a ConfigurationError
+
+    It will raise a ConfigurationError if :
+        o view=""
+        o if view_name is not registred
+    """
+
+    if view_name is None:
+        raise ConfigurationError(
+            "Within a addMenuItem directive the view attribut"
+            " is optional but can\'t be empty"
+            )
+
+    gsm = getGlobalSiteManager()
+    if gsm.adapters.lookup((for_, layer),
+                           Interface, view_name) is None:
+        raise ConfigurationError(
+            "view name %s not found " %view_name
+            )
+
+def addMenuItem(_context, title, description='', menu=None, for_=None,
+                class_=None, factory=None, view=None, icon=None, filter=None,
+                permission=None, layer=IDefaultBrowserLayer, extra=None,
+                order=0, item_class=None):
+    """Create an add menu item for a given class or factory
+
+    As a convenience, a class can be provided, in which case, a
+    factory is automatically defined based on the class.  In this
+    case, the factory id is based on the class name.
+
+    """
+
+    if for_ is not None:
+        _context.action(
+            discriminator = None,
+            callable = provideInterface,
+            args = ('', for_)
+            )
+        forname = 'For' + for_.getName()
+    else:
+        for_ = IAdding
+        forname = ''
+
+    if menu is not None:
+        if isinstance(menu, (str, unicode)):
+            menu = getUtility(IMenuItemType, menu)
+            if menu is None:
+                raise ValueError("Missing menu id '%s'" % menu)
+
+    if class_ is None:
+        if factory is None:
+            raise ValueError("Must specify either class or factory")
+    else:
+        if factory is not None:
+            raise ValueError("Can't specify both class and factory")
+        if permission is None:
+            raise ValueError(
+                "A permission must be specified when a class is used")
+        factory = "BrowserAdd%s__%s.%s" % (
+            forname, class_.__module__, class_.__name__)
+        ClassDirective(_context, class_).factory(_context, id=factory)
+
+    extra = {'factory': factory}
+
+    if view:
+        action = view
+        # This action will check if the view exists
+        _context.action(
+            discriminator = None,
+            callable = _checkViewFor,
+            args = (for_, layer, view),
+            order=999999
+            )
+    else:
+        action = factory
+
+    if menu == None:
+        menu = AddMenu
+
+    return menuItemsDirective(_context, menu, for_, layer=layer).menuItem(
+        _context, action, title, description, icon, filter,
+        permission, extra, order, item_class)


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/metaconfigure.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/metadirectives.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/metadirectives.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/metadirectives.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,244 @@
+#############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Browser configuration code
+
+This module defines the schemas for browser directives.
+
+$Id: metadirectives.py 103143 2009-08-24 12:28:19Z nadako $
+"""
+from zope.interface import Interface
+from zope.configuration.fields import GlobalObject, GlobalInterface
+from zope.configuration.fields import Tokens, Path, PythonIdentifier, MessageID
+from zope.schema import TextLine, Id, Int, Bool
+from zope.security.zcml import Permission
+
+from zope.component.zcml import IBasicViewInformation
+from zope.browsermenu.fields import MenuField
+
+
+class IMenuDirective(Interface):
+    """Define a browser menu"""
+
+    id = TextLine(
+        title=u"The name of the menu.",
+        description=u"This is, effectively, an id.",
+        required=False
+        )
+
+    title = MessageID(
+        title=u"Title",
+        description=u"A descriptive title for documentation purposes",
+        required=False
+        )
+    
+    description = MessageID(
+        title=u"Description",
+        description=u"A description title of the menu.",
+        required=False
+        )
+
+    class_ = GlobalObject(
+        title=u"Menu Class",
+        description=u"The menu class used to generate the menu.",
+        required=False
+        )
+
+    interface = GlobalInterface(
+        title=u"The menu's interface.",
+        required=False
+        )
+    
+
+class IMenuItemsDirective(Interface):
+    """
+    Define a group of browser menu items
+
+    This directive is useful when many menu items are defined for the
+    same interface and menu.
+    """
+
+    menu = MenuField(
+        title=u"Menu name",
+        description=u"The (name of the) menu the items are defined for",
+        required=True,
+        )
+
+    for_ = GlobalObject(
+        title=u"Interface",
+        description=u"The interface the menu items are defined for",
+        required=True
+        )
+
+    layer = GlobalInterface(
+        title=u"Layer",
+        description=u"The Layer for which the item is declared.",
+        required=False
+        )
+
+    permission = Permission(
+        title=u"The permission needed access the item",
+        description=u"""
+        This can usually be inferred by the system, however, doing so
+        may be expensive. When displaying a menu, the system tries to
+        traverse to the URLs given in each action to determine whether
+        the url is accessible to the current user. This can be
+        avoided if the permission is given explicitly.""",
+        required=False
+        )
+
+
+class IMenuItem(Interface):
+    """Common menu item configuration
+    """
+
+    title = MessageID(
+        title=u"Title",
+        description=u"The text to be displayed for the menu item",
+        required=True
+        )
+
+    description = MessageID(
+        title=u"A longer explanation of the menu item",
+        description=u"""
+        A UI may display this with the item or display it when the
+        user requests more assistance.""",
+        required=False
+        )
+
+    icon = TextLine(
+        title=u"Icon Path",
+        description=u"Path to the icon resource representing this menu item.",
+        required=False
+        )
+
+    permission = Permission(
+        title=u"The permission needed access the item",
+        description=u"""
+        This can usually be inferred by the system, however, doing so
+        may be expensive. When displaying a menu, the system tries to
+        traverse to the URLs given in each action to determine whether
+        the url is accessible to the current user. This can be
+        avoided if the permission is given explicitly.""",
+        required=False
+        )
+
+    filter = TextLine(
+        title=u"A condition for displaying the menu item",
+        description=u"""
+        The condition is given as a TALES expression. The expression
+        has access to the variables:
+
+        context -- The object the menu is being displayed for
+
+        request -- The browser request
+
+        nothing -- None
+
+        The menu item will not be displayed if there is a filter and
+        the filter evaluates to a false value.""",
+        required=False
+        )
+
+    order = Int(
+        title=u"Order",
+        description=u"A relative position of the menu item in the menu.",
+        required=False,
+        default=0
+        )
+
+    item_class = GlobalObject(
+        title=u"Menu item class",
+        description=u"""
+        A class to be used as a factory for creating menu item""",
+        required=False
+        )
+
+class IMenuItemSubdirective(IMenuItem):
+    """Define a menu item within a group of menu items"""
+
+    action = TextLine(
+        title=u"The relative url to use if the item is selected",
+        description=u"""
+        The url is relative to the object the menu is being displayed
+        for.""",
+        required=True
+        )
+
+class IMenuItemDirective(IMenuItemsDirective, IMenuItemSubdirective):
+    """Define one menu item"""
+
+class ISubMenuItemSubdirective(IMenuItem):
+    """Define a menu item that represents a a sub menu.
+
+    For a sub-menu menu item, the action is optional, this the item itself
+    might not represent a destination, but just an entry point to the sub menu. 
+    """
+
+    action = TextLine(
+        title=u"The relative url to use if the item is selected",
+        description=u"""
+        The url is relative to the object the menu is being displayed
+        for.""",
+        required=False
+        )
+
+    submenu = TextLine(
+        title=u"Sub-Menu Id",
+        description=u"The menu that will be used to provide the sub-entries.",
+        required=True,
+        )
+    
+class ISubMenuItemDirective(IMenuItemsDirective, ISubMenuItemSubdirective):
+    """Define one menu item"""
+
+class IAddMenuItemDirective(IMenuItem):
+    """Define an add-menu item"""
+
+    for_ = GlobalInterface(
+        title=u"Interface",
+        description=u"The interface the menu items are defined for",
+        required=False
+        )
+
+    class_ = GlobalObject(
+        title=u"Class",
+        description=u"""
+        A class to be used as a factory for creating new objects""",
+        required=False
+        )
+
+    factory = Id(
+        title=u"Factory",
+        description=u"A factory id for creating new objects",
+        required = False,
+        )
+
+    view = TextLine(
+        title=u"Custom view name",
+        description=u"The name of a custom add view",
+        required = False,
+        )
+
+    menu = MenuField(
+        title=u"Menu name",
+        description=u"The (name of the) menu the items are defined for",
+        required=False,
+        )
+
+    layer = GlobalInterface(
+        title=u"The layer the custom view is declared for",
+        description=u"The default layer for which the custom view is "
+                    u"applicable. By default it is applied to all layers.",
+        required=False
+        )


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/metadirectives.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/addmenuitems.zcml
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/addmenuitems.zcml	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/addmenuitems.zcml	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,23 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zope">
+
+  <view
+      for="zope.browser.interfaces.IAdding"
+      type="zope.browsermenu.tests.test_addMenuItem.ILayerStub"
+      name="add2.html"
+      factory="zope.publisher.browser.BrowserPage"
+      permission="zope.Public"
+      allowed_interface="zope.publisher.interfaces.browser.IBrowserPage"
+      />
+
+  <browser:addMenuItem
+      class=".tests.test_menudirectives.I1"
+      title="Add menu Item"
+      view="add2.html"
+      permission="zope.Public" 
+      layer="zope.browsermenu.tests.test_addMenuItem.ILayerStub"
+      />
+
+</configure>


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/addmenuitems.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/menus-permissions.zcml
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/menus-permissions.zcml	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/menus-permissions.zcml	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,19 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zope">
+
+  <permission id="zope.View" title="Test permission"/>
+
+  <browser:menu 
+      id="test_id" 
+      title="test menu" />
+
+  <browser:menuItems 
+      menu="test_id"
+      for=".tests.test_menudirectives.I2"
+      permission="zope.View">
+    <browser:menuItem action="b1" title="b1" />
+  </browser:menuItems>
+
+</configure>


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/menus-permissions.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/menus.zcml
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/menus.zcml	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/menus.zcml	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,63 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zope">
+
+  <browser:menu 
+      id="test_id" 
+      title="test menu" />
+
+  <browser:menu 
+      id="test_sub_id" 
+      title="test sub menu 2" />
+
+  <browser:menuItems 
+      menu="test_id" 
+      for="zope.interface.Interface">
+    <browser:menuItem action="a1" title="t1" />
+    <browser:subMenuItem submenu="test_sub_id" title="s1" />
+  </browser:menuItems>
+
+  <browser:menuItems 
+      menu="test_id"
+      for=".tests.test_menudirectives.I1">
+    <browser:menuItem action="a2" title="t2" />
+  </browser:menuItems>
+
+  <browser:menuItems 
+      menu="test_id"
+      for=".tests.test_menudirectives.I11">
+    <browser:menuItem action="a3" title="t3" filter="context" />
+    <browser:menuItem action="a4" title="t4" filter="not:context" />
+  </browser:menuItems>
+
+  <browser:menuItems 
+      menu="test_id"
+      for=".tests.test_menudirectives.I111">
+    <browser:menuItem action="a5" title="t5" />
+    <browser:menuItem action="a6" title="t6" />
+    <browser:menuItem action="f7" title="t7" />
+    <browser:menuItem action="u8" title="t8" />
+  </browser:menuItems>
+
+  <browser:menuItems 
+      menu="test_id"
+      for=".tests.test_menudirectives.I12">
+    <browser:menuItem action="a9" title="t9" />
+  </browser:menuItems>
+
+  <browser:menuItems 
+      menu="test_sub_id"
+      for=".tests.test_menudirectives.I111">
+    <browser:menuItem action="a10" title="t10" />
+  </browser:menuItems>
+
+  <browser:menuItems 
+      menu="test_id"
+      for=".tests.test_menudirectives.I111"
+      layer=".tests.test_menudirectives.IMyLayer">
+    <browser:menuItem action="a11" title="t11" />
+    <browser:subMenuItem submenu="test_sub_id" title="s2" />
+  </browser:menuItems>
+
+</configure>


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/menus.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_addMenuItem.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/test_addMenuItem.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/test_addMenuItem.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,427 @@
+#############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Test the addMenuItem directive
+
+>>> context = Context()
+>>> addMenuItem(context, class_=X, title="Add an X",
+...             permission="zope.ManageContent")
+>>> context
+((('utility',
+   <InterfaceClass zope.component.interfaces.IFactory>,
+   'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'),
+  <function handler>,
+  ('registerUtility',
+   <Factory for <class 'zope.browsermenu.tests.test_addMenuItem.X'>>,
+   <InterfaceClass zope.component.interfaces.IFactory>,
+   'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'),
+  {'factory': None}),
+ (None,
+  <function provideInterface>,
+  ('zope.component.interfaces.IFactory',
+   <InterfaceClass zope.component.interfaces.IFactory>),
+  {}),
+ (('adapter',
+   (<InterfaceClass zope.browser.interfaces.IAdding>,
+    <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+   <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+   'Add an X'),
+  <function handler>,
+  ('registerAdapter',
+   <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+   (<InterfaceClass zope.browser.interfaces.IAdding>,
+    <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+   <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+   'Add an X',
+   ''),
+  {}),
+ (None,
+  <function provideInterface>,
+  ('',
+   <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+  {}),
+ (None,
+  <function provideInterface>,
+  ('', <InterfaceClass zope.browser.interfaces.IAdding>),
+  {}),
+ (None,
+  <function provideInterface>,
+  ('',
+   <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+  {}))
+
+$Id: test_addMenuItem.py 100126 2009-05-19 12:55:59Z faassen $
+"""
+
+import unittest
+from zope.testing.doctest import DocTestSuite
+import re
+import pprint
+import cStringIO
+from zope.interface import Interface
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.browsermenu.metaconfigure import addMenuItem
+
+atre = re.compile(' at [0-9a-fA-Fx]+')
+
+class IX(Interface):
+    pass
+
+class X(object):
+    pass
+
+class ILayerStub(IBrowserRequest):
+    pass
+
+class MenuStub(object):
+    pass
+
+
+class Context(object):
+    actions = ()
+    info = ''
+
+    def action(self, discriminator, callable, args=(), kw={}, order=0):
+        self.actions += ((discriminator, callable, args, kw), )
+
+    def __repr__(self):
+        stream = cStringIO.StringIO()
+        pprinter = pprint.PrettyPrinter(stream=stream, width=60)
+        pprinter.pprint(self.actions)
+        r = stream.getvalue()
+        return (''.join(atre.split(r))).strip()
+
+
+def test_w_factory():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, factory="x.y.z", title="Add an X",
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo")
+    >>> context
+    ((('adapter',
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('', <InterfaceClass zope.browser.interfaces.IAdding>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+      {}))
+    """
+
+def test_w_factory_and_view():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, factory="x.y.z", title="Add an X",
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo", view="AddX")
+    >>> context
+    ((None,
+      <function _checkViewFor>,
+      (<InterfaceClass zope.browser.interfaces.IAdding>,
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>,
+       'AddX'),
+      {}),
+     (('adapter',
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('', <InterfaceClass zope.browser.interfaces.IAdding>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+      {}))
+    """
+
+def test_w_factory_class_view():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, class_=X, title="Add an X",
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo", view="AddX")
+    >>> import pprint
+    >>> context
+    ((('utility',
+       <InterfaceClass zope.component.interfaces.IFactory>,
+       'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'),
+      <function handler>,
+      ('registerUtility',
+       <Factory for <class 'zope.browsermenu.tests.test_addMenuItem.X'>>,
+       <InterfaceClass zope.component.interfaces.IFactory>,
+       'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'),
+      {'factory': None}),
+     (None,
+      <function provideInterface>,
+      ('zope.component.interfaces.IFactory',
+       <InterfaceClass zope.component.interfaces.IFactory>),
+      {}),
+     (None,
+      <function _checkViewFor>,
+      (<InterfaceClass zope.browser.interfaces.IAdding>,
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>,
+       'AddX'),
+      {}),
+     (('adapter',
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('', <InterfaceClass zope.browser.interfaces.IAdding>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+      {}))
+    """
+
+def test_w_for_factory():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, for_=IX, factory="x.y.z", title="Add an X",
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo")
+    >>> context
+    ((None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>),
+      {}),
+     (('adapter',
+       (<InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+      {}))
+    """
+
+def test_w_factory_layer():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, factory="x.y.z", title="Add an X", layer=ILayerStub,
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo")
+    >>> context
+    ((('adapter',
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.browsermenu.tests.test_addMenuItem.ILayerStub>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.browsermenu.tests.test_addMenuItem.ILayerStub>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('', <InterfaceClass zope.browser.interfaces.IAdding>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.tests.test_addMenuItem.ILayerStub>),
+      {}))
+    """
+
+def test_w_for_menu_factory():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, for_=IX, menu=MenuStub, factory="x.y.z", title="Add an X",
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo")
+    >>> context
+    ((None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>),
+      {}),
+     (('adapter',
+       (<InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <class 'zope.browsermenu.tests.test_addMenuItem.MenuStub'>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <class 'zope.browsermenu.tests.test_addMenuItem.MenuStub'>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <class 'zope.browsermenu.tests.test_addMenuItem.MenuStub'>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.tests.test_addMenuItem.IX>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+      {}))
+    """
+
+def test_w_factory_icon_extra_order():
+    """
+    >>> context = Context()
+    >>> addMenuItem(context, factory="x.y.z", title="Add an X",
+    ...             permission="zope.ManageContent", description="blah blah",
+    ...             filter="context/foo", icon=u'/@@/icon.png', extra='Extra',
+    ...             order=99)
+    >>> context
+    ((('adapter',
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X'),
+      <function handler>,
+      ('registerAdapter',
+       <zope.browsermenu.metaconfigure.MenuItemFactory object>,
+       (<InterfaceClass zope.browser.interfaces.IAdding>,
+        <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>,
+       'Add an X',
+       ''),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.browsermenu.interfaces.AddMenu>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('', <InterfaceClass zope.browser.interfaces.IAdding>),
+      {}),
+     (None,
+      <function provideInterface>,
+      ('',
+       <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+      {}))
+    """
+
+from zope.configuration.xmlconfig import XMLConfig
+
+import zope.browsermenu
+import zope.component
+from zope.testing import cleanup
+
+class TestAddMenuItem(cleanup.CleanUp, unittest.TestCase):
+
+    def setUp(self):
+        super(TestAddMenuItem, self).setUp()
+        XMLConfig('meta.zcml', zope.component)()
+        XMLConfig('meta.zcml', zope.browsermenu)()
+
+    def test_addMenuItemDirectives(self):
+        XMLConfig('tests/addmenuitems.zcml', zope.browsermenu)()
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite(),
+        unittest.makeSuite(TestAddMenuItem),
+        ))
+
+if __name__ == '__main__':
+    unittest.main()


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_addMenuItem.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_directives.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/test_directives.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/test_directives.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,194 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""'browser' namespace directive tests
+
+$Id: test_directives.py 103143 2009-08-24 12:28:19Z nadako $
+"""
+
+import sys
+import os
+import unittest
+from cStringIO import StringIO
+
+from zope import component
+from zope.interface import Interface, implements, directlyProvides, providedBy
+
+import zope.security.management
+from zope.configuration.xmlconfig import xmlconfig, XMLConfig
+from zope.configuration.exceptions import ConfigurationError
+from zope.publisher.browser import TestRequest
+from zope.publisher.interfaces.browser import IBrowserPublisher
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.browser import IBrowserSkinType
+from zope.security.proxy import removeSecurityProxy, ProxyFactory
+from zope.security.permission import Permission
+from zope.security.interfaces import IPermission
+from zope.testing.doctest import DocTestSuite
+from zope.traversing.adapters import DefaultTraversable
+from zope.traversing.interfaces import ITraversable
+
+import zope.browsermenu
+from zope.component.testfiles.views import IC, V1, VZMI, R1, IV
+from zope.browsermenu.menu import getFirstMenuItem, BrowserMenu
+from zope.browsermenu.interfaces import IMenuItemType, IBrowserMenu
+from zope.testing import cleanup
+
+tests_path = os.path.join(
+    os.path.dirname(zope.browsermenu.__file__),
+    'tests')
+
+template = """<configure
+   xmlns='http://namespaces.zope.org/zope'
+   xmlns:browser='http://namespaces.zope.org/browser'
+   i18n_domain='zope'>
+   %s
+   </configure>"""
+
+
+request = TestRequest()
+
+class M1(BrowserMenu):
+    pass
+
+class V2(V1, object):
+
+    def action(self):
+        return self.action2()
+
+    def action2(self):
+        return "done"
+
+class VT(V1, object):
+    def publishTraverse(self, request, name):
+        try:
+            return int(name)
+        except:
+            return super(VT, self).publishTraverse(request, name)
+
+class Ob(object):
+    implements(IC)
+
+ob = Ob()
+
+class NCV(object):
+    "non callable view"
+
+    def __init__(self, context, request):
+        pass
+
+class CV(NCV):
+    "callable view"
+    def __call__(self):
+        pass
+
+
+class C_w_implements(NCV):
+    implements(Interface)
+
+    def index(self):
+        return self
+
+class ITestMenu(Interface):
+    """Test menu."""
+
+directlyProvides(ITestMenu, IMenuItemType)
+
+
+class ITestLayer(IBrowserRequest):
+    """Test Layer."""
+
+class ITestSkin(ITestLayer):
+    """Test Skin."""
+
+
+class MyResource(object):
+
+    def __init__(self, request):
+        self.request = request
+
+
+class Test(cleanup.CleanUp, unittest.TestCase):
+
+    def setUp(self):
+        super(Test, self).setUp()
+        XMLConfig('meta.zcml', zope.browsermenu)()
+        component.provideAdapter(DefaultTraversable, (None,), ITraversable)
+
+    def tearDown(self):
+        if 'test_menu' in dir(sys.modules['zope.app.menus']):
+            delattr(sys.modules['zope.app.menus'], 'test_menu')
+        super(Test, self).tearDown()
+
+    def testMenuOverride(self):
+        self.assertEqual(
+            component.queryMultiAdapter((ob, request), name='test'),
+            None)
+
+        xmlconfig(StringIO(template % (
+            '''
+            <browser:menu
+                id="test_menu" title="Test menu" />
+            <browser:menuItem
+                action="@@test"
+                for="zope.component.testfiles.views.IC"
+                permission="zope.Public"
+                menu="test_menu"
+                title="Test View"
+                />
+            '''
+            )))
+        menu1 = component.getUtility(IBrowserMenu, 'test_menu')
+        menuItem1 = getFirstMenuItem('test_menu', ob, TestRequest())
+        xmlconfig(StringIO(template % (
+            '''
+            <browser:menu
+                id="test_menu" title="Test menu"
+                class="zope.browsermenu.tests.test_directives.M1" />
+            '''
+            )))
+        menu2 = component.getUtility(IBrowserMenu, 'test_menu')
+        menuItem2 = getFirstMenuItem('test_menu', ob, TestRequest())
+        self.assertNotEqual(menu1, menu2)
+        self.assertEqual(menuItem1, menuItem2)
+
+
+    def testMenuItemNeedsFor(self):
+        # <browser:menuItem> directive fails if no 'for' argument was provided
+        from zope.configuration.exceptions import ConfigurationError
+        self.assertRaises(ConfigurationError, xmlconfig, StringIO(template %
+            '''
+            <browser:menu
+                id="test_menu" title="Test menu" />
+            <browser:menuItem
+                title="Test Entry"
+                menu="test_menu"
+                action="@@test"
+            />
+            '''
+            ))
+
+	    # it works, when the argument is there and a valid interface
+        xmlconfig(StringIO(template %
+            '''
+            <browser:menuItem
+                for="zope.component.testfiles.views.IC"
+                title="Test Entry"
+                menu="test_menu"
+                action="@@test"
+            />
+            '''
+            ))
+
+def test_suite():
+    return unittest.makeSuite(Test)


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_directives.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_fields.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/test_fields.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/test_fields.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Test fields.
+
+$Id: test_fields.py 29570 2005-03-18 22:53:37Z rogerineichen $
+"""
+import unittest
+from zope.testing import cleanup, doctest
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocTestSuite('zope.browsermenu.fields',
+                     setUp=lambda test:cleanup.setUp(),
+                     tearDown=lambda test:cleanup.tearDown()),
+        ))


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_fields.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menu.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menu.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menu.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Browser Menu Item Tests
+
+$Id: test_menu.py 29570 2005-03-18 22:53:37Z rogerineichen $
+"""
+import unittest
+from zope.testing import doctest, cleanup, doctestunit
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('../menu.txt',
+                             setUp=lambda test:cleanup.setUp(),
+                             tearDown=lambda test:cleanup.tearDown(),
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        ))


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menu.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native

Added: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menudirectives.py
===================================================================
--- zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menudirectives.py	                        (rev 0)
+++ zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menudirectives.py	2009-08-24 14:27:26 UTC (rev 103151)
@@ -0,0 +1,163 @@
+##############################################################################
+#
+# Copyright (c) 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.
+#
+##############################################################################
+"""Browser Menu Directives Tests
+
+$Id: test_menudirectives.py 70102 2006-09-11 20:47:15Z ctheune $
+"""
+import unittest
+
+from zope.configuration.xmlconfig import XMLConfig
+from zope.interface import Interface, implements
+from zope.publisher.browser import TestRequest
+from zope.publisher.interfaces.browser import IBrowserPublisher
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.browsermenu.interfaces import IBrowserMenu
+from zope.security.interfaces import Unauthorized, Forbidden
+import zope.component
+
+import zope.security
+
+from zope.testing import cleanup
+
+import zope.browsermenu
+
+
+template = """<configure
+   xmlns='http://namespaces.zope.org/zope'
+   xmlns:browser='http://namespaces.zope.org/browser'
+   i18n_domain='zope'>
+   %s
+   </configure>"""
+
+class I1(Interface): pass
+class I11(I1): pass
+class I12(I1): pass
+class I111(I11): pass
+
+
+class C1(object):
+    implements(I1)
+
+class I2(Interface): pass
+
+class C2(object):
+    implements(I2)
+
+class TestObject(object):
+    implements(IBrowserPublisher, I111)
+
+    def f(self):
+        pass
+
+    def browserDefault(self, r):
+        return self, ()
+
+    def publishTraverse(self, request, name):
+        if name[:1] == 'f':
+            raise Forbidden(name)
+        if name[:1] == 'u':
+            raise Unauthorized(name)
+        return self.f
+
+class IMyLayer(Interface):
+    pass
+
+class IMySkin(IMyLayer, IDefaultBrowserLayer):
+    pass
+
+
+class TestPermissions(cleanup.CleanUp, unittest.TestCase):
+
+    def setUp(self):
+        super(TestPermissions, self).setUp()
+        XMLConfig('meta.zcml', zope.browsermenu)()
+        XMLConfig('meta.zcml', zope.security)()
+
+    def testMenuItemsPermission(self):
+        XMLConfig('tests/menus-permissions.zcml', zope.browsermenu)()
+
+        menu = zope.component.getUtility(IBrowserMenu, 'test_id')
+        # This is a bit icky, but the menu hides too much stuff from us.
+        items = zope.component.getAdapters((C2(), TestRequest()),
+                                           menu.getMenuItemType())
+        item = list(items)[0][1]
+        self.assertEquals("zope.View", item.permission)
+
+
+class Test(cleanup.CleanUp, unittest.TestCase):
+
+    def setUp(self):
+        super(Test, self).setUp()
+        XMLConfig('meta.zcml', zope.browsermenu)()
+
+    def testMenusAndMenuItems(self):
+        XMLConfig('tests/menus.zcml', zope.browsermenu)()
+
+        menu = zope.browsermenu.menu.getMenu(
+            'test_id', TestObject(), TestRequest())
+
+        def d(n):
+            return {'action': "a%s" % n,
+                    'title':  "t%s" % n,
+                    'description': u'',
+                    'selected': '',
+                    'submenu': None,
+                    'icon': None,
+                    'extra': None}
+
+        self.assertEqual(menu[:-1], [d(5), d(6), d(3), d(2), d(1)])
+        self.assertEqual(
+            menu[-1],
+            {'submenu': [{'submenu': None,
+                          'description': u'',
+                          'extra': None,
+                          'selected': u'',
+                          'action': u'a10',
+                          'title': u't10',
+                          'icon': None}],
+             'description': u'',
+             'extra': None,
+             'selected': u'',
+             'action': u'',
+             'title': u's1',
+             'icon': None})
+
+        first = zope.browsermenu.menu.getFirstMenuItem(
+            'test_id', TestObject(), TestRequest())
+
+        self.assertEqual(first, d(5))
+
+    def testMenuItemWithLayer(self):
+        XMLConfig('tests/menus.zcml', zope.browsermenu)()
+
+        menu = zope.browsermenu.menu.getMenu(
+            'test_id', TestObject(), TestRequest())
+        self.assertEqual(len(menu), 6)
+
+        menu = zope.browsermenu.menu.getMenu(
+            'test_id', TestObject(), TestRequest(skin=IMyLayer))
+        self.assertEqual(len(menu), 2)
+
+        menu = zope.browsermenu.menu.getMenu(
+            'test_id', TestObject(), TestRequest(skin=IMySkin))
+        self.assertEqual(len(menu), 8)
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(Test),
+        unittest.makeSuite(TestPermissions),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: zope.browsermenu/trunk/src/zope/browsermenu/tests/test_menudirectives.py
___________________________________________________________________
Added: svn:keywords
   + Id,svn:eol-style=native



More information about the Checkins mailing list