diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/bootstrap.inc | 4 | ||||
-rw-r--r-- | includes/common.inc | 19 | ||||
-rw-r--r-- | includes/locale.inc | 1 | ||||
-rw-r--r-- | includes/menu.inc | 1390 | ||||
-rw-r--r-- | includes/module.inc | 2 | ||||
-rw-r--r-- | includes/path.inc | 20 |
6 files changed, 324 insertions, 1112 deletions
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 995b2f05f..308dafc47 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -550,7 +550,7 @@ function drupal_page_cache_header($cache) { * Define the critical hooks that force modules to always be loaded. */ function bootstrap_hooks() { - return array('init', 'exit'); + return array('boot', 'exit'); } /** @@ -856,7 +856,7 @@ function _drupal_cache_init($phase) { } elseif (variable_get('cache', CACHE_DISABLED) == CACHE_NORMAL) { require_once './includes/module.inc'; - bootstrap_invoke_all('init'); + bootstrap_invoke_all('boot'); drupal_page_cache_header($cache); bootstrap_invoke_all('exit'); exit(); diff --git a/includes/common.inc b/includes/common.inc index 8827a732b..8811a36d8 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2209,3 +2209,22 @@ function element_child($key) { function element_children($element) { return array_filter(array_keys((array) $element), 'element_child'); } + +/** + * Generate vancode. + * + * Consists of a leading character indicating length, followed by N digits + * with a numerical value in base 36. Vancodes can be sorted as strings + * without messing up numerical order. + * + * It goes: + * 00, 01, 02, ..., 0y, 0z, + * 110, 111, ... , 1zy, 1zz, + * 2100, 2101, ..., 2zzy, 2zzz, + * 31000, 31001, ... + */ +function int2vancode($i = 0) { + $num = base_convert((int)$i, 10, 36); + $length = strlen($num); + return chr($length + ord('0') - 1) . $num; +} diff --git a/includes/locale.inc b/includes/locale.inc index 376dee5f1..02a4eed68 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -119,7 +119,6 @@ function _locale_admin_manage_screen_submit($form_id, $form_values) { drupal_set_message(t('Configuration saved.')); // Changing the locale settings impacts the interface: - cache_clear_all('*', 'cache_menu', TRUE); cache_clear_all('*', 'cache_page', TRUE); return 'admin/settings/locale/language/overview'; diff --git a/includes/menu.inc b/includes/menu.inc index 3ff61171e..8a5ac6d8c 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -172,327 +172,220 @@ define('MENU_SITE_OFFLINE', 4); */ /** - * Return the menu data structure. - * - * The returned structure contains much information that is useful only - * internally in the menu system. External modules are likely to need only - * the ['visible'] element of the returned array. All menu items that are - * accessible to the current user and not hidden will be present here, so - * modules and themes can use this structure to build their own representations - * of the menu. - * - * $menu['visible'] will contain an associative array, the keys of which - * are menu IDs. The values of this array are themselves associative arrays, - * with the following key-value pairs defined: - * - 'title' - The displayed title of the menu or menu item. It will already - * have been translated by the locale system. - * - 'description' - The description (link title attribute) of the menu item. - * It will already have been translated by the locale system. - * - 'path' - The Drupal path to the menu item. A link to a particular item - * can thus be constructed with - * l($item['title'], $item['path'], array('title' => $item['description'])). - * - 'children' - A linear list of the menu ID's of this item's children. - * - * Menu ID 0 is the "root" of the menu. The children of this item are the - * menus themselves (they will have no associated path). Menu ID 1 will - * always be one of these children; it is the default "Navigation" menu. - */ -function menu_get_menu() { - global $_menu; - global $user; - global $locale; - - if (!isset($_menu['items'])) { - // _menu_build() may indirectly call this function, so prevent infinite loops. - $_menu['items'] = array(); - - $cid = "$user->uid:$locale"; - if ($cached = cache_get($cid, 'cache_menu')) { - $_menu = unserialize($cached->data); + * Returns the ancestors (and relevant placeholders) for any given path. + * + * For example, the ancestors of node/12345/edit are: + * + * node/12345/edit + * node/12345/% + * node/%/edit + * node/%/% + * node/12345 + * node/% + * node + * + * To generate these, we will use binary numbers. Each bit represents a + * part of the path. If the bit is 1, then it represents the original + * value while 0 means wildcard. If the path is node/12/edit/foo + * then the 1011 bitstring represents node/%/edit/foo where % means that + * any argument matches that part. + * + * @param $parts + * An array of path parts, for the above example + * array('node', '12345', 'edit'). + * @return + * An array which contains the ancestors and placeholders. Placeholders + * simply contain as many %s as the ancestors. + */ +function menu_get_ancestors($parts) { + $n1 = count($parts); + $placeholders = array(); + $ancestors = array(); + $end = (1 << $n1) - 1; + $length = $n1 - 1; + for ($i = $end; $i > 0; $i--) { + $current = ''; + $count = 0; + for ($j = $length; $j >= 0; $j--) { + if ($i & (1 << $j)) { + $count++; + $current .= $parts[$length - $j]; + } + else { + $current .= '%'; + } + if ($j) { + $current .= '/'; + } } - else { - _menu_build(); - // Cache the menu structure for this user, to expire after one day. - cache_set($cid, 'cache_menu', serialize($_menu), time() + (60 * 60 * 24)); + // If the number was like 10...0 then the next number will be 11...11, + // one bit less wide. + if ($count == 1) { + $length--; } - - // Make sure items that cannot be cached are added. - _menu_append_contextual_items(); - - // Reset the cached $menu in menu_get_item(). - menu_get_item(NULL, NULL, TRUE); + $placeholders[] = "'%s'"; + $ancestors[] = $current; } - - return $_menu; + return array($ancestors, $placeholders); } /** - * Return the local task tree. + * The menu system uses serialized arrays stored in the database for + * arguments. However, often these need to change according to the + * current path. This function unserializes such an array and does the + * necessary change. * - * Unlike the rest of the menu structure, the local task tree cannot be cached - * nor determined too early in the page request, because the user's current - * location may be changed by a menu_set_location() call, and the tasks shown - * (just as the breadcrumb trail) need to reflect the changed location. - */ -function menu_get_local_tasks() { - global $_menu; - - // Don't cache the local task tree, as it varies by location and tasks are - // allowed to be dynamically determined. - if (!isset($_menu['local tasks'])) { - // _menu_build_local_tasks() may indirectly call this function, so prevent - // infinite loops. - $_menu['local tasks'] = array(); - $pid = menu_get_active_nontask_item(); - if (!_menu_build_local_tasks($pid)) { - // If the build returned FALSE, the tasks need not be displayed. - $_menu['local tasks'][$pid]['children'] = array(); - } - } - - return $_menu['local tasks']; -} - -/** - * Retrieves the menu item specified by $mid, or by $path if $mid is not given. - * - * @param $mid - * The menu ID of the menu item to retrieve. - * @param $path - * The internal path of the menu item to retrieve. Defaults to NULL. Only - * used if $mid is not set. - * @param $reset - * Optional flag that resets the static variable cache of the menu tree, if - * set to TRUE. Default is FALSE. + * Integer values are mapped according to the $map parameter. For + * example, if unserialize($data) is array('node_load', 1) and $map is + * array('node', '12345') then 'node_load' will not be changed + * because it is not an integer, but 1 will as it is an integer. As + * $map[1] is '12345', 1 will be replaced with '12345'. So the result + * will be array('node_load', '12345'). * + * @param @data + * A serialized array. + * @param @map + * An array of potential replacements. * @return - * The menu item found in the site menu, or an empty array if none could be - * found. + * The $data array unserialized and mapped. */ -function menu_get_item($mid, $path = NULL, $reset = FALSE) { - static $menu; - - if (!isset($menu) || $reset) { - $menu = menu_get_menu(); - } - - if (isset($mid)) { - return $menu['items'][$mid]; +function menu_unserialize($data, $map) { + if ($data = unserialize($data)) { + foreach ($data as $k => $v) { + if (is_int($v)) { + $data[$k] = isset($map[$v]) ? $map[$v] : ''; + } + } + return $data; } - - if (isset($path)) { - return $menu['items'][$menu['path index'][$path]]; + else { + return array(); } - - return array(); } /** - * Retrieves the menu ID and title of all root menus. + * Replaces the statically cached item for a given path. * - * @return - * Array containing all menus (but not menu items), in the form mid => title. + * @param $path + * The path + * @param $item + * The menu item. This is a menu entry, an associative array, + * with keys like title, access callback, access arguments etc. */ -function menu_get_root_menus() { - $menu = menu_get_menu(); - $root_menus = array(); - - foreach ($menu['items'][0]['children'] as $mid) { - $root_menus[$mid] = $menu['items'][$mid]['title']; - } - - return $root_menus; +function menu_set_item($path, $item) { + menu_get_item($path, TRUE, $item); } -/** - * Change the current menu location of the user. - * - * Frequently, modules may want to make a page or node act as if it were - * in the menu tree somewhere, even though it was not registered in a - * hook_menu() implementation. If the administrator has rearranged the menu, - * the newly set location should respect this in the breadcrumb trail and - * expanded/collapsed status of menu items in the tree. This function - * allows this behavior. - * - * @param $location - * An array specifying a complete or partial breadcrumb trail for the - * new location, in the same format as the return value of hook_menu(). - * The last element of this array should be the new location itself. - * - * This function will set the new breadcrumb trail to the passed-in value, - * but if any elements of this trail are visible in the site tree, the - * trail will be "spliced in" to the existing site navigation at that point. - */ -function menu_set_location($location) { - global $_menu; - $temp_id = min(array_keys($_menu['items'])) - 1; - $prev_id = 0; - - // Don't allow this function to change the actual current path, just the - // position in the menu tree. - $location[count($location) - 1]['path'] = $_GET['q']; - - foreach (array_reverse($location) as $item) { - if (isset($_menu['path index'][$item['path']])) { - $mid = $_menu['path index'][$item['path']]; - if (isset($_menu['visible'][$mid])) { - // Splice in the breadcrumb at this location. - if ($prev_id) { - $_menu['items'][$prev_id]['pid'] = $mid; - } - $prev_id = 0; - break; - } - else { - // A hidden item; show it, but only temporarily. - $_menu['items'][$mid]['type'] |= MENU_VISIBLE_IN_BREADCRUMB; - if ($prev_id) { - $_menu['items'][$prev_id]['pid'] = $mid; - } - $prev_id = $mid; +function menu_get_item($path = NULL, $execute = TRUE, $item = NULL) { + static $items; + if (!isset($path)) { + $path = $_GET['q']; + } + if (isset($item)) { + $items[$path] = $item; + } + if (!isset($items[$path])) { + $map = arg(NULL, $path); + $parts = array_slice($map, 0, 6); + list($ancestors, $placeholders) = menu_get_ancestors($parts); + if ($item = db_fetch_object(db_query_range('SELECT * FROM {menu} WHERE path IN ('. implode (',', $placeholders) .') ORDER BY fit DESC', $ancestors, 0, 1))) { + $item->access = _menu_access($item, $map); + if ($map === FALSE) { + $items[$path] = FALSE; + return FALSE; } - } - else { - $item['type'] |= MENU_VISIBLE_IN_BREADCRUMB; - if ($prev_id) { - $_menu['items'][$prev_id]['pid'] = $temp_id; + if ($execute) { + $item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($parts, $item->number_parts)); } - $_menu['items'][$temp_id] = $item; - $_menu['path index'][$item['path']] = $temp_id; - - $prev_id = $temp_id; - $temp_id--; } + $items[$path] = $item; } - - if ($prev_id) { - // Didn't find a home, so attach this to the main navigation menu. - $_menu['items'][$prev_id]['pid'] = 1; - } - - $final_item = array_pop($location); - menu_set_active_item($final_item['path']); + return $items[$path]; } /** * Execute the handler associated with the active menu item. - * - * This is called early in the page request. The active menu item is at - * this point determined exclusively by the URL. The handler that is called - * here may, as a side effect, change the active menu item so that later - * menu functions (that display the menus and breadcrumbs, for example) - * act as if the user were in a different location on the site. */ function menu_execute_active_handler() { - if (_menu_site_is_offline()) { - return MENU_SITE_OFFLINE; - } - - $menu = menu_get_menu(); - - // Determine the menu item containing the callback. - $path = $_GET['q']; - while ($path && !isset($menu['callbacks'][$path])) { - $path = substr($path, 0, strrpos($path, '/')); - } - - if (!isset($menu['callbacks'][$path])) { - return MENU_NOT_FOUND; + if ($item = menu_get_item()) { + return $item->access ? call_user_func_array($item->page_callback, $item->page_arguments) : MENU_ACCESS_DENIED; } + return MENU_NOT_FOUND; +} - if (!function_exists($menu['callbacks'][$path]['callback'])) { - return MENU_NOT_FOUND; +function _menu_access($item, &$map) { + if ($item->map_callback) { + $map = call_user_func_array($item->map_callback, array_merge(array($map), unserialize($item->map_arguments))); + if ($map === FALSE) { + return FALSE; + } } - - if (!_menu_item_is_accessible(menu_get_active_item())) { - return MENU_ACCESS_DENIED; + $callback = $item->access_callback; + if (is_numeric($callback)) { + return $callback; } - - // We found one, and are allowed to execute it. - $arguments = isset($menu['callbacks'][$path]['callback arguments']) ? $menu['callbacks'][$path]['callback arguments'] : array(); - $arg = substr($_GET['q'], strlen($path) + 1); - if (strlen($arg)) { - $arguments = array_merge($arguments, explode('/', $arg)); + $arguments = menu_unserialize($item->access_arguments, $map); + // As call_user_func_array is quite slow and user_access is a very common + // callback, it is worth making a special case for it. + if ($callback == 'user_access') { + return (count($arguments) == 1) ? user_access($arguments[0]) : user_access($arguments[0], $arguments[1]); } - - return call_user_func_array($menu['callbacks'][$path]['callback'], $arguments); + return call_user_func_array($callback, $arguments); } /** - * Returns the ID of the active menu item. + * Returns a rendered menu tree. */ -function menu_get_active_item() { - return menu_set_active_item(); +function menu_tree() { + $item = menu_get_item(); + list(, $menu) = _menu_tree(db_query('SELECT * FROM {menu} WHERE pid IN ('. $item->parents .') AND visible = 1 ORDER BY vancode')); + return $menu; } -/** - * Sets the path of the active menu item. - */ -function menu_set_active_item($path = NULL) { - static $stored_mid; - - if (!isset($stored_mid) || isset($path)) { - if (!isset($path)) { - $path = $_GET['q']; +function _menu_tree($result = NULL, $depth = 0, $link = array('link' => '', 'has_children' => FALSE)) { + static $original_map; + $remnant = array('link' => '', 'has_children' => FALSE); + $tree = ''; + while ($item = db_fetch_object($result)) { + $map = arg(NULL, $item->path); + if (!_menu_access($item, $map)) { + continue; } - else { - $_GET['q'] = $path; + $menu_link = array('link' => $item->menu_link, 'has_children' => $item->has_children); + if ($item->depth > $depth) { + list($remnant, $menu) = _menu_tree($result, $item->depth, $menu_link); + $tree .= theme('menu_tree', $link, $menu); + $link = $remnant; + $remnant = ''; } - $menu = menu_get_menu(); - - while ($path && !isset($menu['path index'][$path])) { - $path = substr($path, 0, strrpos($path, '/')); + elseif ($item->depth == $depth) { + $tree .= theme('menu_link', $link); + $link = $menu_link; } - $stored_mid = isset($menu['path index'][$path]) ? $menu['path index'][$path] : 0; - - // Search for default local tasks to activate instead of this item. - $continue = TRUE; - while ($continue) { - $continue = FALSE; - if (isset($menu['items'][$stored_mid]['children'])) { - foreach ($menu['items'][$stored_mid]['children'] as $cid) { - if ($menu['items'][$cid]['type'] & MENU_LINKS_TO_PARENT) { - $stored_mid = $cid; - $continue = TRUE; - } - } - } + else { + $remnant = $menu_link; + break; } - - // Reset the cached $menu in menu_get_item(). - menu_get_item(NULL, NULL, TRUE); } - - return $stored_mid; + if ($link['link']) { + $tree .= theme('menu_link', $link); + } + return array($remnant, $tree); } /** - * Returns the ID of the current menu item or, if the current item is a - * local task, the menu item to which this task is attached. + * Generate the HTML for a menu tree. */ -function menu_get_active_nontask_item() { - $mid = menu_get_active_item(); - - // Find the first non-task item: - while ($mid) { - $item = menu_get_item($mid); - - if (!($item['type'] & MENU_IS_LOCAL_TASK)) { - return $mid; - } - - $mid = $item['pid']; - } +function theme_menu_tree($link, $tree) { + $tree = '<ul class="menu">'. $tree .'</ul>'; + return $link['link'] ? theme('menu_link', $link, $tree) : $tree; } /** - * Returns the title of the active menu item. + * Generate the HTML for a menu link. */ -function menu_get_active_title() { - if ($mid = menu_get_active_nontask_item()) { - $item = menu_get_item($mid); - return $item['title']; - } +function theme_menu_link($link, $menu = '') { + return '<li class="'. ($menu ? 'expanded' : ($link['has_children'] ? 'collapsed' : 'leaf')) .'">'. $link['link'] . $menu .'</li>' . "\n"; } /** @@ -501,8 +394,9 @@ function menu_get_active_title() { function menu_get_active_help() { $path = $_GET['q']; $output = ''; + $item = menu_get_item(); - if (!_menu_item_is_accessible(menu_get_active_item())) { + if (!$item->access) { // Don't return help text for areas the user cannot access. return; } @@ -510,7 +404,7 @@ function menu_get_active_help() { foreach (module_list() as $name) { if (module_hook($name, 'help')) { if ($temp = module_invoke($name, 'help', $path)) { - $output .= $temp . "\n"; + $output .= $temp ."\n"; } if (module_hook('help', 'page')) { if (arg(0) == "admin") { @@ -525,868 +419,166 @@ function menu_get_active_help() { } /** - * Returns an array of rendered menu items in the active breadcrumb trail. - */ -function menu_get_active_breadcrumb() { - - // No breadcrumb for the front page. - if (drupal_is_front_page()) { - return array(); - } - - // We do *not* want to use "variable_get('site_frontpage', 'node)" here - // as that will create the link '/node'. This is unsightly and creates - // a second URL for the homepage ('/' *and* '/node'). - $links[] = l(t('Home'), ''); - - $trail = _menu_get_active_trail(); - foreach ($trail as $mid) { - $item = menu_get_item($mid); - if ($item['type'] & MENU_VISIBLE_IN_BREADCRUMB) { - $links[] = menu_item_link($mid); - } - } - - // The last item in the trail is the page title; don't display it here. - array_pop($links); - - return $links; -} - -/** - * Returns TRUE when the menu item is in the active trail. - */ -function menu_in_active_trail($mid) { - $trail = _menu_get_active_trail(); - - return in_array($mid, $trail); -} - -/** - * Returns TRUE when the menu item is in the active trail within a - * specific subsection of the menu tree. - * - * @param $mid - * The menu item being considered. - * @param $pid - * The root of the subsection of the menu tree in which to look. - */ -function menu_in_active_trail_in_submenu($mid, $pid) { - $trail = _menu_get_active_trail_in_submenu($pid); - - if (!$trail) { - return FALSE; - } - - return in_array($mid, $trail); -} - -/** * Populate the database representation of the menu. - * - * This need only be called at the start of pages that modify the menu. */ function menu_rebuild() { - // Clear the page cache, so that changed menus are reflected for anonymous users. - cache_clear_all('*', 'cache_page', TRUE); - // Also clear the menu cache. - cache_clear_all('*', 'cache_menu', TRUE); - - _menu_build(); - - if (module_exists('menu')) { - $menu = menu_get_menu(); - - // Fill a queue of new menu items which are modifiable. - $new_items = array(); - foreach ($menu['items'] as $mid => $item) { - if ($mid < 0 && ($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) { - $new_items[$mid] = $item; - } + $next = array(); + db_query('DELETE FROM {menu}'); + $menu = module_invoke_all('menu'); + foreach (module_implements('menu_alter') as $module) { + $function = $module .'_menu_alter'; + $function($menu); + } + $mid = 1; + // First pass. + foreach ($menu as $path => $item) { + $item = &$menu[$path]; + $parts = explode('/', $path, 6); + $number_parts = count($parts); + // We store the highest index of parts here to save some work in the weight + // calculation loop. + $slashes = $number_parts - 1; + // If there is no %, it fits maximally. + if (strpos($path, '%') === FALSE) { + $fit = (1 << $number_parts) - 1; } - - $old_count = -1; - // Save the new items updating the pids in each iteration - while (($c = count($new_items)) && ($c != $old_count)) { - $old_count = count($new_items); - foreach ($new_items as $mid => $item) { - // If the item has a valid parent, save it - if ($item['pid'] >= 0) { - // The new menu ID gets passed back by reference as $item['mid'] - menu_save_item($item); - // Fix parent IDs for the children of the menu item just saved - if ($item['children']) { - foreach ($item['children'] as $child) { - if (isset($new_items[$child])) { - $new_items[$child]['pid'] = $item['mid']; - } - } + else { + // We need to calculate the fitness. + $fit = 0; + foreach ($parts as $k => $part) { + // ($part != '%') is the bit we want and we shift it to its place + // by shifting to left by ($slashes - $k) bits. + $fit |= ($part != '%') << ($slashes - $k); + } + } + if (!isset($item['_visible'])) { + $item['_visible'] = (!isset($item['type']) || ($item['type'] & MENU_VISIBLE_IN_TREE)) ? 1 : 0; + } + $depth = 1; + if (!isset($item['_mid'])) { + $item['_mid'] = $mid++; + } + $parents = array($item['_mid']); + for ($i = $slashes; $i; $i--) { + $parent_path = implode('/', array_slice($parts, 0, $i)); + // We need to calculate depth to be able to sort. depth needs visibility. + if (isset($menu[$parent_path])) { + $parent = &$menu[$parent_path]; + // It's possible that the parent was not processed yet. + if (!isset($parent['_mid'])) { + $parent['_mid'] = $mid++; + } + if (!isset($parent['_visible'])) { + $parent['_visible'] = (!isset($parent['type']) || ($parent['type'] & MENU_VISIBLE_IN_TREE)) ? 1 : 0; + } + if ($item['_visible'] && $parent['_visible']) { + $parent['_has_children'] = 1; + $depth++; + $parents[] = $parent['_mid']; + if (!isset($item['_pid'])) { + $item['_pid'] = $parent['_mid']; + $item['_visible_parent_path'] = $parent_path; } - // remove the item - unset($new_items[$mid]); } - } - } - // Rebuild the menu to account for the changes. - _menu_build(); - } - - // Reset the cached $menu in menu_get_item(). - menu_get_item(NULL, NULL, TRUE); - -} - -/** - * Generate the HTML for a menu tree. - * - * @param $pid - * The parent id of the menu. - * - * @ingroup themeable - */ -function theme_menu_tree($pid = 1) { - if ($tree = menu_tree($pid)) { - return "\n<ul class=\"menu\">\n". $tree ."\n</ul>\n"; - } -} - -/** - * Returns a rendered menu tree. - * - * @param $pid - * The parent id of the menu. - */ -function menu_tree($pid = 1) { - $menu = menu_get_menu(); - $output = ''; - - if (isset($menu['visible'][$pid]) && $menu['visible'][$pid]['children']) { - foreach ($menu['visible'][$pid]['children'] as $mid) { - $type = isset($menu['visible'][$mid]['type']) ? $menu['visible'][$mid]['type'] : NULL; - $children = isset($menu['visible'][$mid]['children']) ? $menu['visible'][$mid]['children'] : NULL; - $output .= theme('menu_item', $mid, menu_in_active_trail($mid) || ($type & MENU_EXPANDED) ? theme('menu_tree', $mid) : '', count($children) == 0); - } - } - - return $output; -} - -/** - * Generate the HTML output for a single menu item. - * - * @param $mid - * The menu id of the item. - * @param $children - * A string containing any rendered child items of this menu. - * @param $leaf - * A boolean indicating whether this menu item is a leaf. - * - * @ingroup themeable - */ -function theme_menu_item($mid, $children = '', $leaf = TRUE) { - return '<li class="'. ($leaf ? 'leaf' : ($children ? 'expanded' : 'collapsed')) .'">'. menu_item_link($mid) . $children ."</li>\n"; -} - -/** - * Generate the HTML representing a given menu item ID. - * - * @param $item - * The menu item to render. - * @param $link_item - * The menu item which should be used to find the correct path. - * - * @ingroup themeable - */ -function theme_menu_item_link($item, $link_item) { - return l($item['title'], $link_item['path'], !empty($item['description']) ? array('title' => $item['description']) : array(), isset($item['query']) ? $item['query'] : NULL); -} - -/** - * Returns the rendered link to a menu item. - * - * @param $mid - * The menu item id to render. - * @param $theme - * Whether to return a themed link or the link as an array - */ -function menu_item_link($mid, $theme = TRUE) { - $item = menu_get_item($mid); - $link_item = $item; - $link = ''; - - while ($link_item['type'] & MENU_LINKS_TO_PARENT) { - $link_item = menu_get_item($link_item['pid']); - } - - if ($theme) { - $link = theme('menu_item_link', $item, $link_item); - } - else { - $link = array( - 'title' => $item['title'], - 'href' => $link_item['path'], - 'attributes' => !empty($item['description']) ? array('title' => $item['description']) : array() + unset($parent); + } + } + $parents[] = 0; + $parents = implode(',', array_reverse($parents)); + // Store variables and set defaults. + $item += array( + '_fit' => $fit, + '_number_parts' => $number_parts, + '_parts' => $parts, + '_pid' => 0, + '_depth' => $depth, + '_parents' => $parents, + '_has_children' => 0, + 'title' => '', + 'weight' => 0, ); - } - - return $link; -} - -/** - * Returns the rendered local tasks. The default implementation renders - * them as tabs. - * - * @ingroup themeable - */ -function theme_menu_local_tasks() { - $output = ''; - - if ($primary = menu_primary_local_tasks()) { - $output .= "<ul class=\"tabs primary\">\n". $primary ."</ul>\n"; - } - if ($secondary = menu_secondary_local_tasks()) { - $output .= "<ul class=\"tabs secondary\">\n". $secondary ."</ul>\n"; - } - - return $output; -} - -/** - * Returns the rendered HTML of the primary local tasks. - */ -function menu_primary_local_tasks() { - $local_tasks = menu_get_local_tasks(); - $pid = menu_get_active_nontask_item(); - $output = ''; - - if (count($local_tasks[$pid]['children'])) { - foreach ($local_tasks[$pid]['children'] as $mid) { - $output .= theme('menu_local_task', $mid, menu_in_active_trail($mid), TRUE); - } - } - - return $output; -} - -/** - * Returns the rendered HTML of the secondary local tasks. - */ -function menu_secondary_local_tasks() { - $local_tasks = menu_get_local_tasks(); - $pid = menu_get_active_nontask_item(); - $output = ''; - - if (count($local_tasks[$pid]['children'])) { - foreach ($local_tasks[$pid]['children'] as $mid) { - if (menu_in_active_trail($mid) && count($local_tasks[$mid]['children']) > 1) { - foreach ($local_tasks[$mid]['children'] as $cid) { - $output .= theme('menu_local_task', $cid, menu_in_active_trail($cid), FALSE); + $sort[$path] = ($item['_visible'] ? $depth : $number_parts) . sprintf('%05d', $item['weight']) . $item['title']; + unset($item); + } + array_multisort($sort, $menu); + // Second pass: calculate ancestors, vancode and store into the database. + foreach ($menu as $path => $item) { + $item = &$menu[$path]; + for ($i = $item['_number_parts'] - 1; $i; $i--) { + $parent_path = implode('/', array_slice($item['_parts'], 0, $i)); + if (isset($menu[$parent_path])) { + $parent = $menu[$parent_path]; + // If a callback is not found, we try to find the first parent that + // has this callback. When found, its callback argument will also be + // copied but only if there is none in the current item. + foreach (array('access', 'map', 'page') as $type) { + if (!isset($item["$type callback"]) && isset($parent["$type callback"])) { + $item["$type callback"] = $parent["$type callback"]; + if (!isset($item["$type arguments"]) && isset($parent["$type arguments"])) { + $item["$type arguments"] = $parent["$type arguments"]; + } + } } } } - } - - return $output; -} - -/** - * Generate the HTML representing a given menu item ID as a tab. - * - * @param $mid - * The menu ID to render. - * @param $active - * Whether this tab or a subtab is the active menu item. - * @param $primary - * Whether this tab is a primary tab or a subtab. - * - * @ingroup themeable - */ -function theme_menu_local_task($mid, $active, $primary) { - if ($active) { - return '<li class="active">'. menu_item_link($mid) ."</li>\n"; - } - else { - return '<li>'. menu_item_link($mid) ."</li>\n"; - } -} - -/** - * Returns an array containing the primary links. - * Can optionally descend from the root of the Primary links menu towards the - * current node for a specified number of levels and return that submenu. - * Used to generate a primary/secondary menu from different levels of one menu. - * - * @param $start_level - * This optional parameter can be used to retrieve a context-sensitive array - * of links at $start_level levels deep into the Primary links menu. - * The default is to return the top-level links. - * @param $pid - * The parent menu ID from which to search for children. Defaults to the - * menu_primary_menu setting. - * @return A nested array of links and their properties. The keys of - * the array contain some extra encoded information about the results. - * The format of the key is {level}-{num}{-active}. - * level is the depth within the menu tree of this list. - * num is the number within this array, used only to make the key unique. - * -active is appended if this element is in the active trail. - */ -function menu_primary_links($start_level = 1, $pid = 0) { - if (!module_exists('menu')) { - return NULL; - } - if (!$pid) { - $pid = variable_get('menu_primary_menu', 0); - } - if (!$pid) { - return NULL; - } - - if ($start_level < 1) { - $start_level = 1; - } - - if ($start_level > 1) { - $trail = _menu_get_active_trail_in_submenu($pid); - if (!$trail) { - return NULL; + if (!isset($item['access callback'])) { + $menu[$path]['access callback'] = isset($item['access arguments']) ? 'user_access' : 0; } - else { - $pid = $trail[$start_level - 1]; + if (!isset($item['map callback']) && isset($item['map arguments'])) { + $item['map callback'] = 'menu_map'; } - } - - $menu = menu_get_menu(); - $links = array(); - if ($pid && is_array($menu['visible'][$pid]) && isset($menu['visible'][$pid]['children'])) { - $count = 1; - foreach ($menu['visible'][$pid]['children'] as $cid) { - $index = "menu-$start_level-$count-$pid"; - if (menu_in_active_trail_in_submenu($cid, $pid)) { - $index .= "-active"; + foreach (array('access', 'map', 'page') as $type) { + if (isset($item["$type callback"]) && !isset($item["$type arguments"])) { + $item["$type arguments"] = array(); } - $links[$index] = menu_item_link($cid, FALSE); - $count++; } - } - - // Special case - provide link to admin/build/menu if primary links is empty. - if (empty($links) && $start_level == 1 && $pid == variable_get('menu_primary_menu', 0) && user_access('administer menu')) { - $links['1-1'] = array( - 'title' => t('Edit primary links'), - 'href' => 'admin/build/menu' - ); - } - - return $links; -} - -/** - * Returns an array containing the secondary links. - * Secondary links can be either a second level of the Primary links - * menu or generated from their own menu. - */ -function menu_secondary_links() { - $msm = variable_get('menu_secondary_menu', 0); - if ($msm == 0) { - return NULL; - } - - if ($msm == variable_get('menu_primary_menu', 0)) { - return menu_primary_links(2, $msm); - } - - return menu_primary_links(1, $msm); -} - -/** - * Returns the themed HTML for primary and secondary links. - * Note that this function is overridden by most core themes because - * those themes display links in "link | link" format, not from a list. - * Also note that by default links rendered with this function are - * displayed with the same CSS as is used for the local tasks. - * If a theme wishes to render links from a ul it is expected that - * the theme will provide suitable CSS. - * - * @param $links - * An array containing links to render. - * @return - * A string containing the themed links. - * - * @ingroup themeable - */ -function theme_menu_links($links) { - if (!count($links)) { - return ''; - } - $level_tmp = explode('-', key($links)); - $level = $level_tmp[0]; - $output = "<ul class=\"links-$level\">\n"; - foreach ($links as $index => $link) { - $output .= '<li'; - if (stristr($index, 'active')) { - $output .= ' class="active"'; - } - $output .= ">". l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment']) ."</li>\n"; - } - $output .= '</ul>'; - - return $output; -} - -/** - * @} End of "defgroup menu". - */ - -/** - * Returns an array with the menu items that lead to the current menu item. - */ -function _menu_get_active_trail() { - static $trail; - - if (!isset($trail)) { - $trail = array(); - - $mid = menu_get_active_item(); - - // Follow the parents up the chain to get the trail. - while ($mid && ($item = menu_get_item($mid))) { - array_unshift($trail, $mid); - $mid = $item['pid']; + if (is_bool($item['access callback'])) { + $item['access callback'] = intval($item['access callback']); } - } - return $trail; -} - -/** - * Find the active trail through a specific subsection of the menu tree. - * - * @param $pid - * The root item from which the active trail must descend. - */ -function _menu_get_active_trail_in_submenu($pid) { - static $trails; - - if (!isset($trails)) { - // Find all menu items which point to the current node and for each - // follow the parents up the chain to build an active trail. - $trails = array(); - $menu = menu_get_menu(); - $path = $_GET['q']; - $count = 0; - while ($path && !$count) { - foreach ($menu['items'] as $key => $item) { - if (isset($item['path']) && $item['path'] == $path) { - $trails[$count] = array(); - $mid = $key; - while ($mid && $menu['items'][$mid]) { - array_unshift($trails[$count], $mid); - $mid = $menu['items'][$mid]['pid']; - } - $count ++; - } + if ($item['_visible']) { + $prefix = isset($item['_visible_parent_path']) ? $menu[$item['_visible_parent_path']]['_prefix'] : ''; + if (!isset($next[$prefix])) { + $next[$prefix] = 0; } - $path = substr($path, 0, strrpos($path, '/')); + $vancode = $prefix . int2vancode($next[$prefix]++); + $menu[$path]['_prefix'] = $vancode .'.'; + $link = l($item['title'], $path, isset($item['attributes']) ? $item['attributes'] : array(), isset($item['query']) ? $item['query'] : NULL, isset($item['fragment']) ? $item['fragment'] : NULL); } - } - - if ($trails) { - foreach ($trails as $trail) { - $count_trail = count($trail); - for ($i = 0; $i < $count_trail; $i++) { - if ($trail[$i] == $pid) { - // Return a trail from $pid down to the current page inclusive. - for ( ; $i < $count_trail; $i++) { - $subtrail[] = $trail[$i]; - } - return $subtrail; - } - } + else { + $vancode = ''; + $link = ''; } + db_query("INSERT INTO {menu} (mid, pid, path, access_callback, access_arguments, page_callback, page_arguments, map_callback, map_arguments, fit, number_parts, vancode, menu_link, visible, parents, depth, has_children) VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, '%s', %d, %d)", $item['_mid'], $item['_pid'], $path, $item['access callback'], serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['map callback'], serialize($item['map arguments']), $item['_fit'], $item['_number_parts'], $vancode .'+', $link, $item['_visible'], $item['_parents'], $item['_depth'], $item['_has_children']); + // $item needs to be unset because of the reference above. + unset($item); } - - return NULL; } -/** - * Comparator routine for use in sorting menu items. - */ -function _menu_sort($a, $b) { - $menu = menu_get_menu(); - - $a = $menu['items'][$a]; - $b = $menu['items'][$b]; - - if ($a['weight'] < $b['weight']) { - return -1; - } - elseif ($a['weight'] > $b['weight']) { - return 1; - } - elseif (isset($a['title']) && isset($b['title'])) { - return strnatcasecmp($a['title'], $b['title']); - } - else { - return 1; - } +function menu_map($arg, $function, $index, $default = FALSE) { + $arg[$index] = is_numeric($arg[$index]) ? $function($arg[$index]) : $default; + return $arg[$index] ? $arg : FALSE; } -/** - * Build the menu by querying both modules and the database. - */ -function _menu_build() { - global $_menu; - global $user; - - // Start from a clean slate. - $_menu = array(); - - $_menu['path index'] = array(); - // Set up items array, including default "Navigation" menu. - $_menu['items'] = array( - 0 => array('path' => '', 'title' => '', 'type' => MENU_IS_ROOT), - 1 => array('pid' => 0, 'path' => '', 'title' => t('Navigation'), 'weight' => -50, 'access' => TRUE, 'type' => MENU_IS_ROOT | MENU_VISIBLE_IN_TREE) - ); - $_menu['callbacks'] = array(); - - // Build a sequential list of all menu items. - $menu_item_list = module_invoke_all('menu', TRUE); - - // Menu items not in the DB get temporary negative IDs. - $temp_mid = -1; - - foreach ($menu_item_list as $item) { - if (!isset($item['path'])) { - $item['path'] = ''; - } - if (!isset($item['type'])) { - $item['type'] = MENU_NORMAL_ITEM; - } - if (!isset($item['weight'])) { - $item['weight'] = 0; - } - $mid = $temp_mid; - if (isset($_menu['path index'][$item['path']])) { - // Newer menu items overwrite older ones. - unset($_menu['items'][$_menu['path index'][$item['path']]]); - } - if (isset($item['callback'])) { - $_menu['callbacks'][$item['path']] = array('callback' => $item['callback']); - if (isset($item['callback arguments'])) { - $_menu['callbacks'][$item['path']]['callback arguments'] = $item['callback arguments']; - unset($item['callback arguments']); - } - unset($item['callback']); - } - $_menu['items'][$mid] = $item; - $_menu['path index'][$item['path']] = $mid; - - $temp_mid--; - } - - // Now fetch items from the DB, reassigning menu IDs as needed. - if (module_exists('menu')) { - $result = db_query(db_rewrite_sql('SELECT m.mid, m.* FROM {menu} m ORDER BY m.mid ASC', 'm', 'mid')); - while ($item = db_fetch_object($result)) { - // Handle URL aliases if entered in menu administration. - if (!isset($_menu['path index'][$item->path])) { - $item->path = drupal_get_normal_path($item->path); - } - if (isset($_menu['path index'][$item->path])) { - // The path is already declared. - $old_mid = $_menu['path index'][$item->path]; - if ($old_mid < 0) { - // It had a temporary ID, so use a permanent one. - $_menu['items'][$item->mid] = $_menu['items'][$old_mid]; - if ($_menu['items'][$item->mid]['type'] & $item->type) { - // If the item is of the same type, delete the old item. - unset($_menu['items'][$old_mid]); - $_menu['path index'][$item->path] = $item->mid; - } - // The new menu item gets all the custom type flags from the database - $_menu['items'][$item->mid]['type'] &= $item->type; - } - else { - // It has a permanent ID. Only replace with non-custom menu items. - if ($item->type & MENU_CREATED_BY_ADMIN) { - $_menu['items'][$item->mid] = array('path' => $item->path); - } - else { - // Leave the old item around as a shortcut to this one. - $_menu['items'][$item->mid] = $_menu['items'][$old_mid]; - $_menu['path index'][$item->path] = $item->mid; - } - } - } - else { - // The path was not declared, so this is a custom item or an orphaned one. - if ($item->type & MENU_CREATED_BY_ADMIN) { - $_menu['items'][$item->mid] = array('path' => $item->path); - if (!empty($item->path)) { - $_menu['path index'][$item->path] = $item->mid; - } - } - } - - // If the administrator has changed the item, reflect the change. - if ($item->type & MENU_MODIFIED_BY_ADMIN) { - $_menu['items'][$item->mid]['title'] = $item->title; - $_menu['items'][$item->mid]['description'] = $item->description; - $_menu['items'][$item->mid]['pid'] = $item->pid; - $_menu['items'][$item->mid]['weight'] = $item->weight; - $_menu['items'][$item->mid]['type'] = $item->type; - } - } - } - - // Associate parent and child menu items. - _menu_find_parents($_menu['items']); - - // Prepare to display trees to the user as required. - _menu_build_visible_tree(); +// Placeholders. +function menu_primary_links() { } -/** - * Determine whether the given menu item is accessible to the current user. - * - * Use this instead of just checking the "access" property of a menu item - * to properly handle items with fall-through semantics. - */ -function _menu_item_is_accessible($mid) { - $menu = menu_get_menu(); - - // Follow the path up to find the first "access" attribute. - $path = isset($menu['items'][$mid]['path']) ? $menu['items'][$mid]['path'] : NULL; - while ($path && (!isset($menu['path index'][$path]) || !isset($menu['items'][$menu['path index'][$path]]['access']))) { - $path = substr($path, 0, strrpos($path, '/')); - } - if (empty($path)) { - // Items without any access attribute up the chain are denied, unless they - // were created by the admin. They most likely point to non-Drupal directories - // or to an external URL and should be allowed. - return $menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN; - } - return $menu['items'][$menu['path index'][$path]]['access']; +function menu_secondary_links() { } -/** - * Find all visible items in the menu tree, for ease in displaying to user. - * - * Since this is only for display, we only need title, path, and children - * for each item. - */ -function _menu_build_visible_tree($pid = 0) { - global $_menu; - - if (isset($_menu['items'][$pid])) { - $parent = $_menu['items'][$pid]; - - $children = array(); - if (isset($parent['children'])) { - usort($parent['children'], '_menu_sort'); - foreach ($parent['children'] as $mid) { - $children = array_merge($children, _menu_build_visible_tree($mid)); - } - } - $visible = ($parent['type'] & MENU_VISIBLE_IN_TREE) || - ($parent['type'] & MENU_VISIBLE_IF_HAS_CHILDREN && count($children) > 0); - $allowed = _menu_item_is_accessible($pid); - - if (($parent['type'] & MENU_IS_ROOT) || ($visible && $allowed)) { - $_menu['visible'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children, 'type' => $parent['type']); - foreach ($children as $mid) { - $_menu['visible'][$mid]['pid'] = $pid; - } - return array($pid); - } - else { - return $children; - } - } - - return array(); +function menu_primary_local_tasks() { } -/** - * Account for menu items that are only defined at certain paths, so will not - * be cached. - * - * We don't support the full range of menu item options for these menu items. We - * don't support MENU_VISIBLE_IF_HAS_CHILDREN, and we require parent items to be - * declared before their children. - */ -function _menu_append_contextual_items() { - global $_menu; - - // Build a sequential list of all menu items. - $menu_item_list = module_invoke_all('menu', FALSE); - - // Menu items not in the DB get temporary negative IDs. - $temp_mid = min(array_keys($_menu['items'])) - 1; - $new_items = array(); - - foreach ($menu_item_list as $item) { - if (isset($item['callback'])) { - $_menu['callbacks'][$item['path']] = array('callback' => $item['callback']); - if (isset($item['callback arguments'])) { - $_menu['callbacks'][$item['path']]['callback arguments'] = $item['callback arguments']; - unset($item['callback arguments']); - } - unset($item['callback']); - } - if (!isset($_menu['path index'][$item['path']])) { - if (!isset($item['path'])) { - $item['path'] = ''; - } - if (!isset($item['type'])) { - $item['type'] = MENU_NORMAL_ITEM; - } - if (!isset($item['weight'])) { - $item['weight'] = 0; - } - $_menu['items'][$temp_mid] = $item; - $_menu['path index'][$item['path']] = $temp_mid; - $new_items[$temp_mid] = $item; - $temp_mid--; - } - else { - $mid = $_menu['path index'][$item['path']]; - if ($_menu['items'][$mid]['type'] & MENU_CREATED_BY_ADMIN) { - $_menu['items'][$mid]['access'] = $item['access']; - if (isset($_menu['items'][$mid]['callback'])) { - $_menu['items'][$mid]['callback'] = $item['callback']; - } - if (isset($_menu['items'][$mid]['callback arguments'])) { - $_menu['items'][$mid]['callback arguments'] = $item['callback arguments']; - } - } - if ($item['type'] & MENU_LOCAL_TASK && !($_menu['items'][$mid]['type'] & MENU_LOCAL_TASK)) { - // A local task is in the menu table and the path is already present - $_menu['items'][$mid]['type'] = MENU_LOCAL_TASK; - $new_items[$mid] = $item; - } - } - } - - // Establish parent-child relationships. - _menu_find_parents($new_items); - - // Add new items to the visible tree if necessary. - foreach ($new_items as $mid => $item) { - $item = $_menu['items'][$mid]; - if (($item['type'] & MENU_VISIBLE_IN_TREE) && _menu_item_is_accessible($mid)) { - $pid = $item['pid']; - while ($pid && !isset($_menu['visible'][$pid])) { - $pid = $_menu['items'][$pid]['pid']; - } - $_menu['visible'][$mid] = array('title' => $item['title'], 'path' => $item['path'], 'pid' => $pid); - $_menu['visible'][$pid]['children'][] = $mid; - usort($_menu['visible'][$pid]['children'], '_menu_sort'); - } - } +function menu_secondary_local_tasks() { } -/** - * Establish parent-child relationships. - */ -function _menu_find_parents(&$items) { - global $_menu; - - foreach ($items as $mid => $item) { - if (!isset($item['pid'])) { - // Parent's location has not been customized, so figure it out using the path. - $parent = $item['path']; - if ($parent) { - do { - $parent = substr($parent, 0, strrpos($parent, '/')); - } - while ($parent && !isset($_menu['path index'][$parent])); - } - - $pid = $parent ? $_menu['path index'][$parent] : 1; - $_menu['items'][$mid]['pid'] = $pid; - } - else { - $pid = $item['pid']; - } - - // Don't make root a child of itself. - if ($mid) { - if (isset ($_menu['items'][$pid])) { - $_menu['items'][$pid]['children'][] = $mid; - } - else { - // If parent is missing, it is a menu item that used to be defined - // but is no longer. Default to a root-level "Navigation" menu item. - $_menu['items'][1]['children'][] = $mid; - } - } - } +function menu_set_active_item() { } -/** - * Find all the items in the current local task tree. - * - * Since this is only for display, we only need title, path, and children - * for each item. - * - * At the close of this function, $_menu['local tasks'] is populated with the - * menu items in the local task tree. - * - * @return - * TRUE if the local task tree is forked. It does not need to be displayed - * otherwise. - */ -function _menu_build_local_tasks($pid) { - global $_menu; - - $forked = FALSE; - - if (isset($_menu['items'][$pid])) { - $parent = $_menu['items'][$pid]; - - $children = array(); - if (isset($parent['children'])) { - foreach ($parent['children'] as $mid) { - if (($_menu['items'][$mid]['type'] & MENU_IS_LOCAL_TASK) && _menu_item_is_accessible($mid)) { - $children[] = $mid; - // Beware short-circuiting || operator! - $forked = _menu_build_local_tasks($mid) || $forked; - } - } - } - usort($children, '_menu_sort'); - $forked = $forked || count($children) > 1; - - $_menu['local tasks'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children); - foreach ($children as $mid) { - $_menu['local tasks'][$mid]['pid'] = $pid; - } - } - - return $forked; +function menu_set_location() { } -/** - * Returns TRUE if the site is off-line for maintenance. - */ -function _menu_site_is_offline() { - // Check if site is set to off-line mode - if (variable_get('site_offline', 0)) { - // Check if the user has administration privileges - if (!user_access('administer site configuration')) { - // Check if this is an attempt to login - if (drupal_get_normal_path($_GET['q']) != 'user') { - return TRUE; - } - } - else { - $offline_message = t('Operating in off-line mode.'); - $messages = drupal_set_message(); - // Ensure that the off-line message is displayed only once [allowing for page redirects]. - if (!isset($messages) || !isset($messages['status']) || !in_array($offline_message, $messages['status'])) { - drupal_set_message($offline_message); - } - } - } - return FALSE; +function menu_get_active_breadcrumb() { + return array(l(t('Home'), '')); } + diff --git a/includes/module.inc b/includes/module.inc index 84cb4653e..44aba1874 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -253,7 +253,6 @@ function module_enable($module_list) { module_list(TRUE, FALSE); // Force to regenerate the stored list of hook implementations. module_implements('', FALSE, TRUE); - cache_clear_all('*', 'cache_menu', TRUE); } foreach ($invoke_modules as $module) { @@ -283,7 +282,6 @@ function module_disable($module_list) { module_list(TRUE, FALSE); // Force to regenerate the stored list of hook implementations. module_implements('', FALSE, TRUE); - cache_clear_all('*', 'cache_menu', TRUE); } } diff --git a/includes/path.inc b/includes/path.inc index b911dae60..4801458db 100644 --- a/includes/path.inc +++ b/includes/path.inc @@ -145,16 +145,20 @@ function drupal_get_normal_path($path) { * The component specified by $index, or FALSE if the specified component was * not found. */ -function arg($index) { - static $arguments, $q; +function arg($index = NULL, $path = NULL) { + static $arguments; - if (empty($arguments) || $q != $_GET['q']) { - $arguments = explode('/', $_GET['q']); - $q = $_GET['q']; + if (!isset($path)) { + $path = $_GET['q']; } - - if (isset($arguments[$index])) { - return $arguments[$index]; + if (!isset($arguments[$path])) { + $arguments[$path] = explode('/', $path); + } + if (!isset($index)) { + return $arguments[$path]; + } + if (isset($arguments[$path][$index])) { + return $arguments[$path][$index]; } } |