/src/Orchard/UI/Navigation/MenuFilter.cs

# · C# · 242 lines · 146 code · 36 blank · 60 comment · 37 complexity · 62af17397c49f4f182436968a58f6a99 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web.Mvc;
  5. using System.Web.Routing;
  6. using Orchard.DisplayManagement;
  7. using Orchard.Mvc.Filters;
  8. using Orchard.UI.Admin;
  9. namespace Orchard.UI.Navigation {
  10. public class MenuFilter : FilterProvider, IResultFilter {
  11. private readonly INavigationManager _navigationManager;
  12. private readonly IWorkContextAccessor _workContextAccessor;
  13. private readonly dynamic _shapeFactory;
  14. public MenuFilter(INavigationManager navigationManager,
  15. IWorkContextAccessor workContextAccessor,
  16. IShapeFactory shapeFactory) {
  17. _navigationManager = navigationManager;
  18. _workContextAccessor = workContextAccessor;
  19. _shapeFactory = shapeFactory;
  20. }
  21. public void OnResultExecuting(ResultExecutingContext filterContext) {
  22. // should only run on a full view rendering result
  23. if (!(filterContext.Result is ViewResult)) {
  24. return;
  25. }
  26. WorkContext workContext = _workContextAccessor.GetContext(filterContext);
  27. string menuName = "main";
  28. if (AdminFilter.IsApplied(filterContext.RequestContext)) {
  29. menuName = "admin";
  30. }
  31. IEnumerable<MenuItem> menuItems = _navigationManager.BuildMenu(menuName);
  32. // Set the currently selected path
  33. Stack<MenuItem> selectedPath = SetSelectedPath(menuItems, filterContext.RouteData);
  34. // Populate main nav
  35. dynamic menuShape = _shapeFactory.Menu().MenuName(menuName);
  36. PopulateMenu(_shapeFactory, menuShape, menuShape, menuItems);
  37. // Add any know image sets to the main nav
  38. IEnumerable<string> menuImageSets = _navigationManager.BuildImageSets(menuName);
  39. if (menuImageSets != null && menuImageSets.Count() > 0)
  40. menuShape.ImageSets(menuImageSets);
  41. workContext.Layout.Navigation.Add(menuShape);
  42. // Populate local nav
  43. dynamic localMenuShape = _shapeFactory.LocalMenu().MenuName(string.Format("local_{0}", menuName));
  44. PopulateLocalMenu(_shapeFactory, localMenuShape, localMenuShape, selectedPath);
  45. workContext.Layout.LocalNavigation.Add(localMenuShape);
  46. }
  47. /// <summary>
  48. /// Populates the menu shapes.
  49. /// </summary>
  50. /// <param name="shapeFactory">The shape factory.</param>
  51. /// <param name="parentShape">The menu parent shape.</param>
  52. /// <param name="menu">The menu shape.</param>
  53. /// <param name="menuItems">The current level to populate.</param>
  54. protected void PopulateMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
  55. foreach (MenuItem menuItem in menuItems) {
  56. dynamic menuItemShape = BuildMenuItemShape(shapeFactory, parentShape, menu, menuItem);
  57. if (menuItem.Items != null && menuItem.Items.Any()) {
  58. PopulateMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
  59. }
  60. parentShape.Add(menuItemShape, menuItem.Position);
  61. }
  62. }
  63. /// <summary>
  64. /// Populates the local menu starting from the first non local task parent.
  65. /// </summary>
  66. /// <param name="shapeFactory">The shape factory.</param>
  67. /// <param name="parentShape">The menu parent shape.</param>
  68. /// <param name="menu">The menu shape.</param>
  69. /// <param name="selectedPath">The selection path.</param>
  70. protected void PopulateLocalMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, Stack<MenuItem> selectedPath) {
  71. MenuItem parentMenuItem = FindParentLocalTask(selectedPath);
  72. // find childs tabs and expand them
  73. if (parentMenuItem != null && parentMenuItem.Items != null && parentMenuItem.Items.Any()) {
  74. PopulateLocalMenu(shapeFactory, parentShape, menu, parentMenuItem.Items);
  75. }
  76. }
  77. /// <summary>
  78. /// Populates the local menu shapes.
  79. /// </summary>
  80. /// <param name="shapeFactory">The shape factory.</param>
  81. /// <param name="parentShape">The menu parent shape.</param>
  82. /// <param name="menu">The menu shape.</param>
  83. /// <param name="menuItems">The current level to populate.</param>
  84. protected void PopulateLocalMenu(dynamic shapeFactory, dynamic parentShape, dynamic menu, IEnumerable<MenuItem> menuItems) {
  85. foreach (MenuItem menuItem in menuItems) {
  86. dynamic menuItemShape = BuildLocalMenuItemShape(shapeFactory, parentShape, menu, menuItem);
  87. if (menuItem.Items != null && menuItem.Items.Any()) {
  88. PopulateLocalMenu(shapeFactory, menuItemShape, menu, menuItem.Items);
  89. }
  90. parentShape.Add(menuItemShape, menuItem.Position);
  91. }
  92. }
  93. /// <summary>
  94. /// Identifies the currently selected path, starting from the selected node.
  95. /// </summary>
  96. /// <param name="menuItems">All the menuitems in the navigation menu.</param>
  97. /// <param name="currentRouteData">The current route data.</param>
  98. /// <returns>A stack with the selection path being the last node the currently selected one.</returns>
  99. protected static Stack<MenuItem> SetSelectedPath(IEnumerable<MenuItem> menuItems, RouteData currentRouteData) {
  100. if (menuItems == null)
  101. return null;
  102. foreach (MenuItem menuItem in menuItems) {
  103. Stack<MenuItem> selectedPath = SetSelectedPath(menuItem.Items, currentRouteData);
  104. if (selectedPath != null) {
  105. menuItem.Selected = true;
  106. selectedPath.Push(menuItem);
  107. return selectedPath;
  108. }
  109. if (RouteMatches(menuItem.RouteValues, currentRouteData.Values)) {
  110. menuItem.Selected = true;
  111. selectedPath = new Stack<MenuItem>();
  112. selectedPath.Push(menuItem);
  113. return selectedPath;
  114. }
  115. }
  116. return null;
  117. }
  118. /// <summary>
  119. /// Find the first level in the selection path, starting from the bottom, that is not a local task.
  120. /// </summary>
  121. /// <param name="selectedPath">The selection path stack. The bottom node is the currently selected one.</param>
  122. /// <returns>The first node, starting from the bottom, that is not a local task. Otherwise, null.</returns>
  123. protected static MenuItem FindParentLocalTask(Stack<MenuItem> selectedPath) {
  124. if (selectedPath != null) {
  125. MenuItem parentMenuItem = selectedPath.Pop();
  126. if (parentMenuItem != null) {
  127. while (selectedPath.Count > 0) {
  128. MenuItem currentMenuItem = selectedPath.Pop();
  129. if (currentMenuItem.LocalNav) {
  130. return parentMenuItem;
  131. }
  132. parentMenuItem = currentMenuItem;
  133. }
  134. }
  135. }
  136. return null;
  137. }
  138. /// <summary>
  139. /// Determines if a menu item corresponds to a given route.
  140. /// </summary>
  141. /// <param name="itemValues">The menu item.</param>
  142. /// <param name="requestValues">The route data.</param>
  143. /// <returns>True if the menu item's action corresponds to the route data; false otherwise.</returns>
  144. protected static bool RouteMatches(RouteValueDictionary itemValues, RouteValueDictionary requestValues) {
  145. if (itemValues == null && requestValues == null) {
  146. return true;
  147. }
  148. if (itemValues == null || requestValues == null) {
  149. return false;
  150. }
  151. if (itemValues.Keys.Any(key => requestValues.ContainsKey(key) == false)) {
  152. return false;
  153. }
  154. return itemValues.Keys.All(key => string.Equals(Convert.ToString(itemValues[key]), Convert.ToString(requestValues[key]), StringComparison.OrdinalIgnoreCase));
  155. }
  156. /// <summary>
  157. /// Builds a menu item shape.
  158. /// </summary>
  159. /// <param name="shapeFactory">The shape factory.</param>
  160. /// <param name="parentShape">The parent shape.</param>
  161. /// <param name="menu">The menu shape.</param>
  162. /// <param name="menuItem">The menu item to build the shape for.</param>
  163. /// <returns>The menu item shape.</returns>
  164. protected dynamic BuildMenuItemShape(dynamic shapeFactory, dynamic parentShape, dynamic menu, MenuItem menuItem) {
  165. var menuItemShape = shapeFactory.MenuItem()
  166. .Text(menuItem.Text)
  167. .IdHint(menuItem.IdHint)
  168. .Href(menuItem.Href)
  169. .LinkToFirstChild(menuItem.LinkToFirstChild)
  170. .LocalNav(menuItem.LocalNav)
  171. .Selected(menuItem.Selected)
  172. .RouteValues(menuItem.RouteValues)
  173. .Item(menuItem)
  174. .Menu(menu)
  175. .Parent(parentShape);
  176. foreach (var className in menuItem.Classes)
  177. menuItemShape.Classes.Add(className);
  178. return menuItemShape;
  179. }
  180. /// <summary>
  181. /// Builds a local menu item shape.
  182. /// </summary>
  183. /// <param name="shapeFactory">The shape factory.</param>
  184. /// <param name="parentShape">The parent shape.</param>
  185. /// <param name="menu">The menu shape.</param>
  186. /// <param name="menuItem">The menu item to build the shape for.</param>
  187. /// <returns>The menu item shape.</returns>
  188. protected dynamic BuildLocalMenuItemShape(dynamic shapeFactory, dynamic parentShape, dynamic menu, MenuItem menuItem) {
  189. var menuItemShape = shapeFactory.LocalMenuItem()
  190. .Text(menuItem.Text)
  191. .IdHint(menuItem.IdHint)
  192. .Href(menuItem.Href)
  193. .LinkToFirstChild(menuItem.LinkToFirstChild)
  194. .LocalNav(menuItem.LocalNav)
  195. .Selected(menuItem.Selected)
  196. .RouteValues(menuItem.RouteValues)
  197. .Item(menuItem)
  198. .Menu(menu)
  199. .Parent(parentShape);
  200. foreach (var className in menuItem.Classes)
  201. menuItemShape.Classes.Add(className);
  202. return menuItemShape;
  203. }
  204. public void OnResultExecuted(ResultExecutedContext filterContext) { }
  205. }
  206. }