SharePoint Custom Current Navigation / PortalSiteMapProvider
Asked Answered
H

4

10

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.

Hughhughes answered 8/4, 2010 at 18:44 Comment(5)
Does your solution work?Weltpolitik
Yup! I guess I'm kind of looking for validation that I've understood what I'm doing and how I should be doing it, or if I need to go buy some bad code offsets :P I mean, near-inifinite StaticDisplayLevels... and if the PortalSiteMapDataSource has a StartingNodeOffset of 0 (from the root) I get exceptions... so it smells a little.Hughhughes
This is the sort of thing that Sharepoint should really allow you to do with the out-of-the-box navigation control, seeing how commonly it is used on the internet - maybe in the next version after 2010...Triennium
I'm looking for something similar. Did your solution work, and if so, can you share a bit of your code?Lingle
@Lingle - Iwish I could, but I'm no longer at that company, and I didn't keep a copy as I wasn't proud of the solution... I'll see if some msdn reading will refresh my memory.Hughhughes
H
1

@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.

Hughhughes answered 6/2, 2011 at 4:37 Comment(1)
Looking at @Pauli Østerø's answer I wonder if method SiteMap.CurrentNode.IsEqualToOrDescendantOf(SiteMapNode) could be used to avoid creating so many SPWeb objects.Hughhughes
F
0

If you want to do a custom coded solution you could create a class inheriting from HierarchicalDataBoundControl. Hook it up with the PortalSiteMapDataSource in you masterpage/pagelayout. This will give you full control of the output and is as optimized as can be.

Fabien answered 15/12, 2010 at 15:56 Comment(0)
H
0

What does your code look like... a typical menu like this using standard SiteMapProvider can't be made much simpler than this

public class SideMenu : Control
{
    private SiteMapNode _rootNode = SiteMap.RootNode;
    public SiteMapNode RootNode
    {
        get { return this._rootNode; }
        set { this._rootNode = value; }
    }

    public SideMenu()
    {
        ID = "SideMenu";
    }

    protected override void CreateChildControls()
    {
        var div = new HtmlGenericControl("div");
        div.Attributes.Add("id", ID);
        Controls.Add(div);

        CreateMenuNodes(RootNode, div);

        base.CreateChildControls();
    }

    protected override void Render(HtmlTextWriter writer)
    {
        if (!ChildControlsCreated)
        {
            CreateChildControls();
        }

        base.Render(writer);
    }

    private void CreateMenuNodes(SiteMapNode node, HtmlGenericControl container)
    {
        if (node.HasChildNodes)
        {
            var ul = new HtmlGenericControl("ul");
            container.Controls.Add(ul);

            foreach (SiteMapNode child in node.ChildNodes)
            {
                var li = new HtmlGenericControl("li");
                ul.Controls.Add(li);

                var a = new HtmlAnchor()
                {
                    InnerHtml = HttpUtility.HtmlEncode(child.Title),
                    Title = child.Title,
                    HRef = child.Url
                };

                li.Controls.Add(a);

                if (SiteMap.CurrentNode.IsEqualToOrDescendantOf(child))
                {
                    li.Attributes["class"] = "selected";

                    CreateMenuNodes(child, li);
                }
            }
        }
    }
}
Harriettharrietta answered 6/1, 2011 at 6:20 Comment(0)
A
0

Here is another option that is a lot more elegant. http://sharepoint2010customnavigation.blogspot.com/

Amytal answered 26/5, 2011 at 15:50 Comment(1)
That article is about SharePoint 2010; I didn't specify in my question, but it had been about MOSS2007Hughhughes

© 2022 - 2024 — McMap. All rights reserved.