Skip to main content

A ready to go menu for Zope3

Project description

This package provides a “ready-to-go” menu implementation based on viewlets for Zope 3.

Ready 2 go Menu

The z3c.menu.ready2go package provides a menu implementation which allows you to implement menus based on content providers and viewlets.

First let’s setup our defualt menu item template:

>>> import os
>>> import zope.component
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from zope.publisher.interfaces.browser import IBrowserView
>>> from z3c.template.interfaces import IContentTemplate
>>> from z3c.template.template import TemplateFactory
>>> import z3c.menu.ready2go
>>> baseDir = os.path.split(z3c.menu.ready2go.__file__)[0]
>>> itemTemplate = os.path.join(baseDir, 'item.pt')
>>> itemTemplateFactory = TemplateFactory(itemTemplate, 'text/html')
>>> zope.component.provideAdapter(itemTemplateFactory,
...     (IBrowserView, IDefaultBrowserLayer), IContentTemplate)

Context Menu Item

Now we register a context menu item for our IContextMenu:

>>> import zope.component
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from z3c.menu.ready2go.item import ContextMenuItem
>>> class MyContextMenuItem(ContextMenuItem):
...     viewName = 'context.html'
...     weight = 1

Now we need a security checker for our menu item

>>> from zope.security.checker import NamesChecker, defineChecker
>>> viewletChecker = NamesChecker(('update', 'render'))
>>> defineChecker(MyContextMenuItem, viewletChecker)

And we configure our menu item for IContextMenu. This is normaly done by the viewlet ZCML directive:

>>> zope.component.provideAdapter(
...     MyContextMenuItem,
...     (zope.interface.Interface, IDefaultBrowserLayer,
...     IBrowserView, IContextMenu),
...     IViewlet, name='My Context')

Now let’s render the context menu again. You can see that we ve got a menu item. Another important point here is, that the url of such ContextMemuItem implementations point to the context of the view:

>>> contextMenu.update()
>>> print(contextMenu.render())
<li>
  <a href="http://127.0.0.1/site/content/context.html"><span>My Context</span></a>
</li>

Let’s set the view __name__ to context.html. This will reflect that the view offers the same name that our context menu needs to get rendered as selected:

>>> view.__name__ = 'context.html'

Now try again and see if the context menu item get rendered as selected:

>>> contextMenu.update()
>>> print(contextMenu.render())
<li class="selected">
  <a href="http://127.0.0.1/site/content/context.html"><span>My Context</span></a>
</li>

Also, let’s check that menu item is marked selected even if we provided a viewName in the @@context.html form:

>>> MyContextMenuItem.viewName = '@@context.html'
>>> contextMenu.update()
>>> print(contextMenu.render())
<li class="selected">
  <a href="http://127.0.0.1/site/content/@@context.html"><span>My Context</span></a>
</li>

Okay, change viewName back to context.html for further tests:

>>> MyContextMenuItem.viewName = 'context.html'

Now add a second context menu item and check if we can use the cssInActive argument which is normaly a empty string:

>>> class InActiveMenuItem(ContextMenuItem):
...     viewName = 'inActive.html'
...     cssInActive = 'inActive'
...     weight = 2
>>> defineChecker(InActiveMenuItem, viewletChecker)
>>> zope.component.provideAdapter(
...     InActiveMenuItem,
...     (zope.interface.Interface, IDefaultBrowserLayer,
...     IBrowserView, IContextMenu),
...     IViewlet, name='In Active')

Now update and render again:

>>> contextMenu.update()
>>> print(contextMenu.render())
<li class="selected">
  <a href="http://127.0.0.1/site/content/context.html"><span>My Context</span></a>
</li>
<li class="inActive">
  <a href="http://127.0.0.1/site/content/inActive.html"><span>In Active</span></a>
</li>

AddMenu

The add menu can be used for offering links to any kind of add forms per context. This allows us to offer independent add form links doesn’t matter which form framework is used. Let’s now define such a simple AddMenuItem pointing to a add form url. Not; the add form and it’s url do not exist in thsi test. This aslo means there is no guarantee that a form exist if a add menu item is configured.

>>> from z3c.menu.ready2go.item import AddMenuItem
>>> class MyAddMenuItem(AddMenuItem):
...
...     viewName = 'addSomething.html'

Now we need a security checker for our menu item

>>> from zope.security.checker import NamesChecker, defineChecker
>>> viewletChecker = NamesChecker(('update', 'render'))
>>> defineChecker(MyAddMenuItem, viewletChecker)

And we configure our menu item for IAddMenu. This is normaly done by the viewlet ZCML directive:

>>> zope.component.provideAdapter(
...     MyAddMenuItem,
...     (zope.interface.Interface, IDefaultBrowserLayer,
...     IBrowserView, IAddMenu),
...     IViewlet, name='My AddMenu')

Now we can update and render our add menu:

>>> addMenu.update()
>>> print(addMenu.render())
<li>
  <a href="http://127.0.0.1/site/content/addSomething.html"><span>My AddMenu</span></a>
</li>

EmptyMenuManager

