mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-07-31 20:01:11 +08:00
Implementing Menu Widgets
--HG-- branch : 1.x
This commit is contained in:
parent
ea1e5fba36
commit
1900aaa01a
@ -1,6 +1,8 @@
|
||||
using Orchard.Commands;
|
||||
using System.Linq;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Core.Title.Models;
|
||||
|
||||
namespace Orchard.Core.Navigation.Commands {
|
||||
public class MenuCommands : DefaultOrchardCommandHandler {
|
||||
@ -20,19 +22,48 @@ namespace Orchard.Core.Navigation.Commands {
|
||||
public string Url { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool OnMainMenu { get; set; }
|
||||
public string MenuName { get; set; }
|
||||
|
||||
[CommandName("menuitem create")]
|
||||
[CommandHelp("menuitem create /MenuPosition:<position> /MenuText:<text> /Url:<url> [/OnMainMenu:true|false]\r\n\t" + "Creates a new menu item")]
|
||||
[OrchardSwitches("MenuPosition,MenuText,Url,OnMainMenu")]
|
||||
[CommandHelp("menuitem create /MenuPosition:<position> /MenuText:<text> /Url:<url> /MenuName:<name>\r\n\t" + "Creates a new menu item")]
|
||||
[OrchardSwitches("MenuPosition,MenuText,Url,MenuName")]
|
||||
public void Create() {
|
||||
// flushes before doing a query in case a previous command created the menu
|
||||
_contentManager.Flush();
|
||||
|
||||
var menu = _contentManager.Query<TitlePart, TitlePartRecord>()
|
||||
.Where(x => x.Title == MenuName)
|
||||
.ForType("Menu")
|
||||
.Slice(0, 1)
|
||||
.FirstOrDefault();
|
||||
|
||||
if(menu == null) {
|
||||
Context.Output.WriteLine(T("Menu not found.").Text);
|
||||
return;
|
||||
}
|
||||
|
||||
var menuItem = _contentManager.Create("MenuItem");
|
||||
menuItem.As<MenuPart>().MenuPosition = MenuPosition;
|
||||
menuItem.As<MenuPart>().MenuText = T(MenuText).ToString();
|
||||
menuItem.As<MenuPart>().OnMainMenu = OnMainMenu;
|
||||
menuItem.As<MenuPart>().MenuRecord = menu.ContentItem.Record;
|
||||
menuItem.As<MenuItemPart>().Url = Url;
|
||||
|
||||
Context.Output.WriteLine(T("Menu item created successfully.").Text);
|
||||
}
|
||||
|
||||
[CommandName("menu create")]
|
||||
[CommandHelp("menu create /MenuName:<name>\r\n\t" + "Creates a new menu")]
|
||||
[OrchardSwitches("MenuName")]
|
||||
public void CreateMenu() {
|
||||
if (string.IsNullOrWhiteSpace(MenuName)) {
|
||||
Context.Output.WriteLine(T("Menu name can't be empty.").Text);
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = _contentManager.Create("Menu");
|
||||
menu.As<TitlePart>().Title = MenuName;
|
||||
|
||||
Context.Output.WriteLine(T("Menu created successfully.").Text);
|
||||
}
|
||||
}
|
||||
}
|
@ -106,35 +106,6 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
};
|
||||
}
|
||||
|
||||
public ActionResult Create() {
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Create(NavigationManagementViewModel model) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMainMenu, T("Couldn't manage the main menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var menuPart = Services.ContentManager.New<MenuPart>("MenuItem");
|
||||
menuPart.OnMainMenu = true;
|
||||
menuPart.MenuText = model.NewMenuItem.Text;
|
||||
menuPart.MenuPosition = model.NewMenuItem.Position;
|
||||
if (string.IsNullOrEmpty(menuPart.MenuPosition))
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu("main"));
|
||||
|
||||
var menuItem = menuPart.As<MenuItemPart>();
|
||||
menuItem.Url = model.NewMenuItem.Url;
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
Services.TransactionManager.Cancel();
|
||||
return View("Index", model);
|
||||
}
|
||||
|
||||
Services.ContentManager.Create(menuPart);
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Delete(int id) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMainMenu, T("Couldn't manage the main menu")))
|
||||
@ -143,10 +114,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
MenuPart menuPart = _menuService.Get(id);
|
||||
|
||||
if (menuPart != null) {
|
||||
if (menuPart.Is<MenuItemPart>())
|
||||
_menuService.Delete(menuPart);
|
||||
else
|
||||
menuPart.OnMainMenu = false;
|
||||
_menuService.Delete(menuPart);
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
@ -165,7 +133,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
// create a new temporary menu item
|
||||
MenuPart menuPart = Services.ContentManager.New<MenuPart>(id);
|
||||
var menuPart = Services.ContentManager.New<MenuPart>(id);
|
||||
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
@ -178,7 +146,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
|
||||
try {
|
||||
// filter the content items for this specific menu
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu("main").Where(x => x.MenuId == menuId));
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu(menu));
|
||||
|
||||
dynamic model = Services.ContentManager.BuildEditor(menuPart);
|
||||
|
||||
@ -197,7 +165,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMainMenu, T("Couldn't manage the main menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
MenuPart menuPart = Services.ContentManager.New<MenuPart>(id);
|
||||
var menuPart = Services.ContentManager.New<MenuPart>(id);
|
||||
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
@ -210,11 +178,8 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
|
||||
var model = Services.ContentManager.UpdateEditor(menuPart, this);
|
||||
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu("main").Where(x => x.MenuId == menuId));
|
||||
menuPart.OnMainMenu = true;
|
||||
|
||||
// the menu is the container for the menu item
|
||||
menuPart.As<CommonPart>().Container = menu;
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu(menu));
|
||||
menuPart.MenuRecord = menu.Record;
|
||||
|
||||
Services.ContentManager.Create(menuPart);
|
||||
|
||||
|
@ -41,7 +41,7 @@ namespace Orchard.Core.Navigation.Drivers {
|
||||
|
||||
updater.TryUpdateModel(part, Prefix, null, null);
|
||||
|
||||
if (part.OnMainMenu && string.IsNullOrEmpty(part.MenuText))
|
||||
if (string.IsNullOrEmpty(part.MenuText))
|
||||
updater.AddModelError("MenuText", T("The MenuText field is required"));
|
||||
|
||||
return Editor(part, shapeHelper);
|
||||
@ -58,16 +58,15 @@ namespace Orchard.Core.Navigation.Drivers {
|
||||
part.MenuPosition = position;
|
||||
}
|
||||
|
||||
var onMainMenu = context.Attribute(part.PartDefinition.Name, "OnMainMenu");
|
||||
if (onMainMenu != null) {
|
||||
part.OnMainMenu = Convert.ToBoolean(onMainMenu);
|
||||
}
|
||||
context.ImportAttribute(part.PartDefinition.Name, "Menu", x => part.MenuRecord = context.GetItemFromSession(x).Record);
|
||||
}
|
||||
|
||||
protected override void Exporting(MenuPart part, ContentManagement.Handlers.ExportContentContext context) {
|
||||
var menuIdentity = _orchardServices.ContentManager.GetItemMetadata(_orchardServices.ContentManager.Get(part.MenuRecord.Id)).Identity;
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("Menu", menuIdentity);
|
||||
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("MenuText", part.MenuText);
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("MenuPosition", part.MenuPosition);
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("OnMainMenu", part.OnMainMenu);
|
||||
}
|
||||
}
|
||||
}
|
111
src/Orchard.Web/Core/Navigation/Drivers/MenuWidgetPartDriver.cs
Normal file
111
src/Orchard.Web/Core/Navigation/Drivers/MenuWidgetPartDriver.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Core.Navigation.ViewModels;
|
||||
using Orchard.Core.Title.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Core.Navigation.Drivers {
|
||||
public class MenuWidgetPartDriver : ContentPartDriver<MenuWidgetPart> {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly INavigationManager _navigationManager;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public MenuWidgetPartDriver(
|
||||
IContentManager contentManager,
|
||||
INavigationManager navigationManager,
|
||||
IWorkContextAccessor workContextAccessor) {
|
||||
_contentManager = contentManager;
|
||||
_navigationManager = navigationManager;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override string Prefix {
|
||||
get {
|
||||
return "MenuWidget";
|
||||
}
|
||||
}
|
||||
protected override DriverResult Display(MenuWidgetPart part, string displayType, dynamic shapeHelper) {
|
||||
return ContentShape( "Parts_MenuWidget", () => {
|
||||
var menu = _contentManager.Get(part.Menu.Id, VersionOptions.Published, new QueryHints().ExpandRecords<TitlePartRecord>());
|
||||
|
||||
if(menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var menuName = menu.As<TitlePart>().Title.HtmlClassify();
|
||||
|
||||
IEnumerable<MenuItem> menuItems = _navigationManager.BuildMenu(menu);
|
||||
|
||||
var routeData = _workContextAccessor.GetContext().HttpContext.Request.RequestContext.RouteData;
|
||||
|
||||
// Set the currently selected path
|
||||
Stack<MenuItem> selectedPath = NavigationHelper.SetSelectedPath(menuItems, routeData);
|
||||
|
||||
// Populate main nav
|
||||
if(part.Breadcrumb) {
|
||||
menuItems = NavigationHelper.SetSelectedPath(menuItems, routeData);
|
||||
}
|
||||
|
||||
dynamic menuShape = shapeHelper.Menu().MenuName(menuName);
|
||||
NavigationHelper.PopulateMenu(shapeHelper, menuShape, menuShape, menuItems);
|
||||
|
||||
return shapeHelper.Parts_MenuWidget(Menu: menuShape);
|
||||
});
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(MenuWidgetPart part, dynamic shapeHelper) {
|
||||
return ContentShape("Parts_MenuWidget_Edit", () => {
|
||||
var model = new MenuWidgetViewModel {
|
||||
CurrentMenuId = part.Menu == null ? -1 : part.Menu.Id,
|
||||
StartLevel = part.StartLevel,
|
||||
StopLevel = part.Levels,
|
||||
Breadcrumb = part.Breadcrumb,
|
||||
Menus = _contentManager.Query().ForType("Menu").Join<TitlePartRecord>().OrderBy(x => x.Title).List()
|
||||
};
|
||||
|
||||
return shapeHelper.EditorTemplate(TemplateName: "Parts.MenuWidget.Edit", Model: model, Prefix: Prefix);
|
||||
});
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(MenuWidgetPart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||
var model = new MenuWidgetViewModel();
|
||||
|
||||
if(updater.TryUpdateModel(model, Prefix, null, null)) {
|
||||
part.StartLevel = model.StartLevel;
|
||||
part.Levels = model.StopLevel;
|
||||
part.Breadcrumb = model.Breadcrumb;
|
||||
part.Menu = _contentManager.Get(model.CurrentMenuId).Record;
|
||||
}
|
||||
|
||||
return Editor(part, shapeHelper);
|
||||
}
|
||||
|
||||
protected override void Importing(MenuWidgetPart part, ImportContentContext context) {
|
||||
context.ImportAttribute(part.PartDefinition.Name, "StartLevel", x => part.StartLevel = Convert.ToInt32(x));
|
||||
context.ImportAttribute(part.PartDefinition.Name, "Levels", x => part.Levels = Convert.ToInt32(x));
|
||||
context.ImportAttribute(part.PartDefinition.Name, "Breadcrumb", x => part.Breadcrumb = Convert.ToBoolean(x));
|
||||
|
||||
context.ImportAttribute(part.PartDefinition.Name, "Menu", x => part.Menu = context.GetItemFromSession(x).Record);
|
||||
}
|
||||
|
||||
protected override void Exporting(MenuWidgetPart part, ExportContentContext context) {
|
||||
var menuIdentity = _contentManager.GetItemMetadata(_contentManager.Get(part.Menu.Id)).Identity;
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("Menu", menuIdentity);
|
||||
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("StartLevel", part.StartLevel);
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("Levels", part.Levels);
|
||||
context.Element(part.PartDefinition.Name).SetAttributeValue("Breadcrumb", part.Breadcrumb);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ namespace Orchard.Core.Navigation.Handlers {
|
||||
Filters.Add(StorageFilter.For(menuPartRepository));
|
||||
|
||||
OnInitializing<MenuPart>((ctx, x) => {
|
||||
x.OnMainMenu = false;
|
||||
x.MenuText = String.Empty;
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Data;
|
||||
|
||||
namespace Orchard.Core.Navigation.Handlers {
|
||||
public class MenuWidgetPartHandler : ContentHandler {
|
||||
public MenuWidgetPartHandler(IRepository<MenuWidgetPartRecord> repository) {
|
||||
Filters.Add(StorageFilter.For(repository));
|
||||
|
||||
OnInitializing<MenuWidgetPart>((context, part) => { part.StartLevel = 1; });
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ namespace Orchard.Core.Navigation {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
|
||||
public int Create() {
|
||||
ContentDefinitionManager.AlterPartDefinition("MenuPart", builder => builder.Attachable());
|
||||
|
||||
SchemaBuilder.CreateTable("MenuItemPartRecord",
|
||||
table => table
|
||||
.ContentPartRecord()
|
||||
@ -17,14 +19,39 @@ namespace Orchard.Core.Navigation {
|
||||
.ContentPartRecord()
|
||||
.Column<string>("MenuText")
|
||||
.Column<string>("MenuPosition")
|
||||
.Column<bool>("OnMainMenu")
|
||||
.Column<int>("MenuRecord_id")
|
||||
);
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("Page", cfg => cfg.WithPart("MenuPart"));
|
||||
ContentDefinitionManager.AlterTypeDefinition("MenuItem", cfg => cfg.WithPart("MenuPart"));
|
||||
ContentDefinitionManager.AlterPartDefinition("MenuPart", builder => builder.Attachable());
|
||||
|
||||
return 1;
|
||||
ContentDefinitionManager.AlterTypeDefinition("MenuItem", cfg => cfg
|
||||
.WithPart("MenuPart")
|
||||
.WithPart("CommonPart")
|
||||
.DisplayedAs("Custom Link")
|
||||
.WithSetting("Description", "Represents a simple custom link with a text and an url.")
|
||||
.WithSetting("Stereotype", "MenuItem") // because we declare a new stereotype, the Shape MenuItem_Edit is needed
|
||||
);
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("Menu", cfg => cfg
|
||||
.WithPart("CommonPart", p => p.WithSetting("OwnerEditorSettings.ShowOwnerEditor", "false"))
|
||||
.WithPart("TitlePart")
|
||||
.WithPart("Identity")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("MenuWidgetPartRecord", table => table
|
||||
.ContentPartRecord()
|
||||
.Column<int>("StartLevel")
|
||||
.Column<int>("Levels")
|
||||
.Column<bool>("Breadcrumb")
|
||||
.Column<int>("Menu_id")
|
||||
);
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("MenuWidget", cfg => cfg
|
||||
.WithPart("CommonPart")
|
||||
.WithPart("WidgetPart")
|
||||
.WithPart("MenuWidgetPart")
|
||||
.WithSetting("Stereotype", "Widget")
|
||||
);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
public int UpdateFrom1() {
|
||||
@ -54,6 +81,28 @@ namespace Orchard.Core.Navigation {
|
||||
.WithPart("Identity")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("MenuWidgetPartRecord",table => table
|
||||
.ContentPartRecord()
|
||||
.Column<int>("StartLevel")
|
||||
.Column<int>("Levels")
|
||||
.Column<bool>("Breadcrumb")
|
||||
.Column<int>("Menu_id")
|
||||
);
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("MenuWidget", cfg => cfg
|
||||
.WithPart("CommonPart")
|
||||
.WithPart("WidgetPart")
|
||||
.WithPart("MenuWidgetPart")
|
||||
.WithSetting("Stereotype", "Widget")
|
||||
);
|
||||
|
||||
SchemaBuilder
|
||||
.AlterTable("MenuPartRecord", table => table.DropColumn("OnMainMenu"))
|
||||
.AlterTable("MenuPartRecord", table => table.AddColumn<int>("MenuRecord_id"))
|
||||
;
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("Page", cfg => cfg.RemovePart("MenuPart"));
|
||||
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Navigation.Models {
|
||||
public class ContentMenuItemPartRecord : ContentPartRecord {
|
||||
public virtual ContentItemRecord ContentItemRecord { get; set; }
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Core.Navigation.Models {
|
||||
public class MenuItemPart : ContentPart<MenuItemPartRecord> {
|
||||
[Required]
|
||||
|
||||
public string Url {
|
||||
get { return Record.Url; }
|
||||
set { Record.Url = value; }
|
||||
|
@ -1,12 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Navigation.Models {
|
||||
public class MenuPart : ContentPart<MenuPartRecord> {
|
||||
|
||||
public bool OnMainMenu {
|
||||
get { return Record.OnMainMenu; }
|
||||
set { Record.OnMainMenu = value; }
|
||||
public ContentItemRecord MenuRecord {
|
||||
get { return Record.MenuRecord; }
|
||||
set { Record.MenuRecord = value; }
|
||||
}
|
||||
|
||||
[StringLength(MenuPartRecord.DefaultMenuTextLength)]
|
||||
|
@ -8,6 +8,6 @@ namespace Orchard.Core.Navigation.Models {
|
||||
[StringLength(DefaultMenuTextLength)]
|
||||
public virtual string MenuText { get; set; }
|
||||
public virtual string MenuPosition { get; set; }
|
||||
public virtual bool OnMainMenu { get; set; }
|
||||
public virtual ContentItemRecord MenuRecord { get; set; }
|
||||
}
|
||||
}
|
26
src/Orchard.Web/Core/Navigation/Models/MenuWidgetPart.cs
Normal file
26
src/Orchard.Web/Core/Navigation/Models/MenuWidgetPart.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Navigation.Models {
|
||||
public class MenuWidgetPart : ContentPart<MenuWidgetPartRecord> {
|
||||
public int StartLevel {
|
||||
get { return Record.StartLevel; }
|
||||
set { Record.StartLevel = value; }
|
||||
}
|
||||
|
||||
public int Levels {
|
||||
get { return Record.Levels; }
|
||||
set { Record.Levels = value; }
|
||||
}
|
||||
|
||||
public bool Breadcrumb {
|
||||
get { return Record.Breadcrumb; }
|
||||
set { Record.Breadcrumb = value; }
|
||||
}
|
||||
|
||||
public ContentItemRecord Menu {
|
||||
get { return Record.Menu; }
|
||||
set { Record.Menu = value; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Navigation.Models {
|
||||
public class MenuWidgetPartRecord : ContentPartRecord {
|
||||
public virtual int StartLevel { get; set; }
|
||||
public virtual int Levels { get; set; }
|
||||
public virtual bool Breadcrumb { get; set; }
|
||||
|
||||
public virtual ContentItemRecord Menu { get; set; }
|
||||
}
|
||||
}
|
@ -2,4 +2,6 @@
|
||||
<Place Parts_Navigation_Menu_Edit="Content:9"/>
|
||||
<Place Parts_Navigation_AdminMenu_Edit="Content:9.1"/>
|
||||
<Place Parts_MenuItem_Edit="Content:10"/>
|
||||
<Place Parts_MenuWidget_Edit="Content:10"/>
|
||||
<Place Parts_MenuWidget="Content"/>
|
||||
</Placement>
|
@ -1,37 +1,34 @@
|
||||
using System.Web;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Core.Navigation.Services {
|
||||
[UsedImplicitly]
|
||||
public class MainMenuNavigationProvider : INavigationProvider {
|
||||
public class DefaultMenuProvider : IMenuProvider {
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public MainMenuNavigationProvider(IContentManager contentManager) {
|
||||
public DefaultMenuProvider(IContentManager contentManager) {
|
||||
_contentManager = contentManager;
|
||||
}
|
||||
|
||||
public string MenuName { get { return "main"; } }
|
||||
public void GetMenu(IContent menu, NavigationBuilder builder) {
|
||||
var menuParts = _contentManager
|
||||
.Query<MenuPart, MenuPartRecord>()
|
||||
.Where(x => x.MenuRecord.Id == menu.Id)
|
||||
.WithQueryHints(new QueryHints().ExpandRecords<MenuItemPartRecord>())
|
||||
.List();
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
var menuParts = _contentManager.Query<MenuPart, MenuPartRecord>().Where(x => x.OnMainMenu).WithQueryHints(new QueryHints().ExpandRecords<MenuItemPartRecord>()).List();
|
||||
foreach (var menuPart in menuParts) {
|
||||
if (menuPart != null) {
|
||||
var part = menuPart;
|
||||
int menuId = -1;
|
||||
var commonPart = part.As<CommonPart>().Container;
|
||||
if(commonPart != null) {
|
||||
menuId = commonPart.Id;
|
||||
}
|
||||
|
||||
if (part.Is<MenuItemPart>())
|
||||
builder.Add(new LocalizedString(HttpUtility.HtmlEncode(part.MenuText)), part.MenuPosition, item => item.Url(part.As<MenuItemPart>().Url).MenuId(menuId));
|
||||
builder.Add(new LocalizedString(HttpUtility.HtmlEncode(part.MenuText)), part.MenuPosition, item => item.Url(part.As<MenuItemPart>().Url));
|
||||
else
|
||||
builder.Add(new LocalizedString(HttpUtility.HtmlEncode(part.MenuText)), part.MenuPosition, item => item.Action(_contentManager.GetItemMetadata(part.ContentItem).DisplayRouteValues).MenuId(menuId));
|
||||
builder.Add(new LocalizedString(HttpUtility.HtmlEncode(part.MenuText)), part.MenuPosition, item => item.Action(_contentManager.GetItemMetadata(part.ContentItem).DisplayRouteValues));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
|
||||
namespace Orchard.Core.Navigation.Services {
|
||||
@ -14,13 +13,13 @@ namespace Orchard.Core.Navigation.Services {
|
||||
}
|
||||
|
||||
public IEnumerable<MenuPart> Get() {
|
||||
return _contentManager.Query<MenuPart, MenuPartRecord>().Where(x => x.OnMainMenu).List();
|
||||
return _contentManager.Query<MenuPart, MenuPartRecord>().List();
|
||||
}
|
||||
|
||||
public IEnumerable<MenuPart> GetMenu(int menuId) {
|
||||
return _contentManager
|
||||
.Query<MenuPart, MenuPartRecord>().Where(x => x.OnMainMenu)
|
||||
.Join<CommonPartRecord>().Where( x => x.Container.Id == menuId)
|
||||
.Query<MenuPart, MenuPartRecord>()
|
||||
.Where( x => x.MenuRecord.Id == menuId)
|
||||
.List();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Core.Navigation.ViewModels {
|
||||
public class MenuWidgetViewModel {
|
||||
public IEnumerable<ContentItem> Menus { get; set; }
|
||||
public int CurrentMenuId { get; set; }
|
||||
|
||||
public int StartLevel { get; set; }
|
||||
public int StopLevel { get; set; }
|
||||
public bool Breadcrumb { get; set; }
|
||||
}
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
@model NavigationManagementViewModel
|
||||
@using Orchard.ContentManagement;
|
||||
@using Orchard.Core.Navigation.Models;
|
||||
@using Orchard.Core.Navigation.ViewModels;
|
||||
@using Orchard.Core.Title.Models
|
||||
@using Orchard.Utility.Extensions;
|
||||
|
||||
@{
|
||||
@ -21,11 +18,11 @@
|
||||
<label for="menuId">@T("Current Menu:")</label>
|
||||
<select id="menuId" name="menuId">
|
||||
@foreach (var menu in Model.Menus) {
|
||||
@Html.SelectOption(Model.CurrentMenu.Id, menu.Id, menu.As<TitlePart>().Title)
|
||||
@Html.SelectOption(Model.CurrentMenu.Id, menu.Id, Html.ItemDisplayText(menu).ToString())
|
||||
}
|
||||
</select>
|
||||
<button type="submit" class="apply-bulk-actions-auto">@T("Show")</button>
|
||||
@Html.Link(T("Edit").Text, Url.ItemEditUrl(Model.CurrentMenu), new { @class = "button", returnUrl = Request.RawUrl })
|
||||
@Html.ActionLink(T("Edit").Text, "Edit", "Admin", new { area = "Contents", id = Model.CurrentMenu.Id, returnUrl = Request.RawUrl }, new { @class = "button" })
|
||||
</fieldset>
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
@model Orchard.Core.Navigation.ViewModels.MenuWidgetViewModel
|
||||
@using Orchard.ContentManagement
|
||||
@using Orchard.Core.Navigation.Models;
|
||||
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.CurrentMenuId, T("For Menu"))
|
||||
<select id="@Html.FieldIdFor(m => m.CurrentMenuId)" name="@Html.FieldNameFor(m => m.CurrentMenuId)">
|
||||
@foreach(ContentItem menu in Model.Menus) {
|
||||
@Html.SelectOption(Model.CurrentMenuId, menu.Id, Html.ItemDisplayText(menu).ToString())
|
||||
}
|
||||
</select>
|
||||
<span class="hint">@T("Select which menu you want to display")</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="@Html.FieldIdFor(m => m.StartLevel)">@T("Start Level")</label>
|
||||
@Html.TextBoxFor(m => m.StartLevel, new { @class = "text text-small" })
|
||||
<span class="hint">@T("The level the menu should start at.")</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="@Html.FieldIdFor(m => m.StopLevel)">@T("Levels to display")</label>
|
||||
@Html.TextBoxFor(m => m.StopLevel, new { @class = "text text-small" })
|
||||
<span class="hint">@T("The number of levels to display, \"0\" meaning all levels.")</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
@Html.EditorFor(m => m.Breadcrumb)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Breadcrumb)">@T("Display as Breadcrumb")</label>
|
||||
<span class="hint">@T("Check to render the path to the current content item.")</span>
|
||||
</fieldset>
|
@ -4,12 +4,9 @@
|
||||
@if (!Model.ContentItem.TypeDefinition.Settings.ContainsKey("Stereotype") || Model.ContentItem.TypeDefinition.Settings["Stereotype"] != "MenuItem") {
|
||||
|
||||
<fieldset>
|
||||
@Html.EditorFor(m => m.OnMainMenu)
|
||||
<label for="OnMainMenu" class="forcheckbox">@T("Show on main menu")</label>
|
||||
<div data-controllerid="OnMainMenu" class="">
|
||||
<label for="MenuText">@T("Menu text")</label>
|
||||
@Html.TextBoxFor(m => m.MenuText, new { @class = "text-box single-line" })
|
||||
</div>
|
||||
<label for="MenuText">@T("Menu text")</label>
|
||||
@Html.TextBoxFor(m => m.MenuText, new { @class = "text-box single-line" })
|
||||
<span class="hint">@T("The text that should appear in the menu.")</span>
|
||||
</fieldset>
|
||||
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
@Display(Model.Content)
|
||||
@Display(Model.Content)
|
||||
|
@ -0,0 +1 @@
|
||||
@Display(Model.Menu)
|
@ -131,16 +131,22 @@
|
||||
<Compile Include="Dashboard\Services\CompilationErrorBanner.cs" />
|
||||
<Compile Include="Navigation\Commands\MenuCommands.cs" />
|
||||
<Compile Include="Navigation\Drivers\AdminMenuPartDriver.cs" />
|
||||
<Compile Include="Navigation\Drivers\MenuWidgetPartDriver.cs" />
|
||||
<Compile Include="Navigation\Handlers\AdminMenuPartHandler.cs" />
|
||||
<Compile Include="Navigation\Handlers\MenuWidgetPartHandler.cs" />
|
||||
<Compile Include="Navigation\Models\AdminMenuPart.cs" />
|
||||
<Compile Include="Navigation\Models\AdminMenuPartRecord.cs" />
|
||||
<Compile Include="Navigation\Models\ContentMenuItemPartRecord.cs" />
|
||||
<Compile Include="Navigation\Models\MenuWidgetPartRecord.cs" />
|
||||
<Compile Include="Navigation\Models\MenuWidgetPart.cs" />
|
||||
<Compile Include="Navigation\Services\AdminMenuNavigationProvider.cs" />
|
||||
<Compile Include="Navigation\Services\DefaultMenuManager.cs" />
|
||||
<Compile Include="Navigation\Services\IMenuManager.cs" />
|
||||
<Compile Include="Navigation\Services\MainMenuNavigationProvider.cs" />
|
||||
<Compile Include="Navigation\Services\DefaultMenuProvider.cs" />
|
||||
<Compile Include="Navigation\Settings\AdminMenuPartTypeSettings.cs" />
|
||||
<Compile Include="Contents\ViewModels\ListContentsViewModel.cs" />
|
||||
<Compile Include="Contents\ViewModels\ListContentTypesViewModel.cs" />
|
||||
<Compile Include="Navigation\ViewModels\MenuWidgetViewModel.cs" />
|
||||
<Compile Include="Reports\AdminMenu.cs" />
|
||||
<Compile Include="Reports\Controllers\AdminController.cs" />
|
||||
<Compile Include="Reports\Routes.cs" />
|
||||
@ -506,6 +512,12 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Navigation\Views\EditorTemplates\Parts.MenuItem.Edit.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Navigation\Views\EditorTemplates\Parts.MenuWidget.Edit.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Navigation\Views\Parts.MenuWidget.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.
|
||||
|
@ -56,6 +56,8 @@
|
||||
site setting set baseurl
|
||||
theme activate "The Theme Machine"
|
||||
blog create /Title:"Blog" /Homepage:true /Description:"This is your Orchard Blog."
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
menu create /MenuName:"Main Menu"
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /MenuName:"Main Menu"
|
||||
widget create MenuWidget /Title:"Main Menu" /RenderTitle:false /Zone:"Navigation" /Position:"1" /Layer:"Default" /Identity:"MenuWidget1" /MenuName:"Main Menu"
|
||||
</Command>
|
||||
</Orchard>
|
||||
|
@ -27,6 +27,5 @@
|
||||
|
||||
<Command>
|
||||
page create /Slug:"welcome-to-orchard" /Title:"Welcome to Orchard!" /Path:"welcome-to-orchard" /Homepage:true /Publish:true /Text:"Welcome To Orchard!"
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
</Command>
|
||||
</Orchard>
|
||||
|
@ -52,7 +52,9 @@
|
||||
widget create HtmlWidget /Title:"Third Leader Aside" /Zone:"TripelThird" /Position:"5" /Layer:"TheHomepage" /Identity:"SetupHtmlWidget3" /UseLoremIpsumText:true
|
||||
site setting set baseurl
|
||||
page create /Slug:"welcome-to-orchard" /Title:"Welcome to Orchard!" /Path:"welcome-to-orchard" /Homepage:true /Publish:true /UseWelcomeText:true
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
menu create /MenuName:"Main Menu"
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /MenuName:"Main Menu"
|
||||
widget create MenuWidget /Title:"Main Menu" /RenderTitle:false /Zone:"Navigation" /Position:"1" /Layer:"Default" /Identity:"MenuWidget1" /MenuName:"Main Menu"
|
||||
theme activate "The Theme Machine"
|
||||
</Command>
|
||||
</Orchard>
|
||||
|
@ -4,6 +4,8 @@ using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Core.Title.Models;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
using Orchard.Widgets.Models;
|
||||
@ -14,17 +16,28 @@ namespace Orchard.Widgets.Commands {
|
||||
private readonly IWidgetsService _widgetsService;
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IMembershipService _membershipService;
|
||||
private readonly IContentManager _contentManager;
|
||||
private const string LoremIpsum = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur a nibh ut tortor dapibus vestibulum. Aliquam vel sem nibh. Suspendisse vel condimentum tellus.</p>";
|
||||
|
||||
public WidgetCommands(IWidgetsService widgetsService, ISiteService siteService, IMembershipService membershipService) {
|
||||
public WidgetCommands(
|
||||
IWidgetsService widgetsService,
|
||||
ISiteService siteService,
|
||||
IMembershipService membershipService,
|
||||
IContentManager contentManager) {
|
||||
_widgetsService = widgetsService;
|
||||
_siteService = siteService;
|
||||
_membershipService = membershipService;
|
||||
_contentManager = contentManager;
|
||||
|
||||
RenderTitle = true;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Title { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool RenderTitle { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Zone { get; set; }
|
||||
|
||||
@ -49,9 +62,12 @@ namespace Orchard.Widgets.Commands {
|
||||
[OrchardSwitch]
|
||||
public bool Publish { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string MenuName { get; set; }
|
||||
|
||||
[CommandName("widget create")]
|
||||
[CommandHelp("widget create <type> /Title:<title> /Zone:<zone> /Position:<position> /Layer:<layer> [/Identity:<identity>] [/Owner:<owner>] [/Text:<text>] [/UseLoremIpsumText:true|false]\r\n\t" + "Creates a new widget")]
|
||||
[OrchardSwitches("Title,Zone,Position,Layer,Identity,Owner,Text,UseLoremIpsumText")]
|
||||
[CommandHelp("widget create <type> /Title:<title> /Zone:<zone> /Position:<position> /Layer:<layer> [/Identity:<identity>] [/RenderTitle:true|false] [/Owner:<owner>] [/Text:<text>] [/UseLoremIpsumText:true|false] [/MenuName:<name>]\r\n\t" + "Creates a new widget")]
|
||||
[OrchardSwitches("Title,Zone,Position,Layer,Identity,Owner,Text,UseLoremIpsumText,MenuName,RenderTitle")]
|
||||
public void Create(string type) {
|
||||
var widgetTypeNames = _widgetsService.GetWidgetTypeNames();
|
||||
if (!widgetTypeNames.Contains(type)) {
|
||||
@ -80,6 +96,25 @@ namespace Orchard.Widgets.Commands {
|
||||
}
|
||||
widget.As<BodyPart>().Text = text;
|
||||
}
|
||||
|
||||
widget.RenderTitle = RenderTitle;
|
||||
|
||||
if(widget.Has<MenuWidgetPart>() && !String.IsNullOrWhiteSpace(MenuName)) {
|
||||
// flushes before doing a query in case a previous command created the menu
|
||||
_contentManager.Flush();
|
||||
|
||||
var menu = _contentManager.Query<TitlePart, TitlePartRecord>()
|
||||
.Where(x => x.Title == MenuName)
|
||||
.ForType("Menu")
|
||||
.Slice(0, 1)
|
||||
.FirstOrDefault();
|
||||
|
||||
if(menu != null) {
|
||||
widget.RenderTitle = false;
|
||||
widget.As<MenuWidgetPart>().Menu = menu.ContentItem.Record;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(Owner)) {
|
||||
Owner = _siteService.GetSiteSettings().SuperUser;
|
||||
}
|
||||
|
@ -267,6 +267,8 @@
|
||||
<Compile Include="Time\SiteTimeZoneSelector.cs" />
|
||||
<Compile Include="Time\TimeZoneSelectorResult.cs" />
|
||||
<Compile Include="UI\FlatPositionComparer.cs" />
|
||||
<Compile Include="UI\Navigation\IMenuProvider.cs" />
|
||||
<Compile Include="UI\Navigation\NavigationHelper.cs" />
|
||||
<Compile Include="UI\Navigation\Pager.cs" />
|
||||
<Compile Include="UI\Navigation\PagerParameters.cs" />
|
||||
<Compile Include="UI\Resources\IResourceManifestProvider.cs" />
|
||||
|
7
src/Orchard/UI/Navigation/IMenuProvider.cs
Normal file
7
src/Orchard/UI/Navigation/IMenuProvider.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public interface IMenuProvider : IDependency {
|
||||
void GetMenu(IContent menu, NavigationBuilder builder);
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public interface INavigationManager : IDependency {
|
||||
IEnumerable<MenuItem> BuildMenu(string menuName);
|
||||
IEnumerable<MenuItem> BuildMenu(IContent menu);
|
||||
IEnumerable<string> BuildImageSets(string menuName);
|
||||
string GetUrl(string menuItemUrl, RouteValueDictionary routeValueDictionary);
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
@ -30,213 +28,33 @@ namespace Orchard.UI.Navigation {
|
||||
|
||||
WorkContext workContext = _workContextAccessor.GetContext(filterContext);
|
||||
|
||||
string menuName = "main";
|
||||
if (AdminFilter.IsApplied(filterContext.RequestContext)) {
|
||||
menuName = "admin";
|
||||
const string menuName = "admin";
|
||||
if (!AdminFilter.IsApplied(filterContext.RequestContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<MenuItem> menuItems = _navigationManager.BuildMenu(menuName);
|
||||
|
||||
// Set the currently selected path
|
||||
Stack<MenuItem> selectedPath = SetSelectedPath(menuItems, filterContext.RouteData);
|
||||
Stack<MenuItem> selectedPath = NavigationHelper.SetSelectedPath(menuItems, filterContext.RouteData);
|
||||
|
||||
// Populate main nav
|
||||
dynamic menuShape = _shapeFactory.Menu().MenuName(menuName);
|
||||
PopulateMenu(_shapeFactory, menuShape, menuShape, menuItems);
|
||||
NavigationHelper.PopulateMenu(_shapeFactory, menuShape, menuShape, menuItems);
|
||||
|
||||
// Add any know image sets to the main nav
|
||||
IEnumerable<string> menuImageSets = _navigationManager.BuildImageSets(menuName);
|
||||
if (menuImageSets != null && menuImageSets.Count() > 0)
|
||||
if (menuImageSets != null && menuImageSets.Any())
|
||||
menuShape.ImageSets(menuImageSets);
|
||||
|
||||
workContext.Layout.Navigation.Add(menuShape);
|
||||
|
||||
// Populate local nav
|
||||
dynamic localMenuShape = _shapeFactory.LocalMenu().MenuName(string.Format("local_{0}", menuName));
|
||||
PopulateLocalMenu(_shapeFactory, localMenuShape, localMenuShape, selectedPath);
|
||||
NavigationHelper.PopulateLocalMenu(_shapeFactory, localMenuShape, localMenuShape, selectedPath);
|
||||
workContext.Layout.LocalNavigation.Add(localMenuShape);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the menu shapes.
|
||||
/// </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="menuItems">The current level to populate.</param>
|
||||
protected void PopulateMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
dynamic menuItemShape = BuildMenuItemShape(shapeFactory, parentShape, menu, menuItem);
|
||||
|
||||
if (menuItem.Items != null && menuItem.Items.Any()) {
|
||||
PopulateMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
|
||||
}
|
||||
|
||||
parentShape.Add(menuItemShape, menuItem.Position);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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()) {
|
||||
PopulateLocalMenu(shapeFactory, parentShape, menu, parentMenuItem.Items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the local menu shapes.
|
||||
/// </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="menuItems">The current level to populate.</param>
|
||||
protected void PopulateLocalMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
dynamic menuItemShape = BuildLocalMenuItemShape(shapeFactory, parentShape, menu, menuItem);
|
||||
|
||||
if (menuItem.Items != null && menuItem.Items.Any()) {
|
||||
PopulateLocalMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
|
||||
}
|
||||
|
||||
parentShape.Add(menuItemShape, menuItem.Position);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
if (menuItems == null)
|
||||
return null;
|
||||
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
Stack<MenuItem> selectedPath = SetSelectedPath(menuItem.Items, currentRouteData);
|
||||
if (selectedPath != null) {
|
||||
menuItem.Selected = true;
|
||||
selectedPath.Push(menuItem);
|
||||
return selectedPath;
|
||||
}
|
||||
|
||||
if (RouteMatches(menuItem.RouteValues, currentRouteData.Values)) {
|
||||
menuItem.Selected = true;
|
||||
|
||||
selectedPath = new Stack<MenuItem>();
|
||||
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="itemValues">The menu item.</param>
|
||||
/// <param name="requestValues">The route data.</param>
|
||||
/// <returns>True if the menu item's action corresponds to the route data; false otherwise.</returns>
|
||||
protected static bool RouteMatches(RouteValueDictionary itemValues, RouteValueDictionary requestValues) {
|
||||
if (itemValues == null && requestValues == null) {
|
||||
return true;
|
||||
}
|
||||
if (itemValues == null || requestValues == null) {
|
||||
return false;
|
||||
}
|
||||
if (itemValues.Keys.Any(key => requestValues.ContainsKey(key) == false)) {
|
||||
return false;
|
||||
}
|
||||
return itemValues.Keys.All(key => string.Equals(Convert.ToString(itemValues[key]), Convert.ToString(requestValues[key]), 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) {
|
||||
var menuItemShape = shapeFactory.MenuItem()
|
||||
.Text(menuItem.Text)
|
||||
.IdHint(menuItem.IdHint)
|
||||
.Href(menuItem.Href)
|
||||
.LinkToFirstChild(menuItem.LinkToFirstChild)
|
||||
.LocalNav(menuItem.LocalNav)
|
||||
.Selected(menuItem.Selected)
|
||||
.RouteValues(menuItem.RouteValues)
|
||||
.Item(menuItem)
|
||||
.Menu(menu)
|
||||
.Parent(parentShape);
|
||||
|
||||
foreach (var className in menuItem.Classes)
|
||||
menuItemShape.Classes.Add(className);
|
||||
|
||||
return menuItemShape;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a local 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 BuildLocalMenuItemShape(dynamic shapeFactory, dynamic parentShape, dynamic menu, MenuItem menuItem) {
|
||||
var menuItemShape = shapeFactory.LocalMenuItem()
|
||||
.Text(menuItem.Text)
|
||||
.IdHint(menuItem.IdHint)
|
||||
.Href(menuItem.Href)
|
||||
.LinkToFirstChild(menuItem.LinkToFirstChild)
|
||||
.LocalNav(menuItem.LocalNav)
|
||||
.Selected(menuItem.Selected)
|
||||
.RouteValues(menuItem.RouteValues)
|
||||
.Item(menuItem)
|
||||
.Menu(menu)
|
||||
.Parent(parentShape);
|
||||
|
||||
foreach (var className in menuItem.Classes)
|
||||
menuItemShape.Classes.Add(className);
|
||||
|
||||
return menuItemShape;
|
||||
}
|
||||
|
||||
public void OnResultExecuted(ResultExecutedContext filterContext) { }
|
||||
}
|
||||
}
|
189
src/Orchard/UI/Navigation/NavigationHelper.cs
Normal file
189
src/Orchard/UI/Navigation/NavigationHelper.cs
Normal file
@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public static class NavigationHelper {
|
||||
|
||||
/// <summary>
|
||||
/// Populates the menu shapes.
|
||||
/// </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="menuItems">The current level to populate.</param>
|
||||
public static void PopulateMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
dynamic menuItemShape = BuildMenuItemShape(shapeFactory, parentShape, menu, menuItem);
|
||||
|
||||
if (menuItem.Items != null && menuItem.Items.Any()) {
|
||||
PopulateMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
|
||||
}
|
||||
|
||||
parentShape.Add(menuItemShape, menuItem.Position);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static 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()) {
|
||||
PopulateLocalMenu(shapeFactory, parentShape, menu, parentMenuItem.Items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the local menu shapes.
|
||||
/// </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="menuItems">The current level to populate.</param>
|
||||
public static void PopulateLocalMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
dynamic menuItemShape = BuildLocalMenuItemShape(shapeFactory, parentShape, menu, menuItem);
|
||||
|
||||
if (menuItem.Items != null && menuItem.Items.Any()) {
|
||||
PopulateLocalMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
|
||||
}
|
||||
|
||||
parentShape.Add(menuItemShape, menuItem.Position);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static Stack<MenuItem> SetSelectedPath(IEnumerable<MenuItem> menuItems, RouteData currentRouteData) {
|
||||
if (menuItems == null)
|
||||
return null;
|
||||
|
||||
foreach (MenuItem menuItem in menuItems) {
|
||||
Stack<MenuItem> selectedPath = SetSelectedPath(menuItem.Items, currentRouteData);
|
||||
if (selectedPath != null) {
|
||||
menuItem.Selected = true;
|
||||
selectedPath.Push(menuItem);
|
||||
return selectedPath;
|
||||
}
|
||||
|
||||
if (RouteMatches(menuItem.RouteValues, currentRouteData.Values)) {
|
||||
menuItem.Selected = true;
|
||||
|
||||
selectedPath = new Stack<MenuItem>();
|
||||
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>
|
||||
public 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="itemValues">The menu item.</param>
|
||||
/// <param name="requestValues">The route data.</param>
|
||||
/// <returns>True if the menu item's action corresponds to the route data; false otherwise.</returns>
|
||||
public static bool RouteMatches(RouteValueDictionary itemValues, RouteValueDictionary requestValues) {
|
||||
if (itemValues == null && requestValues == null) {
|
||||
return true;
|
||||
}
|
||||
if (itemValues == null || requestValues == null) {
|
||||
return false;
|
||||
}
|
||||
if (itemValues.Keys.Any(key => requestValues.ContainsKey(key) == false)) {
|
||||
return false;
|
||||
}
|
||||
return itemValues.Keys.All(key => string.Equals(Convert.ToString(itemValues[key]), Convert.ToString(requestValues[key]), 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>
|
||||
public static dynamic BuildMenuItemShape(dynamic shapeFactory, dynamic parentShape, dynamic menu, MenuItem menuItem) {
|
||||
var menuItemShape = shapeFactory.MenuItem()
|
||||
.Text(menuItem.Text)
|
||||
.IdHint(menuItem.IdHint)
|
||||
.Href(menuItem.Href)
|
||||
.LinkToFirstChild(menuItem.LinkToFirstChild)
|
||||
.LocalNav(menuItem.LocalNav)
|
||||
.Selected(menuItem.Selected)
|
||||
.RouteValues(menuItem.RouteValues)
|
||||
.Item(menuItem)
|
||||
.Menu(menu)
|
||||
.Parent(parentShape);
|
||||
|
||||
foreach (var className in menuItem.Classes)
|
||||
menuItemShape.Classes.Add(className);
|
||||
|
||||
return menuItemShape;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a local 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>
|
||||
public static dynamic BuildLocalMenuItemShape(dynamic shapeFactory, dynamic parentShape, dynamic menu, MenuItem menuItem) {
|
||||
var menuItemShape = shapeFactory.LocalMenuItem()
|
||||
.Text(menuItem.Text)
|
||||
.IdHint(menuItem.IdHint)
|
||||
.Href(menuItem.Href)
|
||||
.LinkToFirstChild(menuItem.LinkToFirstChild)
|
||||
.LocalNav(menuItem.LocalNav)
|
||||
.Selected(menuItem.Selected)
|
||||
.RouteValues(menuItem.RouteValues)
|
||||
.Item(menuItem)
|
||||
.Menu(menu)
|
||||
.Parent(parentShape);
|
||||
|
||||
foreach (var className in menuItem.Classes)
|
||||
menuItemShape.Classes.Add(className);
|
||||
|
||||
return menuItemShape;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,19 +3,27 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Security;
|
||||
using Orchard.Security.Permissions;
|
||||
|
||||
namespace Orchard.UI.Navigation {
|
||||
public class NavigationManager : INavigationManager {
|
||||
private readonly IEnumerable<INavigationProvider> _providers;
|
||||
private readonly IEnumerable<INavigationProvider> _navigationProviders;
|
||||
private readonly IEnumerable<IMenuProvider> _menuProviders;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly UrlHelper _urlHelper;
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
|
||||
public NavigationManager(IEnumerable<INavigationProvider> providers, IAuthorizationService authorizationService, UrlHelper urlHelper, IOrchardServices orchardServices) {
|
||||
_providers = providers;
|
||||
public NavigationManager(
|
||||
IEnumerable<INavigationProvider> navigationProviders,
|
||||
IEnumerable<IMenuProvider> menuProviders,
|
||||
IAuthorizationService authorizationService,
|
||||
UrlHelper urlHelper,
|
||||
IOrchardServices orchardServices) {
|
||||
_navigationProviders = navigationProviders;
|
||||
_menuProviders = menuProviders;
|
||||
_authorizationService = authorizationService;
|
||||
_urlHelper = urlHelper;
|
||||
_orchardServices = orchardServices;
|
||||
@ -26,6 +34,11 @@ namespace Orchard.UI.Navigation {
|
||||
|
||||
public IEnumerable<MenuItem> BuildMenu(string menuName) {
|
||||
var sources = GetSources(menuName);
|
||||
return FinishMenu(Reduce(Merge(sources)).ToArray());
|
||||
}
|
||||
|
||||
public IEnumerable<MenuItem> BuildMenu(IContent menu) {
|
||||
var sources = GetSources(menu);
|
||||
return FinishMenu(Reduce(Arrange(Merge(sources))).ToArray());
|
||||
}
|
||||
|
||||
@ -62,7 +75,9 @@ namespace Orchard.UI.Navigation {
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the items by checking for permissions
|
||||
/// </summary>
|
||||
private IEnumerable<MenuItem> Reduce(IEnumerable<MenuItem> items) {
|
||||
var hasDebugShowAllMenuItems = _authorizationService.TryCheckAccess(Permission.Named("DebugShowAllMenuItems"), _orchardServices.WorkContext.CurrentUser, null);
|
||||
foreach (var item in items) {
|
||||
@ -80,14 +95,15 @@ namespace Orchard.UI.Navigation {
|
||||
Classes = item.Classes,
|
||||
Url = item.Url,
|
||||
LinkToFirstChild = item.LinkToFirstChild,
|
||||
Href = item.Href
|
||||
Href = item.Href,
|
||||
MenuId = item.MenuId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IEnumerable<MenuItem>> GetSources(string menuName) {
|
||||
foreach (var provider in _providers) {
|
||||
foreach (var provider in _navigationProviders) {
|
||||
if (provider.MenuName == menuName) {
|
||||
var builder = new NavigationBuilder();
|
||||
IEnumerable<MenuItem> items = null;
|
||||
@ -105,8 +121,25 @@ namespace Orchard.UI.Navigation {
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IEnumerable<MenuItem>> GetSources(IContent menu) {
|
||||
foreach (var provider in _menuProviders) {
|
||||
var builder = new NavigationBuilder();
|
||||
IEnumerable<MenuItem> items = null;
|
||||
try {
|
||||
provider.GetMenu(menu, builder);
|
||||
items = builder.Build();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, "Unexpected error while querying a menu provider. It was ignored. The menu provided by the provider may not be complete.");
|
||||
}
|
||||
if (items != null) {
|
||||
yield return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IEnumerable<string>> GetImageSets(string menuName) {
|
||||
foreach (var provider in _providers) {
|
||||
foreach (var provider in _navigationProviders) {
|
||||
if (provider.MenuName == menuName) {
|
||||
var builder = new NavigationBuilder();
|
||||
IEnumerable<string> imageSets = null;
|
||||
@ -139,24 +172,18 @@ namespace Orchard.UI.Navigation {
|
||||
.SelectMany(positionGroup => positionGroup.OrderBy(item => item.Text == null ? "" : item.Text.TextHint));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Organizes a list of <see cref="MenuItem"/> into a hierarchy based on their positions
|
||||
/// </summary>
|
||||
private static IEnumerable<MenuItem> Arrange(IEnumerable<MenuItem> items) {
|
||||
|
||||
var indexes = new Dictionary<int, Dictionary<string, MenuItem>>();
|
||||
|
||||
var result = new List<MenuItem>();
|
||||
var index = new Dictionary<string, MenuItem>();
|
||||
|
||||
foreach (var item in items) {
|
||||
MenuItem parent = null;
|
||||
var position = item.Position;
|
||||
|
||||
Dictionary<string, MenuItem> index = null;
|
||||
if(indexes.ContainsKey(item.MenuId)) {
|
||||
index = indexes[item.MenuId];
|
||||
}
|
||||
else {
|
||||
indexes.Add(item.MenuId, index = new Dictionary<string, MenuItem>());
|
||||
}
|
||||
|
||||
|
||||
while (parent == null && !String.IsNullOrEmpty(position)) {
|
||||
if (index.TryGetValue(position, out parent)) {
|
||||
@ -166,7 +193,11 @@ namespace Orchard.UI.Navigation {
|
||||
position = position.Substring(0, position.Length - 1);
|
||||
};
|
||||
|
||||
index.Add(item.Position, item);
|
||||
if (!index.ContainsKey(item.Position)) {
|
||||
// prevent invalid positions
|
||||
index.Add(item.Position, item);
|
||||
}
|
||||
|
||||
|
||||
// if the current element has no parent, it's a top level item
|
||||
if (parent == null) {
|
||||
@ -193,6 +224,7 @@ namespace Orchard.UI.Navigation {
|
||||
Items = Merge(items.Select(x => x.Items)).ToArray(),
|
||||
Position = SelectBestPositionValue(items.Select(x => x.Position)),
|
||||
Permissions = items.SelectMany(x => x.Permissions).Distinct(),
|
||||
MenuId = items.First().MenuId,
|
||||
};
|
||||
return joined;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user