mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Implemented Menu and Breadcrumbs elements.
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Navigation.Services;
|
||||
using Orchard.Core.Title.Models;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Layouts.Elements;
|
||||
using Orchard.Layouts.Framework.Display;
|
||||
using Orchard.Layouts.Framework.Drivers;
|
||||
using Orchard.Layouts.ViewModels;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Layouts.Drivers {
|
||||
[OrchardFeature("Orchard.Layouts.UI")]
|
||||
public class BreadcrumbsElementDriver : ElementDriver<Breadcrumbs> {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IMenuService _menuService;
|
||||
private readonly INavigationManager _navigationManager;
|
||||
|
||||
public BreadcrumbsElementDriver(IMenuService menuService, INavigationManager navigationManager, IWorkContextAccessor workContextAccessor, IShapeFactory shapeFactory) {
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_menuService = menuService;
|
||||
_navigationManager = navigationManager;
|
||||
New = shapeFactory;
|
||||
}
|
||||
|
||||
public dynamic New { get; set; }
|
||||
|
||||
protected override EditorResult OnBuildEditor(Breadcrumbs element, ElementEditorContext context) {
|
||||
var viewModel = new BreadcrumbsEditorViewModel {
|
||||
CurrentMenuId = element.MenuContentItemId,
|
||||
StartLevel = element.StartLevel,
|
||||
StopLevel = element.Levels,
|
||||
AddCurrentPage = element.AddCurrentPage,
|
||||
AddHomePage = element.AddHomePage,
|
||||
Menus = _menuService.GetMenus(),
|
||||
};
|
||||
var editor = context.ShapeFactory.EditorTemplate(TemplateName: "Elements.Breadcrumbs", Model: viewModel);
|
||||
|
||||
if (context.Updater != null) {
|
||||
if (context.Updater.TryUpdateModel(viewModel, context.Prefix, null, null)) {
|
||||
element.StartLevel = viewModel.StartLevel;
|
||||
element.Levels = viewModel.StopLevel;
|
||||
element.AddCurrentPage = viewModel.AddCurrentPage;
|
||||
element.AddHomePage = viewModel.AddHomePage;
|
||||
element.MenuContentItemId = viewModel.CurrentMenuId;
|
||||
}
|
||||
}
|
||||
|
||||
return Editor(context, editor);
|
||||
}
|
||||
|
||||
protected override void OnDisplaying(Breadcrumbs element, ElementDisplayingContext context) {
|
||||
var menu = _menuService.GetMenu(element.MenuContentItemId);
|
||||
|
||||
if (menu == null)
|
||||
return;
|
||||
|
||||
var menuName = menu.As<TitlePart>().Title.HtmlClassify();
|
||||
var currentCulture = _workContextAccessor.GetContext().CurrentCulture;
|
||||
var menuItems = _navigationManager.BuildMenu(menu);
|
||||
var localized = new List<MenuItem>();
|
||||
foreach (var menuItem in menuItems) {
|
||||
// If there is no associated content, it as culture neutral.
|
||||
if (menuItem.Content == null)
|
||||
localized.Add(menuItem);
|
||||
|
||||
// If the menu item is culture neutral or of the current culture.
|
||||
else if (String.IsNullOrEmpty(menuItem.Culture) || String.Equals(menuItem.Culture, currentCulture, StringComparison.OrdinalIgnoreCase))
|
||||
localized.Add(menuItem);
|
||||
}
|
||||
|
||||
menuItems = localized;
|
||||
|
||||
var shapeHelper = New;
|
||||
var request = _workContextAccessor.GetContext().HttpContext.Request;
|
||||
var routeData = request.RequestContext.RouteData;
|
||||
var selectedPath = NavigationHelper.SetSelectedPath(menuItems, request, routeData);
|
||||
var menuShape = shapeHelper.Menu();
|
||||
|
||||
|
||||
menuItems = selectedPath ?? new Stack<MenuItem>();
|
||||
foreach (var menuItem in menuItems) {
|
||||
menuItem.Items = Enumerable.Empty<MenuItem>();
|
||||
}
|
||||
|
||||
// Apply level limits to breadcrumb.
|
||||
menuItems = menuItems.Skip(element.StartLevel - 1);
|
||||
if (element.Levels > 0) {
|
||||
menuItems = menuItems.Take(element.Levels);
|
||||
}
|
||||
|
||||
var result = new List<MenuItem>(menuItems);
|
||||
|
||||
// Inject the home page.
|
||||
if (element.AddHomePage) {
|
||||
result.Insert(0, new MenuItem {
|
||||
Href = _navigationManager.GetUrl("~/", null),
|
||||
Text = T("Home")
|
||||
});
|
||||
}
|
||||
|
||||
// Inject the current page.
|
||||
if (!element.AddCurrentPage && selectedPath != null) {
|
||||
result.RemoveAt(result.Count - 1);
|
||||
}
|
||||
|
||||
// Prevent the home page to be added as the home page and the current page.
|
||||
if (result.Count == 2 && String.Equals(result[0].Href, result[1].Href, StringComparison.OrdinalIgnoreCase)) {
|
||||
result.RemoveAt(1);
|
||||
}
|
||||
|
||||
menuItems = result;
|
||||
menuShape = shapeHelper.Breadcrumb();
|
||||
menuShape.MenuName(menuName);
|
||||
menuShape.ContentItem(menu);
|
||||
|
||||
NavigationHelper.PopulateMenu(shapeHelper, menuShape, menuShape, menuItems);
|
||||
|
||||
context.ElementShape.Menu = menuShape;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Navigation.Services;
|
||||
using Orchard.Core.Title.Models;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Layouts.Elements;
|
||||
using Orchard.Layouts.Framework.Display;
|
||||
using Orchard.Layouts.Framework.Drivers;
|
||||
using Orchard.Layouts.ViewModels;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Layouts.Drivers {
|
||||
[OrchardFeature("Orchard.Layouts.UI")]
|
||||
public class MenuElementDriver : ElementDriver<Menu> {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IMenuService _menuService;
|
||||
private readonly INavigationManager _navigationManager;
|
||||
|
||||
public MenuElementDriver(IMenuService menuService, INavigationManager navigationManager, IWorkContextAccessor workContextAccessor, IShapeFactory shapeFactory) {
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_menuService = menuService;
|
||||
_navigationManager = navigationManager;
|
||||
New = shapeFactory;
|
||||
}
|
||||
|
||||
public dynamic New { get; set; }
|
||||
|
||||
protected override EditorResult OnBuildEditor(Menu element, ElementEditorContext context) {
|
||||
var viewModel = new MenuEditorViewModel {
|
||||
CurrentMenuId = element.MenuContentItemId,
|
||||
StartLevel = element.StartLevel,
|
||||
StopLevel = element.Levels,
|
||||
ShowFullMenu = element.ShowFullMenu,
|
||||
Menus = _menuService.GetMenus(),
|
||||
};
|
||||
var editor = context.ShapeFactory.EditorTemplate(TemplateName: "Elements.Menu", Model: viewModel);
|
||||
|
||||
if (context.Updater != null) {
|
||||
if (context.Updater.TryUpdateModel(viewModel, context.Prefix, null, null)) {
|
||||
element.StartLevel = viewModel.StartLevel;
|
||||
element.Levels = viewModel.StopLevel;
|
||||
element.ShowFullMenu = viewModel.ShowFullMenu;
|
||||
element.MenuContentItemId = viewModel.CurrentMenuId;
|
||||
}
|
||||
}
|
||||
|
||||
return Editor(context, editor);
|
||||
}
|
||||
|
||||
protected override void OnDisplaying(Menu element, ElementDisplayingContext context) {
|
||||
var menu = _menuService.GetMenu(element.MenuContentItemId);
|
||||
|
||||
if (menu == null)
|
||||
return;
|
||||
|
||||
var menuName = menu.As<TitlePart>().Title.HtmlClassify();
|
||||
var currentCulture = _workContextAccessor.GetContext().CurrentCulture;
|
||||
var menuItems = _navigationManager.BuildMenu(menu);
|
||||
var localized = new List<MenuItem>();
|
||||
foreach (var menuItem in menuItems) {
|
||||
// If there is no associated content, it as culture neutral.
|
||||
if (menuItem.Content == null)
|
||||
localized.Add(menuItem);
|
||||
|
||||
// If the menu item is culture neutral or of the current culture.
|
||||
else if (String.IsNullOrEmpty(menuItem.Culture) || String.Equals(menuItem.Culture, currentCulture, StringComparison.OrdinalIgnoreCase))
|
||||
localized.Add(menuItem);
|
||||
}
|
||||
|
||||
menuItems = localized;
|
||||
|
||||
var shapeHelper = New;
|
||||
var request = _workContextAccessor.GetContext().HttpContext.Request;
|
||||
var routeData = request.RequestContext.RouteData;
|
||||
var selectedPath = NavigationHelper.SetSelectedPath(menuItems, request, routeData);
|
||||
var menuShape = shapeHelper.Menu();
|
||||
var topLevelItems = menuItems.ToList();
|
||||
|
||||
// Apply start level by pushing children as top level items. When the start level is
|
||||
// greater than 1 (ie. below the top level), only menu items along the selected path
|
||||
// will be displayed.
|
||||
for (var i = 0; topLevelItems.Any() && i < element.StartLevel - 1; i++) {
|
||||
var temp = new List<MenuItem>();
|
||||
// Should the menu be filtered on the currently displayed page?
|
||||
if (element.ShowFullMenu) {
|
||||
foreach (var menuItem in topLevelItems) {
|
||||
temp.AddRange(menuItem.Items);
|
||||
}
|
||||
}
|
||||
else if (selectedPath != null) {
|
||||
topLevelItems = topLevelItems.Intersect(selectedPath.Where(x => x.Selected)).ToList();
|
||||
foreach (var menuItem in topLevelItems) {
|
||||
temp.AddRange(menuItem.Items);
|
||||
}
|
||||
}
|
||||
topLevelItems = temp;
|
||||
}
|
||||
|
||||
// Limit the number of levels to display (down from and including the start level).
|
||||
if (element.Levels > 0) {
|
||||
var current = topLevelItems.ToList();
|
||||
for (var i = 1; current.Any() && i < element.Levels; i++) {
|
||||
var temp = new List<MenuItem>();
|
||||
foreach (var menuItem in current) {
|
||||
temp.AddRange(menuItem.Items);
|
||||
}
|
||||
current = temp;
|
||||
}
|
||||
// Cut the sub-levels beneath any menu items that are at the lowest level being displayed.
|
||||
foreach (var menuItem in current) {
|
||||
menuItem.Items = Enumerable.Empty<MenuItem>();
|
||||
}
|
||||
}
|
||||
|
||||
menuItems = topLevelItems;
|
||||
menuShape.MenuName(menuName);
|
||||
menuShape.ContentItem(menu);
|
||||
|
||||
NavigationHelper.PopulateMenu(shapeHelper, menuShape, menuShape, menuItems);
|
||||
|
||||
context.ElementShape.Menu = menuShape;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using Orchard.Layouts.Helpers;
|
||||
|
||||
namespace Orchard.Layouts.Elements {
|
||||
public class Breadcrumbs : UIElement {
|
||||
public int StartLevel {
|
||||
get { return this.Retrieve(x => x.StartLevel); }
|
||||
set { this.Store(x => x.StartLevel, value); }
|
||||
}
|
||||
|
||||
public int Levels {
|
||||
get { return this.Retrieve(x => x.Levels); }
|
||||
set { this.Store(x => x.Levels, value); }
|
||||
}
|
||||
|
||||
public bool AddHomePage {
|
||||
get { return this.Retrieve(x => x.AddHomePage); }
|
||||
set { this.Store(x => x.AddHomePage, value); }
|
||||
}
|
||||
|
||||
public bool AddCurrentPage {
|
||||
get { return this.Retrieve(x => x.AddCurrentPage); }
|
||||
set { this.Store(x => x.AddCurrentPage, value); }
|
||||
}
|
||||
|
||||
public int MenuContentItemId {
|
||||
get { return this.Retrieve(x => x.MenuContentItemId); }
|
||||
set { this.Store(x => x.MenuContentItemId, value); }
|
||||
}
|
||||
}
|
||||
}
|
25
src/Orchard.Web/Modules/Orchard.Layouts/Elements/Menu.cs
Normal file
25
src/Orchard.Web/Modules/Orchard.Layouts/Elements/Menu.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Orchard.Layouts.Helpers;
|
||||
|
||||
namespace Orchard.Layouts.Elements {
|
||||
public class Menu : UIElement {
|
||||
public int StartLevel {
|
||||
get { return this.Retrieve(x => x.StartLevel); }
|
||||
set { this.Store(x => x.StartLevel, value); }
|
||||
}
|
||||
|
||||
public int Levels {
|
||||
get { return this.Retrieve(x => x.Levels); }
|
||||
set { this.Store(x => x.Levels, value); }
|
||||
}
|
||||
|
||||
public int MenuContentItemId {
|
||||
get { return this.Retrieve(x => x.MenuContentItemId); }
|
||||
set { this.Store(x => x.MenuContentItemId, value); }
|
||||
}
|
||||
|
||||
public bool ShowFullMenu {
|
||||
get { return this.Retrieve(x => x.ShowFullMenu); }
|
||||
set { this.Store(x => x.ShowFullMenu, value); }
|
||||
}
|
||||
}
|
||||
}
|
@@ -423,10 +423,14 @@
|
||||
<Compile Include="Controllers\BlueprintAdminController.cs" />
|
||||
<Compile Include="Controllers\TemplateController.cs" />
|
||||
<Compile Include="Drivers\CanvasElementDriver.cs" />
|
||||
<Compile Include="Drivers\BreadcrumbsElementDriver.cs" />
|
||||
<Compile Include="Drivers\MenuElementDriver.cs" />
|
||||
<Compile Include="Drivers\NotificationsElementDriver.cs" />
|
||||
<Compile Include="Drivers\ShapeElementDriver.cs" />
|
||||
<Compile Include="Elements\Canvas.cs" />
|
||||
<Compile Include="Elements\ContentElement.cs" />
|
||||
<Compile Include="Elements\Breadcrumbs.cs" />
|
||||
<Compile Include="Elements\Menu.cs" />
|
||||
<Compile Include="Elements\Notifications.cs" />
|
||||
<Compile Include="Elements\Shape.cs" />
|
||||
<Compile Include="Elements\UIElement.cs" />
|
||||
@@ -521,6 +525,8 @@
|
||||
<Compile Include="ViewModels\HtmlEditorViewModel.cs" />
|
||||
<Compile Include="ViewModels\LayoutEditor.cs" />
|
||||
<Compile Include="ViewModels\LayoutEditorPropertiesViewModel.cs" />
|
||||
<Compile Include="ViewModels\BreadcrumbsEditorViewModel.cs" />
|
||||
<Compile Include="ViewModels\MenuEditorViewModel.cs" />
|
||||
<Compile Include="ViewModels\VectorImageEditorViewModel.cs" />
|
||||
<Compile Include="ViewModels\ImageEditorViewModel.cs" />
|
||||
<Compile Include="ViewModels\MarkdownEditorViewModel.cs" />
|
||||
@@ -634,6 +640,24 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Elements\Notifications.Design.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\Elements.Menu.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Elements\Menu.Design.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Elements\Menu.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Elements\Breadcrumbs.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Elements\Breadcrumbs.Design.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\Elements.Breadcrumbs.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Layouts.ViewModels {
|
||||
public class BreadcrumbsEditorViewModel {
|
||||
public IEnumerable<ContentItem> Menus { get; set; }
|
||||
public int CurrentMenuId { get; set; }
|
||||
|
||||
public int StartLevel { get; set; }
|
||||
public int StopLevel { get; set; }
|
||||
public bool AddHomePage { get; set; }
|
||||
public bool AddCurrentPage { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Layouts.ViewModels {
|
||||
public class MenuEditorViewModel {
|
||||
public IEnumerable<ContentItem> Menus { get; set; }
|
||||
public int CurrentMenuId { get; set; }
|
||||
|
||||
public int StartLevel { get; set; }
|
||||
public int StopLevel { get; set; }
|
||||
public bool ShowFullMenu { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
@model Orchard.Layouts.ViewModels.BreadcrumbsEditorViewModel
|
||||
@{
|
||||
var menuOptions = Model.Menus.Select(x => new SelectListItem { Text = Html.ItemDisplayText(x).ToString(), Value = x.Id.ToString(), Selected = x.Id == Model.CurrentMenuId });
|
||||
}
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.CurrentMenuId, T("For Menu"))
|
||||
@Html.DropDownListFor(m => m.CurrentMenuId, menuOptions)
|
||||
@Html.Hint(T("Select which menu you want to display"))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.StartLevel, T("Start Level"))
|
||||
@Html.TextBoxFor(m => m.StartLevel, new { @class = "text small" })
|
||||
@Html.Hint(T("The level the menu should start at."))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.StopLevel, T("Levels to display"))
|
||||
@Html.TextBoxFor(m => m.StopLevel, new { @class = "text small" })
|
||||
@Html.Hint(T("The number of levels to display, \"0\" meaning all levels."))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.CheckBoxFor(m => m.AddHomePage)
|
||||
@Html.LabelFor(m => m.AddHomePage, T("Add the home page as the first element").Text, new { @class = "forcheckbox" })
|
||||
@Html.Hint(T("Check to render the home page as the first element of the breadcrumb."))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.CheckBoxFor(m => m.AddCurrentPage)
|
||||
@Html.LabelFor(m => m.AddCurrentPage, T("Add the current content item as the last element").Text, new { @class = "forcheckbox" })
|
||||
@Html.Hint(T("Check to render the current content item as the last element."))
|
||||
</fieldset>
|
@@ -0,0 +1,24 @@
|
||||
@model Orchard.Layouts.ViewModels.MenuEditorViewModel
|
||||
@{
|
||||
var menuOptions = Model.Menus.Select(x => new SelectListItem {Text = Html.ItemDisplayText(x).ToString(), Value = x.Id.ToString(), Selected = x.Id == Model.CurrentMenuId});
|
||||
}
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.CurrentMenuId, T("For Menu"))
|
||||
@Html.DropDownListFor(m => m.CurrentMenuId, menuOptions)
|
||||
@Html.Hint(T("Select which menu you want to display"))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.StartLevel, T("Start Level"))
|
||||
@Html.TextBoxFor(m => m.StartLevel, new {@class = "text small"})
|
||||
@Html.Hint(T("The level the menu should start at."))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.StopLevel, T("Levels to display"))
|
||||
@Html.TextBoxFor(m => m.StopLevel, new {@class = "text small"})
|
||||
@Html.Hint(T("The number of levels to display, \"0\" meaning all levels."))
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@Html.CheckBoxFor(m => m.ShowFullMenu)
|
||||
@Html.LabelFor(m => m.ShowFullMenu, T("No filter on selected page").Text, new { @class = "forcheckbox" })
|
||||
@Html.Hint(T("Check for the menu to be display without filtering the selected current page."))
|
||||
</fieldset>
|
@@ -0,0 +1,7 @@
|
||||
<div class="layout-breadcrumbs-element">
|
||||
<ul class="menu-items">
|
||||
<li>Item 1</li>
|
||||
<li>Item 2</li>
|
||||
<li>Item 3</li>
|
||||
</ul>
|
||||
</div>
|
@@ -0,0 +1,7 @@
|
||||
@using Orchard.Layouts.Helpers
|
||||
@{
|
||||
var tagBuilder = TagBuilderExtensions.CreateElementTagBuilder(Model);
|
||||
}
|
||||
@tagBuilder.StartElement
|
||||
@Display(Model.Menu)
|
||||
@tagBuilder.EndElement
|
@@ -0,0 +1,7 @@
|
||||
<div class="layout-menu-element">
|
||||
<ul class="menu-items">
|
||||
<li>Item 1</li>
|
||||
<li>Item 2</li>
|
||||
<li>Item 3</li>
|
||||
</ul>
|
||||
</div>
|
@@ -0,0 +1,8 @@
|
||||
@using Orchard.Layouts.Helpers
|
||||
@{
|
||||
var tagBuilder = TagBuilderExtensions.CreateElementTagBuilder(Model);
|
||||
tagBuilder.AddCssClass("group");
|
||||
}
|
||||
@tagBuilder.StartElement
|
||||
@Display(Model.Menu)
|
||||
@tagBuilder.EndElement
|
Reference in New Issue
Block a user