diff options
Diffstat (limited to 'engine/classes/ElggMenuBuilder.php')
| -rw-r--r-- | engine/classes/ElggMenuBuilder.php | 249 | 
1 files changed, 249 insertions, 0 deletions
diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php new file mode 100644 index 000000000..b57ea6e18 --- /dev/null +++ b/engine/classes/ElggMenuBuilder.php @@ -0,0 +1,249 @@ +<?php +/** + * Elgg Menu Builder + * + * @package    Elgg.Core + * @subpackage Navigation + * + * @since 1.8.0 + */ +class ElggMenuBuilder { + +	protected $menu = array(); + +	protected $selected = null; + +	/** +	 * ElggMenuBuilder constructor +	 * +	 * @param string $name  Identifier of the menu +	 */ +	public function __construct($name) { +		global $CONFIG; + +		$this->menu = $CONFIG->menus[$name]; +	} + +	/** +	 * Get a prepared menu array +	 * +	 * @param mixed $sort_by +	 * @return array +	 */ +	public function getMenu($sort_by) { + +		$this->selectFromContext(); + +		$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 ($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) { +				$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; +			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; +						$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->getURL()) { +				if (elgg_http_url_is_identical(full_url(), $menu_item->getURL())) { +					$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 'title': +				$sort_callback = array('ElggMenuBuilder', 'compareByTitle'); +				break; +			case 'name'; +				$sort_callback = array('ElggMenuBuilder', 'compareByName'); +				break; +			case 'order': +				// use registration order +				return; +				break; +			default: +				if (is_callable($sort_by)) { +					$sort_callback = $sort_by; +				} else { +					return; +				} +				break; +		} + +		// sort each section +		foreach ($this->menu as $index => $section) { +			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); +					$node->sortChildren($sort_callback); +					$children = $node->getChildren(); +					if ($children) { +						$stack = array_merge($stack, $children); +					} +					$p = count($stack); +				} +			} +		} +	} + +	/** +	 * Compare two menu items by their titles +	 * +	 * @param ElggMenuItem $a +	 * @param ElggMenuItem $b +	 * @return bool +	 */ +	public static function compareByTitle($a, $b) { +		$a = $a->getTitle(); +		$b = $b->getTitle(); + +		return strnatcmp($a, $b); +	} + +	/** +	 * Compare two menu items by their identifiers +	 * +	 * @param ElggMenuItem $a +	 * @param ElggMenuItem $b +	 * @return bool +	 */ +	public static function compareByName($a, $b) { +		$a = $a->getName(); +		$b = $b->getName(); + +		return strcmp($a, $b); +	} +}  | 
