Megrok.navigation lets you easily add all sorts of menus to a site.
Menus are implemented as viewletmanagers, and items as viewlets.
You can also override the default templates by registering your own IPageTemplate
>>> import grok
>>> class MySite(grok.Container, grok.Application):
... pass
>>> grok_component('mysite', MySite)
True
>>> root = getRootFolder()
>>> root['site'] = site = MySite()
Let us now define a menu
We’ll first define an Interface, so that later on we won’t need to redefine some menus
after we redefined
the Navigation menu, so this is not necessary to do, although it can lessen dependencies.
>>> from zope.security.testing import Principal, Participation
>>> from zope.security.management import newInteraction, endInteraction
>>> participation = Participation(Principal('zope.anybody'))
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> newInteraction(participation)
>>> nav = Navigation(site, request, grok.View(site, request))
>>> nav.update()
>>> len(nav.viewlets)
0
>>> print nav.render()
<div class="">
</div>
Menu Items can be categorized into groups by adding a group parameter to all menuitem,
submenu and the patentmenu
directives:
>>> class IGroupedMenu(navigation.interfaces.IMenu):
... pass
>>> class GroupedMenu(navigation.Menu):
... grok.implements(IGroupedMenu)
>>> grok_component('GroupedMenu', GroupedMenu)
True
>>> class Index(grok.View):
... grok.context(MySite)
... navigation.sitemenuitem(IGroupedMenu, 'Home', order=1, group='Group 1')
... def render(self):
... return 'test'
>>> grok_component('Index', Index)
True
>>> class View2(grok.View):
... grok.context(MySite)
... navigation.sitemenuitem(IGroupedMenu, 'View 2', order=0, group='Group 2')
... def render(self):
... return 'test'
>>> grok_component('View2', View2)
True
>>> class View3(grok.View):
... grok.context(MySite)
... navigation.sitemenuitem(IGroupedMenu, 'View 3', order=2, group='Group 1')
... def render(self):
... return 'test'
>>> grok_component('View3', View3)
True
The default templates render a separate <ul> for each group:
>>> gm = GroupedMenu(site, request, grok.View(site, request))
>>> gm.update()
>>> print gm.render()
<div class="">
<ul>
<li class="">
<a href="http://127.0.0.1/site/view2">View 2</a>
<BLANKLINE>
</li>
</ul>
<ul>
<li class="">
<a href="http://127.0.0.1/site/index">Home</a>
<BLANKLINE>
</li>
<li class="">
<a href="http://127.0.0.1/site/view3">View 3</a>
<BLANKLINE>
</li>
</ul>
</div>
You can change the order of the groups by setting the grouporder attribute on the Menu
class:
>>> class GroupedMenu(navigation.Menu):
... grok.implements(IGroupedMenu)
... grouporder=['Group 1', 'Group 2']
>>> grok_component('GroupedMenu', GroupedMenu)
True
>>> gm = GroupedMenu(site, request, grok.View(site, request))
>>> gm.update()
>>> print gm.render()
<div class="">
<ul>
<li class="">
<a href="http://127.0.0.1/site/index">Home</a>
<BLANKLINE>
</li>
<li class="">
<a href="http://127.0.0.1/site/view3">View 3</a>
<BLANKLINE>
</li>
</ul>
<ul>
<li class="">
<a href="http://127.0.0.1/site/view2">View 2</a>
<BLANKLINE>
</li>
</ul>
</div>
megrok.navigation uses zope.pagetemplate (or megrok.pagetemplate) to allow you to
override the default templates.
Let’s define a template based on divs, instead of ul
>>> mt = """<div tal:attributes='class menu/cssClass'>
... <div tal:repeat='group menu/groups'>
... <tal:repeat tal:repeat="item group/items"
... tal:replace='structure item/render' />
... </div>
... </div>"""
>>> from megrok import pagetemplate
>>> class DivMenu(pagetemplate.PageTemplate):
... template = grok.PageTemplate(mt)
... pagetemplate.view(navigation.interfaces.IMenu)
>>> grok_component('divmenu', DivMenu)
True
>>> it = """<div tal:attributes='class menu/cssItemClass'>
... <a tal:attributes="href item/link;
... title viewlet/description|nothing">
... <img tal:condition="item/icon | nothing"
... tal:attributes="src item/icon"/>
... <span tal:replace="item/title"/></a>
... <tal:replace tal:condition="item/submenu | nothing"
... tal:replace="structure provider:${item/submenu}"/>
... </div>"""
>>> class DivMenuItem(pagetemplate.PageTemplate):
... template = grok.PageTemplate(it)
... pagetemplate.view(navigation.interfaces.IMenuItem)
>>> grok_component('divmenuitem', DivMenuItem)
True
>>> print actions.render()
<div class="">
<div>
<div class="">
<a href="http://127.0.0.1/site/foo/fooindex">
<BLANKLINE>
Details</a>
<BLANKLINE>
</div>
<div class="">
<a href="http://127.0.0.1/site/foo/fooedit">
<BLANKLINE>
Edit</a>
<BLANKLINE>
</div>
<div class="">
<a href="http://127.0.0.1/site/foo/fooprotected">
<BLANKLINE>
Manage</a>
<BLANKLINE>
</div>
</div>
</div>
But what if you want 2 different templates for items to be used in different menus? You
never specify any Items
yourself, so you can’t tell them to implement a different interface and register the
template to that interface.
To allow this, the itemsimplement directive was introduced.
>>> class IIconItem(navigation.interfaces.IMenuItem):
... pass
>>> class IconMenu(navigation.Menu):
... grok.name('icons')
... navigation.itemsimplement(IIconItem)
... navigation.globalmenuitem('http://grok.zope.org', 'Grok!',
... icon='icon.jpg')
>>> grok_component('nav', IconMenu)
True
>>> it = """<img tal:condition="item/icon | nothing"
... tal:attributes="src item/icon"/>"""
>>> class IconMenuItem(pagetemplate.PageTemplate):
... template = grok.PageTemplate(it)
... pagetemplate.view(IIconItem)
>>> grok_component('iconmenuitem', IconMenuItem)
True
>>> icons = IconMenu(site, request, grok.View(site, request))
>>> icons.update()
>>> print icons.render()
<div class="">
<div>
<img src="icon.jpg" />
</div>
</div>