/engine/classes/ElggMenuBuilder.php

https://github.com/wangaiying/elgg4ysu · PHP · 268 lines · 151 code · 36 blank · 81 comment · 17 complexity · 92e0e9e74de8a4f25ae35f25310a60be MD5 · raw file

  1. <?php
  2. /**
  3. * Elgg Menu Builder
  4. *
  5. * @package Elgg.Core
  6. * @subpackage Navigation
  7. *
  8. * @since 1.8.0
  9. */
  10. class ElggMenuBuilder {
  11. protected $menu = array();
  12. protected $selected = null;
  13. /**
  14. * ElggMenuBuilder constructor
  15. *
  16. * @param string $name Identifier of the menu
  17. */
  18. public function __construct($menu) {
  19. $this->menu = $menu;
  20. }
  21. /**
  22. * Get a prepared menu array
  23. *
  24. * @param mixed $sort_by
  25. * @return array
  26. */
  27. public function getMenu($sort_by = 'text') {
  28. $this->selectFromContext();
  29. $this->selected = $this->findSelected();
  30. $this->setupSections();
  31. $this->setupTrees();
  32. $this->sort($sort_by);
  33. return $this->menu;
  34. }
  35. /**
  36. * Get the selected menu item
  37. *
  38. * @return ElggMenuItem
  39. */
  40. public function getSelected() {
  41. return $this->selected;
  42. }
  43. /**
  44. * Select menu items for the current context
  45. *
  46. * @return void
  47. */
  48. protected function selectFromContext() {
  49. if (!isset($this->menu)) {
  50. $this->menu = array();
  51. return;
  52. }
  53. // get menu items for this context
  54. $selected_menu = array();
  55. foreach ($this->menu as $menu_item) {
  56. if (!is_object($menu_item)) {
  57. elgg_log("A non-object was passed to ElggMenuBuilder", "ERROR");
  58. continue;
  59. }
  60. if ($menu_item->inContext()) {
  61. $selected_menu[] = $menu_item;
  62. }
  63. }
  64. $this->menu = $selected_menu;
  65. }
  66. /**
  67. * Group the menu items into sections
  68. * @return void
  69. */
  70. protected function setupSections() {
  71. $sectioned_menu = array();
  72. foreach ($this->menu as $menu_item) {
  73. if (!isset($sectioned_menu[$menu_item->getSection()])) {
  74. $sectioned_menu[$menu_item->getSection()] = array();
  75. }
  76. $sectioned_menu[$menu_item->getSection()][] = $menu_item;
  77. }
  78. $this->menu = $sectioned_menu;
  79. }
  80. /**
  81. * Create trees for each menu section
  82. *
  83. * @internal The tree is doubly linked (parent and children links)
  84. * @return void
  85. */
  86. protected function setupTrees() {
  87. $menu_tree = array();
  88. foreach ($this->menu as $key => $section) {
  89. $parents = array();
  90. $children = array();
  91. // divide base nodes from children
  92. foreach ($section as $menu_item) {
  93. $parent_name = $menu_item->getParentName();
  94. if (!$parent_name) {
  95. $parents[$menu_item->getName()] = $menu_item;
  96. } else {
  97. $children[] = $menu_item;
  98. }
  99. }
  100. // attach children to parents
  101. $iteration = 0;
  102. $current_gen = $parents;
  103. while (count($children) && $iteration < 5) {
  104. foreach ($children as $index => $menu_item) {
  105. $parent_name = $menu_item->getParentName();
  106. if (array_key_exists($parent_name, $current_gen)) {
  107. $next_gen[$menu_item->getName()] = $menu_item;
  108. $current_gen[$parent_name]->addChild($menu_item);
  109. $menu_item->setParent($current_gen[$parent_name]);
  110. unset($children[$index]);
  111. }
  112. }
  113. $current_gen = $next_gen;
  114. $iteration += 1;
  115. }
  116. // convert keys to indexes for first level of tree
  117. $parents = array_values($parents);
  118. $menu_tree[$key] = $parents;
  119. }
  120. $this->menu = $menu_tree;
  121. }
  122. /**
  123. * Find the menu item that is currently selected
  124. *
  125. * @return ElggMenuItem
  126. */
  127. protected function findSelected() {
  128. // do we have a selected menu item already
  129. foreach ($this->menu as $menu_item) {
  130. if ($menu_item->getSelected()) {
  131. return $menu_item;
  132. }
  133. }
  134. // scan looking for a selected item
  135. foreach ($this->menu as $menu_item) {
  136. if ($menu_item->getHref()) {
  137. if (elgg_http_url_is_identical(full_url(), $menu_item->getHref())) {
  138. $menu_item->setSelected(true);
  139. return $menu_item;
  140. }
  141. }
  142. }
  143. return null;
  144. }
  145. /**
  146. * Sort the menu sections and trees
  147. *
  148. * @param mixed $sort_by Sort type as string or php callback
  149. * @return void
  150. */
  151. protected function sort($sort_by) {
  152. // sort sections
  153. ksort($this->menu);
  154. switch ($sort_by) {
  155. case 'text':
  156. $sort_callback = array('ElggMenuBuilder', 'compareByText');
  157. break;
  158. case 'name':
  159. $sort_callback = array('ElggMenuBuilder', 'compareByName');
  160. break;
  161. case 'priority':
  162. $sort_callback = array('ElggMenuBuilder', 'compareByWeight');
  163. break;
  164. case 'register':
  165. // use registration order - usort breaks this
  166. return;
  167. break;
  168. default:
  169. if (is_callable($sort_by)) {
  170. $sort_callback = $sort_by;
  171. } else {
  172. return;
  173. }
  174. break;
  175. }
  176. // sort each section
  177. foreach ($this->menu as $index => $section) {
  178. usort($section, $sort_callback);
  179. $this->menu[$index] = $section;
  180. // depth first traversal of tree
  181. foreach ($section as $root) {
  182. $stack = array();
  183. array_push($stack, $root);
  184. while (!empty($stack)) {
  185. $node = array_pop($stack);
  186. $node->sortChildren($sort_callback);
  187. $children = $node->getChildren();
  188. if ($children) {
  189. $stack = array_merge($stack, $children);
  190. }
  191. $p = count($stack);
  192. }
  193. }
  194. }
  195. }
  196. /**
  197. * Compare two menu items by their display text
  198. *
  199. * @param ElggMenuItem $a
  200. * @param ElggMenuItem $b
  201. * @return bool
  202. */
  203. public static function compareByText($a, $b) {
  204. $a = $a->getText();
  205. $b = $b->getText();
  206. return strnatcmp($a, $b);
  207. }
  208. /**
  209. * Compare two menu items by their identifiers
  210. *
  211. * @param ElggMenuItem $a
  212. * @param ElggMenuItem $b
  213. * @return bool
  214. */
  215. public static function compareByName($a, $b) {
  216. $a = $a->getName();
  217. $b = $b->getName();
  218. return strcmp($a, $b);
  219. }
  220. /**
  221. * Compare two menu items by their priority
  222. *
  223. * @param ElggMenuItem $a
  224. * @param ElggMenuItem $b
  225. * @return bool
  226. */
  227. public static function compareByWeight($a, $b) {
  228. $a = $a->getWeight();
  229. $b = $b->getWeight();
  230. return $a > $b;
  231. }
  232. }