Attention

This document was written for an old version of Plone, Plone 3, and was last updated 1204 days ago.

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

Customize the Plone 3 navigation.

by Luc Muller last modified Feb 04, 2009 03:04 AM
How to create a navigation that fits more to the needs of a "classic" websites navigation.

Customized navigationPurpose

The portal navigation is a great feature of Plone and fits for most needs. But sometimes, you may need a more 'classic' website navigation. This is a short guide how you may do this.

See on the right side how the navigaton will/should look like when you are finished
(of course, colors can be customized :-) ).

Prerequisities

You need to have a few knowledge of how to customize plone portlets and views in the Zope 3 manner. If you have no idea how this works, the book of Martin Aspeli  "Professional Plone Develoment" or further documentation on plone.org is strongly recommand.

Furthermore, the way how we customized the navigaton may not be "best practice". It's just one way how you can create a website navigation for Plone. Feel free to make other propositions.

Step by step

This are the steps you need to do:

  1. Customize the templates navigation.pt and navigation_recurse.pt.
  2. Customize your stylesheet main.css or modify the stylesheet you registered.

1. Customize the navigation templates

The navigation of Plone 3 has been modified. It's now build in the Zope 3 way. This meens that you have to add a few lines in your browser/configure.zcml to customize the navigation templates.

browser/configure.zcml:

(...)
<plone:portlet
    name="mediamit.Navigation"
    interface="plone.app.portlets.portlets.navigation.INavigationPortlet"
    assignment="plone.app.portlets.portlets.navigation.Assignment"
    renderer=".navigation.Renderer"
    addview="plone.app.portlets.portlets.navigation.AddForm"
    editview="plone.app.portlets.portlets.navigation.EditForm"
    />

<plone:portletRenderer
    portlet="plone.app.portlets.portlets.navigation.INavigationPortlet"
    layer=".interfaces.IThemeSpecific"
    template="templates/navigation.pt"
    />
(...)

This presumes that your templates are installed in browser/templates/.

Copy the navigation view navigation.py from plone/app/portlets/portlets/ to your browser/ folder and overwrite the Renderer class.

browser/navigation.py

from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from plone.app.portlets.portlets import navigation

class Renderer(navigation.Renderer):
    """Modification of the Navigation templates"""
    _template = ViewPageTemplateFile('templates/navigation.pt')
    recurse = ViewPageTemplateFile('templates/navigation_recurse.pt')

 

Copy navigation.pt and recure_navigation.pt from plone/app/portlets/portlets/ to your browser/ folder and first make the following changes to navigation.pt:

  • add a id attribute portlet-navigation to the first dl tag. This is nessessary to decouple navigation styles from the rest of the Plone standard styles.
  • Your may delete the portletHeader tag but we prefere to add a display:none instruction later on in the stylesheets.
  • You have to delete the <img /> tags, so that no icon are shown up even if you want the user to see the icons elsewhere.

browser/templates/navigation.pt:

<tal:block i18n:domain="plone">
<dl id="portlet-navigation"
    class="portlet portletNavigationTree"  
    tal:define="root view/navigation_root">

    <dt class="portletHeader">
        <span class="portletTopLeft"></span>
        <a href="#" 
           class="tile"
           tal:condition="not:view/title"
           tal:attributes="href string:${root/absolute_url}/sitemap" 
           i18n:translate="box_navigation">Navigation</a>
       <a href="#" 
           class="tile"
           tal:condition="view/title"
           tal:attributes="href string:${root/absolute_url}/sitemap"
           tal:content="view/title">Navigation</a>
        <span class="portletTopRight"></span>
    </dt>


    <dd class="portletItem lastItem">
        <ul class="portletNavigationTree navTreeLevel0">
            <li class="navTreeItem"
                tal:condition="view/include_top">
                <div tal:define="selectedClass view/root_item_class;
                                 rootIsPortal view/root_is_portal;">
                   <a tal:attributes="href root/absolute_url;
                                      title root/Description;
                                      class selectedClass">
<!-- (or delete it)
                   <img src="" height="16" width="16" alt=""
                        tal:condition="view/root_icon"
                        tal:attributes="src view/root_icon" />
-->
                   <span tal:omit-tag=""
                         tal:condition="rootIsPortal"
                         i18n:translate="tabs_home">Home</span>
                   <span tal:condition="not:rootIsPortal"
                         tal:replace="root/Title">Root item title</span>
                   </a>
                </div>
            </li>
            <li tal:replace="structure view/createNavTree">
                SUBTREE
            </li>
        </ul>
        <span class="portletBottomLeft"></span>
        <span class="portletBottomRight"></span>
    </dd>


</dl>
</tal:block>

 

Add a new class attribute item in navigation_recurse.pt. We also need it for the stylesheets.

browser/templates/navigation_recurse.pt:

 <tal:master define="level options/level|python:0;
                    children options/children | nothing;
                    bottomLevel options/bottomLevel | nothing;"
            i18n:domain="plone">

