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.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
@ -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>
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>
|
<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.
|
||||||
|
@ -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) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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; }
|
||||||
|
@ -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 };
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user