$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'], 'weight' => $item['weight'], 'type' => $item['type']); } } foreach ($new_items as $item) { db_query('INSERT INTO {menu} (mid, pid, path, title, weight, type) VALUES (%d, %d, \'%s\', \'%s\', %d, %d)', $item['mid'], $item['pid'], $item['path'], $item['title'], $item['weight'], $item['type']); } // Rebuild the menu to account for any changes. _menu_build(); } /** @} end of "menu" function group */ /** * @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 .= "
  • "; $output .= theme('menu_item', $mid); if ($all || menu_in_active_trail($mid)) { $output .= theme('menu_tree', $mid); } $output .= "
  • \n"; } if ($output != '') { $output = "\n\n"; } } return $output; } /** * Generate the HTML representing a given menu item ID. * * @param $mid * The menu ID to render. */ function theme_menu_item($mid) { $menu = menu_get_menu(); return l($menu['items'][$mid]['title'], $menu['items'][$mid]['path']); } /** * Returns the rendered local tasks. The default implementation renders * them as tabs. */ function theme_menu_local_tasks() { $active = true; if ($mid = menu_get_active_nontask_item()) { $menu = menu_get_menu(); if ($children = $menu['items'][$mid]['children']) { foreach ($menu['items'][$mid]['children'] as $cid) { if (($menu['items'][$cid]['type'] & MENU_IS_LOCAL_TASK) && _menu_item_is_accessible($cid)) { if (menu_in_active_trail($cid)) { $tabs[] = theme('menu_local_task', $cid, TRUE); $active = false; } else { $tabs[] = theme('menu_local_task', $cid, FALSE); } } } if ($tabs) { // We add a default view-tab for the parent: $output = "\n"; $output .= theme('menu_local_subtasks', $mid); } } } return $output; } /** * Generate the HTML representing a given menu item ID as a set of tabs. * * @param $mid * The menu ID to render. * @param $active * Whether this tab or a subtab is the active menu item. */ function theme_menu_local_task($mid, $active) { if ($active) { return '
  • '. theme('menu_item', $mid) ."
  • \n"; } else { return '
  • '. theme('menu_item', $mid) ."
  • \n"; } } /** * Generate the HTML representing the children of a given menu item ID * as a set of tabs. * * @param $pid * The menu ID of the parent item. */ function theme_menu_local_subtasks($pid) { $menu = menu_get_menu(); $tabs = ''; if ($children = $menu['items'][$pid]['children']) { foreach ($children as $cid) { if (_menu_item_is_accessible($cid) && ($menu['items'][$cid]['type'] & MENU_IS_LOCAL_SUBTASK)) { $tabs .= theme('menu_local_task', $cid, menu_in_active_trail($cid)); } } if ($tabs) { return "\n"; } } } /** @} End of addtogroup themeable */ /** * Returns an array with the menu items that lead to the specified path. */ function _menu_get_trail($path) { $menu = menu_get_menu(); $trail = array(); // Find the ID of the given path. while ($path && !$menu['path index'][$path]) { $path = substr($path, 0, strrpos($path, '/')); } $mid = $menu['path index'][$path]; // Follow the parents up the chain to get the trail. while ($mid && $menu['items'][$mid]) { array_unshift($trail, $mid); $mid = $menu['items'][$mid]['pid']; } return $trail; } /** * 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]; return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1)); } /** * 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('type' => MENU_IS_ROOT), 1 => array('pid' => 0, 'title' => t('Navigation'), 'weight' => -50, 'access' => TRUE, 'type' => MENU_IS_ROOT | MENU_VISIBLE_IN_TREE) ); // Build a sequential list of all menu items. $menu_item_list = module_invoke_all('menu'); // Menu items not in the DB get temporary negative IDs. $temp_mid = -1; foreach ($menu_item_list as $item) { if (!isset($item['type'])) { $item['type'] = MENU_NORMAL_ITEM; } $mid = $temp_mid; if (isset($_menu['path index'][$item['path']])) { // Newer menu items overwrite older ones. unset($_menu['items'][$_menu['path index'][$item['path']]]); } $_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_exist('menu')) { $result = db_query('SELECT * FROM {menu}'); while ($item = db_fetch_object($result)) { // Don't display non-custom menu items if no module declared them. if ($old_mid = $_menu['path index'][$item->path]) { $_menu['items'][$item->mid] = $_menu['items'][$old_mid]; unset($_menu['items'][$old_mid]); $_menu['path index'][$item->path] = $item->mid; // If administrator has changed item position, reflect the change. if ($item->type & MENU_MODIFIED_BY_ADMIN) { $_menu['items'][$item->mid]['title'] = $item->title; $_menu['items'][$item->mid]['pid'] = $item->pid; $_menu['items'][$item->mid]['weight'] = $item->weight; $_menu['items'][$item->mid]['type'] = $item->type; } } // Next, add any custom items added by the administrator. else if ($item->type & MENU_CREATED_BY_ADMIN) { $_menu['items'][$item->mid] = array('pid' => $item->pid, 'path' => $item->path, 'title' => $item->title, 'access' => TRUE, 'weight' => $item->weight, 'type' => $item->type); $_menu['path index'][$item->path] = $item->mid; } } } // Establish parent-child relationships. foreach ($_menu['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']; do { $parent = substr($parent, 0, strrpos($parent, '/')); } while ($parent && !$_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; } } } // Prepare to display trees to the user as required. _menu_build_visible_tree(); } /** * 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(); if (isset($menu['items'][$mid]['access'])) { return $menu['items'][$mid]['access']; } // Follow the path up to find the actual callback. $path = $menu['items'][$mid]['path']; while ($path && (!$menu['path index'][$path] || !$menu['items'][$menu['path index'][$path]]['callback'])) { $path = substr($path, 0, strrpos($path, '/')); } $callback_mid = $menu['path index'][$path]; return $menu['items'][$callback_mid]['access']; } /** * 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 ($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); foreach ($children as $mid) { $_menu['visible'][$mid]['pid'] = $pid; } return array($pid); } else { return $children; } } return array(); } ?>