<metal:main define-macro="nav_main">
<tal:navitem repeat="node children">
<li tal:define="show_children   node/show_children;
                children        node/children;
                item_url        node/getURL;
                item_icon       node/item_icon;
                remote_url      node/getRemoteUrl;
                link_remote     node/link_remote;
                is_current      node/currentItem;
                is_in_path      node/currentParent;
                li_class        python:is_current and ' navTreeCurrentNode' or '';
                li_extr_class   python:is_in_path and ' navTreeItemInPath' or '';
                li_folder_class python:show_children and ' navTreeFolderish' or '';"
    tal:attributes="class string:navTreeItem visualNoMarker${li_class}${li_extr_class}${li_folder_class}"
    tal:condition="python:bottomLevel &lt;= 0 or level &lt;= bottomLevel">
    
    <tal:level define="item_class string:state-${node/normalized_review_state}">

      <div class="item" 
          tal:define="item_class python:is_current and item_class + ' navTreeCurrentItem' or item_class" >

        <a tal:attributes="href python:link_remote and remote_url or item_url;
                           title node/Description;
                           class string:$item_class${li_class}${li_extr_class}${li_folder_class}">
            <span tal:content="node/Title">Selected Item Title</span>
        </a>
        
    </div>

    <ul tal:attributes="class python:'navTree navTreeLevel'+str(level)"
        tal:condition="python: len(children) > 0 and show_children">  
        <span tal:replace="structure 
              python:view.recurse(children=children, level=level+1, bottomLevel=bottomLevel)" />
    </ul>
    </tal:level>
</li>
</tal:navitem>
</metal:main>
</tal:master>

That's all for the templates. Perhaps someone will find a better solution to customize the navigation without changing the navigations templates. This would be much smarter but so far we could not find a better solution.  

2. Customize the stylesheets

Next step is to customize the stylesheets. You may put a main.css in your stylesheets folder (usualy browser/stylesheets/) or change it in your skin folder (i.e. skins/myproject/myproject_styles/navigation.css) .

browser/stylesheets/main.css:

(...)

/* ============================================== */
/* NAVIGATION                                     */
/* ============================================== */

/* Customized Portlet-Style */

#portlet-navigation  {
    background-color:white; !important;
}

#portlet-navigation .portletHeader {
    display:none;
}

#portlet-navigation .portletItem {
    padding:0;
}

#portlet-navigation ul,
#portlet-navigation li {
    margin:0px !important;
    padding:0px !important;  
    line-height: 1.8em !important;  
}

#portlet-navigation a {
    border:none !important;
}

#portlet-navigation a:hover {
    border:none !important;
    background-color:transparent;
}

#portlet-navigation .item {
    border-bottom:2px solid white;
}

/* --- here is the best place for customization ---- */
.navTreeLevel0 { background-color:#ffdd00; }
.navTreeLevel1 { background-color:#ffdd33; }
.navTreeLevel2 { background-color:#ffdd66; }
.navTreeLevel3 { background-color:#ffdd99; }
.navTreeLevel4 { background-color:#ffddcc; }
.navTreeLevel5 { background-color:#ffddff; }

.navTreeLevel0 .navTreeItemInPath { background-color:#dddd00; font-weight:bold}
.navTreeLevel1 .navTreeItemInPath { background-color:#dddd33; font-weight:bold}
.navTreeLevel2 .navTreeItemInPath { background-color:#dddd66; font-weight:bold}
.navTreeLevel3 .navTreeItemInPath { background-color:#dddd99; font-weight:bold}
.navTreeLevel4 .navTreeItemInPath { background-color:#ddddcc; font-weight:bold}
.navTreeLevel6 .navTreeItemInPath { background-color:#ddddff; font-weight:bold}

.navTreeLevel0 a { margin-left: 10px !important; font-weight:normal;}
.navTreeLevel1 a { margin-left: 20px !important; font-weight:normal;}
.navTreeLevel2 a { margin-left: 30px !important; font-weight:normal;}
.navTreeLevel3 a { margin-left: 40px !important; font-weight:normal;}
.navTreeLevel4 a { margin-left: 50px !important; font-weight:normal;}
.navTreeLevel5 a { margin-left: 60px !important; font-weight:normal;}

.navTreeLevel0 a:hover { 
    margin-left: 0px; padding-left:10px; background-color:#ffcc00 !important;}
.navTreeLevel1 a:hover { 
    margin-left: 0px; padding-left:20px; background-color:#ffcc33 !important;}
.navTreeLevel2 a:hover { 
    margin-left: 0px; padding-left:30px; background-color:#ffcc66 !important;}
.navTreeLevel3 a:hover { 
    margin-left: 0px; padding-left:40px; background-color:#ffcc99 !important;}
.navTreeLevel4 a:hover { 
    margin-left: 0px; padding-left:50px; background-color:#ffcccc !important;}
.navTreeLevel5 a:hover { 
    margin-left: 0px; padding-left:60px; background-color:#ffccff !important;}

.navTreeLevel0 a.navTreeCurrentItem { 
    margin-left: 0px !important; padding-left:10px; background-color:#ffcc00; 
    color:white !important; font-weight:bold;}
.navTreeLevel1 a.navTreeCurrentItem { 
    margin-left: 0px !important; padding-left:20px; background-color:#ffcc33;}
.navTreeLevel2 a.navTreeCurrentItem { 
    margin-left: 0px !important; padding-left:30px; background-color:#ffcc66;}
.navTreeLevel3 a.navTreeCurrentItem { 
    margin-left: 0px !important; padding-left:40px; background-color:#ffcc99;}
.navTreeLevel4 a.navTreeCurrentItem { 
    margin-left: 0px !important; padding-left:50px; background-color:#ffcccc;} 
.navTreeLevel5 a.navTreeCurrentItem { 
   margin-left: 0px !important; padding-left:60px; background-color:#ffccff;}


That's it! Play around with the colors, font-size and font-weight. Instead of using background-color, you may also use background-images.

Feel free to commend this How-to.

 


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.