$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; if (!isset($_menu['items'])) { // _menu_build() may indirectly call this function, so prevent infinite loops. $_menu['items'] = array(); _menu_build(); } return $_menu; } /** * Return the local task tree. * * 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']; } /** * 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; 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; } } else { $item['type'] |= MENU_VISIBLE_IN_BREADCRUMB; if ($prev_id) { $_menu['items'][$prev_id]['pid'] = $temp_id; } $_menu['items'][$temp_id] = $item; $_menu['path index'][$item['path']] = $temp_id; $prev_id = $temp_id; $temp_id--; } } 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']); } /** * 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() { $menu = menu_get_menu(); // Determine the menu item containing the callback. $path = $_GET['q']; while ($path && (!array_key_exists($path, $menu['path index']) || empty($menu['items'][$menu['path index'][$path]]['callback']))) { $path = substr($path, 0, strrpos($path, '/')); } if (!array_key_exists($path, $menu['path index'])) { return MENU_NOT_FOUND; } $mid = $menu['path index'][$path]; if (empty($menu['items'][$mid]['callback'])) { return MENU_NOT_FOUND; } if (!_menu_item_is_accessible(menu_get_active_item())) { return MENU_ACCESS_DENIED; } // We found one, and are allowed to execute it. $arguments = $menu['items'][$mid]['callback arguments']; $arg = substr($_GET['q'], strlen($menu['items'][$mid]['path']) + 1); if (strlen($arg)) { $arguments = array_merge($arguments, explode('/', $arg)); } call_user_func_array($menu['items'][$mid]['callback'], $arguments); return MENU_FOUND; } /** * Returns the ID of the active menu item. */ function menu_get_active_item() { return menu_set_active_item(); } /** * Sets the path of the active menu item. */ function menu_set_active_item($path = NULL) { static $stored_mid; $menu = menu_get_menu(); if (is_null($stored_mid) || !empty($path)) { if (empty($path)) { $path = $_GET['q']; } else { $_GET['q'] = $path; } while ($path && !array_key_exists($path, $menu['path index'])) { $path = substr($path, 0, strrpos($path, '/')); } $stored_mid = array_key_exists($path, $menu['path index']) ? $menu['path index'][$path] : 0; // Search for default local tasks to activate instead of this item. $continue = TRUE; while ($continue) { $continue = FALSE; if (array_key_exists('children', $menu['items'][$stored_mid])) { foreach ($menu['items'][$stored_mid]['children'] as $cid) { if ($menu['items'][$cid]['type'] & MENU_LINKS_TO_PARENT) { $stored_mid = $cid; $continue = TRUE; } } } } } return $stored_mid; } /** * 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. */ function menu_get_active_nontask_item() { $menu = menu_get_menu(); $mid = menu_get_active_item(); // Find the first non-task item: while ($mid && ($menu['items'][$mid]['type'] & MENU_IS_LOCAL_TASK)) { $mid = $menu['items'][$mid]['pid']; } if ($mid) { return $mid; } } /** * Returns the title of the active menu item. */ function menu_get_active_title() { $menu = menu_get_menu(); if ($mid = menu_get_active_nontask_item()) { return $menu['items'][$mid]['title']; } } /** * Returns the help associated with the active menu item. */ function menu_get_active_help() { $path = $_GET['q']; $output = ''; if (!_menu_item_is_accessible(menu_get_active_item())) { // Don't return help text for areas the user cannot access. return; } $return = module_invoke_all('help', $path); foreach ($return as $item) { if (!empty($item)) { $output .= $item ."\n"; } } return $output; } /** * Returns an array of rendered menu items in the active breadcrumb trail. */ function menu_get_active_breadcrumb() { $menu = menu_get_menu(); $links[] = l(t('Home'), ''); $trail = _menu_get_active_trail(); foreach ($trail as $mid) { if ($menu['items'][$mid]['type'] & MENU_VISIBLE_IN_BREADCRUMB) { $links[] = theme('menu_item', $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); } /** * Populate the database representation of the menu. * * This need only be called at the start of pages that modify the menu. */ function menu_rebuild() { cache_clear_all(); _menu_build(); $menu = menu_get_menu(); $new_items = array(); foreach ($menu['items'] as $mid => $item) { if ($mid < 0 && ($item['type'] & MENU_MODIFIABLE_BY_ADMIN)) { $new_mid = db_next_id('{menu}_mid'); if (isset($new_items[$item['pid']])) { $new_pid = $new_items[$item['pid']]['mid']; } else { $new_pid = $item['pid']; } // Fix parent IDs for menu items already added. if ($item['children']) { foreach ($item['children'] as $child) { if (isset($new_items[$child])) { $new_items[$child]['pid'] = $new_mid; } } } $new_items[$mid] = array('mid' => $new_mid, 'pid' => $new_pid, 'path' => $item['path'], 'title' => $item['title'], 'description' => $item['description'], 'weight' => $item['weight'], 'type' => $item['type']); } } foreach ($new_items as $item) { db_query('INSERT INTO {menu} (mid, pid, path, title, description, weight, type) VALUES (%d, %d, \'%s\', \'%s\', \'%s\', %d, %d)', $item['mid'], $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type']); } // Rebuild the menu to account for any changes. _menu_build(); } /** * @} end of defgroup menu */ /** * @addtogroup themeable * @{ */ /** * Returns a rendered menu tree. */ function theme_menu_tree($pid = 1, $all = FALSE) { $menu = menu_get_menu(); $output = ''; if (isset($menu['visible'][$pid]) && $menu['visible'][$pid]['children']) { foreach ($menu['visible'][$pid]['children'] as $mid) { $style = (count($menu['visible'][$mid]['children']) ? (menu_in_active_trail($mid) ? 'expanded' : 'collapsed') : 'leaf'); $output .= "