diff --git a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php index 44233e8..603f6b6 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php @@ -141,7 +141,7 @@ public function getTitle() { public function getWeight() { // By default the weight is 0, or -10 for the root tab. if (!isset($this->pluginDefinition['weight'])) { - if ($this->pluginDefinition['tab_root_id'] == $this->pluginDefinition['id']) { + if ($this->pluginDefinition['tab_root'] == $this->pluginDefinition['route_name'] && empty($this->pluginDefinition['tab_parent_id'])) { $this->pluginDefinition['weight'] = -10; } else { diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index a02b8bb..35ed9a2 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -39,8 +39,8 @@ class LocalTaskManager extends DefaultPluginManager { 'route_parameters' => array(), // The static title for the local task. 'title' => '', - // The plugin ID of the root tab. - 'tab_root_id' => '', + // The route name defining a group of tabs this one is associated with. + 'base_route_name' => '', // The plugin ID of the parent tab (or NULL for the top-level tab). 'tab_parent_id' => NULL, // The weight of the tab. @@ -127,6 +127,54 @@ public function processDefinition(&$definition, $plugin_id) { if (empty($definition['route_name'])) { throw new PluginException(sprintf('Plugin (%s) definition must include "route_name"', $plugin_id)); } + // If the root is not specified assume it's the same as the route. + if (empty($definition['base_route_name'])) { + $definition['base_route_name'] = $definition['route_name']; + } + } + + /** + * Finds plugin definitions. + * + * @return array + * List of definitions to store in cache. + */ + protected function findDefinitions() { + $definitions = parent::findDefinitions(); + // Add root tab in any cases where it is missing. + $found_routes = array(); + foreach ($definitions as $plugin_id => $definition) { + $found_routes[$definition['route_name']] = $definition['base_route_name']; + } + $added_tabs = array(); + foreach ($definitions as $plugin_id => &$definition) { + if (!isset($found_routes[$definition['base_route_name']])) { + // Create the missing tab. + $tabs = $this->createMissingTab($definition); + foreach ($tabs as $new_definition) { + $found_routes[$new_definition['route_name']] = $new_definition['base_route_name']; + } + $added_tabs += $tabs; + } + else { + // If a developer adds a new tab and references an existing tab as + // the root, we want to copy that existing tab's root so that the + // value is consistent. + $definition['base_route_name'] = $found_routes[$definition['base_route_name']]; + } + } + return $definitions + $added_tabs; + } + + protected function addMissingTab($definition) { + // @todo: Load/validate the route object and get the title. + $plugin_id = 'default_local_task:' . $definition['base_route_name']; + $definitions[$plugin_id] = array( + 'route_name' => $definition['base_route_name'], + 'title' => 'Default', + 'weight' => -10, + ); + return $definitions; } /** @@ -160,7 +208,7 @@ public function getLocalTasksForRoute($route_name) { if (!isset($this->instances[$route_name])) { $this->instances[$route_name] = array(); if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $route_name)) { - $tab_root_ids = $cache->data['tab_root_ids']; + $base_route_names = $cache->data['base_route_names']; $parents = $cache->data['parents']; $children = $cache->data['children']; } @@ -168,12 +216,12 @@ public function getLocalTasksForRoute($route_name) { $definitions = $this->getDefinitions(); // We build the hierarchy by finding all tabs that should // appear on the current route. - $tab_root_ids = array(); + $base_route_names = array(); $parents = array(); $children = array(); foreach ($definitions as $plugin_id => $task_info) { if ($route_name == $task_info['route_name']) { - $tab_root_ids[$task_info['tab_root_id']] = $task_info['tab_root_id']; + $base_route_names[$task_info['base_route_name']] = $task_info['base_route_name']; // Tabs that link to the current route are viable parents // and their parent and children should be visible also. // @todo - this only works for 2 levels of tabs. @@ -184,26 +232,26 @@ public function getLocalTasksForRoute($route_name) { } } } - if ($tab_root_ids) { + if ($base_route_names) { // Find all the plugins with the same root and that are at the top // level or that have a visible parent. foreach ($definitions as $plugin_id => $task_info) { - if (!empty($tab_root_ids[$task_info['tab_root_id']]) && (empty($task_info['tab_parent_id']) || !empty($parents[$task_info['tab_parent_id']]))) { + if (!empty($base_route_names[$task_info['base_route_name']]) && (empty($task_info['tab_parent_id']) || !empty($parents[$task_info['tab_parent_id']]))) { // Concat '> ' with root ID for the parent of top-level tabs. - $parent = empty($task_info['tab_parent_id']) ? '> ' . $task_info['tab_root_id'] : $task_info['tab_parent_id']; + $parent = empty($task_info['tab_parent_id']) ? '> ' . $task_info['base_route_name'] : $task_info['tab_parent_id']; $children[$parent][$plugin_id] = $task_info; } } } $data = array( - 'tab_root_ids' => $tab_root_ids, + 'base_route_names' => $base_route_names, 'parents' => $parents, 'children' => $children, ); $this->cacheBackend->set($this->cacheKey . ':' . $route_name, $data, CacheBackendInterface::CACHE_PERMANENT, array('local_task')); } // Create a plugin instance for each element of the hierarchy. - foreach ($tab_root_ids as $root_id) { + foreach ($base_route_names as $root_id) { // Convert the tree keyed by plugin IDs into a simple one with // integer depth. Create instances for each plugin along the way. $level = 0; diff --git a/core/modules/comment/comment.local_tasks.yml b/core/modules/comment/comment.local_tasks.yml index ee8d3bf..ce8fe10 100644 --- a/core/modules/comment/comment.local_tasks.yml +++ b/core/modules/comment/comment.local_tasks.yml @@ -1,15 +1,15 @@ comment_permalink_tab: route_name: comment_permalink title: 'View comment' - tab_root_id: comment_permalink_tab + base_route_name: comment_permalink comment_edit_page_tab: route_name: comment_edit_page title: 'Edit' - tab_root_id: comment_permalink_tab + base_route_name: comment_permalink weight: 0 comment_confirm_delete_tab: route_name: comment_confirm_delete title: 'Delete' - tab_root_id: comment_permalink_tab + base_route_name: comment_permalink weight: 10 diff --git a/core/modules/system/tests/modules/menu_test/menu_test.local_tasks.yml b/core/modules/system/tests/modules/menu_test/menu_test.local_tasks.yml index 7d270f3..e18289b 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.local_tasks.yml +++ b/core/modules/system/tests/modules/menu_test/menu_test.local_tasks.yml @@ -1,54 +1,64 @@ menu_local_task_test_tasks_view: route_name: menu_test.local_task_test_tasks_view title: View - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view + menu_local_task_test_tasks_edit: route_name: menu_test.local_task_test_tasks_edit title: Edit - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view + menu_local_task_test_tasks_settings: route_name: menu_test.local_task_test_tasks_settings title: Settings - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view + menu_local_task_test_tasks_settings_sub1: route_name: menu_test.local_task_test_tasks_settings_sub1 title: sub1 - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view tab_parent_id: menu_local_task_test_tasks_settings class: Drupal\menu_test\Plugin\Menu\LocalTask\TestTasksSettingsSub1 weight: -10 + menu_local_task_test_tasks_settings_sub2: route_name: menu_test.local_task_test_tasks_settings_sub2 title: sub2 - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view tab_parent_id: menu_local_task_test_tasks_settings + menu_local_task_test_tasks_settings_sub3: route_name: menu_test.local_task_test_tasks_settings_sub3 title: sub3 - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view tab_parent_id: menu_local_task_test_tasks_settings weight: 20 + menu_local_task_test_tasks_settings_derived: route_name: menu_test.local_task_test_tasks_settings_derived title: derived - tab_root_id: menu_local_task_test_tasks_view + base_route_name: menu_test.local_task_test_tasks_view tab_parent_id: menu_local_task_test_tasks_settings derivative: Drupal\menu_test\Plugin\Derivative\LocalTaskTest weight: 50 + menu_local_task_test.placeholder_sub1: route_name: menu_test.local_task_test_placeholder_sub1 title: 'placeholder sub1' - tab_root_id: menu_local_task_test_placeholder_sub1 + base_route_name: menu_test.local_task_test_placeholder_sub1 + menu_local_task_test_placeholder_sub2: route_name: menu_test.local_task_test_placeholder_sub2 title: 'placeholder sub2' - tab_root_id: menu_local_task_test_placeholder_sub1 + base_route_name: menu_test.local_task_test_placeholder_sub1 + menu_local_task_test.upcasting_sub1: route_name: menu_test.local_task_test_upcasting_sub1 title: 'upcasting sub1' - tab_root_id: menu_local_task_test_upcasting_sub1 + base_route_name: menu_test.local_task_test_placeholder_sub1 + menu_local_task_test_upcasting_sub2: route_name: menu_test.local_task_test_upcasting_sub2 title: 'upcasting sub2' - tab_root_id: menu_local_task_test_upcasting_sub1 + base_route_name: menu_test.local_task_test_placeholder_sub1 weight: 10 diff --git a/core/modules/views_ui/views_ui.local_tasks.yml b/core/modules/views_ui/views_ui.local_tasks.yml index 383ef10..68e961e 100644 --- a/core/modules/views_ui/views_ui.local_tasks.yml +++ b/core/modules/views_ui/views_ui.local_tasks.yml @@ -1,22 +1,22 @@ views_ui_settings_tab: route_name: views_ui.settings_basic title: Settings - tab_root_id: views_ui_list_tab + base_route_name: views_ui.list views_ui_settings_basic_tab: route_name: views_ui.settings_basic title: Basic - tab_root_id: views_ui_list_tab + base_route_name: views_ui.list tab_parent_id: views_ui_settings_tab views_ui_settings_advanced_tab: route_name: views_ui.settings_advanced title: Advanced - tab_root_id: views_ui_list_tab + base_route_name: views_ui.list tab_parent_id: views_ui_settings_tab weight: 10 views_ui_list_tab: route_name: views_ui.list title: List - tab_root_id: views_ui_list_tab + base_route_name: views_ui.list diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php index 6a7c8af..1969937 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php @@ -190,15 +190,17 @@ public function providerTestGetWeight() { // Ensure that a default tab get a lower weight. array( array( - 'tab_root_id' => 'local_task_default', - 'id' => 'local_task_default' + 'tab_root' => 'fake_route.default', + 'route_name' => 'fake_route.default', + 'id' => 'local_task_default', ), -10 ), array( array( - 'tab_root_id' => 'local_task_example', - 'id' => 'local_task_default' + 'tab_root' => 'fake_route.example', + 'route_name' => 'fake_route.default', + 'id' => 'local_task_default', ), 0 ), diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php index f10bb38..4e3f291 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php @@ -120,7 +120,7 @@ public function testGetLocalTasksForRouteSingleLevelTitle() { $this->setupFactory($mock_plugin); $this->setupLocalTaskManager(); - $local_tasks = $this->manager->getLocalTasksForRoute('menu_local_task_test_tasks_view'); + $local_tasks = $this->manager->getLocalTasksForRoute('menu_test.local_task_test_tasks_view'); $result = array( 0 => array( @@ -152,7 +152,7 @@ public function testGetLocalTaskForRouteWithEmptyCache() { $this->cacheBackend->expects($this->at(0)) ->method('get') - ->with('local_task:en:menu_local_task_test_tasks_view'); + ->with('local_task:en:menu_test.local_task_test_tasks_view'); $this->cacheBackend->expects($this->at(1)) ->method('get') @@ -166,9 +166,9 @@ public function testGetLocalTaskForRouteWithEmptyCache() { $this->cacheBackend->expects($this->at(3)) ->method('set') - ->with('local_task:en:menu_local_task_test_tasks_view', $expected_set, CacheBackendInterface::CACHE_PERMANENT, array('local_task')); + ->with('local_task:en:menu_test.local_task_test_tasks_view', $expected_set, CacheBackendInterface::CACHE_PERMANENT, array('local_task')); - $local_tasks = $this->manager->getLocalTasksForRoute('menu_local_task_test_tasks_view'); + $local_tasks = $this->manager->getLocalTasksForRoute('menu_test.local_task_test_tasks_view'); $this->assertEquals($result, $local_tasks); } @@ -188,14 +188,14 @@ public function testGetLocalTaskForRouteWithFilledCache() { $this->cacheBackend->expects($this->at(0)) ->method('get') - ->with('local_task:en:menu_local_task_test_tasks_view') + ->with('local_task:en:menu_test.local_task_test_tasks_view') ->will($this->returnValue((object) array('data' => $result))); $this->cacheBackend->expects($this->never()) ->method('set'); $result = $this->getLocalTasksForRouteResult($mock_plugin); - $local_tasks = $this->manager->getLocalTasksForRoute('menu_local_task_test_tasks_view'); + $local_tasks = $this->manager->getLocalTasksForRoute('menu_test.local_task_test_tasks_view'); $this->assertEquals($result, $local_tasks); } @@ -267,22 +267,22 @@ protected function getLocalTaskFixtures() { $definitions = array(); $definitions['menu_local_task_test_tasks_settings'] = array( 'id' => 'menu_local_task_test_tasks_settings', - 'route_name' => 'menu_local_task_test_tasks_settings', + 'route_name' => 'menu_test.local_task_test_tasks_settings', 'title' => 'Settings', - 'tab_root_id' => 'menu_local_task_test_tasks_view', + 'base_route_name' => 'menu_test.local_task_test_tasks_view', ); $definitions['menu_local_task_test_tasks_edit'] = array( 'id' => 'menu_local_task_test_tasks_edit', - 'route_name' => 'menu_local_task_test_tasks_edit', + 'route_name' => 'menu_test.local_task_test_tasks_edit', 'title' => 'Settings', - 'tab_root_id' => 'menu_local_task_test_tasks_view', + 'base_route_name' => 'menu_test.local_task_test_tasks_view', 'weight' => 20, ); $definitions['menu_local_task_test_tasks_view'] = array( 'id' => 'menu_local_task_test_tasks_view', - 'route_name' => 'menu_local_task_test_tasks_view', + 'route_name' => 'menu_test.local_task_test_tasks_view', 'title' => 'Settings', - 'tab_root_id' => 'menu_local_task_test_tasks_view', + 'base_route_name' => 'menu_test.local_task_test_tasks_view', ); // Add the defaults from the LocalTaskManager. foreach ($definitions as $id => &$info) { @@ -291,7 +291,7 @@ protected function getLocalTaskFixtures() { 'route_name' => '', 'route_parameters' => array(), 'title' => '', - 'tab_root_id' => '', + 'base_route_name' => '', 'tab_parent_id' => NULL, 'weight' => 0, 'options' => array(), @@ -345,14 +345,14 @@ protected function getLocalTasksForRouteResult($mock_plugin) { */ protected function getLocalTasksCache() { return array( - 'tab_root_ids' => array( - 'menu_local_task_test_tasks_view' => 'menu_local_task_test_tasks_view', + 'base_route_names' => array( + 'menu_test.local_task_test_tasks_view' => 'menu_test.local_task_test_tasks_view', ), 'parents' => array( 'menu_local_task_test_tasks_view' => 1, ), 'children' => array( - '> menu_local_task_test_tasks_view' => $this->getLocalTaskFixtures(), + '> menu_test.local_task_test_tasks_view' => $this->getLocalTaskFixtures(), ) ); }