2002-12-24 15:40:32 +00:00
|
|
|
<?php
|
2003-12-27 21:29:20 +00:00
|
|
|
/* $Id$ */
|
|
|
|
|
2003-12-17 22:15:35 +00:00
|
|
|
/**
|
|
|
|
* @defgroup menu Menu system
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* Flags for use in the "type" attribute of menu items.
|
|
|
|
*/
|
|
|
|
define('MENU_IS_ROOT', 0x0001);
|
|
|
|
define('MENU_VISIBLE_IN_TREE', 0x0002);
|
|
|
|
define('MENU_VISIBLE_IN_BREADCRUMB', 0x0004);
|
|
|
|
define('MENU_VISIBLE_IF_HAS_CHILDREN', 0x0008);
|
|
|
|
define('MENU_MODIFIABLE_BY_ADMIN', 0x0010);
|
|
|
|
define('MENU_MODIFIED_BY_ADMIN', 0x0020);
|
|
|
|
define('MENU_CREATED_BY_ADMIN', 0x0040);
|
|
|
|
define('MENU_IS_LOCAL_TASK', 0x0080);
|
|
|
|
define('MENU_IS_LOCAL_SUBTASK', 0x0100);
|
2003-12-17 22:15:35 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* Normal menu items show up in the menu tree and can be moved/hidden by
|
|
|
|
* the administrator.
|
|
|
|
*/
|
|
|
|
define('MENU_NORMAL_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_MODIFIABLE_BY_ADMIN);
|
2004-04-15 20:49:42 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* Item groupings are used for pages like "node/add" that simply list
|
|
|
|
* subpages to visit.
|
|
|
|
*/
|
|
|
|
define('MENU_ITEM_GROUPING', MENU_VISIBLE_IF_HAS_CHILDREN | MENU_VISIBLE_IN_BREADCRUMB | MENU_MODIFIABLE_BY_ADMIN);
|
2004-04-21 13:56:38 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* Callbacks simply register a path so that the correct function is fired
|
|
|
|
* when the URL is accessed.
|
|
|
|
*/
|
|
|
|
define('MENU_CALLBACK', MENU_VISIBLE_IN_BREADCRUMB);
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2003-09-28 10:51:40 +00:00
|
|
|
/**
|
2004-06-18 15:04:37 +00:00
|
|
|
* Dynamic menu items change frequently, and so should not be stored in the
|
|
|
|
* database for administrative customization.
|
|
|
|
*/
|
|
|
|
define('MENU_DYNAMIC_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB);
|
2003-01-14 20:33:42 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* Modules may "suggest" menu items that the administrator may enable.
|
|
|
|
*/
|
|
|
|
define('MENU_SUGGESTED_ITEM', MENU_MODIFIABLE_BY_ADMIN);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Local tasks are rendered as tabs by default.
|
|
|
|
*/
|
|
|
|
define('MENU_LOCAL_TASK', MENU_IS_LOCAL_TASK);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Local subtasks are rendered as a horizontal listing below the tabs by default.
|
|
|
|
*/
|
|
|
|
define('MENU_LOCAL_SUBTASK', MENU_IS_LOCAL_SUBTASK);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Custom items are those defined by the administrator.
|
|
|
|
*/
|
|
|
|
define('MENU_CUSTOM_ITEM', MENU_VISIBLE_IN_TREE | MENU_VISIBLE_IN_BREADCRUMB | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Custom menus are those defined by the administrator.
|
|
|
|
*/
|
|
|
|
define('MENU_CUSTOM_MENU', MENU_IS_ROOT | MENU_VISIBLE_IN_TREE | MENU_CREATED_BY_ADMIN | MENU_MODIFIABLE_BY_ADMIN);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status codes for menu callbacks.
|
|
|
|
*/
|
|
|
|
define('MENU_FOUND', 1);
|
|
|
|
define('MENU_NOT_FOUND', 2);
|
|
|
|
define('MENU_ACCESS_DENIED', 3);
|
2004-04-15 20:49:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
* - 'path' - The Drupal path to the menu item. A link to a particular item
|
|
|
|
* can thus be constructed with l($item['title'], $item['path']).
|
|
|
|
* - '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;
|
|
|
|
|
|
|
|
if (!isset($_menu['items'])) {
|
2004-06-18 15:04:37 +00:00
|
|
|
_menu_build();
|
2004-04-15 20:49:42 +00:00
|
|
|
}
|
2004-04-27 19:35:10 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
return $_menu;
|
2003-02-20 22:44:51 +00:00
|
|
|
}
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2003-09-28 10:51:40 +00:00
|
|
|
/**
|
2004-06-18 15:04:37 +00:00
|
|
|
* 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.
|
2003-09-28 10:51:40 +00:00
|
|
|
*/
|
2004-06-18 15:04:37 +00:00
|
|
|
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;
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
$prev_id = $temp_id;
|
|
|
|
$temp_id--;
|
|
|
|
}
|
|
|
|
}
|
2003-09-26 10:04:09 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
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 excusively 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 && (!$menu['path index'][$path] || !$menu['items'][$menu['path index'][$path]]['callback'])) {
|
2004-04-21 13:56:38 +00:00
|
|
|
$path = substr($path, 0, strrpos($path, '/'));
|
|
|
|
}
|
|
|
|
$mid = $menu['path index'][$path];
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
if (!is_string($menu['items'][$mid]['callback'])) {
|
|
|
|
return MENU_NOT_FOUND;
|
2003-09-26 10:04:09 +00:00
|
|
|
}
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
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'] ? $menu['items'][$mid]['callback arguments'] : array();
|
|
|
|
$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;
|
2003-09-26 10:04:09 +00:00
|
|
|
}
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2004-04-15 20:49:42 +00:00
|
|
|
* Returns the ID of the active menu item.
|
2003-09-28 11:08:17 +00:00
|
|
|
*/
|
|
|
|
function menu_get_active_item() {
|
2003-12-16 21:06:34 +00:00
|
|
|
return menu_set_active_item();
|
|
|
|
}
|
|
|
|
|
2003-12-17 22:15:35 +00:00
|
|
|
/**
|
|
|
|
* Sets the path of the active menu item.
|
|
|
|
*/
|
2003-12-16 21:06:34 +00:00
|
|
|
function menu_set_active_item($path = NULL) {
|
2004-04-15 20:49:42 +00:00
|
|
|
static $stored_mid;
|
|
|
|
$menu = menu_get_menu();
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
if (is_null($stored_mid) || !empty($path)) {
|
2003-12-16 21:06:34 +00:00
|
|
|
if (empty($path)) {
|
2004-04-15 20:49:42 +00:00
|
|
|
$path = $_GET['q'];
|
2003-12-16 21:06:34 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$_GET['q'] = $path;
|
|
|
|
}
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
while ($path && !$menu['path index'][$path]) {
|
|
|
|
$path = substr($path, 0, strrpos($path, '/'));
|
2003-02-20 22:44:51 +00:00
|
|
|
}
|
2004-04-15 20:49:42 +00:00
|
|
|
$stored_mid = $menu['path index'][$path];
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
return $stored_mid;
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* 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_LOCAL_TASK) || ($menu['items'][$mid]['type'] & MENU_LOCAL_SUBTASK))) {
|
|
|
|
$mid = $menu['items'][$mid]['pid'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($mid) {
|
|
|
|
return $mid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2003-12-08 06:32:19 +00:00
|
|
|
* Returns the title of the active menu item.
|
|
|
|
*/
|
2003-09-28 10:51:40 +00:00
|
|
|
function menu_get_active_title() {
|
2004-04-15 20:49:42 +00:00
|
|
|
$menu = menu_get_menu();
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
if ($mid = menu_get_active_nontask_item()) {
|
2004-04-15 20:49:42 +00:00
|
|
|
return ucfirst($menu['items'][$mid]['title']);
|
2003-09-28 10:51:40 +00:00
|
|
|
}
|
|
|
|
}
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2003-12-08 06:32:19 +00:00
|
|
|
* Returns the help associated with the active menu item.
|
|
|
|
*/
|
2003-09-28 10:51:40 +00:00
|
|
|
function menu_get_active_help() {
|
2004-06-18 15:04:37 +00:00
|
|
|
$path = $_GET['q'];
|
|
|
|
$output = '';
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
if (!_menu_item_is_accessible(menu_get_active_item())) {
|
|
|
|
// Don't return help text for areas the user cannot access.
|
|
|
|
return;
|
|
|
|
}
|
2003-12-18 21:13:17 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
$return = module_invoke_all('help', $path);
|
|
|
|
foreach ($return as $item) {
|
|
|
|
if (!empty($item)) {
|
|
|
|
$output .= $item ."\n";
|
2003-10-09 19:24:09 +00:00
|
|
|
}
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
2004-06-18 15:04:37 +00:00
|
|
|
return $output;
|
2003-09-28 10:51:40 +00:00
|
|
|
}
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
|
|
|
* Returns an array of rendered menu items in the active breadcrumb trail.
|
|
|
|
*/
|
|
|
|
function menu_get_active_breadcrumb() {
|
2004-04-15 20:49:42 +00:00
|
|
|
$menu = menu_get_menu();
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
$links[] = l(t('Home'), '');
|
2003-09-28 10:51:40 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
$trail = _menu_get_trail($_GET['q']);
|
2004-04-15 20:49:42 +00:00
|
|
|
foreach ($trail as $mid) {
|
2004-06-18 15:04:37 +00:00
|
|
|
if ($menu['items'][$mid]['type'] & MENU_VISIBLE_IN_BREADCRUMB) {
|
|
|
|
$links[] = theme('menu_item', $mid);
|
2004-04-15 20:49:42 +00:00
|
|
|
}
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
// The last item in the trail is the page title; don't display it here.
|
|
|
|
array_pop($links);
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
return $links;
|
2003-09-28 10:51:40 +00:00
|
|
|
}
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2004-06-18 15:04:37 +00:00
|
|
|
* Returns true when the menu item is in the active trail.
|
2003-09-28 11:08:17 +00:00
|
|
|
*/
|
2004-06-18 15:04:37 +00:00
|
|
|
function menu_in_active_trail($mid) {
|
|
|
|
static $trail;
|
2003-09-28 10:51:40 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
if (empty($trail)) {
|
|
|
|
$trail = _menu_get_trail($_GET['q']);
|
2003-09-28 11:08:17 +00:00
|
|
|
}
|
2004-04-21 13:56:38 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
return in_array($mid, $trail);
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
/**
|
2004-06-18 15:04:37 +00:00
|
|
|
* Populate the database representation of the menu.
|
|
|
|
*
|
|
|
|
* This need only be called at the start of pages that modify the menu.
|
2004-04-15 20:49:42 +00:00
|
|
|
*/
|
2004-06-18 15:04:37 +00:00
|
|
|
function menu_rebuild() {
|
|
|
|
cache_clear_all();
|
|
|
|
_menu_build();
|
2004-04-15 20:49:42 +00:00
|
|
|
$menu = menu_get_menu();
|
2003-11-08 09:56:22 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
$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'];
|
|
|
|
}
|
2003-11-08 09:56:22 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
// 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']);
|
|
|
|
}
|
2004-04-21 13:56:38 +00:00
|
|
|
}
|
2004-06-18 15:04:37 +00:00
|
|
|
|
|
|
|
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']);
|
2004-04-21 13:56:38 +00:00
|
|
|
}
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
// Rebuild the menu to account for any changes.
|
|
|
|
_menu_build();
|
2003-11-08 09:56:22 +00:00
|
|
|
}
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/** @} end of "menu" function group */
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2004-06-18 15:04:37 +00:00
|
|
|
* @addtogroup themeable
|
|
|
|
* @{
|
2003-09-28 11:08:17 +00:00
|
|
|
*/
|
2002-12-24 15:40:32 +00:00
|
|
|
|
2004-02-15 14:56:50 +00:00
|
|
|
/**
|
2004-04-15 20:49:42 +00:00
|
|
|
* Returns a rendered menu tree.
|
2004-02-15 14:56:50 +00:00
|
|
|
*/
|
2004-06-18 15:04:37 +00:00
|
|
|
function theme_menu_tree($pid = 1, $all = FALSE) {
|
2004-04-15 20:49:42 +00:00
|
|
|
$menu = menu_get_menu();
|
|
|
|
$output = '';
|
2004-02-15 14:56:50 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
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 .= "<li class=\"$style\">";
|
2004-06-18 15:04:37 +00:00
|
|
|
$output .= theme('menu_item', $mid);
|
|
|
|
if ($all || menu_in_active_trail($mid)) {
|
|
|
|
$output .= theme('menu_tree', $mid);
|
2004-02-15 14:56:50 +00:00
|
|
|
}
|
2004-04-15 20:49:42 +00:00
|
|
|
$output .= "</li>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($output != '') {
|
|
|
|
$output = "\n<ul>\n$output\n</ul>\n";
|
2004-02-15 14:56:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
return $output;
|
2004-02-15 14:56:50 +00:00
|
|
|
}
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
/**
|
|
|
|
* 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 = "<ul class=\"tabs primary\">\n";
|
|
|
|
$output .= theme('menu_local_task', $mid, $active);
|
|
|
|
$output .= implode($tabs);
|
|
|
|
$output .= "</ul>\n";
|
2004-06-19 08:24:14 +00:00
|
|
|
$output .= theme('menu_local_subtasks', $mid);
|
2004-06-18 15:04:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
2004-06-19 08:24:14 +00:00
|
|
|
return '<li class="active">'. theme('menu_item', $mid) ."</li>\n";
|
2004-06-18 15:04:37 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return '<li>'. theme('menu_item', $mid) ."</li>\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 "<ul class=\"tabs secondary\">$tabs</ul>\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));
|
|
|
|
}
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2004-04-15 20:49:42 +00:00
|
|
|
* Build the menu by querying both modules and the database.
|
2003-09-28 11:08:17 +00:00
|
|
|
*/
|
2004-06-18 15:04:37 +00:00
|
|
|
function _menu_build() {
|
2004-04-15 20:49:42 +00:00
|
|
|
global $_menu;
|
|
|
|
global $user;
|
2003-02-20 22:44:51 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
// Start from a clean slate.
|
|
|
|
$_menu = array();
|
|
|
|
|
|
|
|
$_menu['path index'] = array();
|
|
|
|
// Set up items array, including default "Navigation" menu.
|
2004-06-18 15:04:37 +00:00
|
|
|
$_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');
|
2004-04-15 20:49:42 +00:00
|
|
|
|
|
|
|
// Menu items not in the DB get temporary negative IDs.
|
|
|
|
$temp_mid = -1;
|
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
foreach ($menu_item_list as $item) {
|
|
|
|
if (!isset($item['type'])) {
|
|
|
|
$item['type'] = MENU_NORMAL_ITEM;
|
|
|
|
}
|
2004-04-15 20:49:42 +00:00
|
|
|
$mid = $temp_mid;
|
2004-06-18 15:04:37 +00:00
|
|
|
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;
|
2004-04-15 20:49:42 +00:00
|
|
|
|
|
|
|
$temp_mid--;
|
2003-09-28 10:51:40 +00:00
|
|
|
}
|
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
// 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.
|
2004-06-18 15:04:37 +00:00
|
|
|
if ($old_mid = $_menu['path index'][$item->path]) {
|
2004-04-15 20:49:42 +00:00
|
|
|
$_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.
|
2004-06-18 15:04:37 +00:00
|
|
|
if ($item->type & MENU_MODIFIED_BY_ADMIN) {
|
2004-04-15 20:49:42 +00:00
|
|
|
$_menu['items'][$item->mid]['title'] = $item->title;
|
|
|
|
$_menu['items'][$item->mid]['pid'] = $item->pid;
|
|
|
|
$_menu['items'][$item->mid]['weight'] = $item->weight;
|
2004-06-18 15:04:37 +00:00
|
|
|
$_menu['items'][$item->mid]['type'] = $item->type;
|
2003-02-20 22:44:51 +00:00
|
|
|
}
|
|
|
|
}
|
2004-06-18 15:04:37 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2004-04-15 20:49:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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, '/'));
|
2003-11-21 19:08:46 +00:00
|
|
|
}
|
2004-04-15 20:49:42 +00:00
|
|
|
while ($parent && !$_menu['path index'][$parent]);
|
|
|
|
|
|
|
|
$pid = $parent ? $_menu['path index'][$parent] : 1;
|
|
|
|
$_menu['items'][$mid]['pid'] = $pid;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$pid = $item['pid'];
|
2002-12-30 15:54:06 +00:00
|
|
|
}
|
2003-10-14 18:08:33 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2003-10-14 18:08:33 +00:00
|
|
|
}
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
// Prepare to display trees to the user as required.
|
2004-06-18 15:04:37 +00:00
|
|
|
_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'];
|
2002-12-24 15:40:32 +00:00
|
|
|
}
|
|
|
|
|
2003-09-28 11:08:17 +00:00
|
|
|
/**
|
2004-04-15 20:49:42 +00:00
|
|
|
* 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.
|
2003-09-28 11:08:17 +00:00
|
|
|
*/
|
2004-06-18 15:04:37 +00:00
|
|
|
function _menu_build_visible_tree($pid = 0) {
|
2004-04-15 20:49:42 +00:00
|
|
|
global $_menu;
|
2003-03-12 21:46:14 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
if (isset($_menu['items'][$pid])) {
|
|
|
|
$parent = $_menu['items'][$pid];
|
2003-09-28 10:51:40 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
$children = array();
|
|
|
|
if ($parent['children']) {
|
|
|
|
usort($parent['children'], '_menu_sort');
|
|
|
|
foreach ($parent['children'] as $mid) {
|
2004-06-18 15:04:37 +00:00
|
|
|
$children = array_merge($children, _menu_build_visible_tree($mid));
|
2004-05-08 07:17:47 +00:00
|
|
|
}
|
|
|
|
}
|
2004-06-18 15:04:37 +00:00
|
|
|
$visible = ($parent['type'] & MENU_VISIBLE_IN_TREE) ||
|
|
|
|
($parent['type'] & MENU_VISIBLE_IF_HAS_CHILDREN && count($children) > 0);
|
|
|
|
$allowed = _menu_item_is_accessible($pid);
|
2004-05-08 07:17:47 +00:00
|
|
|
|
2004-06-18 15:04:37 +00:00
|
|
|
if (($parent['type'] & MENU_IS_ROOT) || ($visible && $allowed)) {
|
2004-04-15 20:49:42 +00:00
|
|
|
$_menu['visible'][$pid] = array('title' => $parent['title'], 'path' => $parent['path'], 'children' => $children);
|
2004-04-21 13:56:38 +00:00
|
|
|
foreach ($children as $mid) {
|
|
|
|
$_menu['visible'][$mid]['pid'] = $pid;
|
|
|
|
}
|
2004-04-15 20:49:42 +00:00
|
|
|
return array($pid);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return $children;
|
|
|
|
}
|
|
|
|
}
|
2003-09-28 10:51:40 +00:00
|
|
|
|
2004-04-15 20:49:42 +00:00
|
|
|
return array();
|
|
|
|
}
|
2003-09-28 10:51:40 +00:00
|
|
|
|
2003-02-20 22:44:51 +00:00
|
|
|
?>
|