Implemented Menu and Breadcrumbs elements.

This commit is contained in:
Sipke Schoorstra
2015-04-19 21:12:28 +02:00
parent df9eb83a6c
commit 7be9428fac
13 changed files with 442 additions and 0 deletions

View File

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

View File

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

View File

@@ -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); }
}
}
}

View 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); }
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
@using Orchard.Layouts.Helpers
@{
var tagBuilder = TagBuilderExtensions.CreateElementTagBuilder(Model);
}
@tagBuilder.StartElement
@Display(Model.Menu)
@tagBuilder.EndElement

View File

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

View File

@@ -0,0 +1,8 @@
@using Orchard.Layouts.Helpers
@{
var tagBuilder = TagBuilderExtensions.CreateElementTagBuilder(Model);
tagBuilder.AddCssClass("group");
}
@tagBuilder.StartElement
@Display(Model.Menu)
@tagBuilder.EndElement