diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/classes/ElggMenuBuilder.php | 249 | ||||
| -rw-r--r-- | engine/classes/ElggMenuItem.php | 329 | ||||
| -rw-r--r-- | engine/lib/navigation.php | 64 | ||||
| -rw-r--r-- | engine/lib/views.php | 37 | 
4 files changed, 679 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); +	} +} diff --git a/engine/classes/ElggMenuItem.php b/engine/classes/ElggMenuItem.php new file mode 100644 index 000000000..97dabe62a --- /dev/null +++ b/engine/classes/ElggMenuItem.php @@ -0,0 +1,329 @@ +<?php +/** + * Elgg Menu Item + * + * @package    Elgg.Core + * @subpackage Navigation + * + * @since 1.8.0 + */ +class ElggMenuItem { +	/** +	 * @var string Identifier of the menu +	 */ +	protected $name; + +	/** +	 * @var string The menu display string +	 */ +	protected $title; + +	/** +	 * @var string The menu url +	 */ +	protected $url; + +	/** +	 * @var array Page context array +	 */ +	protected $contexts = array('all'); + +	/** +	 * @var string Menu section identifier +	 */ +	protected $section = 'default'; + +	/** +	 * @var string Tooltip +	 */ +	protected $tooltip = ''; + +	/** +	 * @var bool Is this the currently selected menu item +	 */ +	protected $selected = false; + +	/** +	 * @var string Identifier of this item's parent +	 */ +	 protected $parent_name = ''; + +	 /** +	  * @var ElggMenuItem The parent object or null +	  */ +	 protected $parent = null; + +	 /** +	  * @var array Array of children objects or empty array +	  */ +	 protected $children = array(); + +	/** +	 * ElggMenuItem constructor +	 * +	 * @param string $name  Identifier of the menu item +	 * @param string $title Title of the menu item +	 * @param string $url   URL of the menu item +	 */ +	public function __construct($name, $title, $url) { +		$this->name = $name; +		$this->title = $title; +		$this->url = $url; +	} + +	/** +	 * ElggMenuItem factory method +	 * +	 * This static method creates an ElggMenuItem from an associative array. +	 * Required keys are name, title, and url. +	 * +	 * @param array $options Option array of key value pairs +	 * +	 * @return ElggMenuItem or NULL on error +	 */ +	public static function factory($options) { +		if (!isset($options['name']) || !isset($options['title']) || !isset($options['url'])) { +			return NULL; +		} + +		$item = new ElggMenuItem($options['name'], $options['title'], $options['url']); + +		// special catch in case someone uses context rather than contexts +		if (isset($options['context'])) { +			$options['contexts'] = $options['context']; +			unset($options['context']); +		} + +		foreach ($options as $key => $value) { +			$item->$key = $value; +		} + +		// make sure contexts is set correctly +		if (isset($options['contexts'])) { +			$item->setContext($options['contexts']); +		} + +		return $item; +	} + +	/** +	 * Get the identifier of the menu item +	 * +	 * @return string +	 */ +	public function getName() { +		return $this->name; +	} + +	/** +	 * Get the display title of the menu +	 * +	 * @return string +	 */ +	public function getTitle() { +		return $this->title; +	} + +	/** +	 * Get the URL of the menu item +	 * +	 * @return string +	 */ +	public function getURL() { +		return $this->url; +	} + +	/** +	 * Set the contexts that this menu item is available for +	 * +	 * @param array $contexts An array of context strings +	 * +	 * @return void +	 */ +	public function setContext($contexts) { +		if (is_string($contexts)) { +			$contexts = array($contexts); +		} +		$this->contexts = $contexts; +	} + +	/** +	 * Get an array of context strings +	 * +	 * @return array +	 */ +	public function getContext() { +		return $this->contexts; +	} + +	/** +	 * Should this menu item be used given the current context +	 * +	 * @param string $context A context string (default is empty string for +	 *                        current context stack. +	 * +	 * @return bool +	 */ +	public function inContext($context = '') { +		if ($context) { +			return in_array($context, $this->contexts); +		} + +		if (in_array('all', $this->contexts)) { +			return true; +		} + +		foreach ($this->contexts as $context) { +			if (elgg_in_context($context)) { +				return true; +			} +		} +		return false; +	} + +	/** +	 * Set the selected flag +	 * +	 * @param bool $state Selected state (default is true) +	 * +	 * @return void +	 */ +	public function setSelected($state = true) { +		$this->selected = $state; +	} + +	/** +	 * Get selected state +	 * +	 * @return bool +	 */ +	public function getSelected() { +		return $this->selected; +	} + +	/** +	 * Set the tool tip text +	 * +	 * @param string $text The text of the tool tip +	 * +	 * @return void +	 */ +	public function setTooltip($text) { +		$this->tooltip = $text; +	} + +	/** +	 * Get the tool tip text +	 * +	 * @return string +	 */ +	public function getTooltip() { +		return $this->tooltip; +	} + +	/** +	 * Set the section identifier +	 * +	 * @param string $section The identifier of the section +	 * +	 * @return void +	 */ +	public function setSection($section) { +		$this->section = $section; +	} + +	/** +	 * Get the section identifier +	 * +	 * @return string +	 */ +	public function getSection() { +		return $this->section; +	} + +	/** +	 * Set the parent identifier +	 * +	 * @param string $parent_name The identifier of the parent ElggMenuItem +	 * +	 * @return void +	 */ +	public function setParentName($parent_name) { +		$this->parent_name = $parent_name; +	} + +	/** +	 * Get the parent identifier +	 * +	 * @return string +	 */ +	public function getParentName() { +		return $this->parent_name; +	} + +	/** +	 * Set the parent menu item +	 * +	 * @param ElggMenuItem $parent +	 * +	 * @return void +	 */ +	public function setParent($parent) { +		$this->parent = $parent; +	} + +	/** +	 * Get the parent menu item +	 * +	 * @return ElggMenuItem or null +	 */ +	public function getParent() { +		return $this->parent; +	} + +	/** +	 * Add a child menu item +	 * +	 * @param ElggMenuItem $item +	 * +	 * @return void +	 */ +	public function addChild($item) { +		$this->children[] = $item; +	} + +	/** +	 * Get the children menu items +	 * +	 * @return array +	 */ +	public function getChildren() { +		return $this->children; +	} + +	/** +	 * Sort the children +	 * +	 * @param string $sort_function +	 * +	 * @return void +	 */ +	public function sortChildren($sort_function) { +		usort($this->children, $sort_function); +	} + +	/** +	 * Get the menu link +	 * +	 * @todo add styling +	 * +	 * @return string +	 */ +	public function getLink() { +		$vars = array( +			'href' => $this->url, +			'text' => $this->title +		); +		return elgg_view('output/url', $vars); +	} +} diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index 75c5958f4..7b8c32f02 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -8,6 +8,70 @@   */  /** + * Register an item for an Elgg menu + * + * @param string $menu_name The name of the menu: site, page, userhover, + *                          userprofile, groupprofile, or any custom menu + * @param mixed  $menu_item A ElggMenuItem object or an array of options in format: + *                          name        => STR  Menu item identifier (required) + *                          title       => STR  Menu item title (required) + *                          url         => STR  Menu item URL (required) + *                          contexts    => ARR  Page context strings + *                          section     => STR  Menu section identifier + *                          tooltip     => STR  Menu item tooltip + *                          selected    => BOOL Is this menu item currently selected + *                          parent_name => STR  Identifier of the parent menu item + * + *                          Custom options can be added as key value pairs. + * + * @return bool + * @since 1.8.0 + */ +function elgg_register_menu_item($menu_name, $menu_item) { +	global $CONFIG; + +	if (!isset($CONFIG->menus[$menu_name])) { +		$CONFIG->menus[$menu_name] = array(); +	} + +	if (is_array($menu_item)) { +		$menu_item = ElggMenuItem::factory($menu_item); +		if (!$menu_item) { +			return false; +		} +	} + +	$CONFIG->menus[$menu_name][] = $menu_item; +	return true; +} + +/** + * Remove an item from a menu + * + * @param string $menu_name The name of the menu + * @param string $item_name The unique identifier for this menu item + * + * @return bool + * @since 1.8.0 + */ +function elgg_unregister_menu_item($menu_name, $item_name) { +	global $CONFIG; + +	if (!isset($CONFIG->menus[$menu_name])) { +		return false; +	} + +	foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { +		if ($menu_object->name == $item_name) { +			unset($CONFIG->menus[$menu_name][$index]); +			return true; +		} +	} + +	return false; +} + +/**   * Deprecated by elgg_add_submenu_item()   *   * @see elgg_add_submenu_item() diff --git a/engine/lib/views.php b/engine/lib/views.php index ade5ff678..71922cb6c 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -644,6 +644,43 @@ function elgg_view_layout($layout_name, $vars = array()) {  }  /** + * Render a menu + * + * @param string $menu_name The name of the menu + * @param array $vars An associative array of display options for the menu. + *                    Options include: + *                    sort_by => string or php callback + *                       string options: 'name', 'title' (default), 'order' (registration order) + *                       php callback: a compare function for usort + * + * @return string + * @since 1.8.0 + */ +function elgg_view_menu($menu_name, array $vars = array()) { + +	$vars['name'] = $menu_name; + +	$sort_by = elgg_get_array_value('sort_by', $vars, 'title'); + +    // Give plugins a chance to add menu items just before creation. +	// This supports context sensitive menus (ex. user hover). +    elgg_trigger_plugin_hook('register', "menu:$menu_name", $vars, NULL); + +	$builder = new ElggMenuBuilder($menu_name); +	$vars['menu'] = $builder->getMenu($sort_by); +	$vars['selected_item'] = $builder->getSelected(); + +	// Let plugins modify the menu +    $vars['menu'] = elgg_trigger_plugin_hook('prepare', "menu:$menu_name", $vars, $vars['menu']); + +    if (elgg_view_exists("navigation/menu/$menu_name")) { +        return elgg_view("navigation/menu/$menu_name", $vars); +    } else { +        return elgg_view("navigation/menu/default", $vars); +    } +} + +/**   * Returns a string of a rendered entity.   *   * Entity views are either determined by setting the view property on the entity  | 
