Creating a custom navigation section
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 togetSectionsbelow. 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_navattribute, which is exposed from thePropertiestab 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 bothinfoitself, and its children. Alternatively, you can get all sub-sections under a given root. - You probably want to highlight the currently selected item.
getSectionsworks out which item you are currently viewing and sets itsselectedproperty to True. This happens recursively, meaning that if you have a two level navigation, and the user is in/sections/contact/email-form, bothcontactandemail-formwould be marked asselected.
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.
