diff --git a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php index 67e0afd..f44b9d8 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php @@ -120,7 +120,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['base_route_name'] == $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..7b19e64 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -19,6 +19,7 @@ use Drupal\Core\Plugin\Factory\ContainerFactory; use Drupal\Core\Routing\RouteProviderInterface; use Symfony\Component\HttpFoundation\Request; +use \Symfony\Component\Routing\Exception\RouteNotFoundException; /** * Manages discovery and instantiation of menu local task plugins. @@ -39,8 +40,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 +128,81 @@ 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 base_route_name is not specified assume it's the same as the route. + if (empty($definition['base_route_name'])) { + $definition['base_route_name'] = $definition['route_name']; + } + } + + /** + * {@inheritdoc} + */ + protected function findDefinitions() { + $definitions = $this->discovery->getDefinitions(); + + // Add base tab in any cases where it is missing. + $found_routes = array(); + foreach ($definitions as $definition) { + $found_routes[$definition['route_name']] = $definition['base_route_name']; + } + $added_tabs = array(); + foreach ($definitions as &$definition) { + if (!isset($found_routes[$definition['base_route_name']])) { + // Create the missing tab. + $tabs = $this->addMissingDefaultTab($definition['base_route_name']); + foreach ($tabs as $new_definition) { + // Add the tab on the new base route as one in the list of those we + // know about. + $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 using + // the base_route_name, we want to copy that existing tab's + // base_route_name so that the value is consistent. + $definition['base_route_name'] = $found_routes[$definition['base_route_name']]; + } + } + $definitions += $added_tabs; + + foreach ($definitions as $plugin_id => &$definition) { + $this->processDefinition($definition, $plugin_id); + } + if ($this->alterHook) { + $this->moduleHandler->alter($this->alterHook, $definitions); + } + + return $definitions; + } + + /** + * Adds a new local task plugin definition as default for an existing route. + * + * @param string $base_route_name + * The route_name to add a new default tab for. + * + * @return array + * An array of plugin definitions with the new default tab included. + */ + protected function addMissingDefaultTab($base_route_name) { + // Load/validate the route object and get the title. + $definitions = array(); + try { + $route = $this->routeProvider->getRouteByName($base_route_name); + $title = $route->getDefault('_title'); + $plugin_id = 'default_local_task:' . $base_route_name; + $definitions[$plugin_id] = array( + 'route_name' => $base_route_name, + 'base_route_name' => $base_route_name, + 'title' => $title ? $title : 'Default', + 'weight' => -10, + ); + } + catch (RouteNotFoundException $e) { + // Suppress the error. + } + return $definitions; } /** @@ -160,7 +236,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 +244,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,31 +260,32 @@ public function getLocalTasksForRoute($route_name) { } } } - if ($tab_root_ids) { - // Find all the plugins with the same root and that are at the top - // level or that have a visible parent. + if ($base_route_names) { + // Find all the plugins with the same base_route_name 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']]))) { - // 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']; + if (!empty($base_route_names[$task_info['base_route_name']]) && (empty($task_info['tab_parent_id']) || !empty($parents[$task_info['tab_parent_id']]))) { + // Concatenate '> ' with the base_route_name for the parent of + // top-level tabs. + $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 $base_name) { // Convert the tree keyed by plugin IDs into a simple one with // integer depth. Create instances for each plugin along the way. $level = 0; // We used this above as the top-level parent array key. - $next_parent = '> ' . $root_id; + $next_parent = '> ' . $base_name; do { $parent = $next_parent; $next_parent = FALSE; 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/filter/filter.local_tasks.yml b/core/modules/filter/filter.local_tasks.yml index 9f7e8e9..301cc52 100644 --- a/core/modules/filter/filter.local_tasks.yml +++ b/core/modules/filter/filter.local_tasks.yml @@ -1,5 +1,5 @@ filter.format_edit_tab: route_name: filter.format_edit title: 'Configure' - tab_root_id: filter.format_edit_tab + base_route_name: filter.format_edit weight: -10 diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php index 9366aef..61a5f19 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php @@ -227,6 +227,24 @@ public function testPluginLocalTask() { $result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]'); $this->assertEqual(1, count($result), 'There is one active tab.'); $this->assertEqual('upcasting sub2', (string) $result[0]->a, 'The "upcasting sub2" tab is active.'); + + $this->drupalGet('menu-test/default-route'); + $tasks = array( + 'menu-test/default-route', + 'menu-test/other-route', + ); + $this->assertLocalTasks($tasks, 0); + + $result = $this->xpath('//ul[contains(@class, "tabs")]//a'); + $this->assertEqual('Default route', (string) $result[0]); + $this->assertEqual('Other route', (string) $result[1]); + + $this->drupalGet('menu-test/other-route'); + $tasks = array( + 'menu-test/default-route', + 'menu-test/other-route', + ); + $this->assertLocalTasks($tasks, 0); } } diff --git a/core/modules/system/system.local_tasks.yml b/core/modules/system/system.local_tasks.yml index b29773d..bb2724c 100644 --- a/core/modules/system/system.local_tasks.yml +++ b/core/modules/system/system.local_tasks.yml @@ -1,14 +1,14 @@ system.rss_feeds_settings_tab: route_name: system.rss_feeds_settings title: Settings - tab_root_id: system.rss_feeds_settings_tab + base_route_name: system.rss_feeds_settings system.site_maintenance_mode_settings_tab: route_name: system.site_maintenance_mode title: Settings - tab_root_id: system.site_maintenance_mode_settings_tab + base_route_name: system.site_maintenance_mode system.site_information_settings_tab: route_name: system.site_information_settings title: Settings - tab_root_id: system.site_information_settings_tab + base_route_name: system.site_information_settings 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..beaf68e 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,69 @@ 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_upcasting_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_upcasting_sub1 weight: 10 + +menu_test.local_task_other_route: + route_name: menu_test.local_task_other_route + base_route_name: menu_test.local_task_default_route + title: Other route diff --git a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml index fd90253..1496461 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml +++ b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml @@ -166,3 +166,18 @@ menu_test.optional_placeholder: placeholder: NULL requirements: _access: 'TRUE' + +menu_test.local_task_default_route: + path: '/menu-test/default-route' + defaults: + _content: '\Drupal\menu_test\TestControllers::test1' + _title: 'Default route' + requirements: + _access: 'TRUE' + +menu_test.local_task_other_route: + path: '/menu-test/other-route' + defaults: + _content: '\Drupal\menu_test\TestControllers::test1' + requirements: + _access: 'TRUE' diff --git a/core/modules/user/user.local_tasks.yml b/core/modules/user/user.local_tasks.yml index c82874b..9d72fae 100644 --- a/core/modules/user/user.local_tasks.yml +++ b/core/modules/user/user.local_tasks.yml @@ -1,4 +1,4 @@ user.role_list_tab: route_name: user.role_list title: 'Roles' - tab_root_id: user.role_list_tab + base_route_name: user.role_list 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 1777ab8..8f72c5f 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php @@ -194,15 +194,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' + 'base_route_name' => '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' + 'base_route_name' => '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..b3ce479 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php @@ -10,7 +10,10 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Language\Language; use Drupal\Tests\UnitTestCase; +use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\Route; use Zend\Stdlib\ArrayObject; /** @@ -76,6 +79,13 @@ class LocalTaskManagerTest extends UnitTestCase { */ protected $accessManager; + /** + * The mocked module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $moduleHandler; + public static function getInfo() { return array( 'name' => 'Local tasks manager.', @@ -96,6 +106,7 @@ protected function setUp() { $this->pluginDiscovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface'); $this->factory = $this->getMock('Drupal\Component\Plugin\Factory\FactoryInterface'); $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); $this->accessManager = $this->getMockBuilder('Drupal\Core\Access\AccessManager') ->disableOriginalConstructor() ->getMock(); @@ -120,7 +131,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 +163,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 +177,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,18 +199,138 @@ 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); } /** + * Tests findDefinitions and ensure that default tabs are added automatically. + * + * @see \Drupal\Core\Menu\LocalTaskManager::findDefinitions() + * @see \Drupal\Core\Menu\LocalTaskManager::addMisingDefaultTab() + */ + public function testFindDefinitions() { + $definitions = $this->getLocalTasksFixture2(); + $this->pluginDiscovery->expects($this->once()) + ->method('getDefinitions') + ->will($this->returnValue($definitions)); + + // Ensure that the alter hook is called. + $this->moduleHandler->expects($this->once()) + ->method('alter') + ->with($this->equalTo('local_tasks')); + + $this->setupLocalTaskManager(); + + $route = new Route('/route-4', array('_title' => 'example')); + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->with($this->equalTo('route_4')) + ->will($this->returnValue($route)); + + $local_tasks = $this->manager->getDefinitions(); + + $this->assertNotEmpty($local_tasks['default_local_task:route_4']); + $local_task = $local_tasks['default_local_task:route_4']; + $this->assertEquals('route_4', $local_task['route_name']); + $this->assertEquals('route_4', $local_task['base_route_name']); + $this->assertEquals('example', $local_task['title']); + } + + /** + * Tests findDefinitions with default tab with wrong specific route name. + * + * @see \Drupal\Core\Menu\LocalTaskManager::findDefinitions() + * @see \Drupal\Core\Menu\LocalTaskManager::addMisingDefaultTab() + */ + public function testFindDefinitionsWithInvalidBaseRouteName() { + $definitions = $this->getLocalTasksFixture2(); + $this->pluginDiscovery->expects($this->once()) + ->method('getDefinitions') + ->will($this->returnValue($definitions)); + + // Ensure that the alter hook is called. + $this->moduleHandler->expects($this->once()) + ->method('alter') + ->with($this->equalTo('local_tasks')); + + $this->setupLocalTaskManager(); + + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->with($this->equalTo('route_4')) + ->will($this->throwException(new RouteNotFoundException())); + + $local_tasks = $this->manager->getDefinitions(); + + $this->assertTrue(empty($local_tasks['default_local_task:route_4'])); + } + + /** + * Tests findDefinitions and ensure that a single getDefinition call works. + * + * @see \Drupal\Core\Menu\LocalTaskManager::findDefinitions() + * @see \Drupal\Core\Menu\LocalTaskManager::getDefinition() + * @see \Drupal\Core\Menu\LocalTaskManager::addMisingDefaultTab() + */ + public function testFindDefinitionsWithSingleDefinition() { + $definitions = $this->getLocalTasksFixture2(); + $this->pluginDiscovery->expects($this->once()) + ->method('getDefinitions') + ->will($this->returnValue($definitions)); + + $this->setupLocalTaskManager(); + + $route = new Route('/route-4', array('_title' => 'example')); + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->with($this->equalTo('route_4')) + ->will($this->returnValue($route)); + + $local_task = $this->manager->getDefinition('default_local_task:route_4'); + $this->assertNotEmpty($local_task); + $this->assertEquals('route_4', $local_task['route_name']); + $this->assertEquals('route_4', $local_task['base_route_name']); + } + + /** + * Tests the processDefinition method. + * + * Ensure that the base_route_name is added automatically. + * + * @see \Drupal\Core\Menu\LocalTaskManager::processDefinition() + */ + public function testProcessDefinition() { + $definition = array( + 'id' => 'tab1', + 'route_name' => 'route_1', + 'title' => 'Tab 1', + ); + $this->manager->processDefinition($definition, 'tab1'); + $this->assertEquals('route_1', $definition['base_route_name']); + } + + /** + * Tests processDefinition with missing route_name. + * + * @expectedException \Drupal\Component\Plugin\Exception\PluginException + */ + public function testProcessDefinitionWithMissingRouteName() { + $definition = array( + 'id' => 'tab1', + 'title' => 'Tab 1', + ); + $this->manager->processDefinition($definition, 'tab1'); + } + + /** * Tests the getTitle method. * * @see \Drupal\system\Plugin\Type\MenuLocalTaskManager::getTitle() @@ -239,6 +370,10 @@ protected function setupLocalTaskManager() { $property->setAccessible(TRUE); $property->setValue($this->manager, $this->accessManager); + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'routeProvider'); + $property->setAccessible(TRUE); + $property->setValue($this->manager, $this->routeProvider); + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'discovery'); $property->setAccessible(TRUE); $property->setValue($this->manager, $this->pluginDiscovery); @@ -255,6 +390,14 @@ protected function setupLocalTaskManager() { ->will($this->returnValue(new Language(array('id' => 'en')))); $this->manager->setCacheBackend($this->cacheBackend, $language_manager, 'local_task'); + + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'moduleHandler'); + $property->setAccessible(TRUE); + $property->setValue($this->manager, $this->moduleHandler); + + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'alterHook'); + $property->setAccessible(TRUE); + $property->setValue($this->manager, 'local_tasks'); } /** @@ -267,22 +410,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 +434,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,17 +488,45 @@ 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(), ) ); } + /** + * Return some local tasks plugin definitions. + * + * @return array + * An array of plugin definition keyed by plugin ID. + */ + protected function getLocalTasksFixture2() { + $definitions['tab1'] = array( + 'id' => 'tab1', + 'route_name' => 'route_1', + 'title' => 'Tab 1', + 'base_route_name' => 'route_1', + ); + $definitions['tab2'] = array( + 'id' => 'tab2', + 'title' => 'Tab 2', + 'route_name' => 'route_2', + 'base_route_name' => 'route_1', + ); + $definitions['tab3'] = array( + 'id' => 'tab3', + 'title' => 'Tab 3', + 'route_name' => 'route_3', + 'base_route_name' => 'route_4', + ); + return $definitions; + } + }