aboutsummaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/lib/elgglib.php477
-rw-r--r--engine/tests/ui/submenu.php97
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);