• BU Home | 
  • News | 
  • Events | 
  •  | 
  •  

Web Services Wiki

Welcome Guest: Login

Menu Rendering Adapters

The rendering of BethelMenus (edited by visiting the "template" tab of a container in the ZMI) is very flexible.  Three ways have currently been developed to transform a BethelMenu to html (render the menu):

  1. Rendering a Left Navigation Menu with adxMenu-compatible popups
  2. Rendering as a horizontal bar menu (a site menu placed in the BU personal bar), using adxmenu's horizontal-top-to-bottom styles)
  3. Rendering as a content menu (placed at the above or below the document content, using adxmenu's horizontal top-to-bottom or bottom-to-top styles)

All are rendered as a lists with various additional attributes (id, class, style, etc).  The names denote their original usages, but certainly these adapters can be used in other templates, in other ways as the design of the template requires.  Or new adapters can be written easily.
A common way of implementing these is to embed this functionality directly within the model, or have some predefined helper class.  The former method isn't ideal, as it is results in "code bloat" with presentation logic embedded in the model.  The latter is much nicer, and Zope 3 has a nice mechanism called adapters to achieve this.  Adapters are pluggable interfaces which provide additional functionality / apis for content objects.  Content objects are "adapted to" an adapter interface, and the Zope 3 framework looks up the appropriate adapter for the adapter interface and content model.

BethelMenu renderers have been implemented using Zope 3 adapters.  Adapters have an interface, and a implementing class for each content interface (e.g. ISilvaObject) that uses the adapter.  The interfaces are defined in BethelTemplates.interfaces, the adapters are implemented in BethelTemplates.adapters.menu_renderers, and all are tied together in BethelTemplates/configure.zcml

Interfaces

  • IBethelMenu
  • ISidebarMenuRenderer

    • render an IBethelMenu as an adxmenu-compatible vertical-left-to-right unordered list
    • Takes "site_id" parameter.
  • IHorizontalBarMenuRenderer

    • render an IBethelMenu as an adxmenu-compatible horizontal-top-to-bottom unordered list (to be placed in the personal bar)
    • Takes paramters: site_id, ul_id
  • IContentMenuRenderer

    • render an IBethelMenu as a BU content menu unordered list
    • This renders the children of an 'all' menu item and looks up the matching menu item (via the attached locations setting), rendering it's immediate children
    • Takes parameters: site_id, ul_id (the id attribute of the <ul>) and the direction (either htb or hbt)

Adapters

Base Class

Each of the three adapter implementations extends BaseMenuRenderer.  All output unordered lists with additional markup.  The base class includes defaults for the components of the list:

  1. link_norm — a normal link
  2. link_start — the parent link containing a submenu (starts the nested <ul>)
  3. link_end — the end link of a submenu (closing the parent <li>)
  4. link_title — a "title" menu item
  5. link_delim — a delimiter menu item

The generateMenu method of the base class calls the menu's generateMenu4 to generate a flat list of menu items.  It returns a 2-tuple containing:

  1. The flat menu list, in the form ( (depth, menuitem1),(depth,menuitem2),...)
  2. The "active menu", which is the menu with a "locations attached to" property that most closely matches the current requested model.

The render_items method takes the menu_list and renders it using the 5 default components listed above, and returns the html as a list of string-chunks.  This renders just the list items, and not the root's <ul>, the implementing classes do that part.

Each adapter has a __call__ method which does the actual transforming of the menu into html.

HorizontalBarMenuRenderer

Generates the menu, and outputs it within the following html:

<ul id="<<ul_id>>" class="adxm menu menu-htb">
  <![[ output from BaseMenuRenderer.render_items ]]>
</ul>

SidebarMenuRenderer

Generates the menu, and outputs it within the following html:

<ul class="adxm menu menu-vlr">
 <![[ output from BaseMenuRenderer.render_items ]]>
</ul>

SubMenuRenderer

NOTE: see this document to learn how content menus are used in the BU template.
Generates the menu by:

  1. finding the active menu (menu.findMenu)
  2. Add the immediate children of the 'all' menu item located within the root.
  3. Add the immediate children of the active menu

The menu is rendered and output within the following html:

<ul id="<<ul_id>>" class="adxm menu menu-<<direction>>">
 <![[ output from BaseMenuRenderer.render_items ]]>
</ul>

The calling template code will likely place this output within a div which can be used for styling the menu via css.

configuring the adapters (configure.zcml)

Each adapter is configured very similarly to the ISidebarMenuRenderer (just replace the names):

<adapter
   for="Products.BethelTemplates.interfaces.IBethelMenu"
   provides=".interfaces.ISidebarMenuRenderer"
   factory=".adapters.menu_renderers.SidebarMenuRenderer"
  />

Using the adapters from template code

The adapters are difficult to call directly from template code, so the view class (BaseViewMixin) has helper functions to call and render the adapters.  BaseViewMixin has the following methods: renderSidebarMenu, renderTopMenu, renderSubMenu which render the respective menus.  These methods take the site_config object and the menu name/id (string) as parameters.

The view code for one of these looks like:

 def renderContentMenu(self, site_config, menu, bar_id, direction):
     menu = site_config.menus[menu]
     return interfaces.ISubMenuRenderer(menu)(site_config.aq_parent.id, bar_id, direction)