SharePoint Custom Current Navigation / PortalSiteMapProvider

asp.netnavigationportalsitemapprovidersharepointsharepoint-2007

I'm working on a custom current (left) navigation on a SharePoint solution.

What I need is that the root of the navigation is a variation web, the immediate child of the root web. All the sites and pages which are immediate children of this variation should be visible, though not expanded. Only sites which are ancestors of the current site should be expanded… all the way down to the current site/page.

An example… if I start on page http://spsite.ex/variation/site2/subsite2.1/subsite2.1.1/subsite2.1.1.3/page.aspx I should see…

Site1
Site2
    SubSite2.1
        SubSite2.1.1
            SubSite2.1.1.1
            SubSite2.1.1.2
            SubSite2.1.1.3
                page.aspx (YOU ARE HERE)
    SubSite2.2
    Site2Page1
    Site2Page2
Site3
Site4
Site5

If I then click on the link for SubSite2.1 I should see something like…

Site1
Site2
    SubSite2.1 (YOU ARE HERE)
        SubSite2.1.1
    SubSite2.2
    Site2Page1
    Site2Page2
Site3
Site4
Site5

If I then navigate to http://spsite.ex/variation/site5/subsite5.1/page.aspx I should see something like…

Site1
Site2
Site3
Site4
Site5
    SubSite5.1
        SubSite5.1.1
        page.aspx (YOU ARE HERE)

I've written a solution, but I feel like it's not one I should feel proud of; I've given the AspMenu a near-inifinite StaticDisplayLevels and then extended PortalSiteMapProvider, overriding GetChildNode(node) to not get child nodes, except for ancestors of the current web.

Best Answer

@ScottE, I think I've managed to reproduce the code I used to solve this problem:

using System;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.Navigation;

namespace StackOverflow.SharePoint
{
    public class Question2602537PortalSiteMapProvider : PortalSiteMapProvider
    {

        public override SiteMapNodeCollection GetChildNodes(System.Web.SiteMapNode node)
        {
            bool expandChildNodes = false;
            if (SPContext.Current != null)
            {
                expandChildNodes = NodeIsAncestorOfCurrentNode(node);
            }

            if (expandChildNodes)
            {
                return base.GetChildNodes(node);
            }
            else
            {
                return new SiteMapNodeCollection();
            }
        }

        private bool NodeIsAncestorOfCurrentNode(System.Web.SiteMapNode node)
        {
            bool returnvalue = false;
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite thisSite = new SPSite(SPContext.Current.Site.ID))
                {
                    using (SPWeb nodeWeb = this.OpenWeb(thisSite, node))
                    {
                        using (SPWeb currentWeb = this.OpenNavWeb(thisSite))
                        {
                            returnvalue = this.AncestorDescendantWebs(nodeWeb, currentWeb);
                        }
                    }
                }
            });
            return returnvalue;
        }

        private SPWeb OpenWeb(SPSite thisSite, System.Web.SiteMapNode node)
        {
            // need to use Uri objects, as sometimes the node URL contains a query string
            // but calling OpenWeb(...) with a ? in your URL throws an exception
            // using Uri.LocalPath removes the Query String
            Uri siteUri = new Uri(thisSite.Url);
            Uri nodeUri = new Uri(siteUri, node.Url);
            return thisSite.OpenWeb(nodeUri.LocalPath.Split(new string[] { "/_" }, StringSplitOptions.RemoveEmptyEntries)[0], false);
        }

        private SPWeb OpenNavWeb(SPSite thisSite)
        {
            using (SPWeb currentWeb = thisSite.OpenWeb(this.CurrentWeb.ID))
            {
                SPWeb web = currentWeb;
                PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);

                // Loop all the way up the webs until we find the one which doesn't inherit
                // (there's gotta be a better way of doing this)
                while (publishingWeb.InheritCurrentNavigation &&
                    !web.ID.Equals(thisSite.RootWeb.ID))
                {
                    web = web.ParentWeb;
                    publishingWeb = PublishingWeb.GetPublishingWeb(web);
                }

                return web;
            }
        }

        private bool AncestorDescendantWebs(SPWeb ancestor, SPWeb descendant)
        {
            // check the URLs to determine if descendant is a subweb or ancestor
            // (there's gotta be a better way...)
            if ((descendant.ServerRelativeUrl + "/").ToUpper().StartsWith(ancestor.ServerRelativeUrl.ToUpper() + "/"))
            {
                return true;
            }
            return false;
        }

    }
}

Perhaps not the best solution... but a solution.

Related Topic