There is a empty menu manager whihc could be used for override existing menu managers.

>>> from z3c.menu.ready2go.manager import EmptyMenuManager
>>> emptyMenu = EmptyMenuManager(None, None, None)

Our empty menu manager implements IMenuManager:

>>> interfaces.IMenuManager.providedBy(emptyMenu)
True

This empty menu manager returns allways an empty string if we render them:

>>> emptyMenu.update()
>>> emptyMenu.render()
''

Special use case

We have some special use case because of Zope’s internals. One important part is that our menu heavy depend on context and it’s __parent__ chain to the zope application root. This is not allways supported by Zopes default setup. One part is the bad integrated application control part which fakes a root object which doesn’t know about the real childs of the real root from the ZODB e.g. application root. Now we will show you that our menu by default render no items if we get such a fake root which messes up our menu structure.

Let’s define a object which does not know about any __parent__.

>>> nirvana = Content()
>>> nirvanaView = View(nirvana, request)

Now we can check what’s happen to the menus if we adapt the parent less nirvana context and update and render the menus. You can see that the global menu does not contain any menu item. That’s because the global menu items tries to find the root by traversing from the context to the root by the __parent__ chain and we don’t support any parent for our nirvana object:

>>> globalMenu = GlobalMenu(nirvana, request, nirvanaView)
>>> globalMenu.update()
>>> globalMenu.render()
''

Also the SiteMenu doesn’t contain any menu item because of the parent less object:

>>> siteMenu = SiteMenu(nirvana, request, nirvanaView)
>>> siteMenu.update()
>>> siteMenu.render()
''
>>> contextMenu = ContextMenu(nirvana, request, nirvanaView)
>>> contextMenu.update()
>>> contextMenu.render()
''
>>> addMenu = AddMenu(nirvana, request, nirvanaView)
>>> addMenu.update()
>>> addMenu.render()
''

Z3C Menu directives

Show how we can use the menu directive. Register the meta configuration for the directive.

First let’s setup our defualt menu item template first:

>>> import os
>>> import zope.component
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from zope.publisher.interfaces.browser import IBrowserView
>>> from z3c.template.interfaces import IContentTemplate
>>> from z3c.template.template import TemplateFactory
>>> import z3c.menu.ready2go
>>> baseDir = os.path.split(z3c.menu.ready2go.__file__)[0]
>>> itemTemplate = os.path.join(baseDir, 'item.pt')
>>> itemTemplateFactory = TemplateFactory(itemTemplate, 'text/html')
>>> zope.component.provideAdapter(itemTemplateFactory,
...     (IBrowserView, IDefaultBrowserLayer), IContentTemplate)
>>> import sys
>>> from zope.configuration import xmlconfig
>>> import z3c.menu.ready2go
>>> context = xmlconfig.file('meta.zcml', z3c.menu.ready2go)

We need to register our checker adapter which can check if a menu item is selected or not:

>>> import zope.component
>>> from z3c.menu.ready2go import checker
>>> zope.component.provideAdapter(checker.ContextSelectedChecker)

Let’s define a content object:

>>> from z3c.menu.ready2go import testing
>>> sampleContent = testing.Sample('Sample Content')

Now add the content object to our site root:

>>> root['sample'] = sampleContent

Now we can define our test menu manager:

>>> from zope.viewlet.manager import ViewletManager
>>> from z3c.menu.ready2go import manager
>>> FirstMenu = ViewletManager('left', testing.IFirstMenu,
...     bases=(manager.MenuManager,))
>>> SecondMenu = ViewletManager('left', testing.ISecondMenu,
...     bases=(manager.MenuManager,))

And we need a view which knows about it’s parent:

>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> firstView = testing.FirstView(sampleContent, request)
>>> testing.IFirstView.providedBy(firstView)
True
>>> secondView = testing.SecondView(sampleContent, request)
>>> testing.ISecondView.providedBy(secondView)
True

As you can see the menu is not selected if we access the page:

>>> firstMenu = FirstMenu(sampleContent, request, firstView)
>>> testing.IFirstMenu.providedBy(firstMenu)
True
>>> firstMenu.update()
>>> firstMenu.render()
''
>>> secondMenu = SecondMenu(sampleContent, request, secondView)
>>> testing.ISecondMenu.providedBy(secondMenu)
True
>>> secondMenu.update()
>>> secondMenu.render()
''

Now we need some menu items for the first menu:

>>> from zope.publisher.interfaces.browser import IBrowserView
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> from zope.viewlet.interfaces import IViewlet
>>> zope.component.provideAdapter(
...     testing.FirstMenuItem,
...     (zope.interface.Interface, IBrowserRequest,
...     IBrowserView, testing.IFirstMenu),
...     IViewlet, name='First Menu')
>>> zope.component.provideAdapter(
...     testing.SecondMenuItem,
...     (zope.interface.Interface, IBrowserRequest,
...     IBrowserView, testing.IFirstMenu),
...     IViewlet, name='Second Menu')

And we need some menu items for the second menu:

