- Patch #113603 by chx: first crack at re-implementing tabs.

6.x
Dries Buytaert 2007-02-11 09:30:51 +00:00
parent 373a071fc4
commit a1e6728a46
18 changed files with 397 additions and 214 deletions

View File

@ -171,6 +171,19 @@ define('MENU_SITE_OFFLINE', 4);
* @} End of "Menu status codes".
*/
/**
* @Name Menu operations
* @{
* Menu helper possible operations.
*/
define('MENU_HANDLE_REQUEST', 0);
define('MENU_RENDER_LINK', 1);
/**
* @} End of "Menu helper directions
*/
/**
* Returns the ancestors (and relevant placeholders) for any given path.
*
@ -195,7 +208,7 @@ define('MENU_SITE_OFFLINE', 4);
* array('node', '12345', 'edit').
* @return
* An array which contains the ancestors and placeholders. Placeholders
* simply contain as many %s as the ancestors.
* simply contain as many '%s' as the ancestors.
*/
function menu_get_ancestors($parts) {
$n1 = count($parts);
@ -273,10 +286,10 @@ function menu_unserialize($data, $map) {
* with keys like title, access callback, access arguments etc.
*/
function menu_set_item($path, $item) {
menu_get_item($path, TRUE, $item);
menu_get_item($path, $item);
}
function menu_get_item($path = NULL, $execute = TRUE, $item = NULL) {
function menu_get_item($path = NULL, $item = NULL) {
static $items;
if (!isset($path)) {
$path = $_GET['q'];
@ -289,14 +302,13 @@ function menu_get_item($path = NULL, $execute = TRUE, $item = NULL) {
$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);
list($item->access, $map) = _menu_translate($item, $map);
if ($map === FALSE) {
$items[$path] = FALSE;
return FALSE;
}
if ($execute) {
$item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($parts, $item->number_parts));
}
$item->map = $map;
$item->page_arguments = array_merge(menu_unserialize($item->page_arguments, $map), array_slice($parts, $item->number_parts));
}
$items[$path] = $item;
}
@ -313,45 +325,113 @@ function menu_execute_active_handler() {
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;
/**
* Handles dynamic path translation and menu access control.
*
* When a user arrives on a page such as node/5, this function determines
* what "5" corresponds to, by inspecting the page's menu path definition,
* node/%node. This will call node_load(5) to load the corresponding node
* object.
*
* It also works in reverse, to allow the display of tabs and menu items which
* contain these dynamic arguments, translating node/%node to node/5.
* This operation is called MENU_RENDER_LINK.
*
* @param $item
* A menu item object
* @param $map
* An array of path arguments (ex: array('node', '5'))
* @param $operation
* The path translation operation to perform:
* - MENU_HANDLE_REQUEST: An incoming page reqest; map with appropriate callback.
* - MENU_RENDER_LINK: Render an internal path as a link.
* @return
* Returns an array. The first value is the access, the second is the map
* with objects loaded where appropriate and the third is the path ready for
* printing.
*/
function _menu_translate($item, $map, $operation = MENU_HANDLE_REQUEST) {
$path = '';
// Check if there are dynamic arguments in the path that need to be calculated.
if ($item->load_functions || ($operation == MENU_RENDER_LINK && $item->to_arg_functions)) {
$load_functions = unserialize($item->load_functions);
$to_arg_functions = unserialize($item->to_arg_functions);
$path_map = ($operation == MENU_HANDLE_REQUEST) ? $map : explode('/', $item->path);
foreach ($load_functions as $index => $load_function) {
// Translate place-holders into real values.
if ($operation == MENU_RENDER_LINK) {
if (isset($to_arg_functions[$index])) {
$to_arg_function = $to_arg_functions[$index];
$return = $to_arg_function(!empty($map[$index]) ? $map[$index] : '');
if (!empty($map[$index]) || isset($return)) {
$path_map[$index] = $return;
}
else {
unset($path_map[$index]);
}
}
else {
$path_map[$index] = isset($map[$index]) ? $map[$index] : '';
}
}
// We now have a real path regardless of operation, map it.
if ($load_function) {
$return = $load_function(isset($path_map[$index]) ? $path_map[$index] : '');
// If callback returned an error or there is no callback, trigger 404.
if ($return === FALSE) {
return array(FALSE, FALSE, '');
}
$map[$index] = $return;
}
}
if ($operation != MENU_HANDLE_REQUEST) {
// Re-join the path with the new replacement value.
$path = implode('/', $path_map);
}
}
else {
$path = $item->path;
}
// Determine access callback, which will decide whether or not the current user has
// access to this path.
$callback = $item->access_callback;
// Check for a TRUE or FALSE value.
if (is_numeric($callback)) {
return $callback;
return array($callback, $map, $path);
}
$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]);
$access = (count($arguments) == 1) ? user_access($arguments[0]) : user_access($arguments[0], $arguments[1]);
return array($access, $map, $path);
}
return call_user_func_array($callback, $arguments);
return array(call_user_func_array($callback, $arguments), $map, $path);
}
/**
* Returns a rendered menu tree.
*/
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;
if ($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;
}
}
function _menu_tree($result = NULL, $depth = 0, $link = array('link' => '', 'has_children' => FALSE)) {
static $original_map;
$remnant = array('link' => '', 'has_children' => FALSE);
$tree = '';
$map = arg(NULL);
while ($item = db_fetch_object($result)) {
$map = arg(NULL, $item->path);
if (!_menu_access($item, $map)) {
list($access, , $path) = _menu_translate($item, $map, MENU_RENDER_LINK);
if (!$access) {
continue;
}
$menu_link = array('link' => $item->menu_link, 'has_children' => $item->has_children);
$menu_link = array('link' => l($item->title, $path), '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);
@ -385,7 +465,11 @@ function theme_menu_tree($link, $tree) {
* Generate the HTML for a menu link.
*/
function theme_menu_link($link, $menu = '') {
return '<li class="'. ($menu ? 'expanded' : ($link['has_children'] ? 'collapsed' : 'leaf')) .'">'. $link['link'] . $menu .'</li>' . "\n";
return '<li class="'. ($menu ? 'expanded' : (empty($link['has_children']) ? 'leaf': 'collapsed')) .'">'. $link['link'] . $menu .'</li>' . "\n";
}
function theme_menu_local_task($link, $active = FALSE) {
return '<li '. ($active ? 'class="active" ' : ''). '>'. $link .'</li>';
}
/**
@ -430,47 +514,96 @@ function menu_rebuild() {
$function($menu);
}
$mid = 1;
// First pass.
// First pass: separate callbacks from pathes, making pathes ready for
// matching. Calculate fitness, and fill some default values.
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
// We store the highest index of parts here to save some work in the fit
// calculation loop.
$slashes = $number_parts - 1;
// If there is no %, it fits maximally.
if (strpos($path, '%') === FALSE) {
$fit = (1 << $number_parts) - 1;
}
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);
$fit = 0;
$load_functions = array();
$to_arg_functions = array();
// extract functions
foreach ($parts as $k => $part) {
$match = FALSE;
if (preg_match('/^%([a-z_]*)$/', $part, $matches)) {
if (empty($matches[1])) {
$match = TRUE;
}
else {
if (function_exists($matches[1] .'_load')) {
$load_functions[$k] = $matches[1] .'_load';
$match = TRUE;
}
if (function_exists($matches[1] .'_to_arg')) {
$to_arg_functions[$k] = $matches[1].'_to_arg';
$match = TRUE;
}
if (!isset($load_functions[$k]) && isset($to_arg_functions[$k])) {
$load_functions[$k] = FALSE;
}
}
}
if ($match) {
$parts[$k] = '%';
}
else {
$fit |= 1 << ($slashes - $k);
}
}
if (!isset($item['_visible'])) {
$item['_visible'] = (!isset($item['type']) || ($item['type'] & MENU_VISIBLE_IN_TREE)) ? 1 : 0;
$item['load_functions'] = empty($load_functions) ? '' : serialize($load_functions);
$item['to_arg_functions'] = empty($to_arg_functions) ? '' : serialize($to_arg_functions);
// If there is no %, it fits maximally.
if (!$fit) {
$fit = (1 << $number_parts) - 1;
$move = FALSE;
}
else {
$move = TRUE;
}
$item += array(
'title' => '',
'weight' => 0,
'type' => MENU_NORMAL_ITEM,
'_number_parts' => $number_parts,
'_parts' => $parts,
'_fit' => $fit,
'_mid' => $mid++,
);
$item += array(
'_visible' => (bool)($item['type'] & MENU_VISIBLE_IN_TREE),
'_tab' => (bool)($item['type'] & MENU_IS_LOCAL_TASK),
);
if ($move) {
$new_path = implode('/', $item['_parts']);
unset($menu[$path]);
}
else {
$new_path = $path;
}
$menu[$new_path] = $item;
}
// Second pass: find visible parents and prepare for sorting.
foreach ($menu as $path => $item) {
$item = &$menu[$path];
$number_parts = $item['_number_parts'];
$parents = array($item['_mid']);
if ($item['_visible'] && isset($item['parent'])) {
$parent_parts = explode('/', $item['parent'], 6);
$slashes = count($parent_parts) - 1;
}
else {
$parent_parts = $item['_parts'];
$slashes = $number_parts -1;
}
$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));
$parent_path = implode('/', array_slice($parent_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++;
@ -487,22 +620,16 @@ function menu_rebuild() {
$parents = implode(',', array_reverse($parents));
// Store variables and set defaults.
$item += array(
'_fit' => $fit,
'_number_parts' => $number_parts,
'_parts' => $parts,
'_pid' => 0,
'_depth' => $depth,
'_depth' => $item['_visible'] ? $depth : $number_parts,
'_parents' => $parents,
'_has_children' => 0,
'title' => '',
'weight' => 0,
'type' => MENU_NORMAL_ITEM,
);
$sort[$path] = ($item['_visible'] ? $depth : $number_parts) . sprintf('%05d', $item['weight']) . $item['title'];
$sort[$path] = $item['_depth'] . sprintf('%05d', $item['weight']) . $item['title'];
unset($item);
}
array_multisort($sort, $menu);
// Second pass: calculate ancestors, vancode and store into the database.
// Third 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--) {
@ -512,7 +639,7 @@ function menu_rebuild() {
// 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) {
foreach (array('access', '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"])) {
@ -525,9 +652,6 @@ function menu_rebuild() {
if (!isset($item['access callback'])) {
$menu[$path]['access callback'] = isset($item['access arguments']) ? 'user_access' : 0;
}
if (!isset($item['map callback']) && isset($item['map arguments'])) {
$item['map callback'] = 'menu_map';
}
if (is_bool($item['access callback'])) {
$item['access callback'] = intval($item['access callback']);
}
@ -538,42 +662,39 @@ function menu_rebuild() {
}
$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);
}
else {
$vancode = '';
$link = '';
}
$tab = ($item['type'] & MENU_IS_LOCAL_TASK) ? 1 : 0;
$default_tab = $item['type'] == MENU_DEFAULT_LOCAL_TASK;
if (!isset($item['parent'])) {
if ($tab) {
if ($item['_tab']) {
if (!isset($item['parent'])) {
$item['parent'] = implode('/', array_slice($item['_parts'], 0, $item['_number_parts'] - 1));
}
else {
$item['parent'] = $path;
}
}
else {
// Non-tab items specified the parent for visible links, and it's
// stored in parents, parent stores the tab parent.
$item['parent'] = $path;
}
$insert_item = $item + array(
'access arguments' => array(),
'access callback' => '',
'page arguments' => array(),
'page callback' => '',
'map arguments' => array(),
'map callback' => '',
);
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, tab, default_tab, title, parent)
VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, '%s', %d, %d, %d, %d, '%s', '%s')",
$insert_item['_mid'], $insert_item['_pid'], $path, $insert_item['access callback'],
serialize($insert_item['access arguments']), $insert_item['page callback'],
serialize($insert_item['page arguments']), $insert_item['map callback'],
serialize($insert_item['map arguments']), $insert_item['_fit'],
$insert_item['_number_parts'], $vancode .'+', $link, $insert_item['_visible'],
$insert_item['_parents'], $insert_item['_depth'], $insert_item['_has_children'],
$tab, $default_tab, $insert_item['title'], $insert_item['parent']);
mid, pid, path, load_functions, to_arg_functions,
access_callback, access_arguments, page_callback, page_arguments, fit,
number_parts, vancode, visible, parents, depth, has_children, tab, title, parent, type)
VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s')",
$insert_item['_mid'], $insert_item['_pid'], $path,
$insert_item['load_functions'], $insert_item['to_arg_functions'],
$insert_item['access callback'], serialize($insert_item['access arguments']),
$insert_item['page callback'], serialize($insert_item['page arguments']),
$insert_item['_fit'], $insert_item['_number_parts'], $vancode .'+',
$insert_item['_visible'], $insert_item['_parents'], $insert_item['_depth'],
$insert_item['_has_children'], $item['_tab'], $insert_item['title'],
$insert_item['parent'], $insert_item['type']);
unset($item);
}
}
@ -590,32 +711,79 @@ function menu_primary_links() {
function menu_secondary_links() {
}
function menu_primary_local_tasks() {
$router_item = menu_get_item();
$result = db_query("SELECT * FROM {menu} WHERE parent = '%s' AND tab = 1 ORDER BY vancode", $router_item->parent);
$tabs = array();
while ($item = db_fetch_object($result)) {
$map = explode('/', $item->path);
foreach ($map as $key => $value) {
if ($value == '%') {
$map[$key] = arg($key);
/**
* Collects the local tasks (tabs) for a given level.
*
* @param $level
The level of tasks you ask for. Primary tasks are 0, secondary are 1...
* @return
* An array of links to the tabs.
*/
function menu_local_tasks($level = 0) {
static $tabs = array(), $parents = array(), $parents_done = array();
if (empty($tabs)) {
$router_item = menu_get_item();
$map = arg(NULL);
do {
// Tabs are router items that have the same parent. If there is a new
// parent, let's add it the queue.
if (!empty($router_item->parent)) {
$parents[] = $router_item->parent;
// Do not add the same item twice.
$router_item->parent = '';
}
}
$path = implode('/', $map);
if (_menu_access($item, $map, TRUE)) {
$link = l($item->title, $path);
if ((!$router_item->tab && $item->default_tab) || ($path == $_GET['q'])) {
$tabs[] = array('class' => 'active', 'data' => $link);
$parent = array_shift($parents);
// Do not process the same parent twice.
if (isset($parents_done[$parent])) {
continue;
}
else {
$tabs[] = $link;
// This loads all the tabs.
$result = db_query("SELECT * FROM {menu} WHERE parent = '%s' AND tab = 1 ORDER BY vancode", $parent);
$tabs_current = '';
while ($item = db_fetch_object($result)) {
// This call changes the path from for example user/% to user/123 and
// also determines whether we are allowed to access it.
list($access, , $path) = _menu_translate($item, $map, MENU_RENDER_LINK);
if ($access) {
$depth = $item->depth;
$link = l($item->title, $path);
// We check for the active tab.
if ($item->path == $router_item->path || (!$router_item->tab && $item->type == MENU_DEFAULT_LOCAL_TASK) || $path == $_GET['q']) {
$tabs_current .= theme('menu_local_task', $link, TRUE);
// Let's try to find the router item one level up.
$next_router_item = db_fetch_object(db_query("SELECT path, tab, parent FROM {menu} WHERE path = '%s'", $item->parent));
// We will need to inspect one level down.
$parents[] = $item->path;
}
else {
$tabs_current .= theme('menu_local_task', $link);
}
}
}
}
// If there are tabs, let's add them
if ($tabs_current) {
$tabs[$depth] = $tabs_current;
}
$parents_done[$parent] = TRUE;
if (isset($next_router_item)) {
$router_item = $next_router_item;
}
unset($next_router_item);
} while ($parents);
// Sort by depth
ksort($tabs);
// Remove the depth, we are interested only in their relative placement.
$tabs = array_values($tabs);
}
return theme('item_list', $tabs, NULL, 'ul', array('class' => 'tabs primary'));
return isset($tabs[$level]) ? $tabs[$level] : array();
}
function menu_primary_local_tasks() {
return menu_local_tasks();
}
function menu_secondary_local_tasks() {
return menu_local_tasks(1);
}
function menu_set_active_item() {
@ -631,4 +799,4 @@ function menu_get_active_breadcrumb() {
function menu_get_active_title() {
$item = menu_get_item();
return $item->title;
}
}

View File

@ -34,4 +34,4 @@ elseif (isset($return)) {
}
drupal_page_footer();
drupal_page_footer();

View File

@ -51,19 +51,17 @@ function aggregator_menu() {
'type' => MENU_LOCAL_TASK,
'parent' => 'admin/content/aggregator',
);
$items['admin/content/aggregator/remove/%'] = array(
$items['admin/content/aggregator/remove/%aggregator_feed'] = array(
'title' => t('Remove items'),
'page callback' => 'aggregator_admin_remove_feed',
'page arguments' => array(4),
'map arguments' => array('aggregator_get_feed', 4),
'access arguments' => array('administer news feeds'),
'type' => MENU_CALLBACK,
);
$items['admin/content/aggregator/update/%'] = array(
$items['admin/content/aggregator/update/%aggregator_feed'] = array(
'title' => t('Update items'),
'page callback' => 'aggregator_admin_refresh_feed',
'page arguments' => array(4),
'map arguments' => array('aggregator_get_feed', 4),
'access arguments' => array('administer news feeds'),
'type' => MENU_CALLBACK,
);
@ -137,24 +135,23 @@ function aggregator_menu() {
'weight' => 1,
);
}
$items['aggregator/sources/%'] = array(
$items['aggregator/sources/%aggregator_feed'] = array(
'page callback' => 'aggregator_page_source',
'map arguments' => array('aggregator_get_feed', 2),
'type' => MENU_CALLBACK,
);
$items['aggregator/sources/%/view'] = array(
$items['aggregator/sources/%aggregator_feed/view'] = array(
'title' => t('View'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['aggregator/sources/%/categorize'] = array(
$items['aggregator/sources/%aggregator_feed/categorize'] = array(
'title' => t('Categorize'),
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_page_source'),
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
);
$items['aggregator/sources/%/configure'] = array(
$items['aggregator/sources/%aggregator_feed/configure'] = array(
'title' => t('Configure'),
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_feed', 2),
@ -162,20 +159,18 @@ function aggregator_menu() {
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/content/aggregator/edit/feed/%'] = array(
$items['admin/content/aggregator/edit/feed/%aggregator_feed'] = array(
'title' => t('Edit feed'),
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_feed', 5),
'access arguments' => array('administer news feeds'),
'map arguments' => array('aggregator_get_feed', 5),
'type' => MENU_CALLBACK,
);
$items['admin/content/aggregator/edit/category/%'] = array(
$items['admin/content/aggregator/edit/category/%aggregator_category'] = array(
'title' => t('Edit category'),
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_category', 5),
'access arguments' => array('administer news feeds'),
'map arguments' => array('aggregator_get_category', 5),
'type' => MENU_CALLBACK,
);
@ -964,11 +959,11 @@ function aggregator_save_item($edit) {
}
}
function aggregator_get_feed($fid) {
function aggregator_feed_load($fid) {
return db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $fid));
}
function aggregator_get_category($cid) {
function aggregator_category_load($cid) {
return db_fetch_array(db_query('SELECT * FROM {aggregator_category} WHERE cid = %d', $cid));
}
@ -1035,7 +1030,7 @@ function aggregator_page_last() {
*/
function aggregator_page_source() {
$feed = db_fetch_object(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', arg(2)));
drupal_set_title($feed->title);
drupal_set_title(check_plain($feed->title));
$info = theme('aggregator_feed', $feed);
return _aggregator_page_list('SELECT * FROM {aggregator_item} WHERE fid = '. $feed->fid .' ORDER BY timestamp DESC, iid DESC', arg(3), $info);

View File

@ -105,6 +105,9 @@ function blog_feed_last() {
* Menu callback; displays a Drupal page containing recent blog entries.
*/
function blog_page($a = NULL, $b = NULL) {
if (is_object($a)) {
$a = $a->uid;
}
if (is_numeric($a)) { // $a is a user ID
if ($b == 'feed') {
@ -256,7 +259,7 @@ function blog_menu() {
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
$items['blog/%'] = array(
$items['blog/%user_current'] = array(
'title' => t('My blog'),
'page arguments' => array(1),
'access arguments' => array('edit own blog'),

View File

@ -114,13 +114,12 @@ function book_menu() {
'page arguments' => array(2, 3),
'type' => MENU_CALLBACK,
);
$items['node/%/outline'] = array(
$items['node/%node/outline'] = array(
'title' => t('Outline'),
'page callback' => 'drupal_get_form',
'page arguments' => array('book_outline', 1),
'access callback' => '_book_outline_access',
'access arguments' => array(1),
'map arguments' => array('node_load', 1),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);

View File

@ -195,15 +195,14 @@ function comment_menu() {
'access arguments' => array('post comments'),
'type' => MENU_CALLBACK,
);
$items['comment/reply'] = array(
$items['comment/reply/%node'] = array(
'title' => t('Reply to comment'),
'page callback' => 'comment_reply',
'access callback' => 'node_access',
'access arguments' => array('view', 2),
'map arguments' => array('node_load', 2),
'type' => MENU_CALLBACK,
);
$items['node/%/%'] = array(
$items['node/%node/%'] = array(
'title' => t('View'),
'page callback' => 'node_page_view',
'page arguments' => array(1, 2),

View File

@ -84,14 +84,13 @@ function contact_menu() {
'access arguments' => array('access site-wide contact form'),
'type' => MENU_SUGGESTED_ITEM,
);
$items['user/%/contact'] = array(
$items['user/%user/contact'] = array(
'title' => t('Contact'),
'page callback' => 'contact_user_page',
'page arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'access callback' => '_contact_user_tab_access',
'access arguments' => array(1),
'map arguments' => array('user_load', 1),
'weight' => 2,
);
return $items;

View File

@ -81,26 +81,25 @@ function filter_menu() {
'access callback' => TRUE,
'type' => MENU_SUGGESTED_ITEM,
);
$items['admin/settings/filters/%'] = array(
$items['admin/settings/filters/%filter_format'] = array(
'type' => MENU_CALLBACK,
'page arguments' => array('filter_admin_format_form', 3),
'access arguments' => array('administer filters'),
'map arguments' => array('filter_formats', 3),
);
$items['admin/settings/filters/%/list'] = array(
$items['admin/settings/filters/%filter_format/list'] = array(
'title' => t('View'),
'page arguments' => array('filter_admin_format_form', 3),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/settings/filters/%/configure'] = array(
$items['admin/settings/filters/%filter_format/configure'] = array(
'title' => t('Configure'),
'page arguments' => array('filter_admin_configure', 3),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/settings/filters/%/order'] = array(
$items['admin/settings/filters/%filter_format/order'] = array(
'title' => t('Rearrange'),
'page arguments' => array('filter_admin_order', 3),
'type' => MENU_LOCAL_TASK,
@ -109,6 +108,10 @@ function filter_menu() {
return $items;
}
function filter_format_load($arg) {
return filter_formats($arg);
}
/**
* Implementation of hook_perm().
*/

View File

@ -75,19 +75,21 @@ function forum_menu() {
'type' => MENU_LOCAL_TASK,
'parent' => 'admin/content/forum',
);
$items['admin/content/forum/edit'] = array(
$items['admin/content/forum/edit/%forum_term'] = array(
'page callback' => 'forum_form_main',
'map arguments' => array('_forum_get_term', 5, array()),
'type' => MENU_CALLBACK,
);
$items['admin/content/forum/edit/container/%'] = array(
$items['admin/content/forum/edit/container/%forum_term'] = array(
'title' => t('Edit container'),
'page callback' => 'forum_form_main',
'page arguments' => array('container', 5),
'type' => MENU_CALLBACK,
);
$items['admin/content/forum/edit/forum/%'] = array(
$items['admin/content/forum/edit/forum/%forum_term'] = array(
'title' => t('Edit forum'),
'page callback' => 'forum_form_main',
'page arguments' => array('forum', 5),
'type' => MENU_CALLBACK,
);
return $items;
}
@ -96,7 +98,7 @@ function forum_init() {
drupal_add_css(drupal_get_path('module', 'forum') .'/forum.css');
}
function _forum_get_term($tid) {
function forum_term_load($tid) {
return (array)taxonomy_get_term($tid);
}
@ -295,7 +297,7 @@ function forum_block($op = 'list', $delta = 0, $edit = array()) {
*/
function forum_view(&$node, $teaser = FALSE, $page = FALSE) {
if ($page) {
$vocabulary = taxonomy_get_vocabulary(variable_get('forum_nav_vocabulary', ''));
$vocabulary = taxonomy_vocabulary_load(variable_get('forum_nav_vocabulary', ''));
// Breadcrumb navigation
$breadcrumb = array();
$breadcrumb[] = array('path' => 'forum', 'title' => $vocabulary->name);
@ -876,7 +878,7 @@ function theme_forum_display($forums, $topics, $parents, $tid, $sortby, $forum_p
global $user;
// forum list, topics list, topic browser and 'add new topic' link
$vocabulary = taxonomy_get_vocabulary(variable_get('forum_nav_vocabulary', ''));
$vocabulary = taxonomy_vocabulary_load(variable_get('forum_nav_vocabulary', ''));
$title = $vocabulary->name;
// Breadcrumb navigation:

View File

@ -1166,33 +1166,32 @@ function node_menu() {
}
}
$items['node/%'] = array(
$items['node/%node'] = array(
'title' => t('View'),
'page callback' => 'node_page_view',
'page arguments' => array(1),
'access callback' => 'node_access',
'access arguments' => array('view', 1),
'map arguments' => array('node_load', 1),
'type' => MENU_CALLBACK);
$items['node/%/view'] = array(
$items['node/%node/view'] = array(
'title' => t('View'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10);
$items['node/%/edit'] = array(
$items['node/%node/edit'] = array(
'title' => t('Edit'),
'page callback' => 'node_page_edit',
'page arguments' => array(1),
'access arguments' => array('update', 1),
'weight' => 1,
'type' => MENU_LOCAL_TASK);
$items['node/%/delete'] = array(
$items['node/%node/delete'] = array(
'title' => t('Delete'),
'page callback' => 'drupal_get_form',
'page arguments' => array('node_delete_confirm', 1),
'access arguments' => array('delete', 1),
'weight' => 1,
'type' => MENU_CALLBACK);
$items['node/%/revisions'] = array(
$items['node/%node/revisions'] = array(
'title' => t('Revisions'),
'page callback' => 'node_revisions',
'access callback' => '_node_revision_access',
@ -2464,7 +2463,7 @@ function node_update_index() {
*/
function node_form_alter($form_id, &$form) {
// Advanced node search form
if ($form_id == 'search_form' && arg(1) == 'node' && user_access('use advanced search')) {
if ($form_id == 'search_form' && $form['module']['#value'] == 'node' && user_access('use advanced search')) {
// Keyword boxes:
$form['advanced'] = array(
'#type' => 'fieldset',

View File

@ -239,14 +239,14 @@ function poll_menu() {
'type' => MENU_SUGGESTED_ITEM,
);
$items['poll/cancel/%'] = array(
$items['poll/cancel/%node'] = array(
'title' => t('Cancel'),
'page callback' => 'poll_cancel',
'page arguments' => array(2),
'access arguments' => array('cancel own vote'),
'type' => MENU_CALLBACK,
);
$items['node/%/votes'] = array(
$items['node/%node/votes'] = array(
'title' => t('Votes'),
'page callback' => 'poll_votes',
'access callback' => '_poll_menu_access',
@ -254,7 +254,7 @@ function poll_menu() {
'weight' => 3,
'type' => MENU_LOCAL_TASK,
);
$items['node/%/results'] = array(
$items['node/%node/results'] = array(
'title' => t('Results'),
'page callback' => 'poll_results',
'access callback' => '_poll_menu_access',

View File

@ -355,6 +355,7 @@ function profile_field_form_submit($form_id, $form_values) {
drupal_set_message(t('The field has been updated.'));
}
cache_clear_all();
menu_rebuild();
return 'admin/user/profile';
}

View File

@ -140,7 +140,6 @@ function search_menu() {
$items['search'] = array(
'title' => t('Search'),
'page callback' => 'search_view',
'page arguments' => array('node'),
'access arguments' => array('search content'),
'type' => MENU_SUGGESTED_ITEM,
);
@ -167,13 +166,14 @@ function search_menu() {
);
foreach (module_implements('search') as $name) {
$items['search/'. $name] = array(
$items['search/'. $name .'/%search'] = array(
'title' => module_invoke($name, 'search', 'name', TRUE),
'page callback' => 'search_view',
'page arguments' => array($name),
'access callback' => '_search_menu',
'access arguments' => array($name),
'type' => $name == 'node' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
'parent' => 'search',
);
}
return $items;
@ -183,6 +183,10 @@ function _search_menu($name) {
return user_access('search content') && module_invoke($name, 'search', 'name');
}
function search_to_arg() {
return search_get_keys();
}
/**
* Validate callback.
*/
@ -882,16 +886,21 @@ function do_search($keywords, $type, $join1 = '', $where1 = '1', $arguments1 = a
* Helper function for grabbing search keys.
*/
function search_get_keys() {
// Extract keys as remainder of path
// Note: support old GET format of searches for existing links.
$path = explode('/', $_GET['q'], 3);
return count($path) == 3 ? $path[2] : $_REQUEST['keys'];
static $return;
if (!isset($return)) {
// Extract keys as remainder of path
// Note: support old GET format of searches for existing links.
$path = explode('/', $_GET['q'], 3);
$keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys'];
$return = count($path) == 3 ? $path[2] : $keys;
}
return $return;
}
/**
* Menu callback; presents the search form and/or search results.
*/
function search_view($type = '') {
function search_view($type = 'node') {
// Search form submits with POST but redirects to GET. This way we can keep
// the search query URL clean as a whistle:
// search/type/keyword+keyword
@ -905,6 +914,7 @@ function search_view($type = '') {
$keys = search_get_keys();
// Only perform search if there is non-whitespace search term:
$results = '';
if (trim($keys)) {
// Log the search keys:
watchdog('search', t('%keys (@type).', array('%keys' => $keys, '@type' => module_invoke($type, 'search', 'name'))), WATCHDOG_NOTICE, l(t('results'), 'search/'. $type .'/'. $keys));

View File

@ -143,14 +143,15 @@ function statistics_menu() {
'type' => MENU_NORMAL_ITEM,
'weight' => 3,
);
$items['user/%/track/navigation'] = array(
$items['user/%user/track/navigation'] = array(
'title' => t('Track page visits'),
'page callback' => 'statistics_user_tracker',
'access callback' => 'user_access',
'access arguments' => array('access statistics'),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);
$items['node/%/track'] = array(
$items['node/%node/track'] = array(
'title' => t('Track'),
'page callback' => 'statistics_node_tracker',
'access callback' => 'user_access',

View File

@ -327,31 +327,32 @@ function system_install() {
) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
db_query("CREATE TABLE {menu} (
mid int NOT NULL default '0',
pid int NOT NULL default '0',
path varchar(255) NOT NULL default '',
load_functions varchar(255) NOT NULL default '',
to_arg_functions varchar(255) NOT NULL default '',
access_callback varchar(255) NOT NULL default '',
access_arguments text,
page_callback varchar(255) NOT NULL default '',
page_arguments text,
map_callback varchar(255) NOT NULL default '',
map_arguments text,
fit int NOT NULL default '0',
number_parts int NOT NULL default '0',
vancode varchar(255) NOT NULL default '',
mid int NOT NULL default '0',
pid int NOT NULL default '0',
visible int NOT NULL default '0',
menu_link varchar(255) NOT NULL default '',
parents varchar(255) NOT NULL default '',
depth int NOT NULL default '0',
has_children int NOT NULL default '0',
tab int NOT NULL default 0,
title varchar(255) NOT NULL default '',
default_tab int NOT NULL default '0',
parent varchar(255) NOT NULL default '',
type int NOT NULL default 0,
PRIMARY KEY (path),
KEY vancode (vancode),
KEY fit (fit),
KEY visible (visible)
KEY visible (visible),
KEY pid (pid),
KEY parent (parent)
) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
db_query("CREATE TABLE {node} (
@ -801,30 +802,31 @@ function system_install() {
mid int NOT NULL default '0',
pid int NOT NULL default '0',
path varchar(255) NOT NULL default '',
load_functions varchar(255) NOT NULL default '',
to_arg_functions varchar(255) NOT NULL default '',
access_callback varchar(255) NOT NULL default '',
access_arguments text,
page_callback varchar(255) NOT NULL default '',
page_arguments text,
map_callback varchar(255) NOT NULL default '',
map_arguments text,
fit int NOT NULL default 0,
number_parts int NOT NULL default 0,
fit int NOT NULL default '0',
number_parts int NOT NULL default '0',
vancode varchar(255) NOT NULL default '',
visible int NOT NULL default '0',
menu_link varchar(255) NOT NULL default '',
parents varchar(255) NOT NULL default '',
depth int NOT NULL default '0',
has_children int NOT NULL default '0',
tab int NOT NULL default '0',
tab int NOT NULL default 0,
title varchar(255) NOT NULL default '',
default_tab int NOT NULL default '0',
parent varchar(255) NOT NULL default '',
type int NOT NULL default 0,
PRIMARY KEY (path)
)");
db_query("CREATE INDEX {menu}_vancode_idx ON {menu} (vancode)");
db_query("CREATE INDEX {menu}_fit_idx ON {menu} (fit)");
db_query("CREATE INDEX {menu}_visible_idx ON {menu} (visible)");
db_query("CREATE INDEX {menu}_parent_idx ON {menu} (parent)");
db_query("CREATE INDEX {menu}_pid_idx ON {menu} (parent)");
db_query("CREATE TABLE {node} (
nid serial CHECK (nid >= 0),

View File

@ -59,7 +59,7 @@ function taxonomy_link($type, $node = NULL) {
*/
function taxonomy_term_path($term) {
$vocabulary = taxonomy_get_vocabulary($term->vid);
$vocabulary = taxonomy_vocabulary_load($term->vid);
if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) {
return $path;
}
@ -91,7 +91,7 @@ function taxonomy_menu() {
'parent' => 'admin/content/taxonomy',
);
$items['admin/content/taxonomy/edit/vocabulary/%'] = array(
$items['admin/content/taxonomy/edit/vocabulary/%taxonomy_vocabulary'] = array(
'title' => t('Edit vocabulary'),
'page callback' => 'taxonomy_admin_vocabulary_edit',
'page arguments' => array(5),
@ -117,27 +117,26 @@ function taxonomy_menu() {
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['admin/content/taxonomy/%'] = array(
$items['admin/content/taxonomy/%taxonomy_vocabulary'] = array(
'title' => t('List terms'),
'page callback' => 'taxonomy_overview_terms',
'page arguments' => array(3),
'access arguments' => array('administer taxonomy'),
'map arguments' => array('taxonomy_get_vocabulary', 3),
'type' => MENU_CALLBACK,
);
$items['admin/content/taxonomy/%/list'] = array(
$items['admin/content/taxonomy/%taxonomy_vocabulary/list'] = array(
'title' => t('List'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/content/taxonomy/%/add/term'] = array(
$items['admin/content/taxonomy/%taxonomy_vocabulary/add/term'] = array(
'title' => t('Add term'),
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_form_term', 3),
'type' => MENU_LOCAL_TASK,
'parent' => 'admin/content/taxonomy/%',
'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
);
return $items;
@ -334,7 +333,7 @@ function taxonomy_save_vocabulary(&$edit) {
* Constant indicating items were deleted.
*/
function taxonomy_del_vocabulary($vid) {
$vocabulary = (array) taxonomy_get_vocabulary($vid);
$vocabulary = (array) taxonomy_vocabulary_load($vid);
db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
@ -351,7 +350,7 @@ function taxonomy_del_vocabulary($vid) {
}
function taxonomy_vocabulary_confirm_delete($vid) {
$vocabulary = taxonomy_get_vocabulary($vid);
$vocabulary = taxonomy_vocabulary_load($vid);
$form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
$form['vid'] = array('#type' => 'value', '#value' => $vid);
@ -593,7 +592,7 @@ function taxonomy_term_confirm_delete_submit($form_id, $form_values) {
* Generate a form element for selecting terms from a vocabulary.
*/
function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
$vocabulary = taxonomy_get_vocabulary($vid);
$vocabulary = taxonomy_vocabulary_load($vid);
$help = ($help) ? $help : $vocabulary->help;
if ($vocabulary->required) {
$blank = 0;
@ -764,7 +763,7 @@ function taxonomy_node_validate(&$node) {
$terms = $node->taxonomy;
if ($terms['tags']) {
foreach ($terms['tags'] as $vid => $vid_value) {
$vocabulary = taxonomy_get_vocabulary($vid);
$vocabulary = taxonomy_vocabulary_load($vid);
if (empty($vocabulary->tags)) {
// see form_get_error $key = implode('][', $element['#parents']);
// on why this is the key
@ -1109,7 +1108,7 @@ function taxonomy_get_term_by_name($name) {
* The vocabulary object with all of its metadata.
* Results are statically cached.
*/
function taxonomy_get_vocabulary($vid) {
function taxonomy_vocabulary_load($vid) {
static $vocabularies = array();
if (!array_key_exists($vid, $vocabularies)) {
@ -1388,14 +1387,11 @@ function taxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') {
/**
* Page to edit a vocabulary.
*/
function taxonomy_admin_vocabulary_edit($vid = NULL) {
function taxonomy_admin_vocabulary_edit($vocabulary) {
if ($_POST['op'] == t('Delete') || $_POST['confirm']) {
return drupal_get_form('taxonomy_vocabulary_confirm_delete', $vid);
return drupal_get_form('taxonomy_vocabulary_confirm_delete', $vocabulary->vid);
}
if ($vocabulary = (array)taxonomy_get_vocabulary($vid)) {
return drupal_get_form('taxonomy_form_vocabulary', $vocabulary);
}
return drupal_not_found();
return drupal_get_form('taxonomy_form_vocabulary', (array)$vocabulary);
}
/**

View File

@ -35,18 +35,22 @@ function tracker_menu() {
'type' => MENU_DEFAULT_LOCAL_TASK,
'access callback' => 'user_is_logged_in',
);
$items['tracker/%'] = array(
$items['tracker/%user_current'] = array(
'title' => t('My recent posts'),
'type' => MENU_LOCAL_TASK,
'access callback' => 'user_is_logged_in',
'access arguments' => array(1),
);
$items['user/%/track'] = array(
$items['user/%user/track'] = array(
'title' => t('Track'),
'page callback' => 'tracker_track_user',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_LOCAL_TASK,
);
$items['user/%/track/posts'] = array(
$items['user/%user/track/posts'] = array(
'title' => t('Track posts'),
'type' => MENU_DEFAULT_LOCAL_TASK,
);

View File

@ -730,11 +730,15 @@ function user_menu() {
);
// Registration and login pages.
$items['user/login'] = array(
$items['user'] = array(
'title' => t('Log in'),
'page callback' => 'drupal_get_form',
'page arguments' => array('user_login'),
'access callback' => 'user_is_anonymous',
);
$items['user/login'] = array(
'title' => t('Log in'),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
@ -857,7 +861,6 @@ function user_menu() {
'page callback' => 'user_admin',
'page arguments' => array('search'),
'access arguments' => array('administer users'),
'type' => MENU_NORMAL_ITEM,
);
}
@ -868,32 +871,22 @@ function user_menu() {
'weight' => 10,
);
$items['user'] = array(
$items['user/%user_current'] = array(
'title' => t('My account'),
'page callback' => 'user_view',
'page arguments' => array(1),
'access callback' => 'user_view_access',
'access arguments' => array(1),
'map callback' => 'user_load_self',
'parent' => '',
);
$items['user/%'] = array(
'title' => t('My account'),
'page callback' => 'user_view',
'page arguments' => array(1),
'access callback' => 'user_view_access',
'access arguments' => array(1),
'map arguments' => array('user_load', 1),
'type' => MENU_CALLBACK,
);
$items['user/%/view'] = array(
$items['user/%user/view'] = array(
'title' => t('View'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['user/%/delete'] = array(
$items['user/%user/delete'] = array(
'title' => t('Delete'),
'page callback' => 'user_edit',
'access callback' => 'user_access',
@ -901,7 +894,7 @@ function user_menu() {
'type' => MENU_CALLBACK,
);
$items['user/%/edit'] = array(
$items['user/%user/edit'] = array(
'title' => t('Edit'),
'page callback' => 'drupal_get_form',
'page arguments' => array('user_edit'),
@ -913,7 +906,7 @@ function user_menu() {
$empty_account = new stdClass();
if (($categories = _user_categories($empty_account)) && (count($categories) > 1)) {
foreach ($categories as $key => $category) {
$items['user/%/edit/'. $category['name']] = array(
$items['user/%user/edit/'. $category['name']] = array(
'title' => $category['title'],
'page arguments' => array('user_edit', 3),
'type' => $category['name'] == 'account' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
@ -928,6 +921,14 @@ function user_init() {
drupal_add_css(drupal_get_path('module', 'user') .'/user.css', 'module');
}
function user_current_load($arg) {
return user_load($arg);
}
function user_current_to_arg() {
return $GLOBALS['user']->uid;
}
/**
* Accepts an user object, $account, or a DA name and returns an associative
* array of modules and DA names. Called at external login.
@ -1588,6 +1589,7 @@ function user_edit_submit($form_id, $form_values) {
function user_view($account) {
global $user;
drupal_set_title(check_plain($account->name));
// Retrieve and merge all profile fields:
$fields = array();
foreach (module_list() as $module) {