Skip Ribbon Commands
Skip to main content

Robin | zevenseas | SharePoint Blog

:

The zevenseas Community > Blogs > Robin | zevenseas | SharePoint Blog > Posts > Current Navigation, SPNavigationNodes and the AreaNavigationSettings page
January 06
Current Navigation, SPNavigationNodes and the AreaNavigationSettings page

I was at a customer recently where I had the request that the current navigation of a team site should have a particular ordering. In this ‘current’ navigation, the navigation consisted of links to lists on the specific site to links to sub sites of that site. I knew that in the AreaNavigationSettings.aspx page (see screenshot) you could define such a ordering just by moving links up and down and also check the checkbox to display subsites. So, I thought.. ha that’s easy! I’m just going to check what kind of code is behind that page.. use that et voila.. Robin is a happy man.. :)

image

Boy.. was I wrong.. maybe it was my bad that I couldn’t really figure out what they are doing behind the scenes when I used Reflector to see how the code looked like but maybe it wasn’t ;) So instead of staring at the code and trying to make it work in my dev environment I turned to Google again and did some specific searches and then I came across my lifesaver Gary Lapointe with this post : More Site Navigation Settings Commands.

Apparently, there is no real way of ‘modifying’ the navigation by just moving nodes around and then save the collection if you want make use of the ability of displaying subsites by making use of the IncludeSubSitesInNavigation bool. The thing is that you will have to delete the current nodes and recreate them, in the proper order, and save that back (please correct me if I’m wrong!).

To continue, I used his code and modified it a bit so I wouldn’t need to pass a xml document with Nodes but just a List<Nodes> to create my navigation. Next to that, I was only interested in modifying the Current Navigation and not the Global Navigation so I stripped that bit out.

Here’s the my custom NavigationNode class and the method to create the navigation:

   1: public class NavigationNode
   2: {
   3:     public string Title { get; set; }
   4:     public string Url { get; set; }
   5:     public NodeTypes NodeType { get; set; }
   6:     public SPNavigationNodeCollection NavigationNodeCollection { get; set; }
   7:  
   8:     public NavigationNode(string title, string url, NodeTypes nodeType, SPNavigationNodeCollection collection)
   9:     {
  10:         Title = title;
  11:         Url = url;
  12:         NodeType = nodeType;
  13:         NavigationNodeCollection = collection;
  14:     }
  15: }
   1: protected void CreateNavigation()
   2: {
   3:     PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(CurrentWeb);
   4:     List<NavigationNode> nodeCollection = new List<NavigationNode>();
   5:     nodeCollection.Add(new NavigationNode("Home", "Pages/Default.aspx", NodeTypes.AuthoredLink, publishingWeb.CurrentNavigationNodes));
   6:     nodeCollection.Add(new NavigationNode("Discussions", "Lists/Discussions/AllItems.aspx", NodeTypes.AuthoredLink, publishingWeb.CurrentNavigationNodes));
   7:     nodeCollection.Add(new NavigationNode("Documents", "Shared Documents/Forms/AllItems.aspx", NodeTypes.AuthoredLink, publishingWeb.CurrentNavigationNodes));
   8:     nodeCollection.Add(new NavigationNode("Wiki", CurrentWeb.ServerRelativeUrl + "/Wiki", NodeTypes.Area, publishingWeb.CurrentNavigationNodes));
   9:     nodeCollection.Add(new NavigationNode("Calendar", "Meetings/Lists/Kalender/calendar.aspx", NodeTypes.AuthoredLinkToWeb, publishingWeb.CurrentNavigationNodes));                      
  10:     SetNavigation(CurrentWeb, nodeCollection, true, false, true, true);
  11:     publishingWeb.Update();
  12: }

