SharePoint Custom Current Navigation/PortalSiteMapProvider

Я работаю над пользовательской текущей (левой) навигацией в решении SharePoint.

Что мне нужно, так это то, что корень навигации - это веб-вариант, непосредственный ребенок корневой сети. Все сайты и страницы, которые являются непосредственными детьми этого варианта, должны быть видны, хотя и не расширены. Только сайты, которые являются предками текущего сайта, должны быть расширены... вплоть до текущего сайта/страницы.

Пример... если я начну на странице http://spsite.ex/variation/site2/subsite2.1/subsite2.1.1/subsite2.1.1.3/page.aspx, я должен увидеть...

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

Если я затем нажимаю ссылку для SubSite2.1, я должен увидеть что-то вроде...

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

Если я затем перейду к http://spsite.ex/variation/site5/subsite5.1/page.aspx, я должен увидеть что-то вроде...

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

Я написал решение, но мне кажется, что я не горжусь этим; Я дал AspMenu почти-inifinite StaticDisplayLevels, а затем расширил PortalSiteMapProvider, переопределив GetChildNode(node), чтобы не получить дочерние узлы, за исключением предков текущей сети.

Ответ 1

@ScottE, мне кажется, мне удалось воспроизвести код, который я использовал для решения этой проблемы:

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 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 gotta be a better way...)
            if ((descendant.ServerRelativeUrl + "/").ToUpper().StartsWith(ancestor.ServerRelativeUrl.ToUpper() + "/"))
            {
                return true;
            }
            return false;
        }

    }
}

Возможно, это не лучшее решение... но решение.

Ответ 2

Если вы хотите создать собственное кодированное решение, вы можете создать класс, наследующий от HierarchicalDataBoundControl. Подключите его к PortalSiteMapDataSource в masterpage/pagelayout. Это даст вам полный контроль над выходом и будет таким же оптимизированным, как может быть.

Ответ 3

Как выглядит ваш код... типичное меню, подобное стандарту SiteMapProvider, не может быть сделано намного проще, чем это

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);
                }
            }
        }
    }
}