Recently I had a client that was interested in having two different global navigations. The first global navigation across the top — using the default global navigation that SharePoint implements. However, they wanted a second global navigation in the left column on top of the normal Quick Launch menu. As it turns out this isn’t that hard, if you’re willing to create a site to host the second global navigation. Sahil Malik posted a blog entry titled “Implementing Consistent Navigation across Site Collections” that showed some promise, however, he was using an XmlSiteMap provider and a custom .sitemap file — I needed to allow my customer to perform the modifications to the global navigation themselves.
The answer I came up with was to create a site off of the root, which I called global navigation (/globalnav). I hid this from the real global navigation (so it didn’t show up in the top bar) and managed the navigation in the site so it had the navigation items I needed at the top of my Quick Launch bar. However, the question was how to plug it in.
The left navigation, or Quick Launch, is emitted out of a placeholder in the master page. The default.master file contains the PlaceHolderLeftNavBar which already contains some default content. In that default content is a delegate control called QuickLaunchDataSource. (You can learn more about Delegate controls in my article “SharePoint’s Delegate Control Power“). The default contents of the delegate control is a SiteMapDataSource control:
This is referring to the SPNavigationProvider in the web.config file. There’s a node in /configuration/system.web/siteMap/providers/ which is the site map provider — As shown below:
<add name=”SPNavigationProvider” type=”Microsoft.SharePoint.Navigation.SPNavigationProvider, Microsoft.SharePoint, Version=188.8.131.52, Culture=neutral, PublicKeyToken=71e9bce111e9429c” />
You may notice that the SiteMapDataSource is referencing a StartingNodeUrl of “sid:1025”. This is important for two reasons, first, StartingNodeUrl is useful for navigating to the node in the SiteMapProvider that you want to start at and second because 1025 is the ID for local navigation (quicklaunch) in SharePoint. (By the way, global navigation has an ID of 1002).
This data source is consumed by the SharePoint AspMenu which appears immediately below the delegate control:
The SharePoint AspMenu is a thin shell over the Menu control in ASP.NET. It has the same format for attributes — including the DataSourceId which matches the id of the SiteMapDataSource above. So following the chain… The SharePoint navigation provider returns a set of nodes, the SiteMapDataSource starts at the 1025 (quick launch/local navigation) node and returns the child objects. The SharePoint AspMenu displays the items that are returned (based on the attributes provided.)
With this knowledge we can create a secondary SiteMapDataSource for our global navigation and use the StartingNodeUrl to override where we start at. However, we need one more piece of information. One of the other entries in the web.config file is:
<add name=”GlobalNavSiteMapProvider” description=”CMS provider for Global navigation” type=”Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=184.108.40.206, Culture=neutral, PublicKeyToken=71e9bce111e9429c” NavigationType=”Global” EncodeOutput=”true” />
This site map provider is useful because because it expects to receive a URL in its StartNodeUrl attribute from the SiteMapDataSource. Thus we can create a SiteMapDataSource like this:
Wait a moment you say, what happened to Asp:SiteMapDataSource, well, I wanted to get a few more attributes (described in a moment) so I changed to the actual type of the SiteMapDataSource (PublishingNavigation:PortalSiteMapDataSource). This is a subclass of Asp:SiteMapDataSource so it can be used wherever SiteMapDataSource is expected. Of course, I need to register the PublishingNavigation prefix at the top of the file if it’s not already registered, like this:
<%@ Register TagPrefix=”PublishingNavigation” Namespace=”Microsoft.SharePoint.Publishing.Navigation” Assembly=”Microsoft.SharePoint.Publishing, Version=220.127.116.11, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
So other than the additional attributes, I changed to the CurrentNavSiteMapProvider from SPNavigation Provider, StartingNodeUrl to “/GlobalNav” from “sid:1025”, and changed the ID to “GlobalLeftNav” from “QuickLaunchSiteMap”. This effectively gives me a navigation provider that would be local to /GlobalNav (my global navigation site).
Then all I have to do is copy the <SharePoint:AspMenu> that currently is used for QuickLaunch and replace the DataSourceId with my new GlobalLeftNav Id from my PublishingNavigation:PortalSiteMapDataSource. In the end I have a working second global navigation.
I promised to explain the additional attributes in the PortalSiteMapDataSource. Well, by default the navigation will include sub-sites and pages in the site in question. Rather than relying on the users to remember to hide them if they created any, I can tell the navigation provider that I don’t want any subsites or pages returned. The effect of which is that I get only the headings and links that they added manually to the navigation of the site.
In the end I chose to implement the changes I describe here in a page layout so that the users could easily choose to include the navigation — or not, based on the page they were creating.