And then here is my, slightly, modified version of Gary’s awesome code :

   1: /// Original source is from Gary Lapointe, I've only done a modification so that it accepts a List with NavigationNodes instead of a XML file
   2:        /// <summary>
   3:        /// Sets the navigation.
   4:        /// </summary>
   5:        /// <param name="site">The site.</param>
   6:        /// <param name="web">The web site.</param>
   7:        /// <param name="nodeCollection">A list containing all the nodes.</param>
   8:        /// <param name="showSubSites">if set to <c>true</c> [show sub sites].</param>
   9:        /// <param name="showPages">if set to <c>true</c> [show pages].</param>
  10:        /// <param name="deleteExistingGlobal">if set to <c>true</c> [delete existing global nodes].</param>
  11:        /// <param name="deleteExistingCurrent">if set to <c>true</c> [delete existing current nodes].</param>
  12:        public static void SetNavigation(SPWeb web, List<NavigationNode> nodeCollection, bool showSubSites, bool showPages, bool deleteExistingGlobal, bool deleteExistingCurrent)
  13:        {
  14:            PublishingWeb pubweb = PublishingWeb.GetPublishingWeb(web);
  15:  
  16:            // First need to set whether or not we show sub-sites and pages
  17:            pubweb.IncludeSubSitesInNavigation = showSubSites;
  18:            pubweb.IncludePagesInNavigation = showPages;
  19:            pubweb.Update();
  20:  
  21:            List<SPNavigationNode> existingGlobalNodes = new List<SPNavigationNode>();
  22:            List<SPNavigationNode> existingCurrentNodes = new List<SPNavigationNode>();
  23:            // We can't delete the navigation items until we've added the new ones so store the existing 
  24:            // ones for later deletion (note that we don't have to store all of them - just the top level).
  25:            // I have no idea why this is the case - but when I tried to clear everything out first I got
  26:            // all kinds of funky errors that just made no sense to me - this works so....
  27:            foreach (SPNavigationNode node in pubweb.GlobalNavigationNodes)
  28:                existingGlobalNodes.Add(node);
  29:            foreach (SPNavigationNode node in pubweb.CurrentNavigationNodes)
  30:                existingCurrentNodes.Add(node);
  31:  
  32:            List<NavigationNode> newCurrentNodes = nodeCollection;
  33:  
  34:            if (newCurrentNodes.Count > 0)
  35:            {
  36:                pubweb.InheritCurrentNavigation = false;
  37:                pubweb.Update();
  38:            }
  39:            pubweb = PublishingWeb.GetPublishingWeb(web);
  40:  
  41:            // If we've got global or current nodes in the xml then the intent is to reset those elements.
  42:            // If we've also specified to delete any existing elements then we need to first hide all the
  43:            // sub-sites and pages (you can't delete them because they don't exist as a node).  Note that
  44:            // we are only doing this if showSubSites is true - if it's false we don't see them so no point
  45:            // in hiding them.  Any non-sub-site or non-page will be deleted after we've added the new nodes.
  46:            foreach (SPWeb tempWeb in pubweb.Web.Webs)
  47:            {
  48:                try
  49:                {
  50:                    if (newCurrentNodes.Count > 0 && deleteExistingCurrent && showSubSites)
  51:                    {
  52:                        pubweb.ExcludeFromNavigation(false, tempWeb.ID);
  53:                    }
  54:                }
  55:                finally
  56:                {
  57:                    tempWeb.Dispose();
  58:                }
  59:            }
  60:            pubweb.Update();
  61:  
  62:            // Now we need to add all the current nodes (if any)
  63:            AddNodes(pubweb, false, pubweb.CurrentNavigationNodes, newCurrentNodes);
  64:            // Update the web as the above may have made modifications
  65:            pubweb.Update();
  66:  
  67:            // Now delete all the previously existing current nodes.
  68:            if (newCurrentNodes.Count > 0 && deleteExistingCurrent)
  69:            {
  70:                foreach (SPNavigationNode node in existingCurrentNodes)
  71:                {
  72:                    node.Delete();
  73:                }
  74:            }
  75:        }
   1: /// <summary>
   2:        /// Adds the nodes.
   3:        /// </summary>
   4:        /// <param name="pubWeb">The publishing web.</param>
   5:        /// <param name="isGlobal">if set to <c>true</c> [is global].</param>
   6:        /// <param name="existingNodes">The existing nodes.</param>
   7:        /// <param name="newNodes">The new nodes.</param>
   8:        private static void AddNodes(PublishingWeb pubWeb, bool isGlobal, SPNavigationNodeCollection existingNodes, List<NavigationNode> newNodes)
   9:        {
  10:            if (newNodes.Count == 0)
  11:                return;
  12:  
  13:            for (int i = 0; i < newNodes.Count; i++)
  14:            {
  15:                NavigationNode newNodeXml = (NavigationNode)newNodes[i];
  16:                string url = newNodeXml.Url;
  17:                string title = newNodeXml.Title;
  18:                NodeTypes type = newNodeXml.NodeType;
  19:  
  20:                bool isVisible = true;
  21:  
  22:                if (type == NodeTypes.Area)
  23:                {
  24:                    // You can't just add an "Area" node (which represents a sub-site) to the current web if the
  25:                    // url does not correspond with an actual sub-site (the code will appear to work but you won't
  26:                    // see anything when you load the page).  So we need to check and see if the node actually
  27:                    // points to a sub-site - if it does not then change it to "AuthoredLinkToWeb".
  28:                    SPWeb web = null;
  29:                    try
  30:                    {
  31:                        string name = url.Trim('/');
  32:                        if (name.Length != 0 && name.IndexOf("/") > 0)
  33:                        {
  34:                            name = name.Substring(name.LastIndexOf('/') + 1);
  35:                        }
  36:                        try
  37:                        {
  38:                            // pubWeb.Web.Webs[] does not return null if the item doesn't exist - it simply throws an exception (I hate that!)
  39:                            web = pubWeb.Web.Webs[name];
  40:                        }
  41:                        catch (ArgumentException)
  42:                        {
  43:                        }
  44:                        if (web == null || !web.Exists || web.ServerRelativeUrl.ToLower() != url.ToLower())
  45:                        {
  46:                            // The url doesn't correspond with a sub-site for the current web so change the node type.
  47:                            // This is most likely due to copying navigation elements from another site
  48:                            type = NodeTypes.AuthoredLinkToWeb;
  49:                        }
  50:                        else if (web.Exists && web.ServerRelativeUrl.ToLower() == url.ToLower())
  51:                        {
  52:                            // We did find a matching sub-site so now we need to set the visibility
  53:                            if (isVisible)
  54:                                pubWeb.IncludeInNavigation(isGlobal, web.ID);
  55:                            else
  56:                                pubWeb.ExcludeFromNavigation(isGlobal, web.ID);
  57:                        }
  58:                    }
  59:                    finally
  60:                    {
  61:                        if (web != null)
  62:                            web.Dispose();
  63:                    }
  64:  
  65:                }
  66:                else if (type == NodeTypes.Page)
  67:                {
  68:                    // Adding links to pages has the same limitation as sub-sites (Area nodes) so we need to make
  69:                    // sure it actually exists and if it doesn't then change the node type.
  70:                    PublishingPage page = null;
  71:                    try
  72:                    {
  73:                        // Note that GetPublishingPages()[] does not return null if the item doesn't exist - it simply throws an exception (I hate that!)
  74:                        page = pubWeb.GetPublishingPages()[url];
  75:                    }
  76:                    catch (ArgumentException)
  77:                    {
  78:                    }
  79:                    if (page == null)
  80:                    {
  81:                        // The url doesn't correspond with a page for the current web so change the node type.
  82:                        // This is most likely due to copying navigation elements from another site
  83:                        type = NodeTypes.AuthoredLinkToPage;
  84:                        url = pubWeb.Web.Site.MakeFullUrl(url);
  85:                    }
  86:                    else
  87:                    {
  88:                        // We did find a matching page so now we need to set the visibility
  89:                        if (isVisible)
  90:                            pubWeb.IncludeInNavigation(isGlobal, page.ListItem.UniqueId);
  91:                        else
  92:                            pubWeb.ExcludeFromNavigation(isGlobal, page.ListItem.UniqueId);
  93:                    }
  94:                }
  95:  
  96:                // If it's not a sub-site or a page that's part of the current web and it's set to
  97:                // not be visible then just move on to the next (there is no visibility setting for
  98:                // nodes that are not of type Area or Page).
  99:                if (!isVisible && type != NodeTypes.Area && type != NodeTypes.Page)
 100:                    continue;
 101:  
 102:                // Finally, can add the node to the collection.
 103:                SPNavigationNode node = SPNavigationSiteMapNode.CreateSPNavigationNode(
 104:                    title, url, type, existingNodes);
 105:  
 106:  
 107:                // Now we need to set all the other properties
 108:  
 109:                // If we didn't have a CreatedDate or LastModifiedDate then set them to now.
 110:                if (node.Properties["CreatedDate"] == null)
 111:                    node.Properties["CreatedDate"] = DateTime.Now;
 112:                if (node.Properties["LastModifiedDate"] == null)
 113:                    node.Properties["LastModifiedDate"] = DateTime.Now;
 114:  
 115:                // Save our changes to the node.
 116:                node.Update();
 117:                node.MoveToLast(existingNodes); // Should already be at the end but I prefer to make sure :)
 118:  
 119:            }
 120:  
 121:        }
 122:    }


