Пользовательская текущая навигация SharePoint / 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
почти бесконечный StaticDisplayLevels
а затем продлен PortalSiteMapProvider
переопределяя GetChildNode(node)
не получить дочерние узлы, кроме предков текущей сети.
4 ответа
@ScottE, я думаю, что мне удалось воспроизвести код, который я использовал для решения этой проблемы:
using System;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.Navigation;
namespace Stackru.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;
}
}
}
Возможно, не лучшее решение... но решение.
Вот еще один вариант, который намного элегантнее. http://sharepoint2010customnavigation.blogspot.com/
Если вы хотите создать собственное кодированное решение, вы можете создать класс, унаследованный от HierarchicalDataBoundControl. Подключите его с помощью PortalSiteMapDataSource в вашей главной странице /pagelayout. Это даст вам полный контроль над выходом и будет максимально оптимизировано.
Как выглядит ваш код... типичное меню, как это, используя стандартные 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);
}
}
}
}
}