Attention

This document was written for an unsupported version of Plone, Plone 2.1.x, and was last updated 851 days ago.

For more information, see the version support policy.

To learn how to upgrade to the current version of Plone, read the upgrade manual.

Creating a custom navigation section

by Martin Aspeli last modified Feb 18, 2011 09:08 AM
When creating a custom skin, you may want something more concrete than the navigation tree, but a little more flexible than the portal tabs. This HowTo shows how to create a navigation section which is automatically generated from the contents of a folder.

If you are creating a custom skin, you may wish to have a custom navigation section, perhaps in a portlet, or simply as part of a customised main_template or other standard template (header, perhaps). This document shows how to do just that, with the following assumptions and caveats:

  • All items to appear in the navigation will be under a single folder. For this example, that folder will be /sections, but you can use what you like. (If you want to use the portal root, pass an empty string as the folder name in the call to getSections below. If you want to list items under the current folder no matter where this may be, pass the string . (a single full stop) instead.)
  • The order of navigation is significant. A portal administrator can use the standard folder re-ordering buttons in Plone to change the order of the contents of /sections
  • You may want to ignore some items. The standard Plone exclude_from_nav attribute, which is exposed from the Properties tab of all standard content types will work as it does in the standard navigation tree.
  • You may want sub-menus in your navigation - this method will allow you to specify a fixed depth of sub-menus to consider. For example, if you have a folder /sections/info, and you set the depth to 2, you will be able to create links to both info itself, and its children. Alternatively, you can get all sub-sections under a given root.
  • You probably want to highlight the currently selected item. getSections works out which item you are currently viewing and sets its selected property to True. This happens recursively, meaning that if you have a two level navigation, and the user is in /sections/contact/email-form, both contact and email-form would be marked as selected.

Simple usage

This approach consists of two parts: a script called getSections.py which you should put in your skin folder (or portal_skins/custom, in which case drop the .py suffix) and a snippet of TAL code which you can use as a base to develop your custom navigation. I tend to put this straight into main_template or a template inclded by from main_template as it is the principal way of navigating a site when I use it, but a portlet may be another good place to put it.

The TAL code to display the navigation must call the getSections script and use its returned data-structure in one or more tal:repeat loops. For a two-level navigation showing all folders and documents under /sections in your portal root, you'd use something like:

  <ul class="topLevelNavigation"
      tal:define="sections python:here.getSections('/sections', levels=2, types=['Folder', 'Document'])">

      <li tal:repeat="section sections">
        <a tal:attributes="href section/item/getURL;
                class python:test (section['active'], 'selected', '');" 
           tal:content="section/item/Title">
          Top level section
        </a>

        <ul class="secondLevelNavigation">
          <li tal:repeat="subsection section/children">
             <a tal:attributes="href subsection/item/getURL;
                  class python:test (subsection['active'], 'selected', '');"
                         tal:content="subsection/item/Title">
               Second level section
             </a>
          </li>
        </ul>
      </li>

  </ul>

Notice the use of CSS classes - use these (or similar classes) to appropriately style your navigation. In particular, notice how the class of the <a> tag is set to selected for selected items. Use .selected in your style sheet to highlight the currently selected item in the navigation.

For accessability, navigation items like these should always be in a <ul> styled look the way you want. To check whether your navigation works, try accessing your site in a text-mode browser such as lynx, or turn off the Plone stylesheet in your browser (use Opera or Firefox). If you see a nice, usable nested list, your site is much more likely to be accessible people with disabilities, and via less capable browsers.

Recursive macros and nested lists

To take this a step further, you can use recursive macros to get arbitrarily nested lists, however deep the tree. This requires two templates and the use of macros.

First, create a template called 'navigation_macros.pt':

  <ul metal:define-macro="navigation_menu">

      <li tal:repeat="section sections">
        <a tal:attributes="href section/item/getURL;
                class python:test (section['active'], 'selected', '');" 
           tal:content="section/item/Title">
          Top level section
        </a>

        <tal:recurse define="sections section/children"
                     condition="nocall:sections">
          <metal:call use-macro="here/navigation_macros/macros/navigation_menu" />
        </tal:recurse>

      </li>

  </ul>

Notice the tal:recurse block. This calls the same macro again for children of the current item, hence producing nested lists. It works by expecting the template calling the macro to define a variable sections holding the top level sections, and then re-defines these to point to the sub-sections, recursively.

Now we just need to start the recursion from the template where the navigation menu is to be displayed:

  <tal:block define="sections python:here.getSections('/info')">
    <metal:call use-macro="here/navigation_macros/macros/navigation_menu" />
  </tal:block>

By not passing a levels parameter, you will get all sections under /info.


Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.