Thursday, May 7, 2009

MOSS custom navigation provider for left nav

As promised, here is the code for the custom left navigation provider in MOSS. It uses a similar concept as the custom top navigation provider I detailed in this post, but there are some differences. Unlike a top or global navigation which is fairly consistent across your brand, the left navigation can change from site across site collections. It can even be different in different Webs. So based on that consideration, this solution handles those cases. Again, this navigation reads from a SharePoint list on a site. The navigation can read from a list in the root site of a site collection, the current site, or a site below the root site (this was a specific requirement) - all this is in a case statement so pretty easy to change.

Again, I have used this before successfully for a busy site. There are various settings in the Web.config that allow you to customize as per your needs.

This is demonstrated in the picture below. Only the Quick view is changeable on a site per site basis. The Quick Links section is consistent across the brand.

Again, the navigation is being read from a list in SharePoint, such as this. This doesn't have the same data, but should get the point across anyway.

// Assign the provider a default name if it doesn't have one if (String.IsNullOrEmpty(name)) name = "CustomLeftNavProvider";

// Add a default "description" attribute to config if the // attribute doesn’t exist or is empty if (string.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "CompanyXXX custom current left navigation provider"); }

base.Initialize(name, config); }

/// /// This method will be called for all nodes and subnodes that can have children under them. For eg, NodeTypes.AuthoringLink type node /// cannot have child nodes. /// /// The node to find child nodes for /// The SiteMapNodeCollection which contains the children of the child nodes public override SiteMapNodeCollection GetChildNodes(System.Web.SiteMapNode node) { return ComposeNodes(node); }

try { //Get the web where the current list is created navListWeb = ConfigurationManager.AppSettings["LeftNavigationListWeb"];

//if the list is at the root site of the collection, the current web or even a different //site - this is specified in the config file switch (navListWeb) { case "ROOT": sourceListWeb = SPContext.Current.Site.RootWeb; //leftNavList = sourceListWeb.Lists[currentNavList]; break;

/// /// Compose nodes when the method is called. At a minimum, this method gets called with the root node of every /// site collection. We must attach the top level nodes to the root node for this method to get called for those /// nodes as well. /// /// /// public virtual SiteMapNodeCollection ComposeNodes(System.Web.SiteMapNode node) { //Create the SiteCollection to return SiteMapNodeCollection children = new SiteMapNodeCollection(); SortedList orderedNodes = new SortedList();

//If a rootnode, then add the top level children which are the same for every site collection if (node == node.RootNode) { //Serve it from cache if possible if (cacheLeftNav) { object topNodes = HttpRuntime.Cache["LeftNavTopNodes" + sourceListWeb.ID.ToString()]; if (topNodes != null && topNodes is SiteMapNodeCollection) return ((SiteMapNodeCollection)topNodes); }

//Construct the nodes foreach (SPListItem subitem in ic) { //Change the nodeTypes to Authored link for leaf nodes so that the GetChildNodes method is not called for those nodes. NodeTypes ntypes = NodeTypes.Default; if (subitem.Folder == null) ntypes = NodeTypes.AuthoredLink;

//Copy nodes in the right order foreach (object portalSiteMapNode in orderedNodes.Values) { children.Add(portalSiteMapNode as PortalSiteMapNode); }

//Add them to the cache if (cacheLeftNav) HttpRuntime.Cache["LeftNavTopNodes" + sourceListWeb.ID.ToString()] = children; } } else //Else this is a subnode, get only the children of that subnode { string nodeKey = node.Key;

if (cacheLeftNav) { //Get the children for this nodeKey from cache if they exist there object subNodes = HttpRuntime.Cache["LeftNavChildNodes" + sourceListWeb.ID.ToString() + nodeKey]; if (subNodes != null && subNodes is SiteMapNodeCollection) return ((SiteMapNodeCollection)subNodes); }

leftNavList = sourceListWeb.Lists[currentNavList];

//Create the query to browse the list SPQuery qry = new SPQuery();

//Convert the nodeKey into an integer int ID = Convert.ToInt32(nodeKey);

foreach (SPListItem subitem in ic) { //Change the nodeTypes to Authored link for leaf nodes so that the GetChildNodes method is not called //for those nodes. NodeTypes ntypes = NodeTypes.AuthoredLink; if (subitem.Folder != null) ntypes = NodeTypes.Default;

I need custom left navigation with subchilds. for e.g I need to diplay subsitesname,then name of document library and then alll folders inside libray and subfolders etc.I am having issue with the same.Do you have any code related to this?