Adding local nav functionality.

--HG--
branch : dev
This commit is contained in:
Andre Rodrigues 2011-02-11 12:45:28 -08:00
parent 0b92a82708
commit 042968453b
11 changed files with 360 additions and 70 deletions

View File

@ -1,33 +1,67 @@
using System.Linq; using System.Collections.Generic;
using System.Web.Mvc; using System.Linq;
using System.Web.Routing; using System.Web.Routing;
using Castle.Core;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Orchard.DisplayManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Security;
using Orchard.Tests.Stubs;
using Orchard.UI.Navigation; using Orchard.UI.Navigation;
namespace Orchard.Tests.UI.Navigation { namespace Orchard.Tests.UI.Navigation {
[TestFixture] [TestFixture]
public class MenuFilterTests { 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() { [Test]
var controllerDescriptor = new ReflectedControllerDescriptor(typeof(TController)); public void MockNavManagerWorks() {
var controllerContext = new ControllerContext(new StubHttpContext(), new RouteData(), new TController()); var main = GetNavigationManager().Object.BuildMenu("main");
return new AuthorizationContext( Assert.That(main.Count(), Is.EqualTo(1));
controllerContext,
controllerDescriptor.FindAction(controllerContext, "Index"));
} }
private static IAuthorizer GetAuthorizer(bool result) { [Test]
var authorizer = new Mock<IAuthorizer>(); public void FindSelectedPathScenario2() {
authorizer NavigationBuilder navigationBuilder = BuildMenuScenario2();
.Setup(x => x.Authorize(StandardPermissions.AccessAdminPanel, It.IsAny<LocalizedString>())). IEnumerable<MenuItem> menuItems = navigationBuilder.Build();
Returns(result);
return authorizer.Object; 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() { private static Mock<INavigationManager> GetNavigationManager() {
var mainMenu = new[] { new MenuItem { Text = "The Main Menu" } }; var mainMenu = new[] { new MenuItem { Text = "The Main Menu" } };
var adminMenu = new[] { new MenuItem { Text = "The Admin Menu" } }; var adminMenu = new[] { new MenuItem { Text = "The Admin Menu" } };
@ -37,23 +71,76 @@ namespace Orchard.Tests.UI.Navigation {
return navigationManager; return navigationManager;
} }
[Test] private static NavigationBuilder BuildMenuScenario1() {
public void MockNavManagerWorks() { NavigationBuilder navigationBuilder = new NavigationBuilder();
var main = GetNavigationManager().Object.BuildMenu("main"); navigationBuilder.Add(new LocalizedString("X"), "0",
Assert.That(main.Count(), Is.EqualTo(1)); 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 { return navigationBuilder;
public ActionResult Index() {
return View();
} }
}
public class AdminController : Controller { protected static MenuItem FindMenuItem(IEnumerable<MenuItem> menuItems, string text) {
public ActionResult Index() { Queue<MenuItem> remainingItems = new Queue<MenuItem>(menuItems);
return View();
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);
}
} }
} }
} }

View File

@ -4,8 +4,7 @@ Author: Orchard Team http://www.orchardproject.net
Copyright: 2010, Orchard. All Rights Reserved Copyright: 2010, Orchard. All Rights Reserved
*/ */
/* Color Palette
/* Color Palette
************************************************************** **************************************************************
Background: #2d2f25 Background: #2d2f25
@ -16,8 +15,6 @@ Main Accent:
Links: 1e5d7d Links: 1e5d7d
*/ */
/* Reset /* Reset
***************************************************************/ ***************************************************************/
@ -61,8 +58,7 @@ header, footer, aside, nav, article { display: block; }
/* Clearing Floats /* Clearing Floats
***************************************************************/ ***************************************************************/
.group:after .group:after {
{
content: "."; content: ".";
display: block; display: block;
height: 0; height: 0;
@ -109,7 +105,7 @@ html {
color:#333; color:#333;
} }
body { body {
font-size: 81.3%; font-size: 81.3%;
color: #333; color: #333;
background: #fff; background: #fff;
@ -183,7 +179,7 @@ number of columns: 24; actual width: 946; column width: 26; gutter width:14
#header { #header {
overflow:hidden; overflow:hidden;
} }
#content { #layout-content {
overflow:auto; overflow:auto;
padding:1.4em; padding:1.4em;
background:#fcfcfc; background:#fcfcfc;
@ -305,6 +301,12 @@ form.link button:hover {
padding:.4em 0 0 .4em; padding:.4em 0 0 .4em;
font-size:1.308em; font-size:1.308em;
} }
.menu-local-admin li {
display: inline;
}
.menu-local-admin li.middle {
padding-left: 10px;
}
.section-new { .section-new {
border-top:1px solid #d3d3d3; border-top:1px solid #d3d3d3;
border-bottom:1px solid #d3d3d3; border-bottom:1px solid #d3d3d3;

View File

@ -4,3 +4,4 @@ Author: Jon Wall
Tags: hidden, admin 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. 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 Website: http://www.orchardproject.net
Zones: Header, Messages, BeforeContent, LocalNavigation, Content, AfterContent, Footer

View File

@ -7,33 +7,75 @@
@{ @{
Style.Include("site.css"); Style.Include("site.css");
Style.Include("ie.css").UseCondition("lte IE 8").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"); Style.Include("ie6.css").UseCondition("lte IE 6").SetAttribute("media", "screen, projection");
Script.Require("jQuery"); Script.Require("jQuery");
Script.Require("ShapesBase"); Script.Require("ShapesBase");
Script.Include("admin.js"); 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 // these are just hacked together to fire existing partials... can change
Model.Header.Add(Display.Header()); Model.Header.Add(Display.Header());
// experimentation
var thisUser = Html.Resolve<IAuthenticationService>().GetAuthenticatedUser(); var thisUser = Html.Resolve<IAuthenticationService>().GetAuthenticatedUser();
Model.Header.Add(Display.User(CurrentUser: thisUser)); Model.Header.Add(Display.User(CurrentUser: thisUser));
Model.Footer.Add(Display.OrchardVersion()); Model.Footer.Add(Display.OrchardVersion());
} }
@if (Model.Header != null) {
<div id="header" role="banner"> <div id="header" role="banner">
@Display(Model.Header)</div> @Zone(Model.Header)
<div id="content"> </div>
}
<div id="layout-content">
<div id="navshortcut"> <div id="navshortcut">
<a href="#Menu-admin"> <a href="#Menu-admin">
@T("Skip to navigation")</a></div> @T("Skip to navigation")
</a>
</div>
<div id="main" role="main"> <div id="main" role="main">
@if (Model.Messages != null) {
<div id="messages"> <div id="messages">
@Display(Model.Messages) @Zone(Model.Messages)
</div> </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"> <div id="menu">
@Display(Model.Navigation)</div> @Zone(Model.Navigation)
</div>
}
</div> </div>
@if (Model.Footer != null) {
<div id="footer" role="contentinfo"> <div id="footer" role="contentinfo">
@Display(Model.Footer) @Zone(Model.Footer)
</div> </div>
}

View File

@ -31,9 +31,9 @@
var firstLevelTag = Tag(firstLevelMenuItem, "li"); var firstLevelTag = Tag(firstLevelMenuItem, "li");
@firstLevelTag.StartElement @firstLevelTag.StartElement
<h3>@sectionHeaderMarkup</h3> <h3>@sectionHeaderMarkup</h3>
if (secondLevelMenuItems.Count() > 1 || !firstLevelMenuItem.LinkToFirstChild) { if (secondLevelMenuItems.Where(menuItem => !menuItem.LocalNav).Count() > 1 || !firstLevelMenuItem.LinkToFirstChild) {
<ul class="menuItems"> <ul class="menuItems">
@foreach(var secondLevelMenuItem in secondLevelMenuItems) { @foreach (var secondLevelMenuItem in secondLevelMenuItems.Where(menuItem => !menuItem.LocalNav)) {
<li> <li>
<a href="@secondLevelMenuItem.Href">@secondLevelMenuItem.Text</a> <a href="@secondLevelMenuItem.Href">@secondLevelMenuItem.Text</a>
</li> </li>

View File

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

View File

@ -119,6 +119,9 @@
<ItemGroup> <ItemGroup>
<Content Include="TheThemeMachine\Placement.info" /> <Content Include="TheThemeMachine\Placement.info" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="TheAdmin\Views\Menu__local_admin.cshtml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.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. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -1,6 +1,8 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing;
using Orchard.DisplayManagement; using Orchard.DisplayManagement;
using Orchard.Mvc.Filters; using Orchard.Mvc.Filters;
using Orchard.UI.Admin; using Orchard.UI.Admin;
@ -11,10 +13,10 @@ namespace Orchard.UI.Navigation {
private readonly IWorkContextAccessor _workContextAccessor; private readonly IWorkContextAccessor _workContextAccessor;
private readonly dynamic _shapeFactory; private readonly dynamic _shapeFactory;
public MenuFilter( public MenuFilter(INavigationManager navigationManager,
INavigationManager navigationManager, IWorkContextAccessor workContextAccessor,
IWorkContextAccessor workContextAccessor,
IShapeFactory shapeFactory) { IShapeFactory shapeFactory) {
_navigationManager = navigationManager; _navigationManager = navigationManager;
_workContextAccessor = workContextAccessor; _workContextAccessor = workContextAccessor;
_shapeFactory = shapeFactory; _shapeFactory = shapeFactory;
@ -22,35 +24,37 @@ namespace Orchard.UI.Navigation {
public void OnResultExecuting(ResultExecutingContext filterContext) { public void OnResultExecuting(ResultExecutingContext filterContext) {
// should only run on a full view rendering result // should only run on a full view rendering result
if (!(filterContext.Result is ViewResult)) if (!(filterContext.Result is ViewResult)) {
return; return;
}
var workContext = _workContextAccessor.GetContext(filterContext); WorkContext workContext = _workContextAccessor.GetContext(filterContext);
var menuName = "main"; string menuName = "main";
if (AdminFilter.IsApplied(filterContext.RequestContext)) if (AdminFilter.IsApplied(filterContext.RequestContext)) {
menuName = "admin"; 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); PopulateMenu(_shapeFactory, menuShape, menuShape, menuItems);
workContext.Layout.Navigation.Add(menuShape); 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) { 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()) { if (menuItem.Items != null && menuItem.Items.Any()) {
PopulateMenu(shapeFactory, menuItemShape, menu, menuItem.Items); 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) { } public void OnResultExecuted(ResultExecutedContext filterContext) { }
} }
} }

View File

@ -15,6 +15,8 @@ namespace Orchard.UI.Navigation {
public string Href { get; set; } public string Href { get; set; }
public string Position { get; set; } public string Position { get; set; }
public bool LinkToFirstChild { get; set; } public bool LinkToFirstChild { get; set; }
public bool LocalNav { get; set; }
public bool Selected { get; set; }
public RouteValueDictionary RouteValues { get; set; } public RouteValueDictionary RouteValues { get; set; }
public IEnumerable<MenuItem> Items { get; set; } public IEnumerable<MenuItem> Items { get; set; }
public IEnumerable<Permission> Permissions { get; set; } public IEnumerable<Permission> Permissions { get; set; }

View File

@ -38,6 +38,11 @@ namespace Orchard.UI.Navigation {
return this; return this;
} }
public NavigationItemBuilder LocalTask(bool value = true) {
_item.LocalNav = value;
return this;
}
public new IEnumerable<MenuItem> Build() { public new IEnumerable<MenuItem> Build() {
_item.Items = base.Build(); _item.Items = base.Build();
return new[] { _item }; return new[] { _item };

View File

@ -68,6 +68,7 @@ namespace Orchard.UI.Navigation {
Permissions = item.Permissions, Permissions = item.Permissions,
Position = item.Position, Position = item.Position,
RouteValues = item.RouteValues, RouteValues = item.RouteValues,
LocalNav = item.LocalNav,
Text = item.Text, Text = item.Text,
Url = item.Url, Url = item.Url,
LinkToFirstChild = item.LinkToFirstChild, LinkToFirstChild = item.LinkToFirstChild,