mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-04-05 21:01:35 +08:00
Adding local nav functionality.
--HG-- branch : dev
This commit is contained in:
parent
0b92a82708
commit
042968453b
@ -1,33 +1,67 @@
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Routing;
|
||||
using Castle.Core;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.Tests.Stubs;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Tests.UI.Navigation {
|
||||
[TestFixture]
|
||||
public class MenuFilterTests {
|
||||
private const string FirstLevel1Action = "FirstLevel1";
|
||||
private const string SecondLevel1Action = "SecondLevel1";
|
||||
private const string SecondLevel2Action = "SecondLevel2";
|
||||
private const string ThirdLevel1Action = "ThirdLevel1";
|
||||
private const string ThirdLevel2Action = "ThirdLevel2";
|
||||
private const string ThirdLevel3Action = "ThirdLevel3";
|
||||
private const string ThirdLevel4Action = "ThirdLevel4";
|
||||
private const string FourthLevel1Action = "FourthLevel1";
|
||||
private const string FourthLevel2Action = "FourthLevel2";
|
||||
private const string FourthLevel3Action = "FourthLevel3";
|
||||
private const string FourthLevel4Action = "FourthLevel4";
|
||||
|
||||
private static AuthorizationContext GetAuthorizationContext<TController>() where TController : ControllerBase, new() {
|
||||
var controllerDescriptor = new ReflectedControllerDescriptor(typeof(TController));
|
||||
var controllerContext = new ControllerContext(new StubHttpContext(), new RouteData(), new TController());
|
||||
return new AuthorizationContext(
|
||||
controllerContext,
|
||||
controllerDescriptor.FindAction(controllerContext, "Index"));
|
||||
[Test]
|
||||
public void MockNavManagerWorks() {
|
||||
var main = GetNavigationManager().Object.BuildMenu("main");
|
||||
Assert.That(main.Count(), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
private static IAuthorizer GetAuthorizer(bool result) {
|
||||
var authorizer = new Mock<IAuthorizer>();
|
||||
authorizer
|
||||
.Setup(x => x.Authorize(StandardPermissions.AccessAdminPanel, It.IsAny<LocalizedString>())).
|
||||
Returns(result);
|
||||
return authorizer.Object;
|
||||
[Test]
|
||||
public void FindSelectedPathScenario2() {
|
||||
NavigationBuilder navigationBuilder = BuildMenuScenario2();
|
||||
IEnumerable<MenuItem> menuItems = navigationBuilder.Build();
|
||||
|
||||
MenuItem firstLevel1 = FindMenuItem(menuItems, "X");
|
||||
MenuItem secondLevel2 = FindMenuItem(menuItems, "B");
|
||||
MenuItem thirdLevel2 = FindMenuItem(menuItems, "D");
|
||||
MenuItem fourthLevel3 = FindMenuItem(menuItems, "G");
|
||||
RouteData fourthLevel3RouteData = GetRouteData(fourthLevel3);
|
||||
|
||||
Stack<MenuItem> selectionStack = MenuFilterAccessor.FindSelectedPathAccessor(menuItems, fourthLevel3RouteData);
|
||||
Assert.That(selectionStack.Pop(), Is.EqualTo(firstLevel1));
|
||||
Assert.That(selectionStack.Pop(), Is.EqualTo(secondLevel2));
|
||||
Assert.That(selectionStack.Pop(), Is.EqualTo(thirdLevel2));
|
||||
Assert.That(selectionStack.Pop(), Is.EqualTo(fourthLevel3));
|
||||
Assert.That(selectionStack.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindParentLocalTaskScenario2() {
|
||||
NavigationBuilder navigationBuilder = BuildMenuScenario2();
|
||||
IEnumerable<MenuItem> menuItems = navigationBuilder.Build();
|
||||
|
||||
MenuItem fourthLevel3 = FindMenuItem(menuItems, "G");
|
||||
RouteData fourthLevel3RouteData = GetRouteData(fourthLevel3);
|
||||
Stack<MenuItem> selectedPath = MenuFilterAccessor.FindSelectedPathAccessor(menuItems, fourthLevel3RouteData);
|
||||
|
||||
MenuItem parentNode = MenuFilterAccessor.FindParentLocalTaskAccessor(selectedPath);
|
||||
|
||||
Assert.That(parentNode, Is.EqualTo(FindMenuItem(menuItems, "B")));
|
||||
}
|
||||
|
||||
private static Mock<INavigationManager> GetNavigationManager() {
|
||||
var mainMenu = new[] { new MenuItem { Text = "The Main Menu" } };
|
||||
var adminMenu = new[] { new MenuItem { Text = "The Admin Menu" } };
|
||||
@ -37,23 +71,76 @@ namespace Orchard.Tests.UI.Navigation {
|
||||
return navigationManager;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MockNavManagerWorks() {
|
||||
var main = GetNavigationManager().Object.BuildMenu("main");
|
||||
Assert.That(main.Count(), Is.EqualTo(1));
|
||||
private static NavigationBuilder BuildMenuScenario1() {
|
||||
NavigationBuilder navigationBuilder = new NavigationBuilder();
|
||||
navigationBuilder.Add(new LocalizedString("X"), "0",
|
||||
menu => menu
|
||||
.Add(new LocalizedString("A"), "0", subMenu => subMenu.Action("Index", "Admin", new { area = "Area" })
|
||||
.Add(new LocalizedString("B"), "0", item => item.Action("Index", "Admin", new { area = "Area" }))
|
||||
.Add(new LocalizedString("C"), "1", item => item.Action("Index", "Admin", new { area = "Area" }).LocalTask())))
|
||||
.Add(new LocalizedString("D"), "1", subMenu => subMenu.Action("Index", "Admin", new { area = "Area" }).LocalTask()
|
||||
.Add(new LocalizedString("E"), "0", item => item.Action("Index", "Admin", new { area = "Area" }))
|
||||
.Add(new LocalizedString("F"), "1", item => item.Action("Index", "Admin", new { area = "Area" }).LocalTask()));
|
||||
|
||||
return navigationBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
private static NavigationBuilder BuildMenuScenario2() {
|
||||
NavigationBuilder navigationBuilder = new NavigationBuilder();
|
||||
navigationBuilder.Add(new LocalizedString("X"), "0",
|
||||
menu => menu
|
||||
.Add(new LocalizedString("A"), "0", item => item.Action(SecondLevel1Action, "Admin", new { area = "Area" }))
|
||||
.Add(new LocalizedString("B"), "1",
|
||||
subMenu => subMenu
|
||||
.Add(new LocalizedString("C"), "0", item => item.Action(ThirdLevel1Action, "Admin", new { area = "Area" }).LocalTask())
|
||||
.Add(new LocalizedString("D"), "1",
|
||||
subSubMenu => subSubMenu.LocalTask()
|
||||
.Add(new LocalizedString("E"), "0", item => item.Action(FourthLevel1Action, "Admin", new { area = "Area" }).LocalTask())
|
||||
.Add(new LocalizedString("F"), "1", item => item.Action(FourthLevel2Action, "Admin", new { area = "Area" }).LocalTask())
|
||||
.Add(new LocalizedString("G"), "2", item => item.Action(FourthLevel3Action, "Admin", new { area = "Area" }))
|
||||
.Add(new LocalizedString("W"), "3", item => item.Action(FourthLevel4Action, "Admin", new { area = "Area" })))));
|
||||
|
||||
public class NormalController : Controller {
|
||||
public ActionResult Index() {
|
||||
return View();
|
||||
return navigationBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
public class AdminController : Controller {
|
||||
public ActionResult Index() {
|
||||
return View();
|
||||
protected static MenuItem FindMenuItem(IEnumerable<MenuItem> menuItems, string text) {
|
||||
Queue<MenuItem> remainingItems = new Queue<MenuItem>(menuItems);
|
||||
|
||||
while (remainingItems.Count > 0) {
|
||||
MenuItem currentMenuItem = remainingItems.Dequeue();
|
||||
|
||||
if (currentMenuItem.Text.Equals(text)) {
|
||||
return currentMenuItem;
|
||||
}
|
||||
|
||||
currentMenuItem.Items.ForEach(remainingItems.Enqueue);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static RouteData GetRouteData(MenuItem menuItem) {
|
||||
RouteData routeData = new RouteData();
|
||||
routeData.Values["area"] = menuItem.RouteValues["area"];
|
||||
routeData.Values["controller"] = menuItem.RouteValues["controller"];
|
||||
routeData.Values["action"] = menuItem.RouteValues["action"];
|
||||
|
||||
return routeData;
|
||||
}
|
||||
|
||||
private class MenuFilterAccessor : MenuFilter {
|
||||
public MenuFilterAccessor(INavigationManager navigationManager,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IShapeFactory shapeFactory) :
|
||||
base(navigationManager, workContextAccessor, shapeFactory) {}
|
||||
|
||||
public static Stack<MenuItem> FindSelectedPathAccessor(IEnumerable<MenuItem> menuItems, RouteData currentRouteData) {
|
||||
return SetSelectedPath(menuItems, currentRouteData);
|
||||
}
|
||||
|
||||
public static MenuItem FindParentLocalTaskAccessor(Stack<MenuItem> selectedPath) {
|
||||
return FindParentLocalTask(selectedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,7 @@ Author: Orchard Team – http://www.orchardproject.net
|
||||
Copyright: 2010, Orchard. All Rights Reserved
|
||||
*/
|
||||
|
||||
|
||||
/* Color Palette
|
||||
/* Color Palette
|
||||
**************************************************************
|
||||
|
||||
Background: #2d2f25
|
||||
@ -16,8 +15,6 @@ Main Accent:
|
||||
Links: 1e5d7d
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Reset
|
||||
***************************************************************/
|
||||
|
||||
@ -61,8 +58,7 @@ header, footer, aside, nav, article { display: block; }
|
||||
/* Clearing Floats
|
||||
***************************************************************/
|
||||
|
||||
.group:after
|
||||
{
|
||||
.group:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
@ -109,7 +105,7 @@ html {
|
||||
color:#333;
|
||||
}
|
||||
|
||||
body {
|
||||
body {
|
||||
font-size: 81.3%;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
@ -183,7 +179,7 @@ number of columns: 24; actual width: 946; column width: 26; gutter width:14
|
||||
#header {
|
||||
overflow:hidden;
|
||||
}
|
||||
#content {
|
||||
#layout-content {
|
||||
overflow:auto;
|
||||
padding:1.4em;
|
||||
background:#fcfcfc;
|
||||
@ -305,6 +301,12 @@ form.link button:hover {
|
||||
padding:.4em 0 0 .4em;
|
||||
font-size:1.308em;
|
||||
}
|
||||
.menu-local-admin li {
|
||||
display: inline;
|
||||
}
|
||||
.menu-local-admin li.middle {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.section-new {
|
||||
border-top:1px solid #d3d3d3;
|
||||
border-bottom:1px solid #d3d3d3;
|
||||
|
@ -4,3 +4,4 @@ Author: Jon Wall
|
||||
Tags: hidden, admin
|
||||
Description: An admin theme not to be used for the site so don't click "Activate" (or "Uninstall"). In the near future admin themes won't be mixed in with site themes.
|
||||
Website: http://www.orchardproject.net
|
||||
Zones: Header, Messages, BeforeContent, LocalNavigation, Content, AfterContent, Footer
|
@ -7,33 +7,75 @@
|
||||
|
||||
@{
|
||||
Style.Include("site.css");
|
||||
Style.Include("ie.css").UseCondition("lte IE 8").SetAttribute("media", "screen, projection");
|
||||
Style.Include("ie6.css").UseCondition("lte IE 6").SetAttribute("media", "screen, projection");
|
||||
Style.Include("ie.css").UseCondition("lte IE 8").SetAttribute("media", "screen, projection");
|
||||
Style.Include("ie6.css").UseCondition("lte IE 6").SetAttribute("media", "screen, projection");
|
||||
Script.Require("jQuery");
|
||||
Script.Require("ShapesBase");
|
||||
Script.Include("admin.js");
|
||||
/* Some useful shortcuts or settings
***************************************************************/
Func<dynamic, dynamic> Zone = x => Display(x); // Zone as an alias for Display to help make it obvious when we're displaying zones
/* Inserting some ad hoc shapes
***************************************************************/
|
||||
|
||||
// these are just hacked together to fire existing partials... can change
|
||||
Model.Header.Add(Display.Header());
|
||||
|
||||
// experimentation
|
||||
var thisUser = Html.Resolve<IAuthenticationService>().GetAuthenticatedUser();
|
||||
Model.Header.Add(Display.User(CurrentUser: thisUser));
|
||||
Model.Footer.Add(Display.OrchardVersion());
|
||||
}
|
||||
|
||||
@if (Model.Header != null) {
|
||||
<div id="header" role="banner">
|
||||
@Display(Model.Header)</div>
|
||||
<div id="content">
|
||||
@Zone(Model.Header)
|
||||
</div>
|
||||
}
|
||||
|
||||
<div id="layout-content">
|
||||
<div id="navshortcut">
|
||||
<a href="#Menu-admin">
|
||||
@T("Skip to navigation")</a></div>
|
||||
@T("Skip to navigation")
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="main" role="main">
|
||||
@if (Model.Messages != null) {
|
||||
<div id="messages">
|
||||
@Display(Model.Messages)
|
||||
@Zone(Model.Messages)
|
||||
</div>
|
||||
@Display(Model.Content)</div>
|
||||
}
|
||||
|
||||
@if (Model.BeforeContent != null) {
|
||||
<div id="before-content">
|
||||
@Zone(Model.BeforeContent)
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.LocalNavigation != null) {
|
||||
<div id="before-content">
|
||||
@Zone(Model.LocalNavigation)
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.Content != null) {
|
||||
<div id="content">
|
||||
@Zone(Model.Content)
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.AfterContent != null) {
|
||||
<div id="after-content">
|
||||
@Zone(Model.AfterContent)
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Model.Navigation != null) {
|
||||
<div id="menu">
|
||||
@Display(Model.Navigation)</div>
|
||||
@Zone(Model.Navigation)
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Model.Footer != null) {
|
||||
<div id="footer" role="contentinfo">
|
||||
@Display(Model.Footer)
|
||||
</div>
|
||||
@Zone(Model.Footer)
|
||||
</div>
|
||||
}
|
@ -31,9 +31,9 @@
|
||||
var firstLevelTag = Tag(firstLevelMenuItem, "li");
|
||||
@firstLevelTag.StartElement
|
||||
<h3>@sectionHeaderMarkup</h3>
|
||||
if (secondLevelMenuItems.Count() > 1 || !firstLevelMenuItem.LinkToFirstChild) {
|
||||
if (secondLevelMenuItems.Where(menuItem => !menuItem.LocalNav).Count() > 1 || !firstLevelMenuItem.LinkToFirstChild) {
|
||||
<ul class="menuItems">
|
||||
@foreach(var secondLevelMenuItem in secondLevelMenuItems) {
|
||||
@foreach (var secondLevelMenuItem in secondLevelMenuItems.Where(menuItem => !menuItem.LocalNav)) {
|
||||
<li>
|
||||
<a href="@secondLevelMenuItem.Href">@secondLevelMenuItem.Text</a>
|
||||
</li>
|
||||
|
@ -0,0 +1,41 @@
|
||||
@using System.Web.Routing;
|
||||
@using Orchard.Utility.Extensions;
|
||||
@{
|
||||
IEnumerable<dynamic> firstLevelMenuItems = Model;
|
||||
|
||||
Model.Attributes.Add("role", "local-navigation");
|
||||
var tag = Tag(Model, "ul");
|
||||
}
|
||||
@tag.StartElement
|
||||
@foreach(var firstLevelMenuItem in Model) {
|
||||
if (firstLevelMenuItem.LocalNav) {
|
||||
string sectionHeaderText = firstLevelMenuItem.Text;
|
||||
|
||||
var sectionHeaderMarkup = firstLevelMenuItem.RouteValues != null || HasText(firstLevelMenuItem.Url)
|
||||
? Html.Link(sectionHeaderText, (string)firstLevelMenuItem.Href)
|
||||
: new HtmlString(string.Format("<span>{0}</span>", Html.Encode(sectionHeaderText)));
|
||||
|
||||
if (firstLevelMenuItem == firstLevelMenuItems.First()) {
|
||||
firstLevelMenuItem.Classes.Add("first");
|
||||
}
|
||||
|
||||
if (firstLevelMenuItem != firstLevelMenuItems.First()) {
|
||||
firstLevelMenuItem.Classes.Add("middle");
|
||||
}
|
||||
|
||||
if (firstLevelMenuItem == firstLevelMenuItems.Last()) {
|
||||
firstLevelMenuItem.Classes.Add("last");
|
||||
}
|
||||
|
||||
if (firstLevelMenuItem.Selected) {
|
||||
firstLevelMenuItem.Classes.Add("selected");
|
||||
}
|
||||
|
||||
firstLevelMenuItem.Classes.Add("local-section-" + sectionHeaderText.HtmlClassify());
|
||||
var firstLevelTag = Tag(firstLevelMenuItem, "li");
|
||||
@firstLevelTag.StartElement
|
||||
@sectionHeaderMarkup
|
||||
@firstLevelTag.EndElement
|
||||
}
|
||||
}
|
||||
@tag.EndElement
|
@ -119,6 +119,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="TheThemeMachine\Placement.info" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="TheAdmin\Views\Menu__local_admin.cshtml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Mvc.Filters;
|
||||
using Orchard.UI.Admin;
|
||||
@ -11,10 +13,10 @@ namespace Orchard.UI.Navigation {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly dynamic _shapeFactory;
|
||||
|
||||
public MenuFilter(
|
||||
INavigationManager navigationManager,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
public MenuFilter(INavigationManager navigationManager,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IShapeFactory shapeFactory) {
|
||||
|
||||
_navigationManager = navigationManager;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_shapeFactory = shapeFactory;
|
||||
@ -22,35 +24,37 @@ namespace Orchard.UI.Navigation {
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext filterContext) {
|
||||
// should only run on a full view rendering result
|
||||
if (!(filterContext.Result is ViewResult))
|
||||
if (!(filterContext.Result is ViewResult)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var workContext = _workContextAccessor.GetContext(filterContext);
|
||||
WorkContext workContext = _workContextAccessor.GetContext(filterContext);
|
||||
|
||||
var menuName = "main";
|
||||
if (AdminFilter.IsApplied(filterContext.RequestContext))
|
||||
string menuName = "main";
|
||||
if (AdminFilter.IsApplied(filterContext.RequestContext)) {
|
||||
menuName = "admin";
|
||||
}
|
||||
|
||||
var menuItems = _navigationManager.BuildMenu(menuName);
|
||||
IEnumerable<MenuItem> menuItems = _navigationManager.BuildMenu(menuName);
|
||||
|
||||
var menuShape = _shapeFactory.Menu().MenuName(menuName);
|
||||
// Set the currently selected path
|
||||
Stack<MenuItem> selectedPath = SetSelectedPath(menuItems, filterContext.RouteData);
|
||||
|
||||
// Populate main nav
|
||||
dynamic menuShape = _shapeFactory.Menu().MenuName(menuName);
|
||||
PopulateMenu(_shapeFactory, menuShape, menuShape, menuItems);
|
||||
|
||||
workContext.Layout.Navigation.Add(menuShape);
|
||||
|
||||
// Populate local nav
|
||||
dynamic localMenuShape = _shapeFactory.Menu().MenuName(string.Format("local_{0}", menuName));
|
||||
PopulateLocalMenu(_shapeFactory, localMenuShape, localMenuShape, selectedPath);
|
||||
workContext.Layout.LocalNavigation.Add(localMenuShape);
|
||||
}
|
||||
|
||||
private void PopulateMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
dynamic menuItemShape = BuildMenuItemShape(shapeFactory, parentShape, menu, menuItem);
|
||||
|
||||
foreach (var menuItem in menuItems) {
|
||||
var menuItemShape = shapeFactory.MenuItem()
|
||||
.Text(menuItem.Text)
|
||||
.Href(menuItem.Href)
|
||||
.LinkToFirstChild(menuItem.LinkToFirstChild)
|
||||
.RouteValues(menuItem.RouteValues)
|
||||
.Item(menuItem)
|
||||
.Menu(menu)
|
||||
.Parent(parentShape);
|
||||
|
||||
if (menuItem.Items != null && menuItem.Items.Any()) {
|
||||
PopulateMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
|
||||
}
|
||||
@ -59,6 +63,108 @@ namespace Orchard.UI.Navigation {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the local menu starting from the first non local task parent.
|
||||
/// </summary>
|
||||
/// <param name="shapeFactory">The shape factory.</param>
|
||||
/// <param name="parentShape">The menu parent shape.</param>
|
||||
/// <param name="menu">The menu shape.</param>
|
||||
/// <param name="selectedPath">The selection path.</param>
|
||||
protected void PopulateLocalMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, Stack<MenuItem> selectedPath) {
|
||||
MenuItem parentMenuItem = FindParentLocalTask(selectedPath);
|
||||
|
||||
// find childs tabs and expand them
|
||||
if (parentMenuItem != null && parentMenuItem.Items != null && parentMenuItem.Items.Any()) {
|
||||
PopulateMenu(shapeFactory, parentShape, menu, parentMenuItem.Items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the currently selected path, starting from the selected node.
|
||||
/// </summary>
|
||||
/// <param name="menuItems">All the menuitems in the navigation menu.</param>
|
||||
/// <param name="currentRouteData">The current route data.</param>
|
||||
/// <returns>A stack with the selection path being the last node the currently selected one.</returns>
|
||||
protected static Stack<MenuItem> SetSelectedPath(IEnumerable<MenuItem> menuItems, RouteData currentRouteData) {
|
||||
foreach(MenuItem menuItem in menuItems) {
|
||||
if (RouteMatches(menuItem, currentRouteData)) {
|
||||
menuItem.Selected = true;
|
||||
|
||||
Stack<MenuItem> selectedPath = new Stack<MenuItem>();
|
||||
selectedPath.Push(menuItem);
|
||||
return selectedPath;
|
||||
}
|
||||
|
||||
if (menuItem.Items != null && menuItem.Items.Any()) {
|
||||
Stack<MenuItem> selectedPath = SetSelectedPath(menuItem.Items, currentRouteData);
|
||||
if (selectedPath != null) {
|
||||
menuItem.Selected = true;
|
||||
selectedPath.Push(menuItem);
|
||||
return selectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the first level in the selection path, starting from the bottom, that is not a local task.
|
||||
/// </summary>
|
||||
/// <param name="selectedPath">The selection path stack. The bottom node is the currently selected one.</param>
|
||||
/// <returns>The first node, starting from the bottom, that is not a local task. Otherwise, null.</returns>
|
||||
protected static MenuItem FindParentLocalTask(Stack<MenuItem> selectedPath) {
|
||||
if (selectedPath != null) {
|
||||
MenuItem parentMenuItem = selectedPath.Pop();
|
||||
if (parentMenuItem != null) {
|
||||
while (selectedPath.Count > 0) {
|
||||
MenuItem currentMenuItem = selectedPath.Pop();
|
||||
if (currentMenuItem.LocalNav) {
|
||||
return parentMenuItem;
|
||||
}
|
||||
|
||||
parentMenuItem = currentMenuItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a menu item corresponds to a given route.
|
||||
/// </summary>
|
||||
/// <param name="menuItem">The menu item.</param>
|
||||
/// <param name="currentRouteData">The route data.</param>
|
||||
/// <returns>True if the menu item's action corresponds to the route data; false otherwise.</returns>
|
||||
protected static bool RouteMatches(MenuItem menuItem, RouteData currentRouteData) {
|
||||
return menuItem.RouteValues != null &&
|
||||
string.Equals((string) menuItem.RouteValues["area"], (string) currentRouteData.Values["area"], StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals((string) menuItem.RouteValues["controller"], (string) currentRouteData.Values["controller"], StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals((string) menuItem.RouteValues["action"], (string) currentRouteData.Values["action"], StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a menu item shape.
|
||||
/// </summary>
|
||||
/// <param name="shapeFactory">The shape factory.</param>
|
||||
/// <param name="parentShape">The parent shape.</param>
|
||||
/// <param name="menu">The menu shape.</param>
|
||||
/// <param name="menuItem">The menu item to build the shape for.</param>
|
||||
/// <returns>The menu item shape.</returns>
|
||||
protected dynamic BuildMenuItemShape(dynamic shapeFactory, dynamic parentShape, dynamic menu, MenuItem menuItem) {
|
||||
return shapeFactory.MenuItem()
|
||||
.Text(menuItem.Text)
|
||||
.Href(menuItem.Href)
|
||||
.LinkToFirstChild(menuItem.LinkToFirstChild)
|
||||
.LocalNav(menuItem.LocalNav)
|
||||
.Selected(menuItem.Selected)
|
||||
.RouteValues(menuItem.RouteValues)
|
||||
.Item(menuItem)
|
||||
.Menu(menu)
|
||||
.Parent(parentShape);
|
||||
}
|
||||
|
||||
public void OnResultExecuted(ResultExecutedContext filterContext) { }
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ namespace Orchard.UI.Navigation {
|
||||
public string Href { get; set; }
|
||||
public string Position { get; set; }
|
||||
public bool LinkToFirstChild { get; set; }
|
||||
public bool LocalNav { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
public RouteValueDictionary RouteValues { get; set; }
|
||||
public IEnumerable<MenuItem> Items { get; set; }
|
||||
public IEnumerable<Permission> Permissions { get; set; }
|
||||
|
@ -38,6 +38,11 @@ namespace Orchard.UI.Navigation {
|
||||
return this;
|
||||
}
|
||||
|
||||
public NavigationItemBuilder LocalTask(bool value = true) {
|
||||
_item.LocalNav = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public new IEnumerable<MenuItem> Build() {
|
||||
_item.Items = base.Build();
|
||||
return new[] { _item };
|
||||
|
@ -68,6 +68,7 @@ namespace Orchard.UI.Navigation {
|
||||
Permissions = item.Permissions,
|
||||
Position = item.Position,
|
||||
RouteValues = item.RouteValues,
|
||||
LocalNav = item.LocalNav,
|
||||
Text = item.Text,
|
||||
Url = item.Url,
|
||||
LinkToFirstChild = item.LinkToFirstChild,
|
||||
|
Loading…
Reference in New Issue
Block a user