diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/lib/elgglib.php | 477 | ||||
| -rw-r--r-- | engine/tests/ui/submenu.php | 97 | 
2 files changed, 476 insertions, 98 deletions
| diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 1f51d7cb2..16ace68be 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -249,10 +249,10 @@ function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $vie  		$viewtype = elgg_get_viewtype();  	} -	// Viewtypes can only be alphanumeric  +	// Viewtypes can only be alphanumeric  	if (preg_match('[\W]', $viewtype)) { -		return '';  -	}  +		return ''; +	}  	// Set up any extensions to the requested view  	if (isset($CONFIG->views->extensions[$view])) { @@ -757,7 +757,7 @@ function elgg_view_annotation(ElggAnnotation $annotation, $bypass = true, $debug  function elgg_view_entity_list($entities, $count, $offset, $limit, $fullview = true, $viewtypetoggle = true, $pagination = true) {  	$count = (int) $count;  	$limit = (int) $limit; -	 +  	// do not require views to explicitly pass in the offset  	if (!$offset = (int) $offset) {  		$offset = sanitise_int(get_input('offset', 0)); @@ -900,131 +900,317 @@ function elgg_view_title($title, $submenu = false) {  }  /** - * Adds an item to the submenu + * Deprecated by elgg_add_submenu_item() + * + * @see elgg_add_submenu_item() + * @deprecated 1.8 + */ +function add_submenu_item($label, $link, $group = 'default', $onclick = false, $selected = NULL) { +	elgg_deprecated_notice('add_submenu_item was deprecated by elgg_add_submenu_item', 1.8); + +	$item = array( +		'text' => $label, +		'url' => $link, +		'selected' => $selected +	); + +	if (!$group) { +		$group = 'default'; +	} + +	if ($onclick) { +		$js = "onclick=\"javascript:return confirm('". elgg_echo('deleteconfirm') . "')\""; +		$item['vars'] = array('js' => $js); +	} +	// submenu items were added in the page setup hook usually by checking +	// the context.  We'll pass in the current context here, which will +	// emulate that effect. +	// if context == 'main' (default) it probably means they always wanted +	// the menu item to show up everywhere. +	$context = get_context(); + +	if ($context == 'main') { +		$context = 'all'; +	} +	return elgg_add_submenu_item($item, $context, $group); +} + +/** + * Add an entry to the submenu. + * + * @param array $item The item as array( + * 	'title' => 'Text to display', + * 	'url' => 'URL of the link', + * 	'id' => 'entry_unique_id' //used by children items to identify parents + * 	'parent_id' => 'id_of_parent', + * 	'selected' => BOOL // Is this item selected? (If NULL or unset will attempt to guess) + * 	'vars' => array() // Array of vars to pass to the navigation/submenu_item view + * )   * - * @param string $label The human-readable label - * @param string $link The URL of the submenu item - * @param boolean $onclick Used to provide a JS popup to confirm delete - * @param mixed $selected BOOL to force on/off, NULL to allow auto selection + * @param string $context Context in which to display this menu item.  'all' will make it show up all the time. Use sparingly. + * @param string $group Group for the item. Each submenu group has its own <ul> + * @return BOOL + * @since 1.8   */ -function add_submenu_item($label, $link, $group = 'a', $onclick = false, $selected = NULL) { +function elgg_add_submenu_item(array $item, $context = 'all', $group = 'default') {  	global $CONFIG; -	if (!isset($CONFIG->submenu)) { -		$CONFIG->submenu = array(); +	if (!isset($CONFIG->submenu_items)) { +		$CONFIG->submenu_items = array();  	} -	if (!isset($CONFIG->submenu[$group])) { -		$CONFIG->submenu[$group] = array(); + +	if (!isset($CONFIG->submenu_items[$context])) { +		$CONFIG->submenu_items[$context] = array(); +	} + +	if (!isset($CONFIG->submenu_items[$context][$group])) { +		$CONFIG->submenu_items[$context][$group] = array(); +	} + +	if (!isset($item['text'])) { +		return FALSE; +	} + +	// we use persistent object properties in the submenu +	// setup function, so normalize the array to an object. +	// we pass it in as an array because this would be the only +	// place in elgg that we ask for an object like this. +	// consistency ftw. +	$item_obj = new StdClass(); + +	foreach ($item as $k => $v) { +		switch ($k) { +			case 'parent_id': +			case 'id': +				// make sure '' and false make sense +				$v = (empty($v)) ? NULL : $v; + +			default: +				$item_obj->$k = $v; +				break; +		}  	} -	$item = new stdClass; -	$item->value = $link; -	$item->name = $label; -	$item->onclick = $onclick; -	$item->selected = $selected; -	$CONFIG->submenu[$group][] = $item; +	$CONFIG->submenu_items[$context][$group][] = $item_obj; + +	return TRUE;  }  /** - * Gets a formatted list of submenu items + * Properly nest all submenu entries for contexts $context and 'all'   * - * @params bool preselected Selected menu item - * @params bool preselectedgroup Selected menu item group - * @return string List of items + * @param string $context + * @param bool $sort Sort the menu items alphabetically + * @since 1.8   */ -function get_submenu() { -	$submenu_total = ""; +function elgg_prepare_submenu($context = 'main', $sort = TRUE) {  	global $CONFIG; -	if (isset($CONFIG->submenu) && $submenu_register = $CONFIG->submenu) { -		ksort($submenu_register); -		$selected_key = NULL; -		$selected_group = NULL; - -		foreach($submenu_register as $groupname => $submenu_register_group) { -			$submenu = ""; - -			foreach($submenu_register_group as $key => $item) { -				$selected = false; -				// figure out the selected item if required -				// if null, try to figure out what should be selected. -				// warning: Fuzzy logic. -				if (!$selected_key && !$selected_group) { -					if ($item->selected === NULL) { -						$uri_info = parse_url($_SERVER['REQUEST_URI']); -						$item_info = parse_url($item->value); - -						// don't want to mangle already encoded queries but want to -						// make sure we're comparing encoded to encoded. -						// for the record, queries *should* be encoded -						$uri_params = array(); -						$item_params = array(); -						if (isset($uri_info['query'])) { -							$uri_info['query'] = html_entity_decode($uri_info['query']); -							$uri_params = elgg_parse_str($uri_info['query']); -						} -						if (isset($item_info['query'])) { -							$item_info['query'] = html_entity_decode($item_info['query']); -							$item_params = elgg_parse_str($item_info['query']); +	if (!isset($CONFIG->submenu_items) || !($CONFIG->submenu_items)) { +		return FALSE; +	} + +	$groups = array(); + +	if (isset($CONFIG->submenu_items['all'])) { +		$groups = $CONFIG->submenu_items['all']; +	} + +	if (isset($CONFIG->submenu_items[$context])) { +		$groups = array_merge_recursive($groups, $CONFIG->submenu_items[$context]); +	} + +	if (!$groups) { +		return FALSE; +	} + +	foreach ($groups as $group => $items) { +		if ($sort) { +			usort($items, 'elgg_submenu_item_cmp'); +		} + +		$parsed_menu = array(); +		// determin which children need to go in this item. +		foreach ($items as $i => $item) { +			// can only support children if there's an id +			if (isset($item->id)) { +				foreach ($items as $child_i => $child_item) { +					// don't check ourselves or used children. +					if ($child_i == $i || $child_item->used == TRUE) { +						continue; +					} + +					if (isset($child_item->parent_id) && $child_item->parent_id == $item->id) { +						if (!isset($item->children)) { +							$item->children = array();  						} +						$item->children[] = $child_item; +						$child_item->parent = $item; +						// don't unset because we still need to check this item for children +						$child_item->used = TRUE; +					} +				} -						$uri_info['path'] = trim($uri_info['path'], '/'); -						$item_info['path'] = trim($item_info['path'], '/'); - -						// only if we're on the same path -						// can't check server because sometimes it's not set in REQUEST_URI -						if ($uri_info['path'] == $item_info['path']) { - -							// if no query terms, we have a match -							if (!isset($uri_info['query']) && !isset($item_info['query'])) { -								$selected_key = $key; -								$selected_group = $groupname; -								$selected = TRUE; -							} else { -								if ($uri_info['query'] == $item_info['query']) { -									$selected_key = $key; -									$selected_group = $groupname; -									$selected = TRUE; -								} elseif (!count(array_diff($uri_params, $item_params))) { -									$selected_key = $key; -									$selected_group = $groupname; -									$selected = TRUE; -								} -							} +				// if the parent doesn't have a url, make it the first child item. +				if (isset($item->children) && $item->children && !$item->url) { +					$child = $item->children[0]; +					while ($child && !isset($child->url)) { +						if (isset($child->children) && isset($child->children[0])) { +							$child = $child->children[0]; +						} else { +							$child = NULL;  						} -					// if TRUE or FALSE, set selected to this item. -					// Group doesn't seem to have anything to do with selected? +					} + +					if ($child && isset($child->url)) { +						$item->url = $child->url;  					} else { -						$selected = $item->selected; -						$selected_key = $key; -						$selected_group = $groupname; +						// @todo There are no URLs anywhere in this tree. +						$item->url = $CONFIG->url;  					}  				} +			} + +			// only add top-level elements to the menu. +			// the rest are children. +			if (!isset($item->parent_id)) { +				$parsed_menu[] = $item; +			} +		} + +		$CONFIG->submenu[$context][$group] = $parsed_menu; +	} +	return TRUE; +} + +/** + * Helper function used to sort submenu items by their display text. + * + * @param object $a + * @param object $b + * @since 1.8 + */ +function elgg_submenu_item_cmp($a, $b) { +	$a = $a->text; +	$b = $b->text; + +	return strnatcmp($a, $b); +} + +/** + * Use elgg_get_submenu(). + * + * @see elgg_get_submenu() + * @deprecated 1.8 + */ +function get_submenu() { +	elgg_deprecated_notice("get_submenu() has been deprecated by elgg_get_submenu()", 1.8); +	return elgg_get_submenu(); +} + +/** + * Return the HTML for a sidemenu. + * + * @param string $context The context of the submenu (defaults to main) + * @param BOOL $sort Sort by display name? + * @return string Formatted HTML. + * @since 1.8 + */ +function elgg_get_submenu($context = NULL, $sort = FALSE) { +	global $CONFIG; -				$submenu .= elgg_view('canvas_header/submenu_template', array( -						'href' => $item->value, -						'label' => $item->name, -						'onclick' => $item->onclick, -						'selected' => $selected, -					)); +	if (!$context) { +		$context = get_context(); +	} + +	if (!elgg_prepare_submenu($context, $sort)) { +		return ''; +	} + +	$groups = $CONFIG->submenu[$context]; +	$submenu_html = ''; + +	// the guessed selected item. +	$auto_selected = NULL; + +	foreach ($groups as $group => $items) { +		// how far down we are in children arrays +		$depth = 0; +		// push and pop parent items +		$temp_items = array(); + +		while ($item = current($items)) { +			$t = ''; +			// ignore parents created by a child but parent never defined properly +			if (!isset($item->text) || !isset($item->url) || !($item->text) || !($item->url)) { +				next($items); +				continue; +			} +			// try to guess if this should be selected if they don't specify +			if ($item->selected === NULL && isset($item->url)) { +				$item->selected = elgg_http_url_is_identical($_SERVER['REQUEST_URI'], $item->url);  			} -			$submenu_total .= elgg_view('canvas_header/submenu_group', array( -					'submenu' => $submenu, -					'group_name' => $groupname -				)); +			// traverse up the parent tree if matached to mark all parents as selected/expanded. +			if ($item->selected && isset($item->parent)) { +				$parent = $item->parent; +				while ($parent) { +					$parent->selected = TRUE; +					if (isset($parent->parent)) { +						$parent = $parent->parent; +					} else { +						$parent = NULL; +					} +				} +			} +			$item->depth = $depth; +// +//			for ($i=0; $i<$depth; $i++) { +//				$t .= " - "; +//			} +// +//			var_dump("$t{$item->text} -> {$item->url} {$item->selected}"); + + +			// get the next item +			if (isset($item->children) && $item->children) { +				$depth++; +				array_push($temp_items, $items); +				$items = $item->children; +			} elseif ($depth > 0) { +				// check if there are more children elements in the current items +				// pop back up to the parent(s) if not +				if ($item = next($items)) { +					continue; +				} else { +					while($depth > 0) { +						$depth--; +						$items = array_pop($temp_items); +						if ($item = next($items)) { +							break; +						} +					} +				} +			} else { +				next($items); +			}  		} + +		$submenu_html .= elgg_view('navigation/submenu_group', array('group' => $group, 'items' => $items));  	} -	return $submenu_total; +	// include the JS for the expand menus too +	return elgg_view('navigation/submenu_js') . $submenu_html;  } +  /**   * Automatically views likes and a like input relating to the given entity   *   * @param ElggEntity $entity The entity to like   * @return string|false The HTML (etc) for the likes, or false on failure + * @since 1.8   */  function elgg_view_likes($entity){  	if (!($entity instanceof ElggEntity)) { @@ -1038,11 +1224,13 @@ function elgg_view_likes($entity){  		return $likes;  	}  } +  /**   * Count the number of likes attached to an entity   *   * @param ElggEntity $entity   * @return int Number of likes + * @since 1.8   */  function elgg_count_likes($entity) {  	if ($likeno = trigger_plugin_hook('likes:count', $entity->getType(), @@ -2402,10 +2590,10 @@ function full_url() {  	$protocol = substr(strtolower($_SERVER["SERVER_PROTOCOL"]), 0, strpos(strtolower($_SERVER["SERVER_PROTOCOL"]), "/")) . $s;  	$port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]); -	$quotes = array('\'', '"');  -	$encoded = array('%27', '%22');  +	$quotes = array('\'', '"'); +	$encoded = array('%27', '%22'); -	return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port . str_replace($quotes, $encoded, $_SERVER['REQUEST_URI']);  +	return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port . str_replace($quotes, $encoded, $_SERVER['REQUEST_URI']);  }  /** @@ -2966,6 +3154,7 @@ function elgg_api_test($hook, $type, $value, $params) {  /**   * Sorts out the featured URLs and the "more" dropdown   * @return array ('featured_urls' and 'more') + * @since 1.8   */  function elgg_get_nav_items() {  	$menu_items = get_register('menu'); @@ -3006,6 +3195,7 @@ function elgg_get_nav_items() {  /**   * Hook that registers the custom menu items. + * @since 1.8   */  function add_custom_menu_items() {  	if ($custom_items = get_config('menu_items_custom_items')) { @@ -3016,6 +3206,97 @@ function add_custom_menu_items() {  }  /** + * Test two URLs to see if they are functionally identical. + * + * @param string $url1 + * @param string $url2 + * @param array $ignore_params - GET params to ignore in the comparison + * @return BOOL + * @since 1.8 + */ +function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) { +	global $CONFIG; + +	// if the server portion is missing but it starts with / then add the url in. +	if (elgg_substr($url1, 0, 1) == '/') { +		$url1 = $CONFIG->url . ltrim($url1, '/'); +	} + +	if (elgg_substr($url1, 0, 1) == '/') { +		$url2 = $CONFIG->url . ltrim($url2, '/'); +	} + +	// @todo - should probably do something with relative URLs + +	if ($url1 == $url2) { +		return TRUE; +	} + +	$url1_info = parse_url($url1); +	$url2_info = parse_url($url2); + +	$url1_info['path'] = trim($url1_info['path'], '/'); +	$url2_info['path'] = trim($url2_info['path'], '/'); + +	// compare basic bits +	$parts = array('scheme', 'host', 'path'); + +	foreach ($parts as $part) { +		if ((isset($url1_info[$part]) && isset($url2_info[$part])) && $url1_info[$part] != $url2_info[$part]) { +			return FALSE; +		} elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) { +			return FALSE; +		} elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) { +			return FALSE; +		} +	} + +	// quick compare of get params +	if (isset($url1_info['query']) && isset($url2_info['query']) && $url1_info['query'] == $url2_info['query']) { +		return TRUE; +	} + +	// compare get params that might be out of order +	$url1_params = array(); +	$url2_params = array(); + +	if (isset($url1_info['query'])) { +		if ($url1_info['query'] = html_entity_decode($url1_info['query'])) { +			$url1_params = elgg_parse_str($url1_info['query']); +		} +	} + +	if (isset($url2_info['query'])) { +		if ($url2_info['query'] = html_entity_decode($url2_info['query'])) { +			$url2_params = elgg_parse_str($url2_info['query']); +		} +	} + +	// drop ignored params +	foreach ($ignore_params as $param) { +		if (isset($url1_params[$param])) { +			unset($url1_params[$param]); +		} +		if (isset($url2_params[$param])) { +			unset($url2_params[$param]); +		} +	} + +	// array_diff_assoc only returns the items in arr1 that aren't in arrN +	// but not the items that ARE in arrN but NOT in arr1 +	// if arr1 is an empty array, this function will return 0 no matter what. +	// since we only care if they're different and not how different, +	// add the results together to get a non-zero (ie, different) result +	$diff_count = count(array_diff_assoc($url1_params, $url2_params)); +	$diff_count += count(array_diff_assoc($url2_params, $url1_params)); +	if ($diff_count > 0) { +		return FALSE; +	} + +	return TRUE; +} + +/**   * Some useful constant definitions   */  define('ACCESS_DEFAULT', -1); diff --git a/engine/tests/ui/submenu.php b/engine/tests/ui/submenu.php new file mode 100644 index 000000000..d7a156417 --- /dev/null +++ b/engine/tests/ui/submenu.php @@ -0,0 +1,97 @@ +<?php +/** + * 1.8 submenu test. + * + * Submenu needs to be able to support being added out of order. + * Children can be added before parents. + * 	Children of parents never defined are never shown. + * + * Test against: + * 	different contexts + * 	different groups + * 	old add_submenu_item() wrapper. + * + */ + +require_once('../../start.php'); + +$url = "{$CONFIG->url}engine/tests/ui/submenu.php"; + +$items = array( +	array( +		'text' => 'Upper level 1', +		'url' => "$url?upper_level_1", +		'id' => 'ul1' +	), +		array( +			'text' => 'CD (No link)', +			'parent_id' => 'cup', +			'id' => 'cd', +		), +			array( +				'text' => 'Sub CD', +				'url' => "$url?sub_cd", +				'parent_id' => 'cd' +			), +	array( +		'text' => 'Cup', +		'url' => "$url?cup", +		'id' => 'cup' +	), +		array( +			'text' => 'Phone', +			'url' => "$url?phone", +			'id' => 'phone', +			'parent_id' => 'cup' +		), +			array( +				'text' => 'Wallet', +				'url' => "$url?wallet", +				'id' => 'wallet', +				'parent_id' => 'phone' +			), +	array( +		'text' => 'Upper level', +		'url' => "$url?upper_level", +		'id' => 'ul' +	), +		array( +			'text' => 'Sub Upper level', +			'url' => "$url?sub_upper_level", +			'parent_id' => 'ul' +		), +	array( +		'text' => 'Root', +		'url' => $url, +	), + +	array( +		'text' => 'I am an orphan', +		'url' => 'http://google.com', +		'parent_id' => 'missing_parent' +	), + +	array( +		'text' => 'JS Test', +		'url' => 'http://elgg.org', +		'vars' => array('js' => 'onclick="alert(\'Link to \' + $(this).attr(\'href\') + \'!\'); return false;"') +	) +); + +foreach ($items as $item) { +	elgg_add_submenu_item($item, 'main'); +} + +add_submenu_item('Old Onclick Test', 'http://elgg.com', NULL, TRUE); +add_submenu_item('Old Selected Test', 'http://elgg.com', NULL, '', TRUE); + + +elgg_add_submenu_item(array('text' => 'Not Main Test', 'url' => "$url?not_main_test"), 'not_main', 'new_menu'); +elgg_add_submenu_item(array('text' => 'Not Main C Test', 'url' => "$url?not_main_c_test"), 'not_main', 'new_menu'); + +elgg_add_submenu_item(array('text' => 'All test', 'url' => "$url?all"), 'all'); + +//set_context('not_main'); + +$body = elgg_view_layout('one_column_with_sidebar', 'Look right.'); +page_draw('Submenu Test', $body); | 