Conclusion

Setting up navigation while provisioning the site in code can be hard. Unfortunately it’s not that easy as the AreaNavigationSettings page (in the screenshot) makes you believe it is.

Took me quite a while to get it working but thanks to guys like Gary I’ve managed it..If you read his comments in the code you can see it was quite the challenge to get this thing right. So thanks again mate! :)

Comments

NavigationNode

  public class NavigationNode
  {
        public string Title { get; set; }
        public string Url { get; set; }
        public NodeTypes NodeType { get; set; }
        public bool BlankUrl { get; set; }
        public List<NavigationNode> Childrens { get; set; }
        public SPNavigationNodeCollection NavigationNodeCollection { get; set; }

        public NavigationNode(string title, string url, NodeTypes nodeType, bool blankurl, List<NavigationNode> childrens, SPNavigationNodeCollection collection)
        {
           Title = title;
           Url = url;
           NodeType = nodeType;
           BlankUrl = blankurl;
           Childrens = childrens;
           NavigationNodeCollection = collection;
       }
  }
 on 09/12/2010 21:35

CreateNavigation

List<NavigationNode> childs = new List<NavigationNode>();
childs.Add(new NavigationNode("test1", "/sites/test", NodeTypes.AuthoredLinkPlain, false, null, null));
childs.Add(new NavigationNode("test2", "/sites/test2", NodeTypes.AuthoredLinkPlain, false, null, null));
childs.Add(new NavigationNode("test3", "/sites/test3", NodeTypes.AuthoredLinkPlain, false, null, null));
nodeCollection.Add(new NavigationNode("test", "/sites/test", NodeTypes.Heading, false, childs, publishingWeb.CurrentNavigationNodes));
 on 09/12/2010 21:36

AddNodes

private static void AddNodes(PublishingWeb pubWeb,
{
...
       bool blank = newNodeXml.BlankUrl;
       if (blank)
       {
           node.Properties["Target"] = "_blank";
       }

       // Save our changes to the node.
       node.Update();
       node.MoveToLast(existingNodes); // Should already be at the end but I prefer to make sure :)

       if (newNodeXml.Childrens != null && newNodeXml.Childrens.Count > 0)
       {
           AddNodes(pubWeb, node.Children, newNodeXml.Childrens);
       }
}
 on 09/12/2010 21:39

Thank you Robin

Thank you Robin for post
 on 09/12/2010 21:41
 

 Statistics

 
Views: 11425
Comments: 0
Tags:
Published:1569 Days Ago