Personal tools
You are here: Home Documentation Manuals Plone Core Developer Reference Specific areas Navigation structures
Support

Get Help

Join our chat rooms or support forums if you have more specific questions.

Plone Training
Learn how to design, build, and deploy a website in Plone through one of the numerous Plone training sessions around the world.
Find Plone training…
 
Document Actions

Navigation structures

Note: Return to reference manual view.

This reference manual describes the conventions, concepts and components of the core Plone codebase. It is intended as a point of reference for new developers who want to be able to contribute, and for old developers who are doing things they haven't done before. At the moment, it is a work in progress, sections will be expanded as we are able to find time. This manual is of course useful to anybody doing development with Plone, but will focus on documenting the areas important to development of Plone itself.

1. Navigation root

How Plone determines the navigation root to use for the sitemap, navigation tree, tabs and breadcrumbs.

As of Plone 2.5, the root property in portal_properties/navtree_properties enables the site admin to set a folder as the root of the site's navigation. This will affect not only the navigation tree (which was all it did in Plone 2.1.3 and later versions in the 2.1 series), but also affect where the breadcrumbs, the tabs and the sitemap are rooted.

The canonical navigation root is returned as a path, relative to the portal root, in CMFPlone.browser.navtree.getNavigationRoot(). This will use the property in navtree_properties, unless the context or a parent object is marked with the CMFPlone.browser.interfaces.INavigationRoot interface. If such an object is found, that is used as the navigation root. By default, the portal root is marked with this interface, but it's possible to mark other objects with it as well.

The root path is used by the navtree and sitemap query builders and the navigation tabs and breadcrumb algorithms (see the following pages) to root the catalog query used to construct these.

2. Constructing the navigation tree

How the navigation tree is constructed from a catalog query and how the settings in portal_properties are able to influence the shape of the final tree.

The navigation tree portlet uses a view, found in CMFPlone.browser.interfaces.INavigationPortlet with a factory in CMFPlone.browser.portlets.navigation. This view in turn uses a more general view, CMFPlone.browser.interfaces.INavigationTree that is implemented in CMFPlone.browser.navigation.CatalogNavigationTree.

Previously, the navigation tree used to be constructed in CMFPlone.PloneTool, and there is still a deprecated createNavTree() method there. A utility method that invokes the view is found in CMFPlone.utils.createNavTree().

The actual construction of the navtree is delegated to a generic function found in CMFPlone.browser.navtree called buildFolderTree(). This function has a single purpose: Execute a catalog query and turn the results into a navigation tree-like structure. It is thus generic, and can be used to construct other navigational structures (such as the TOC for this reference manual).

Because the navigation tree needs to take several properties into account, the navtree builder can take a "strategy" object, which implements CMFPlone.browser.interfaces.INavtreeStrategy. This can provide methods used to decide whether a given node should be included and/or whether a whole subtree should be pruned. It can also decorate the node dicts with additional keys, used to hold domain-specific information.

The standard navigation tree strategy is found in CMFPlone.browser.navtree.DefaultNavtreeStrategy. In fact this extends the sitemap navtree strategy, because a navtree is essentially a more restricted form of a sitemap.

The navtree strategy is an adapter, looked up on the context (the content object being viewed - registered for * by default) and the interface of the view that is constructing it (to distinguish the sitemap from the navtree):

  <adapter for="*
                .interfaces.INavigationTree"
            factory=".navtree.DefaultNavtreeStrategy"
            provides=".interfaces.INavtreeStrategy" />

The CatalogNavigationTree class that implements INavigationTree thus does this:

   def navigationTree(self):
        context = utils.context(self)

        queryBuilder = NavtreeQueryBuilder(context)
        query = queryBuilder()

        strategy = getMultiAdapter((context, self), INavtreeStrategy)

        return buildFolderTree(context, obj=context, query=query, strategy=strategy)

The NavtreeQueryBuilder, is found in CMFPlone.browser.navtree, with an interface in CMFPlone.browser.interfaces.INavigationQueryBuilder. This simply wraps up the task of constructing the catalog query dict that is used to build the navtree, so that it may be re-used.

To make it easier to create custom navtrees and similar structures for site integrators, the contents of CMFPlone.browser.navtree may be imported from protected code, by virtue of the following statement in 'CMFPlone/__init__.py':

  allow_module('Products.CMFPlone.browser.navtree')

To ensure that the methods and attributes of the query builders and navtree strategies are accessible as well, CMFPlone/browser/configure.zcml contains:

  <content class="Products.CMFPlone.Portal.PloneSite">
    <implements interface=".interfaces.INavigationRoot" />
  </content>

  <content class=".navtree.NavtreeStrategyBase">
    <allow interface=".interfaces.INavtreeStrategy" />
  </content>

  <content class=".navtree.NavtreeQueryBuilder">
    <allow interface=".interfaces.INavigationQueryBuilder" />
  </content>

