diff --git a/core/includes/menu.inc b/core/includes/menu.inc index ef9f7e2..006730e 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -1599,6 +1599,9 @@ function theme_menu_link(array $variables) { */ function theme_menu_local_task($variables) { $link = $variables['element']['#link']; + $link += array( + 'localized_options' => array(), + ); $link_text = $link['title']; if (!empty($variables['element']['#active'])) { @@ -1831,21 +1834,20 @@ function menu_navigation_links($menu_name, $level = 0) { * * @return * An array containing - * - tabs: Local tasks for the requested level: - * - count: The number of local tasks. - * - output: The themed output of local tasks. - * - actions: Action links for the requested level: - * - count: The number of action links. - * - output: The themed output of action links. + * - tabs: Local tasks for the requested level. + * - actions: Action links for the requested level. * - root_path: The router path for the current page. If the current page is * a default local task, then this corresponds to the parent tab. + * + * @see hook_menu_local_tasks() + * @see hook_menu_local_tasks_alter() */ function menu_local_tasks($level = 0) { $data = &drupal_static(__FUNCTION__); $root_path = &drupal_static(__FUNCTION__ . ':root_path', ''); $empty = array( - 'tabs' => array('count' => 0, 'output' => array()), - 'actions' => array('count' => 0, 'output' => array()), + 'tabs' => array(), + 'actions' => array(), 'root_path' => &$root_path, ); @@ -1897,8 +1899,7 @@ function menu_local_tasks($level = 0) { // Tab parenting may skip levels, so the number of parts in the path may not // equal the depth. Thus we use the $depth counter (offset by 1000 for ksort). $depth = 1001; - $actions['count'] = 0; - $actions['output'] = array(); + $actions = array(); while (isset($children[$path])) { $tabs_current = array(); $actions_current = array(); @@ -1931,10 +1932,11 @@ function menu_local_tasks($level = 0) { if ($link['href'] != current_path()) { $link['localized_options']['attributes']['class'][] = 'active'; } - $tabs_current[] = array( + $tabs_current[$link['href']] = array( '#theme' => 'menu_local_task', '#link' => $link, '#active' => TRUE, + '#weight' => isset($link['weight']) ? $link['weight'] : NULL, ); $next_path = $item['path']; $tab_count++; @@ -1944,17 +1946,19 @@ function menu_local_tasks($level = 0) { // MENU_IS_LOCAL_ACTION before checking. if (($item['type'] & MENU_IS_LOCAL_ACTION) == MENU_IS_LOCAL_ACTION) { // The item is an action, display it as such. - $actions_current[] = array( + $actions_current[$link['href']] = array( '#theme' => 'menu_local_action', '#link' => $link, + '#weight' => isset($link['weight']) ? $link['weight'] : NULL, ); $action_count++; } else { // Otherwise, it's a normal tab. - $tabs_current[] = array( + $tabs_current[$link['href']] = array( '#theme' => 'menu_local_task', '#link' => $link, + '#weight' => isset($link['weight']) ? $link['weight'] : NULL, ); $tab_count++; } @@ -1962,10 +1966,8 @@ function menu_local_tasks($level = 0) { } } $path = $next_path; - $tabs[$depth]['count'] = $tab_count; - $tabs[$depth]['output'] = $tabs_current; - $actions['count'] += $action_count; - $actions['output'] = array_merge($actions['output'], $actions_current); + $tabs[$depth] = $tabs_current; + $actions = array_merge($actions, $actions_current); $depth++; } $data['actions'] = $actions; @@ -2007,10 +2009,11 @@ function menu_local_tasks($level = 0) { if ($link['href'] != current_path()) { $link['localized_options']['attributes']['class'][] = 'active'; } - $tabs_current[] = array( + $tabs_current[$link['href']] = array( '#theme' => 'menu_local_task', '#link' => $link, '#active' => TRUE, + '#weight' => isset($link['weight']) ? $link['weight'] : NULL, ); $next_path = $item['tab_parent']; if (isset($tasks[$next_path])) { @@ -2018,17 +2021,17 @@ function menu_local_tasks($level = 0) { } } else { - $tabs_current[] = array( + $tabs_current[$link['href']] = array( '#theme' => 'menu_local_task', '#link' => $link, + '#weight' => isset($link['weight']) ? $link['weight'] : NULL, ); } } } $path = $next_path; $parent = $next_parent; - $tabs[$depth]['count'] = $count; - $tabs[$depth]['output'] = $tabs_current; + $tabs[$depth] = $tabs_current; $depth--; } // Sort by depth. @@ -2037,8 +2040,14 @@ function menu_local_tasks($level = 0) { $tabs = array_values($tabs); $data['tabs'] = $tabs; - // Allow modules to alter local tasks or dynamically append further tasks. - drupal_alter('menu_local_tasks', $data, $router_item, $root_path); + // Allow modules to dynamically add further tasks. + $module_handler = drupal_container()->get('module_handler'); + foreach ($module_handler->getImplementations('menu_local_tasks') as $module) { + $function = $module . '_menu_local_tasks'; + $function($data, $router_item, $root_path); + } + // Allow modules to alter local tasks. + $module_handler->alter('menu_local_tasks', $data, $router_item, $root_path); } if (isset($data['tabs'][$level])) { @@ -2048,9 +2057,7 @@ function menu_local_tasks($level = 0) { 'root_path' => $root_path, ); } - // @todo If there are no tabs, then there still can be actions; for example, - // when added via hook_menu_local_tasks_alter(). - elseif (!empty($data['actions']['output'])) { + elseif (!empty($data['actions'])) { return array('actions' => $data['actions']) + $empty; } return $empty; @@ -2163,7 +2170,7 @@ function menu_contextual_links($module, $parent_path, $args) { function menu_primary_local_tasks() { $links = menu_local_tasks(0); // Do not display single tabs. - return ($links['tabs']['count'] > 1 ? $links['tabs']['output'] : ''); + return count(element_get_visible_children($links['tabs'])) > 1 ? $links['tabs'] : ''; } /** @@ -2172,7 +2179,7 @@ function menu_primary_local_tasks() { function menu_secondary_local_tasks() { $links = menu_local_tasks(1); // Do not display single tabs. - return ($links['tabs']['count'] > 1 ? $links['tabs']['output'] : ''); + return count(element_get_visible_children($links['tabs'])) > 1 ? $links['tabs'] : ''; } /** @@ -2180,7 +2187,7 @@ function menu_secondary_local_tasks() { */ function menu_local_actions() { $links = menu_local_tasks(); - return $links['actions']['output']; + return $links['actions']; } /** @@ -2195,11 +2202,12 @@ function menu_tab_root_path() { * Returns a renderable element for the primary and secondary tabs. */ function menu_local_tabs() { - return array( + $build = array( '#theme' => 'menu_local_tasks', '#primary' => menu_primary_local_tasks(), '#secondary' => menu_secondary_local_tasks(), ); + return !empty($build['#primary']) || !empty($build['#secondary']) ? $build : array(); } /** @@ -2958,7 +2966,6 @@ function _menu_router_build($callbacks) { $item['to_arg_functions'] = empty($to_arg_functions) ? '' : serialize($to_arg_functions); $item += array( 'title' => '', - 'weight' => 0, 'type' => MENU_NORMAL_ITEM, 'module' => '', '_number_parts' => $number_parts, @@ -2966,6 +2973,9 @@ function _menu_router_build($callbacks) { '_fit' => $fit, ); $item += array( + // Default MENU_DEFAULT_LOCAL_TASKs to a weight of -10, so they appear as + // first tab by default. + 'weight' => ($item['type'] & MENU_DEFAULT_LOCAL_TASK) == MENU_DEFAULT_LOCAL_TASK ? -10 : 0, '_visible' => (bool) ($item['type'] & MENU_VISIBLE_IN_BREADCRUMB), '_tab' => (bool) ($item['type'] & MENU_IS_LOCAL_TASK), ); diff --git a/core/modules/action/action.module b/core/modules/action/action.module index 6ebf1fb..6396caf 100644 --- a/core/modules/action/action.module +++ b/core/modules/action/action.module @@ -69,7 +69,6 @@ function action_menu() { 'description' => 'Manage the actions defined for your site.', 'page callback' => 'action_admin_manage', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -2, 'file' => 'action.admin.inc', ); $items['admin/config/system/actions/configure'] = array( diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index 860bfa8..7298e4c 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -141,7 +141,6 @@ function aggregator_menu() { $items['admin/config/services/aggregator/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/config/services/aggregator/settings'] = array( 'title' => 'Settings', @@ -150,6 +149,7 @@ function aggregator_menu() { 'page arguments' => array('aggregator_admin_form'), 'access arguments' => array('administer news feeds'), 'type' => MENU_LOCAL_TASK, + 'weight' => 100, 'file' => 'aggregator.admin.inc', ); $items['aggregator'] = array( @@ -196,7 +196,6 @@ function aggregator_menu() { $items['aggregator/categories/%aggregator_category/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['aggregator/categories/%aggregator_category/categorize'] = array( 'title' => 'Categorize', @@ -212,7 +211,7 @@ function aggregator_menu() { 'page arguments' => array('aggregator_form_category', 2), 'access arguments' => array('administer news feeds'), 'type' => MENU_LOCAL_TASK, - 'weight' => 1, + 'weight' => 10, 'file' => 'aggregator.admin.inc', ); $items['aggregator/sources/%aggregator_feed'] = array( @@ -226,7 +225,6 @@ function aggregator_menu() { $items['aggregator/sources/%aggregator_feed/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['aggregator/sources/%aggregator_feed/categorize'] = array( 'title' => 'Categorize', @@ -242,7 +240,7 @@ function aggregator_menu() { 'page arguments' => array(2), 'access arguments' => array('administer news feeds'), 'type' => MENU_LOCAL_TASK, - 'weight' => 1, + 'weight' => 10, 'file' => 'aggregator.admin.inc', ); $items['admin/config/services/aggregator/edit/feed/%aggregator_feed'] = array( diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 8bf4aae..222d09a 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -160,7 +160,6 @@ function block_menu() { 'title' => check_plain($theme->info['name']), 'page arguments' => array($key), 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, - 'weight' => $key == $default_theme ? -10 : 0, 'access callback' => '_block_themes_access', 'access arguments' => array($key), 'file' => 'block.admin.inc', diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 106a57a..a2eae8e 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -149,7 +149,7 @@ function book_menu() { 'page arguments' => array('book_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, - 'weight' => 8, + 'weight' => 100, 'file' => 'book.admin.inc', ); $items['admin/content/book/%node'] = array( diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 6f2795a..9ee6f81 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -233,7 +233,6 @@ function comment_menu() { $items['admin/content/comment/new'] = array( 'title' => 'Published comments', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/content/comment/approval'] = array( 'title' => 'Unapproved comments', @@ -251,7 +250,6 @@ function comment_menu() { $items['comment/%/view'] = array( 'title' => 'View comment', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); // Every other comment path uses %, but this one loads the comment directly, // so we don't end up loading it twice (in the page and access callback). @@ -262,7 +260,6 @@ function comment_menu() { 'access callback' => 'comment_access', 'access arguments' => array('edit', 1), 'type' => MENU_LOCAL_TASK, - 'weight' => 0, ); $items['comment/%/approve'] = array( 'title' => 'Approve', @@ -270,7 +267,7 @@ function comment_menu() { 'page arguments' => array(1), 'access arguments' => array('administer comments'), 'file' => 'comment.pages.inc', - 'weight' => 1, + 'weight' => 10, ); $items['comment/%/delete'] = array( 'title' => 'Delete', @@ -279,7 +276,7 @@ function comment_menu() { 'access arguments' => array('administer comments'), 'type' => MENU_LOCAL_TASK, 'file' => 'comment.admin.inc', - 'weight' => 2, + 'weight' => 20, ); $items['comment/reply/%node'] = array( 'title' => 'Add new comment', diff --git a/core/modules/config/config.module b/core/modules/config/config.module index a41fc09..f8a1874 100644 --- a/core/modules/config/config.module +++ b/core/modules/config/config.module @@ -51,7 +51,6 @@ function config_menu() { $items['admin/config/development/sync/import'] = array( 'title' => 'Import', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); return $items; } diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module index 677a066..d0d59fd 100644 --- a/core/modules/config/tests/config_test/config_test.module +++ b/core/modules/config/tests/config_test/config_test.module @@ -46,7 +46,6 @@ function config_test_menu() { $items['admin/structure/config_test/manage/%config_test/edit'] = array( 'title' => 'Edit', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/structure/config_test/manage/%config_test/delete'] = array( 'title' => 'Delete', diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module index 6e22fe7..a45e97d 100644 --- a/core/modules/contact/contact.module +++ b/core/modules/contact/contact.module @@ -84,7 +84,6 @@ function contact_menu() { $items['admin/structure/contact/manage/%contact_category/edit'] = array( 'title' => 'Edit', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/structure/contact/manage/%contact_category/delete'] = array( 'title' => 'Delete', diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index 751343c..9f64f5f 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -68,9 +68,8 @@ function field_ui_menu() { 'file' => 'field_ui.admin.inc', ); $items['admin/reports/fields/list'] = array( - 'title' => 'List', + 'title' => 'Entities', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); // Create tabs for all possible bundles. @@ -190,9 +189,11 @@ function field_ui_menu() { 'access callback' => '_field_ui_view_mode_menu_access', 'access arguments' => array($entity_type, $bundle_arg, $view_mode, $access_display['access arguments'][0]), 'type' => ($view_mode == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK), - 'weight' => ($view_mode == 'default' ? -10 : $weight++), 'file' => 'field_ui.admin.inc', ); + if ($view_mode != 'default') { + $items["$path/display/$view_mode"]['weight'] = $weight++; + } } } } diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index fb7ea4c..8ee1871 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -148,7 +148,6 @@ function filter_menu() { 'page callback' => 'filter_admin_format_page', 'access arguments' => array('administer filters'), 'type' => MENU_LOCAL_ACTION, - 'weight' => 1, 'file' => 'filter.admin.inc', ); $items['admin/config/content/formats/%filter_format'] = array( diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 872eb95..8b35c3e 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -118,7 +118,6 @@ function forum_menu() { $items['admin/structure/forum/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/structure/forum/add/container'] = array( 'title' => 'Add container', @@ -143,7 +142,7 @@ function forum_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('forum_admin_settings'), 'access arguments' => array('administer forums'), - 'weight' => 5, + 'weight' => 100, 'type' => MENU_LOCAL_TASK, 'parent' => 'admin/structure/forum', 'file' => 'forum.admin.inc', @@ -166,9 +165,9 @@ function forum_menu() { } /** - * Implements hook_menu_local_tasks_alter(). + * Implements hook_menu_local_tasks(). */ -function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) { +function forum_menu_local_tasks(&$data, $router_item, $root_path) { global $user; // Add action link to 'node/add/forum' on 'forum' sub-pages. @@ -213,7 +212,7 @@ function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) { ); } } - $data['actions']['output'] = array_merge($data['actions']['output'], $links); + $data['actions'] += $links; } } } diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 956eafd..d7b02d0 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -121,7 +121,6 @@ function image_menu() { 'page callback' => 'image_style_list', 'access arguments' => array('administer image styles'), 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => 1, 'file' => 'image.admin.inc', ); $items['admin/config/media/image-styles/add'] = array( diff --git a/core/modules/language/language.module b/core/modules/language/language.module index bd368fa..2681956 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -75,7 +75,6 @@ function language_menu() { ); $items['admin/config/regional/language/list'] = array( 'title' => 'List', - 'weight' => 0, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/config/regional/language/add'] = array( @@ -83,7 +82,6 @@ function language_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('language_admin_add_form'), 'access arguments' => array('administer languages'), - 'weight' => 5, 'type' => MENU_LOCAL_ACTION, 'file' => 'language.admin.inc', ); diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 59dfff6..fb72927 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -193,7 +193,6 @@ function locale_menu() { ); $items['admin/config/regional/translate/translate'] = array( 'title' => 'Translate', - 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/config/regional/translate/import'] = array( @@ -219,7 +218,7 @@ function locale_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('locale_translate_settings'), 'access arguments' => array('translate interface'), - 'weight' => 40, + 'weight' => 100, 'type' => MENU_LOCAL_TASK, 'file' => 'locale.pages.inc', ); diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module index 44723dd..c300ed6 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -82,7 +82,6 @@ function menu_menu() { $items['admin/structure/menu/list'] = array( 'title' => 'List menus', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/structure/menu/add'] = array( 'title' => 'Add menu', @@ -97,7 +96,7 @@ function menu_menu() { 'page arguments' => array('menu_configure'), 'access arguments' => array('administer menu'), 'type' => MENU_LOCAL_TASK, - 'weight' => 5, + 'weight' => 100, 'file' => 'menu.admin.inc', ); $items['admin/structure/menu/manage/%menu'] = array( diff --git a/core/modules/node/node.module b/core/modules/node/node.module index ff073ae..dc2c1b9 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1660,7 +1660,6 @@ function node_menu() { $items['admin/content/node'] = array( 'title' => 'Content', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/reports/status/rebuild'] = array( @@ -1684,7 +1683,6 @@ function node_menu() { $items['admin/structure/types/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/structure/types/add'] = array( 'title' => 'Add content type', @@ -1759,7 +1757,6 @@ function node_menu() { $items['node/%node/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['node/%node/edit'] = array( 'title' => 'Edit', @@ -1767,7 +1764,6 @@ function node_menu() { 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('update', 1), - 'weight' => 0, 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', @@ -1778,7 +1774,7 @@ function node_menu() { 'page arguments' => array('node_delete_confirm', 1), 'access callback' => 'node_access', 'access arguments' => array('delete', 1), - 'weight' => 1, + 'weight' => 10, 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', @@ -1789,7 +1785,7 @@ function node_menu() { 'page arguments' => array(1), 'access callback' => '_node_revision_access', 'access arguments' => array(1), - 'weight' => 2, + 'weight' => 20, 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', @@ -1821,14 +1817,14 @@ function node_menu() { } /** - * Implements hook_menu_local_tasks_alter(). + * Implements hook_menu_local_tasks(). */ -function node_menu_local_tasks_alter(&$data, $router_item, $root_path) { +function node_menu_local_tasks(&$data, $router_item, $root_path) { // Add action link to 'node/add' on 'admin/content' page. if ($root_path == 'admin/content') { $item = menu_get_item('node/add'); if ($item['access']) { - $data['actions']['output'][] = array( + $data['actions'][] = array( '#theme' => 'menu_local_action', '#link' => $item, ); diff --git a/core/modules/path/path.module b/core/modules/path/path.module index 7d3fadd..6401a8b 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -64,6 +64,10 @@ function path_menu() { 'weight' => -5, 'file' => 'path.admin.inc', ); + $items['admin/config/search/path/list'] = array( + 'title' => 'List', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); $items['admin/config/search/path/edit/%path'] = array( 'title' => 'Edit alias', 'page callback' => 'path_admin_edit', @@ -78,11 +82,6 @@ function path_menu() { 'access arguments' => array('administer url aliases'), 'file' => 'path.admin.inc', ); - $items['admin/config/search/path/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); $items['admin/config/search/path/add'] = array( 'title' => 'Add alias', 'page callback' => 'path_admin_edit', diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index d7adb65..760947f 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -50,6 +50,7 @@ function simpletest_menu() { 'page arguments' => array('simpletest_settings_form'), 'access arguments' => array('administer unit tests'), 'type' => MENU_LOCAL_TASK, + 'weight' => 100, 'file' => 'simpletest.pages.inc', ); $items['admin/config/development/testing/results/%'] = array( diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php new file mode 100644 index 0000000..5f695c0 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php @@ -0,0 +1,143 @@ + 'Local tasks', + 'description' => 'Tests local tasks derived from router and added/altered via hooks.', + 'group' => 'Menu', + ); + } + + /** + * Tests appearance of local tasks. + * + * @see menu_test_menu() + * @see menu_test_menu_local_tasks() + * @see menu_test_menu_local_tasks_alter() + */ + function testLocalTasks() { + // Verify that there is no local tasks markup if none are defined in the + // router and no module adds any dynamically. + $this->drupalGet('menu-test/tasks/empty'); + $this->assertNoRaw('tabs'); + $this->drupalGet('menu-test/tasks/default'); + $this->assertNoRaw('tabs'); + + // Verify that local tasks appear as defined in the router. + $this->drupalGet('menu-test/tasks/tasks'); + $this->assertLocalTasks(array( + // MENU_DEFAULT_LOCAL_TASK is expected to get a default weight of -10 + // (without having to define it manually), so it should appear first, + // despite that its label is "View". + 'menu-test/tasks/tasks', + 'menu-test/tasks/tasks/edit', + 'menu-test/tasks/tasks/settings', + )); + + // Enable addition of tasks in menu_test_menu_local_tasks(). + config('menu_test.settings')->set('tasks.add', TRUE)->save(); + + // Verify that the added tasks appear even if there are no tasks normally. + $this->drupalGet('menu-test/tasks/empty'); + $this->assertLocalTasks(array( + 'task/foo', + 'task/bar', + )); + + // Verify that the default local task appears before the added tasks. + $this->drupalGet('menu-test/tasks/default'); + $this->assertLocalTasks(array( + 'menu-test/tasks/default', + 'task/foo', + 'task/bar', + )); + + // Verify that the added tasks appear within normal tasks. + $this->drupalGet('menu-test/tasks/tasks'); + $this->assertLocalTasks(array( + 'menu-test/tasks/tasks', + // The Edit task defines no weight, which is expected to sort as 0. + 'menu-test/tasks/tasks/edit', + 'task/foo', + 'task/bar', + 'menu-test/tasks/tasks/settings', + )); + + // Enable manipulation of tasks in menu_test_menu_local_tasks_alter(). + config('menu_test.settings')->set('tasks.alter', TRUE)->save(); + + // Verify that the added tasks appear even if there are no tasks normally. + $this->drupalGet('menu-test/tasks/empty'); + $this->assertLocalTasks(array( + 'task/bar', + 'task/foo', + )); + $this->assertNoText('Show it'); + $this->assertText('Advanced settings'); + + // Verify that the default local task appears before the added tasks. + $this->drupalGet('menu-test/tasks/default'); + $this->assertLocalTasks(array( + 'menu-test/tasks/default', + 'task/bar', + 'task/foo', + )); + $this->assertText('Show it'); + $this->assertText('Advanced settings'); + + // Verify that the added tasks appear within normal tasks. + $this->drupalGet('menu-test/tasks/tasks'); + $this->assertLocalTasks(array( + 'menu-test/tasks/tasks', + 'menu-test/tasks/tasks/edit', + 'task/bar', + 'menu-test/tasks/tasks/settings', + 'task/foo', + )); + $this->assertText('Show it'); + $this->assertText('Advanced settings'); + } + + /** + * Asserts local tasks in the page output. + * + * @param array $hrefs + * A list of expected link hrefs of local tasks to assert on the page (in + * the given order). + * @param int $level + * (optional) The local tasks level to assert; 0 for primary, 1 for + * secondary. Defaults to 0. + */ + protected function assertLocalTasks(array $hrefs, $level = 0) { + $elements = $this->xpath('//*[contains(@class, :class)]//a', array( + ':class' => $level == 0 ? 'tabs primary' : 'tabs secondary', + )); + $this->assertTrue(count($elements), 'Local tasks found.'); + foreach ($hrefs as $index => $element) { + $expected = url($hrefs[$index]); + $method = ($elements[$index]['href'] == $expected ? 'pass' : 'fail'); + $this->{$method}(format_string('Task @number href @value equals @expected.', array( + '@number' => $index + 1, + '@value' => (string) $elements[$index]['href'], + '@expected' => $expected, + ))); + } + } + +} diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 484ee93..2f98dad 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -901,8 +901,7 @@ function hook_menu_alter(&$items) { * Alter tabs and actions displayed on the page before they are rendered. * * This hook is invoked by menu_local_tasks(). The system-determined tabs and - * actions are passed in by reference. Additional tabs or actions may be added, - * or existing items altered. + * actions are passed in by reference. Additional tabs or actions may be added. * * Each tab or action is an associative array containing: * - #theme: The theme function to use to render. @@ -910,30 +909,24 @@ function hook_menu_alter(&$items) { * - title: The localized title of the link. * - href: The system path to link to. * - localized_options: An array of options to pass to l(). + * - #weight: The link's weight compared to other links. * - #active: Whether the link should be marked as 'active'. * - * @param $data + * @param array $data * An associative array containing: - * - actions: An associative array containing: - * - count: The amount of actions determined by the menu system, which can - * be ignored. - * - output: A list of of actions, each one being an associative array - * as described above. - * - tabs: An indexed array (list) of tab levels (up to 2 levels), each - * containing an associative array: - * - count: The amount of tabs determined by the menu system. This value - * does not need to be altered if there is more than one tab. - * - output: A list of of tabs, each one being an associative array as - * described above. - * @param $router_item + * - actions: A list of of actions keyed by their href, each one being an + * associative array as described above. + * - tabs: A list of (up to 2) tab levels that contain a list of of tabs keyed + * by their href, each one being an associative array as described above. + * @param array $router_item * The menu system router item of the page. - * @param $root_path + * @param string $root_path * The path to the root item for this set of tabs. */ -function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) { +function hook_menu_local_tasks(&$data, $router_item, $root_path) { // Add an action linking to node/add to all pages. - $data['actions']['output'][] = array( - '#theme' => 'menu_local_task', + $data['actions']['node/add'] = array( + '#theme' => 'menu_local_action', '#link' => array( 'title' => t('Add new content'), 'href' => 'node/add', @@ -946,7 +939,7 @@ function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) { ); // Add a tab linking to node/add to all pages. - $data['tabs'][0]['output'][] = array( + $data['tabs'][0]['node/add'] = array( '#theme' => 'menu_local_task', '#link' => array( 'title' => t('Example tab'), @@ -957,14 +950,31 @@ function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) { ), ), ), - // Define whether this link is active. This can be omitted for - // implementations that add links to pages outside of the current page - // context. + // Define whether this link is active. This can usually be omitted. '#active' => ($router_item['path'] == $root_path), ); } /** + * Alter tabs and actions displayed on the page before they are rendered. + * + * This hook is invoked by menu_local_tasks(). The system-determined tabs and + * actions are passed in by reference. Existing tabs or actions may be altered. + * + * @param array $data + * An associative array containing tabs and actions. See + * hook_menu_local_tasks() for details. + * @param array $router_item + * The menu system router item of the page. + * @param string $root_path + * The path to the root item for this set of tabs. + * + * @see hook_menu_local_tasks() + */ +function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) { +} + +/** * Alter links in the active trail before it is rendered as the breadcrumb. * * This hook is invoked by menu_get_active_breadcrumb() and allows alteration diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 5f540a3..a223d83 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -695,7 +695,6 @@ function system_menu() { 'title' => 'List', 'description' => 'Select and configure your theme', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -1, 'file' => 'system.admin.inc', ); $items['admin/appearance/enable'] = array( @@ -727,13 +726,12 @@ function system_menu() { 'access arguments' => array('administer themes'), 'type' => MENU_LOCAL_TASK, 'file' => 'system.admin.inc', - 'weight' => 20, + 'weight' => 100, ); // Theme configuration subtabs. $items['admin/appearance/settings/global'] = array( 'title' => 'Global settings', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -1, ); foreach (list_themes() as $key => $theme) { diff --git a/core/modules/system/tests/modules/design_test/design_test.module b/core/modules/system/tests/modules/design_test/design_test.module index b8a1a80..9771ba4 100644 --- a/core/modules/system/tests/modules/design_test/design_test.module +++ b/core/modules/system/tests/modules/design_test/design_test.module @@ -75,7 +75,6 @@ function design_test_menu() { $items['design_test/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); // Lastly, add the category containers. foreach ($categories as $category) { diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module index 36cef57..1b0ada5 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.module +++ b/core/modules/system/tests/modules/menu_test/menu_test.module @@ -124,7 +124,6 @@ function menu_test_menu() { $items['menu-test/hidden/menu/list'] = array( 'title' => 'List menus', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['menu-test/hidden/menu/add'] = array( 'title' => 'Add menu', @@ -146,7 +145,6 @@ function menu_test_menu() { ); $items['menu-test/hidden/menu/manage/%menu/list'] = array( 'title' => 'List links', - 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, ); @@ -178,7 +176,6 @@ function menu_test_menu() { $items['menu-test/hidden/block/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['menu-test/hidden/block/add'] = array( 'title' => 'Add block', @@ -239,6 +236,38 @@ function menu_test_menu() { 'type' => MENU_LOCAL_TASK, ) + $base; + // Menu local tasks tests. + // @see Drupal\system\Tests\Menu\LocalTasksTest + $items['menu-test/tasks'] = array( + 'title' => 'Local tasks', + ) + $base; + $items['menu-test/tasks/empty'] = array( + 'title' => 'Empty', + ) + $base; + $items['menu-test/tasks/default'] = array( + 'title' => 'Default only', + ) + $base; + $items['menu-test/tasks/default/view'] = array( + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ) + $base; + $items['menu-test/tasks/tasks'] = array( + 'title' => 'With tasks', + ) + $base; + $items['menu-test/tasks/tasks/view'] = array( + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ) + $base; + $items['menu-test/tasks/tasks/edit'] = array( + 'title' => 'Edit', + 'type' => MENU_LOCAL_TASK, + ) + $base; + $items['menu-test/tasks/tasks/settings'] = array( + 'title' => 'Settings', + 'type' => MENU_LOCAL_TASK, + 'weight' => 100, + ) + $base; + // Menu trail tests. // @see MenuTrailTestCase $items['menu-test/menu-trail'] = array( @@ -367,6 +396,56 @@ function menu_test_menu() { } /** + * Implements hook_menu_local_tasks(). + */ +function menu_test_menu_local_tasks(&$data, $router_item, $root_path) { + if (!config('menu_test.settings')->get('tasks.add')) { + return; + } + if (strpos($router_item['tab_root'], 'menu-test/tasks/') === 0) { + $data['tabs'][0]['foo'] = array( + '#theme' => 'menu_local_task', + '#link' => array( + 'title' => 'Task 1', + 'href' => 'task/foo', + ), + '#weight' => 10, + ); + $data['tabs'][0]['bar'] = array( + '#theme' => 'menu_local_task', + '#link' => array( + 'title' => 'Task 2', + 'href' => 'task/bar', + ), + '#weight' => 20, + ); + } +} + +/** + * Implements hook_menu_local_tasks_alter(). + */ +function menu_test_menu_local_tasks_alter(&$data, $router_item, $root_path) { + if (!config('menu_test.settings')->get('tasks.alter')) { + return; + } + if (strpos($router_item['tab_root'], 'menu-test/tasks/') === 0) { + // Rename the default local task from 'View' to 'Show'. + // $data['tabs'] is expected to be keyed by link hrefs. + // The default local task always links to its parent path, which means that + // if the tab root path appears as key in $data['tabs'], then that key is + // the default local task. + $key = $router_item['tab_root']; + if (isset($data['tabs'][0][$key])) { + $data['tabs'][0][$key]['#link']['title'] = 'Show it'; + } + // Rename the 'foo' task to "Advanced settings" and put it last. + $data['tabs'][0]['foo']['#link']['title'] = 'Advanced settings'; + $data['tabs'][0]['foo']['#weight'] = 110; + } +} + +/** * Dummy argument loader for hook_menu() to point to. */ function menu_test_argument_load($arg1) { diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 4b1a2b1..55c32ea 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -266,7 +266,6 @@ function taxonomy_menu() { $items['admin/structure/taxonomy/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['admin/structure/taxonomy/add'] = array( 'title' => 'Add vocabulary', @@ -310,7 +309,7 @@ function taxonomy_menu() { 'access callback' => 'entity_page_access', 'access arguments' => array(2, 'delete'), 'type' => MENU_LOCAL_TASK, - 'weight' => 11, + 'weight' => 20, 'file' => 'taxonomy.admin.inc', ); $items['taxonomy/term/%taxonomy_term/feed'] = array( @@ -345,7 +344,6 @@ function taxonomy_menu() { $items['admin/structure/taxonomy/%taxonomy_vocabulary/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -20, ); $items['admin/structure/taxonomy/%taxonomy_vocabulary/edit'] = array( 'title' => 'Edit', @@ -354,7 +352,6 @@ function taxonomy_menu() { 'access callback' => 'entity_page_access', 'access arguments' => array(3, 'update'), 'type' => MENU_LOCAL_TASK, - 'weight' => -10, 'file' => 'taxonomy.admin.inc', ); diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 19dc974..e464b3f 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -908,7 +908,6 @@ function user_menu() { $items['user/login/default'] = array( 'title' => 'Username and password', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['user/register'] = array( @@ -965,7 +964,6 @@ function user_menu() { 'description' => 'Find and manage people interacting with your site.', 'access arguments' => array('administer users'), 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, 'file' => 'user.admin.inc', ); @@ -1032,7 +1030,6 @@ function user_menu() { $items['admin/config/people/accounts/settings'] = array( 'title' => 'Settings', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); $items['user/%user'] = array( @@ -1044,13 +1041,10 @@ function user_menu() { 'access callback' => 'entity_page_access', 'access arguments' => array(1), ); - $items['user/%user/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, ); - $items['user/%user/cancel'] = array( 'title' => 'Cancel account', 'page callback' => 'drupal_get_form', @@ -1059,7 +1053,6 @@ function user_menu() { 'access arguments' => array(1, 'delete'), 'file' => 'user.pages.inc', ); - $items['user/%user/cancel/confirm/%/%'] = array( 'title' => 'Confirm account cancellation', 'page callback' => 'user_cancel_confirm', @@ -1068,7 +1061,6 @@ function user_menu() { 'access arguments' => array(1, 'delete'), 'file' => 'user.pages.inc', ); - $items['user/%user/edit'] = array( 'title' => 'Edit', 'page callback' => 'entity_get_form', diff --git a/core/modules/views/views_ui/views_ui.module b/core/modules/views/views_ui/views_ui.module index 24d54ef..a8c6725 100644 --- a/core/modules/views/views_ui/views_ui.module +++ b/core/modules/views/views_ui/views_ui.module @@ -35,7 +35,6 @@ function views_ui_menu() { $items['admin/structure/views/list'] = array( 'title' => 'List', - 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, 'access arguments' => array('administer views'), ); @@ -68,7 +67,6 @@ function views_ui_menu() { 'title' => 'Edit view', 'type' => MENU_DEFAULT_LOCAL_TASK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, - 'weight' => -10, 'access arguments' => array('administer views'), ); $items['admin/structure/views/view/%/preview/%'] = array( @@ -101,7 +99,6 @@ function views_ui_menu() { 'description' => 'Overview of fields used in all views.', 'page callback' => 'NOT_USED', 'type' => MENU_LOCAL_TASK, - 'weight' => 0, 'access arguments' => array('administer views'), ); diff --git a/core/themes/seven/template.php b/core/themes/seven/template.php index ae9120d..a00f903 100644 --- a/core/themes/seven/template.php +++ b/core/themes/seven/template.php @@ -34,7 +34,7 @@ function seven_preprocess_page(&$vars) { unset($vars['primary_local_tasks']['#secondary']); $vars['secondary_local_tasks'] = array( '#theme' => 'menu_local_tasks', - '#secondary' => $vars['tabs']['#secondary'], + '#secondary' => isset($vars['tabs']['#secondary']) ? $vars['tabs']['#secondary'] : '', ); }