aboutsummaryrefslogtreecommitdiff
path: root/engine/classes/ElggMenuBuilder.php
diff options
context:
space:
mode:
Diffstat (limited to 'engine/classes/ElggMenuBuilder.php')
-rw-r--r--engine/classes/ElggMenuBuilder.php291
1 files changed, 291 insertions, 0 deletions
diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php
new file mode 100644
index 000000000..b463143d8
--- /dev/null
+++ b/engine/classes/ElggMenuBuilder.php
@@ -0,0 +1,291 @@
+<?php
+/**
+ * Elgg Menu Builder
+ *
+ * @package Elgg.Core
+ * @subpackage Navigation
+ * @since 1.8.0
+ */
+class ElggMenuBuilder {
+
+ /**
+ * @var ElggMenuItem[]
+ */
+ protected $menu = array();
+
+ protected $selected = null;
+
+ /**
+ * ElggMenuBuilder constructor
+ *
+ * @param ElggMenuItem[] $menu Array of ElggMenuItem objects
+ */
+ public function __construct(array $menu) {
+ $this->menu = $menu;
+ }
+
+ /**
+ * Get a prepared menu array
+ *
+ * @param mixed $sort_by Method to sort the menu by. @see ElggMenuBuilder::sort()
+ * @return array
+ */
+ public function getMenu($sort_by = 'text') {
+
+ $this->selectFromContext();
+
+ $this->selected = $this->findSelected();
+
+ $this->setupSections();
+
+ $this->setupTrees();
+
+ $this->sort($sort_by);
+
+ return $this->menu;
+ }
+
+ /**
+ * Get the selected menu item
+ *
+ * @return ElggMenuItem
+ */
+ public function getSelected() {
+ return $this->selected;
+ }
+
+ /**
+ * Select menu items for the current context
+ *
+ * @return void
+ */
+ protected function selectFromContext() {
+ if (!isset($this->menu)) {
+ $this->menu = array();
+ return;
+ }
+
+ // get menu items for this context
+ $selected_menu = array();
+ foreach ($this->menu as $menu_item) {
+ if (!is_object($menu_item)) {
+ elgg_log("A non-object was passed to ElggMenuBuilder", "ERROR");
+ continue;
+ }
+ if ($menu_item->inContext()) {
+ $selected_menu[] = $menu_item;
+ }
+ }
+
+ $this->menu = $selected_menu;
+ }
+
+ /**
+ * Group the menu items into sections
+ *
+ * @return void
+ */
+ protected function setupSections() {
+ $sectioned_menu = array();
+ foreach ($this->menu as $menu_item) {
+ if (!isset($sectioned_menu[$menu_item->getSection()])) {
+ $sectioned_menu[$menu_item->getSection()] = array();
+ }
+ $sectioned_menu[$menu_item->getSection()][] = $menu_item;
+ }
+ $this->menu = $sectioned_menu;
+ }
+
+ /**
+ * Create trees for each menu section
+ *
+ * @internal The tree is doubly linked (parent and children links)
+ * @return void
+ */
+ protected function setupTrees() {
+ $menu_tree = array();
+
+ foreach ($this->menu as $key => $section) {
+ $parents = array();
+ $children = array();
+ // divide base nodes from children
+ foreach ($section as $menu_item) {
+ /* @var ElggMenuItem $menu_item */
+ $parent_name = $menu_item->getParentName();
+ if (!$parent_name) {
+ $parents[$menu_item->getName()] = $menu_item;
+ } else {
+ $children[] = $menu_item;
+ }
+ }
+
+ // attach children to parents
+ $iteration = 0;
+ $current_gen = $parents;
+ $next_gen = null;
+ while (count($children) && $iteration < 5) {
+ foreach ($children as $index => $menu_item) {
+ $parent_name = $menu_item->getParentName();
+ if (array_key_exists($parent_name, $current_gen)) {
+ $next_gen[$menu_item->getName()] = $menu_item;
+ if (!in_array($menu_item, $current_gen[$parent_name]->getData('children'))) {
+ $current_gen[$parent_name]->addChild($menu_item);
+ $menu_item->setParent($current_gen[$parent_name]);
+ }
+ unset($children[$index]);
+ }
+ }
+ $current_gen = $next_gen;
+ $iteration += 1;
+ }
+
+ // convert keys to indexes for first level of tree
+ $parents = array_values($parents);
+
+ $menu_tree[$key] = $parents;
+ }
+
+ $this->menu = $menu_tree;
+ }
+
+ /**
+ * Find the menu item that is currently selected
+ *
+ * @return ElggMenuItem
+ */
+ protected function findSelected() {
+
+ // do we have a selected menu item already
+ foreach ($this->menu as $menu_item) {
+ if ($menu_item->getSelected()) {
+ return $menu_item;
+ }
+ }
+
+ // scan looking for a selected item
+ foreach ($this->menu as $menu_item) {
+ if ($menu_item->getHref()) {
+ if (elgg_http_url_is_identical(current_page_url(), $menu_item->getHref())) {
+ $menu_item->setSelected(true);
+ return $menu_item;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sort the menu sections and trees
+ *
+ * @param mixed $sort_by Sort type as string or php callback
+ * @return void
+ */
+ protected function sort($sort_by) {
+
+ // sort sections
+ ksort($this->menu);
+
+ switch ($sort_by) {
+ case 'text':
+ $sort_callback = array('ElggMenuBuilder', 'compareByText');
+ break;
+ case 'name':
+ $sort_callback = array('ElggMenuBuilder', 'compareByName');
+ break;
+ case 'priority':
+ $sort_callback = array('ElggMenuBuilder', 'compareByWeight');
+ break;
+ case 'register':
+ // use registration order - usort breaks this
+ return;
+ break;
+ default:
+ if (is_callable($sort_by)) {
+ $sort_callback = $sort_by;
+ } else {
+ return;
+ }
+ break;
+ }
+
+ // sort each section
+ foreach ($this->menu as $index => $section) {
+ foreach ($section as $key => $node) {
+ $section[$key]->setData('original_order', $key);
+ }
+ usort($section, $sort_callback);
+ $this->menu[$index] = $section;
+
+ // depth first traversal of tree
+ foreach ($section as $root) {
+ $stack = array();
+ array_push($stack, $root);
+ while (!empty($stack)) {
+ $node = array_pop($stack);
+ /* @var ElggMenuItem $node */
+ $node->sortChildren($sort_callback);
+ $children = $node->getChildren();
+ if ($children) {
+ $stack = array_merge($stack, $children);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Compare two menu items by their display text
+ *
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
+ * @return bool
+ */
+ public static function compareByText($a, $b) {
+ $at = $a->getText();
+ $bt = $b->getText();
+
+ $result = strnatcmp($at, $bt);
+ if ($result === 0) {
+ return $a->getData('original_order') - $b->getData('original_order');
+ }
+ return $result;
+ }
+
+ /**
+ * Compare two menu items by their identifiers
+ *
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
+ * @return bool
+ */
+ public static function compareByName($a, $b) {
+ $an = $a->getName();
+ $bn = $b->getName();
+
+ $result = strcmp($an, $bn);
+ if ($result === 0) {
+ return $a->getData('original_order') - $b->getData('original_order');
+ }
+ return $result;
+ }
+
+ /**
+ * Compare two menu items by their priority
+ *
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
+ * @return bool
+ *
+ * @todo change name to compareByPriority
+ */
+ public static function compareByWeight($a, $b) {
+ $aw = $a->getWeight();
+ $bw = $b->getWeight();
+
+ if ($aw == $bw) {
+ return $a->getData('original_order') - $b->getData('original_order');
+ }
+ return $aw - $bw;
+ }
+}