3. Constructing the sitemap

How the sitemap is constructed

The sitemap is constructed using the same mechanisms as the navigation tree - it simply uses a different query builder and navtree strategy.

The view that is used by the sitemap.pt page template is found in CMFPlone.browser.sitemap.SitemapView, and implements CMFPlone.browser.interfaces.ISitemapView. It in turn delegates to the more general view CMFPlone.browser.navigation.CatalogSiteMap which implements CMFPlone.browser.interfaces.ISitemap. This does:

    def siteMap(self):
        context = utils.context(self)
        queryBuilder = SitemapQueryBuilder(context)
        query = queryBuilder()
        strategy = getMultiAdapter((context, self), INavtreeStrategy)
        return buildFolderTree(context, obj=context, query=query, strategy=strategy)

Note that the sitemap and navtree query builders and strategies share much code and are derived from one another.

There is a utilty method in CMFPlone.utils called createSiteMap() that invokes the ISitemap view. The deprecated way of constructing the sitemap is through a call to PloneTool.createNavTree setting sitemap=True.

4. Navigation tabs

How the navigation tabs are constructed

Since Plone 2.1, the navigation tabs at the top of the site have been constructed from two sources:

  • Any actions in the portal_tabs category
  • Any folders in the root of the site

In Plone 2.1, these were created using PloneTool.createTopLevelTabs(). This is now deprecated. A utility method in PloneTool.utils with the same name now wraps the call to the view that implements this functionality. The view can be found in CMFPlone.browser.navigation.CatalogNavigationTabs, with an interface in CMFPlone.browser.INavigationTabs.

The view implementation performs an action lookup, and constructs a query that that finds folderish items in the navigation root. Note that the "navigation root" is not necessarily the portal root - see the section on the navigation root.

5. Breadcrumbs

How the breadcrumbs in the pathbar are constructed.

Plone 2.5 actually ships with two implementations of the breadcrumbs. Both can be used as the view that implements CMFPlone.browser.interfaces.INavigationBreadcrumbs. The older, currently disabled method is found in CMFPlone.browser.navigation.CatalogNavigationBreadcrumbs, and, as it name implies, uses a catalog query to find the breadcrumbs.

The newer method is found in CMFPlone.browser.navigation.PhysicalNavigationBreadcrumbs. This walks up the acquisition parent hierarchy to find each parent. The astute reader will remember that waking objects like this is generally bad. However, some benchmarking revealed that since the direct parents of the context are quite likely to be in the ZODB cache, and since there are not likely to be very many of them, the cost of traversing to these objects is smaller than the cost of constructing and executing a catalog query.

By being based on real objects, the breadcrumbs can make more extensive use of views. The breadcrumbs are constructed in reverse order, starting at the depeest node. The parents are prepended to the breadcrumbs trail of this node by recursively doing a view lookup on the parent, like so:

    def breadcrumbs(self):
        context = utils.context(self)
        request = self.request
        container = utils.parent(context)

        ...

        view = getMultiAdapter((container, request), name='breadcrumbs_view')
        base = tuple(view.breadcrumbs())

        ...

        rootPath = getNavigationRoot(context)
        itemPath = '/'.join(context.getPhysicalPath())

        ...

        if not utils.isDefaultPage(context, request) and not rootPath.startswith(itemPath):
            base += ({'absolute_url': item_url,
                      'Title': utils.pretty_title_or_id(context, context),
                     },)

        return base

The traversal is stopped at the navigation root by the following degenerate view:

    class RootPhysicalNavigationBreadcrumbs(utils.BrowserView):
        implements(INavigationBreadcrumbs)

        def breadcrumbs(self):
            # XXX Root never gets included, it's hardcoded as 'Home' in
            # the template. We will fix and remove the hardcoding and fix
            # the tests.
            context = utils.context(self)
            return ()

Then, the two views are registered in CMFPlone/browser/configure.zcml as follows:

  <browser:page
      for="*"
      name="breadcrumbs_view"
      class=".navigation.PhysicalNavigationBreadcrumbs"
      permission="zope.Public"
      allowed_attributes="breadcrumbs"
      />

  <browser:page
      for=".interfaces.INavigationRoot"
      name="breadcrumbs_view"
      class=".navigation.RootPhysicalNavigationBreadcrumbs"
      permission="zope.Public"
      allowed_attributes="breadcrumbs"
      />

This pattern is quite elegant, and allows for more specific implementations to be plugged based on the interface of the object, rather than having to substitute it globally.


For any issues with the web site functionality, please file a ticket.

Please consult the policy on plone.org content if you want your content published on this site.

Servers and hosting by