Powerful navigation in Symphony CMS

Most of the time creating a menu for your site is easy, but what if you want a little more control? In this tutorial, I'll show you how to reclaim your sites navigation.

The concept

First things first, I'm going to pretend I'm building a simple blog like site, with just three major pages — Home, Blog, and Portfolio.

Our navigation system must do at least two things:

  1. It must flag the current page in the navigation so that users know where they are
  2. It must give us control over when, and when not to, show navigation items

How can we achieve this? It's easy enough, if you define your navigation as XML inside an XSLT utility then you can use the data sources and parameters available on that very page to change your definition:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:exsl="http://exslt.org/common"
	extension-element-prefixes="exsl"
>

	<xsl:param name="navigation-xml">
		<item id="home" title="Home" href="{$root}" />
		<item id="blog" title="Blog" href="{$root}/blog/" />
		<item id="portfolio" title="Portfolio" href="{$root}/portfolio/" />

		<xsl:if test="$is-logged-in">
			<item title="Debug" href="{$current-url}?debug" />
		</xsl:if>
	</xsl:param>
</xsl:stylesheet>

Basically, we've built a string that contains XML, but not in a way XSLT can use. So we need to define a new parameter, and use EXSLT to transform out string into an XSLT node–set:

<xsl:param name="navigation" select="exsl:node-set($navigation-xml)/*" />

Now we need to add a mechanism to identify pages — add the following to each of your pages, replacing <code>home</code> with the Id you gave the page in your XML definition:

<xsl:param name="navigation-id" select="'home'" />

Then add a fallback parameter to the navigation utility:

<xsl:param name="navigation-id" />

Once you've done all that, it's time to think about rendering your navigation, I'm going to go with a simple ordered list:

<ol>
	<li><a href="..." class="active">Home</a></li>
	...
</ol>

So, now add a template to the utility that can be called to render your menu on the page:

<xsl:template name="navigation">
	<ol>
		<xsl:apply-templates select="$navigation" mode="navigation" />
	</ol>
</xsl:template>

And then add a utility to render each item in the format you want:

<xsl:template match="item" mode="navigation">
	<li>
		<a href="{@href}">
			<xsl:if test="$navigation-id = @id">
				<xsl:attribute name="class">active</xsl:attribute>
			</xsl:if>

			<xsl:value-of select="@title" />
		</a>
	</li>
</xsl:template>

To finish it all off, include the navigation utility in your master template, then call it:

<xsl:call-template name="navigation" />

The utility

For easy copy and pasting, here's the full utility:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
	version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:exsl="http://exslt.org/common"
	extension-element-prefixes="exsl"
>
	<xsl:param name="navigation-xml">
		<item id="home" title="Home" href="{$root}" />
		<item id="blog" title="Blog" href="{$root}/blog/" />
		<item id="portfolio" title="Portfolio" href="{$root}/portfolio/" />

		<xsl:if test="$is-logged-in">
			<item title="Debug" href="{$current-url}?debug" />
		</xsl:if>
	</xsl:param>
	<xsl:param name="navigation" select="exsl:node-set($navigation-xml)/*" />
	<xsl:param name="navigation-id" />

	<xsl:template name="navigation">
		<ol>
			<xsl:apply-templates select="$navigation" mode="navigation" />
		</ol>
	</xsl:template>

	<xsl:template match="item" mode="navigation">
		<li>
			<a href="{@href}">
				<xsl:if test="$navigation-id = @id">
					<xsl:attribute name="class">active</xsl:attribute>
				</xsl:if>

				<xsl:value-of select="@title" />
			</a>
		</li>
	</xsl:template>
</xsl:stylesheet>

That should be all you need to create awesome dynamic menus, but if you have any suggestions or thoughts, don't be afraid to let me know.

Share your thoughts...

Brian Zerangue wrote on :

Wonderful tutorial! Never thought of it this way. Thanks for posting! This is awesome!

Carson Sasser (wisolman) wrote on :

I like this a lot better than using the system Nav data source. I've been using my own crude nav utility, but have just switched to this one.

In my implementation I found that the $navigation-id parameter is not necessary. I used $current-page from the param pool instead.