SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/NavigationMenu.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16. use Kohana;
  17.  
  18.  
  19. /**
  20.  * Renders a menu for the page node structure
  21.  *
  22.  * If you would like to edit this, it would be much wiser to sub-class it
  23.  * and override the various methods
  24.  */
  25. class NavigationMenu
  26. {
  27.  
  28. /**
  29.   * HTML to be returned prior to the first navigation item
  30.   */
  31. protected static $open_html = '<ul id="frankenmenu-list" class="-clearfix">';
  32.  
  33. /**
  34.   * HTML to be returned after the last navigation item
  35.   */
  36. protected static $close_html = '</ul>';
  37.  
  38. /**
  39.   * The view name to use for rendering sub-menu dropdowns.
  40.   * Note that if a sub-class overrides the {@see navigation::sub_menu} method
  41.   * then this parameter may get ignored.
  42.   */
  43. protected static $dropdown_view_name = 'sprout/navigation_dropdown';
  44.  
  45.  
  46. /**
  47.   * Return HTML for a mega menu.
  48.   *
  49.   * You shouldn't need to modify or extend this method;
  50.   * If you do then your use case is probably too bespoke for this class.
  51.   *
  52.   * @return string HTML
  53.   */
  54. public static function render()
  55. {
  56. $root = Navigation::getRootNode();
  57. $root->filterChildren(new TreenodeInMenuMatcher());
  58.  
  59. $selected_node = Navigation::getMatchedNode();
  60. if ($selected_node !== null) {
  61. $selected_ancestors = $selected_node->findAncestors();
  62. } else {
  63. $selected_ancestors = array();
  64. }
  65.  
  66. $limit = (int) Kohana::config('sprout.nav_limit');
  67. if ($limit == 0) {
  68. $limit = 9999;
  69. }
  70.  
  71. $out = static::$open_html . PHP_EOL;
  72.  
  73. if (Kohana::config('sprout.nav_home')) {
  74. --$limit;
  75. $out .= static::home() . PHP_EOL;
  76. }
  77.  
  78. $custom_dropdown = Kohana::config('sprout.nav_custom_dropdown');
  79.  
  80. $index = 0;
  81. foreach ($root->children as $node) {
  82. if ($limit-- == 0) break;
  83. ++$index;
  84.  
  85. // Fetch the various navigation groups before the rendering, so it can be determined if there
  86. // actually is a menu or not, so as to affect the classes on the LI
  87. $groups = array();
  88. $group_names = NavigationGroups::getAllNames($node['id']);
  89. foreach ($group_names as $position => $name) {
  90. $group_id = NavigationGroups::getId($node['id'], $position);
  91. $items = $root->findAllNodes(new TreenodeValueMatcher('menu_group', $group_id));
  92. if (count($items) > 0) {
  93. $groups[$name] = $items;
  94. }
  95. }
  96. $has_children = (count($groups) > 0);
  97.  
  98. $classes = static::determineClasses($node, 1, $index, $selected_node, $selected_ancestors, $has_children);
  99.  
  100. $out .= '<li class="' . Enc::html(implode(' ', $classes)) . '">';
  101. $out .= '<a href="' . Enc::html($node->getFriendlyUrl()) . '">' . Enc::html($node->getNavigationName()) . '</a>';
  102.  
  103. if (isset($custom_dropdown[$node['id']])) {
  104. $out .= PHP_EOL . PHP_EOL;
  105. $custom_dropdown[$node['id']], $node, $groups, $selected_node, $selected_ancestors
  106. ));
  107. $out .= PHP_EOL . PHP_EOL;
  108. } else if (count($groups)) {
  109. $out .= PHP_EOL . PHP_EOL;
  110. $out .= trim(static::subMenu($node, $groups, $selected_node, $selected_ancestors));
  111. $out .= PHP_EOL . PHP_EOL;
  112. }
  113.  
  114. $out .= '</li>' . PHP_EOL;
  115. }
  116.  
  117. $out .= static::$close_html;
  118.  
  119. $root->removeFilter();
  120.  
  121. return $out;
  122. }
  123.  
  124.  
  125. /**
  126.   * Return HTML for the home page nav item
  127.   * This would typically be a LI
  128.   *
  129.   * @return string HTML
  130.   */
  131. protected static function home() {
  132. $classes = 'menu-item menu-item-depth1 menu-home-page';
  133. if (Url::current() == 'home_page' || Url::current() == null) {
  134. $classes .= ' menu-current-item';
  135. }
  136.  
  137. $out = '<li class="' . $classes . '">';
  138. $out .= '<a href="' . Enc::html(Url::base()) . '">Home</a>';
  139. $out .= '</li>';
  140.  
  141. return $out;
  142. }
  143.  
  144.  
  145. /**
  146.   * Determine the classes for the LI for a navigation item
  147.   *
  148.   * @param Treenode $node The node which is being rendered
  149.   * @param int $depth The menu depth; 1 for top-level, 2 for a sub menu
  150.   * @param int $index The position in the menu; 1 for the first item, 2 for the second, etc
  151.   * @param Treenode $selected_node The page the user is currently looking at
  152.   * @param array $selected_ancestors All ancestors of the selected node ({@see Treenode::find_ancestors})
  153.   * @param bool $has_children Does the node have children
  154.   * @return array Zero or more class names
  155.   */
  156. public static function determineClasses(Treenode $node, $depth, $index, $selected_node, array $selected_ancestors, $has_children) {
  157. $classes = array('menu-item', "menu-item-depth{$depth}", "menu-item-depth{$depth}--item{$index}");
  158.  
  159. if ($has_children) {
  160. $classes[] = 'menu-item-has-children';
  161. }
  162.  
  163. if ($selected_node === $node) {
  164. $classes[] = 'menu-current-item';
  165. } else if (in_array($node, $selected_ancestors, true)) {
  166. $classes[] = 'menu-current-item-ancestor';
  167. }
  168.  
  169. return $classes;
  170. }
  171.  
  172.  
  173. /**
  174.   * Return HTML for a drop-down sub menu
  175.   *
  176.   * @param Treenode $parent_node The node which is being rendered
  177.   * @param array $groups The groups of items to render, in the format [name => array of Treenode, ...]
  178.   * @param Treenode $selected_node The page the user is currently looking at
  179.   * @param array $selected_ancestors All ancestors of the selected node ({@see Treenode::find_ancestors})
  180.   * @return string HTML
  181.   */
  182. protected static function subMenu(Treenode $parent_node, array $groups, $selected_node, array $selected_ancestors) {
  183. $view = new View(static::$dropdown_view_name);
  184. $view->parent_node = $parent_node;
  185. $view->groups = $groups;
  186. $view->selected_node = $selected_node;
  187. $view->selected_ancestors = $selected_ancestors;
  188. $view->extra = NavigationGroups::getExtras($parent_node['id']);
  189. return $view->render();
  190. }
  191.  
  192. }
  193.