>>> zope.component.provideAdapter(
...     testing.FirstMenuItem,
...     (zope.interface.Interface, IBrowserRequest,
...     IBrowserView, testing.ISecondMenu),
...     IViewlet, name='First Menu')
>>> zope.component.provideAdapter(
...     testing.SecondMenuItem,
...     (zope.interface.Interface, IBrowserRequest,
...     IBrowserView, testing.ISecondMenu),
...     IViewlet, name='Second Menu')

Now render the menu manager again and you can see that we’ve got some menu items. but you can see that this menu items are not selected:

>>> firstMenu = FirstMenu(sampleContent, request, firstView)
>>> firstMenu.update()
>>> print(firstMenu.render())
<li>
  <a><span>First Menu</span></a>
</li>
<li>
  <a><span>Second Menu</span></a>
</li>
>>> secondMenu = SecondMenu(sampleContent, request, firstView)
>>> secondMenu.update()
>>> print(secondMenu.render())
<li>
  <a><span>First Menu</span></a>
</li>
<li>
  <a><span>Second Menu</span></a>
</li>

Now we can register a menu selector for our page whihc renders the menu as selected if we access the page:

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:menuSelector
...       view=".testing.IFirstView"
...       manager=".testing.IFirstMenu"
...       menu=".testing.FirstMenuItem"
...       />
... </configure>
... """, context)

After we registered a menu selector for the first view and first menu, we will see that the first menu get rendered as selected on the first menu:

>>> firstMenu = FirstMenu(sampleContent, request, firstView)
>>> firstMenu.update()
>>> print(firstMenu.render())
<li class="selected">
  <a><span>First Menu</span></a>
</li>
<li>
  <a><span>Second Menu</span></a>
</li>

But not on the second menu:

>>> secondMenu = SecondMenu(sampleContent, request, firstView)
>>> secondMenu.update()
>>> print(secondMenu.render())
<li>
  <a><span>First Menu</span></a>
</li>
<li>
  <a><span>Second Menu</span></a>
</li>

CHANGES

2.0 (2023-02-07)

  • Add support for Python 3.8, 3.9, 3.10, 3.11.

  • Drop support for Python 2.7, 3.5, 3.6.

  • Drop support for deprecated python setup.py test.

1.1.0 (2018-10-09)

  • Add support for Python 3.7.

  • Remove all deprecation warnings.

1.0.0 (2017-04-17)

  • Pin Python support to 2.7, 3.5, 3.6 and PyPy.

1.0.0a1 (2013-03-03)

  • Added support for Python 3.3.

  • Changed zope.testbrowser tests to WebTest, since zope.testbrowser is not yet ported.

  • Replaced deprecated zope.interface.implements usage with equivalent zope.interface.implementer decorator.

  • Dropped support for Python 2.4 and 2.5.

0.8.0 (2010-07-12)

  • Replaced zope.app.pagetemplate test dependency by zope.browserpage as the needed metaconfigure.registerType has been moved there lately without leaving a BBB import.

0.7.1 (2009-12-26)

  • Removed dependency on z3c.i18n by declaring the z3c MessageFactory locally.

  • Using python doctest module instead of zope.testing.doctestunit as it deprecated now.

0.7.0 (2009-11-30)

  • Adjust dependencies and imports, to reflect changes in zope packages.

0.6.0 (2009-02-07)

  • Replaced zope.app.component by zope.site.

  • Replaced zope.app.container by zope.container.

  • zope.app.pagetemplate is only a test dependency.

0.5.1 (2009-01-04)

  • Add support for viewNames that start with @@. They are now processed properly by the ViewNameSelectedChecker.

  • Added documentation to Pypi home page.

0.5.0 (2008-04-11)

  • bugfix: fixed cssInActive usage. This was broken and ended in not using the cssInActive CSS class argument

  • added more tests, now we have 100% coverage

  • make ISiteMenu only available for ISite but not for IContainmentRoot

  • make template pluggable with z3c.template pattern

  • initial Release

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

z3c.menu.ready2go-2.0.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

z3c.menu.ready2go-2.0-py3-none-any.whl (27.3 kB view details)

Uploaded Python 3

File details

Details for the file z3c.menu.ready2go-2.0.tar.gz.

File metadata

  • Download URL: z3c.menu.ready2go-2.0.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.16

File hashes

Hashes for z3c.menu.ready2go-2.0.tar.gz
Algorithm Hash digest
SHA256 89fb65d1ca19d830ce247ffb2572ed68646be4a7d4509b8253e523961abd2f59
MD5 79533fe77723c5894895092364c4a1d8
BLAKE2b-256 420056ad86d815456a1366953b7fa66aeb0c00f723f8787292690cda2165fe92

See more details on using hashes here.

File details

Details for the file z3c.menu.ready2go-2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for z3c.menu.ready2go-2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 45f73babf7b651ef0e02dfe38b719a8f1d89cb9f46bde1d02fcafe012021794e
MD5 543f7bb84fe19e29d488edd50e4cb44c
BLAKE2b-256 59ea07da56edbcbf8b17c44ca7386fa507734713bd2c8be87db5a5ca5429267b

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page