Custom PortalSiteMapDataSource to display full site structure in left navigation in SharePoint

I had a requirement to ensure that all sites within the site collection would display the full site structure (up to the 1st level sub-site) on the left navigation. Given the site structure below for example:

Home
   Site 1
      Site 1.1
      Site 1.1.1
   Site 1.2
      Site 1.2.1
   Site 2
      Site 2.1
         Site 2.1.1
      Site 2.2

For any site underneath Site 1, the following should be shown on the left nav:

Site 1
   Site 1.1
      Site 1.1.1
   Site 1.2
      Site 1.2.1

For any site underneath Site 2, the following should be shown on the left nav:

Site 2
   Site 2.1
      Site 2.1.1
   Site 2.2

This can be achieved by tweaking the StartingNodeOffset property of the PortalSiteMapDataSource. The PortalSiteMapDataSource is a SharePoint control that typically sits on the masterpage and provides data for the menu controls on the page. See http://blogs.msdn.com/b/ecm/archive/2007/02/10/moss-navigation-deep-dive-part-1.aspx for more information about this control and other important elements related to navigation in SharePoint.

When the StartFromCurrentNode = true and StartingNodeOffset = -1, the first node in the left nav will be the parent of the current site. If StartingNodeOffset = -2, then the first node is the parent of the parent of the current site, and so on.

The problem however is OOTB the PortalSiteMapDataSource is defined in the masterpage and hence the StartingNodeOffset is static and the same for all sites. If you set it to -2, it will work for site 2.1.1 but will not work for site 2.1 or 2.1.1.1 for example.

To address this you can developed a custom PortalSiteMapDataSource which will inherit the OOTB class. It is actually relatively simple.

Step 1: Create the custom PortalSiteMapDataSource

In this class we override the GetHierarchicalView method and calculate the ‘level’ of the current site and adjust the StartingNodeOffset property accordingly.

public class MyPortalSiteMapDataSource : PortalSiteMapDataSource
{
	protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath)
	{
		int siteLevel = GetCurrentWebSiteLevel(SPContext.Current.Web);
		this.StartingNodeOffset = siteLevel == 0 ?
			0 : 0 - siteLevel + 1;
		return base.GetHierarchicalView(viewPath);
	}
	private int GetCurrentWebSiteLevel(SPWeb currentWeb)
	{
		//We can't really work out the level from the URL because the URL
		//might contain managed path that is not part of the site collection
		//(e.g. http://server/ or http://server/sites/).
		int level = 0;
		var tempWeb = currentWeb;
		while (!tempWeb.IsRootWeb)
		{
			level++;
			tempWeb = tempWeb.ParentWeb;
			//Official guidance from MS is that we do not need to call Dispose on SPWeb.ParentWeb.
		}
		return level;
	}
}

Step 2: Update the master page to use the custom PortalSiteMapDataSource

Remember to never modify the OOTB master pages. Create a copy of the master page (e.g. v4.master, or nightandday.master) and add the following to the top of the page to register our custom PortalSiteMapDataSource as a user control:

<%@ register tagprefix="MyNavigation" namespace="MyProject"
       assembly="MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f7f490906818fff6" %>

Tip: To quickly get the strongly name of your assembly see this post: http://blah.winsmarts.com/2009-12-SharePoint_Productivity_Tip_of_the_day.aspx. This is one of the first things I setup on my dev environment.

In the master page search for (this is from nightandday.master):

<PublishingNavigation:PortalSiteMapDataSource
			            ID="SiteMapDS"
			            runat="server"
			            EnableViewState="false"
			            SiteMapProvider="CurrentNavigation"
			            StartFromCurrentNode="true"
			            StartingNodeOffset="0"
			            ShowStartingNode="false"
			            TrimNonCurrentTypes="Heading"/>

The key here is the SiteMapProvider=”CurrentNavigation” part. Change this to

<MyNavigation:MyPortalSiteMapDataSource
ID="SiteMapDS"
runat="server"
EnableViewState="false"
SiteMapProvider="CurrentNavigation"
StartFromCurrentNode="true"
StartingNodeOffset="0"
ShowStartingNode="true"
TrimNonCurrentTypes="Heading"/>

The key here is changing from the OOTB class to our class. We also need to set ShowStartingNode to true.

Step 3: Register custom PortalSiteMapDataSource as safe control

This is the final step. If you have built the custom PortalSiteMapDataSource in its own project and reference it from your main project, then you can use Visual Studio’s Package Explorer to add the project to the package and add our custom PortalSiteMapDataSource as a safe control.

If you have everything in one single project, then you will have to manually edit the manifest template of the package to include the PortalSiteMapDataSource as a safe control as Visual Studio does not seem to pick it up automatically.

That’s it – go ahead and deploy your solution.

Advertisements

About Bernado

Based in Australia, I am a freelance SharePoint and Dynamics CRM developer. I love developing innovative solutions that address business and everyday problems. Feel free to contact me if you think I can help you with your SharePoint or CRM implementation.
This entry was posted in Branding, SharePoint 2010, Uncategorized. Bookmark the permalink.

One Response to Custom PortalSiteMapDataSource to display full site structure in left navigation in SharePoint

  1. Ben says:

    Bernado mate you are a champion. This post saved me god knows how much time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s