diff --git a/core/core.services.yml b/core/core.services.yml index 4fab422..295e57d 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -177,7 +177,7 @@ services: arguments: ['@container.namespaces', '@controller_resolver', '@request', '@module_handler', '@cache.cache', '@language_manager'] plugin.manager.menu.local_task: class: Drupal\Core\Menu\LocalTaskManager - arguments: ['@controller_resolver', '@request', '@router.route_provider', '@module_handler', '@cache.cache', '@language_manager'] + arguments: ['@controller_resolver', '@request', '@router.route_provider', '@module_handler', '@cache.cache', '@language_manager', '@access_manager'] request: class: Symfony\Component\HttpFoundation\Request # @TODO the synthetic setting must be uncommented whenever drupal_session_initialize() diff --git a/core/includes/menu.inc b/core/includes/menu.inc index f289cbb..65eea7c 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -1717,8 +1717,15 @@ function theme_menu_local_task($variables) { $link['localized_options']['html'] = TRUE; $link_text = t('!local-task-title!active', array('!local-task-title' => $link['title'], '!active' => $active)); } + if (!empty($link['href'])) { + // @todo - remove this once all pages are converted to routes. + $a_tag = l($link_text, $link['href'], $link['localized_options']); + } + else { + $a_tag = Drupal::l($link_text, $link['route_name'], $link['route_parameters'], $link['localized_options']); + } - return '' . l($link_text, $link['href'], $link['localized_options']) . ''; + return '' . $a_tag . ''; } /** @@ -1958,6 +1965,20 @@ function menu_local_tasks($level = 0) { $actions = $empty['actions']; $tabs = array(); + // Look for route-based tabs. + $data['tabs'] = array(); + $data['actions'] = array(); + + $route_name = Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME); + if (!empty($route_name)) { + $manager = Drupal::service('plugin.manager.menu.local_task'); + $local_tasks = $manager->getTasksBuild($route_name); + foreach ($local_tasks as $level => $items) { + $data['tabs'][$level] = empty($data['tabs'][$level]) ? $items : array_merge($data['tabs'][$level], $items); + } + } + + // @todo Remove the code below once the old menu router system got removed. $router_item = menu_get_item(); // If this router item points to its parent, start from the parents to @@ -2143,16 +2164,7 @@ function menu_local_tasks($level = 0) { ksort($tabs); // Remove the depth, we are interested only in their relative placement. $tabs = array_values($tabs); - $data['tabs'] = $tabs; - // Look for route-based tabs. - $route_name = \Drupal::request()->attributes->get('_route'); - if (!empty($route_name)) { - $manager = \Drupal::service('plugin.manager.menu.local_task'); - $local_tasks = $manager->getTasksBuild($route_name); - foreach ($local_tasks as $level => $items) { - $data['tabs'][$level] = empty($data['tabs'][$level]) ? $items : array_merge($data['tabs'][$level], $items); - } - } + $data['tabs'] += $tabs; // Allow modules to dynamically add further tasks. $module_handler = \Drupal::moduleHandler(); diff --git a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php index 5021e90..8983604 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php @@ -9,9 +9,10 @@ use Drupal\Component\Plugin\PluginBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\HttpFoundation\Request; /** * Default object used for LocalTaskPlugins. @@ -26,11 +27,11 @@ class LocalTaskDefault extends PluginBase implements LocalTaskInterface, Contain protected $stringTranslation; /** - * URL generator object. + * The route provider to load routes by name. * - * @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface + * @var \Drupal\Core\Routing\RouteProviderInterface */ - protected $generator; + protected $routeProvider; /** * TRUE if this plugin is forced active for options attributes. @@ -50,12 +51,12 @@ class LocalTaskDefault extends PluginBase implements LocalTaskInterface, Contain * The plugin implementation definition. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation object. - * @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $generator - * The url generator object. + * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider + * The route provider. */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, TranslationInterface $string_translation, UrlGeneratorInterface $generator) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, TranslationInterface $string_translation, RouteProviderInterface $route_provider) { $this->stringTranslation = $string_translation; - $this->generator = $generator; + $this->routeProvider = $route_provider; parent::__construct($configuration, $plugin_id, $plugin_definition); } @@ -68,7 +69,7 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $container->get('string_translation'), - $container->get('url_generator') + $container->get('router.route_provider') ); } @@ -91,26 +92,43 @@ public function getRouteName() { /** * {@inheritdoc} */ - public function getTitle() { - // Subclasses may pull in the request or specific attributes as parameters. - return $this->t($this->pluginDefinition['title']); + public function getRouteParameters(Request $request) { + $parameters = isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : array(); + $route = $this->routeProvider->getRouteByName($this->getRouteName()); + $variables = $route->compile()->getVariables(); + + // Normally the \Drupal\Core\ParamConverter\ParamConverterManager has + // processed the Request attributes, and in that case the _raw_variables + // attribute holds the original path strings keyed to the corresponding + // slugs in the path patterns. For example, if the route's path pattern is + // /filter/tips/{filter_format} and the path is /filter/tips/plain_text then + // $raw_variables->get('filter_format') == 'plain_text'. + + $raw_variables = $request->attributes->get('_raw_variables'); + + foreach ($variables as $name) { + if (isset($parameters[$name])) { + continue; + } + + if ($raw_variables && $raw_variables->has($name)) { + $parameters[$name] = $raw_variables->get($name); + } + elseif ($request->attributes->has($name)) { + $parameters[$name] = $request->attributes->get($name); + } + } + // The UrlGenerator will throw an exception if expected parameters are + // missing. This method should be overridden if that is possible. + return $parameters; } /** * {@inheritdoc} - * - * @todo update based on https://drupal.org/node/2045267 */ - public function getPath() { - // Subclasses may set a request into the generator or use any desired method - // to generate the path. - $path = $this->generator->generate($this->getRouteName()); - // In order to get the Drupal path the base URL has to be stripped off. - $base_url = $this->generator->getContext()->getBaseUrl(); - if (!empty($base_url) && strpos($path, $base_url) === 0) { - $path = substr($path, strlen($base_url)); - } - return trim($path, '/'); + public function getTitle() { + // Subclasses may pull in the request or specific attributes as parameters. + return $this->t($this->pluginDefinition['title']); } /** @@ -136,7 +154,7 @@ public function getWeight() { /** * {@inheritdoc} */ - public function getOptions() { + public function getOptions(Request $request) { $options = $this->pluginDefinition['options']; if ($this->active) { if (empty($options['attributes']['class']) || !in_array('active', $options['attributes']['class'])) { diff --git a/core/lib/Drupal/Core/Menu/LocalTaskInterface.php b/core/lib/Drupal/Core/Menu/LocalTaskInterface.php index b7f581d..790e801 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskInterface.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskInterface.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Menu; +use Symfony\Component\HttpFoundation\Request; + /** * Defines an interface for menu local tasks. */ @@ -32,15 +34,17 @@ public function getRouteName(); public function getTitle(); /** - * Returns an internal Drupal path to use when creating the link for the tab. + * Returns the route parameters needed to render a link for the local task. * - * Subclasses may add optional arguments like NodeInterface $node = NULL that - * will be supplied by the ControllerResolver. + * @param \Symfony\Component\HttpFoundation\Request $request + * The HttpRequest object representing the current request. * - * @return string - * The path of this local task. + * @return array + * An array of parameter names and values. + * + * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate() */ - public function getPath(); + public function getRouteParameters(Request $request); /** * Returns the weight of the local task. @@ -51,14 +55,17 @@ public function getPath(); public function getWeight(); /** - * Returns an array of options suitable to pass to l(). + * Returns options for rendering a link to the local task. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The HttpRequest object representing the current request. * * @return array - * Associative array of options. + * An array of options. * - * @see l() + * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate() */ - public function getOptions(); + public function getOptions(Request $request); /** * Sets the active status. diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index 3ae67fd..f1139fe 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -8,7 +8,9 @@ namespace Drupal\Core\Menu; use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Core\Access\AccessManager; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageManager; use Drupal\Core\Plugin\DefaultPluginManager; @@ -17,7 +19,6 @@ use Drupal\Core\Plugin\Factory\ContainerFactory; use Drupal\Core\Routing\RouteProviderInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; /** * Manages discovery and instantiation of menu local task plugins. @@ -34,6 +35,8 @@ class LocalTaskManager extends DefaultPluginManager { protected $defaults = array( // (required) The name of the route this task links to. 'route_name' => '', + // Parameters for route variables when generating a link. + 'route_parameters' => array(), // The static title for the local task. 'title' => '', // The plugin ID of the root tab. @@ -53,7 +56,7 @@ class LocalTaskManager extends DefaultPluginManager { /** * A controller resolver object. * - * @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface + * @var \Drupal\Core\Controller\ControllerResolverInterface */ protected $controllerResolver; @@ -79,9 +82,16 @@ class LocalTaskManager extends DefaultPluginManager { protected $routeProvider; /** + * The access manager. + * + * @var \Drupal\Core\Access\AccessManager + */ + protected $accessManager; + + /** * Constructs a \Drupal\Core\Menu\LocalTaskManager object. * - * @param \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $controller_resolver + * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver * An object to use in introspecting route methods. * @param \Symfony\Component\HttpFoundation\Request $request * The request object to use for building titles and paths for plugin instances. @@ -93,14 +103,17 @@ class LocalTaskManager extends DefaultPluginManager { * The cache backend. * @param \Drupal\Core\Language\LanguageManager $language_manager * The language manager. + * @param \Drupal\Core\Access\AccessManager $access_manager + * The access manager. */ - public function __construct(ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager) { + public function __construct(ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager, AccessManager $access_manager) { $this->discovery = new YamlDiscovery('local_tasks', $module_handler->getModuleDirectories()); $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery); $this->factory = new ContainerFactory($this); $this->controllerResolver = $controller_resolver; $this->request = $request; $this->routeProvider = $route_provider; + $this->accessManager = $access_manager; $this->alterInfo($module_handler, 'local_tasks'); $this->setCacheBackend($cache, $language_manager, 'local_task', array('local_task' => TRUE)); } @@ -132,21 +145,6 @@ public function getTitle(LocalTaskInterface $local_task) { } /** - * Gets the Drupal path for a local task. - * - * @param \Drupal\Core\Menu\LocalTaskInterface $local_task - * The local task plugin instance to get the path for. - * - * @return string - * The path. - */ - public function getPath(LocalTaskInterface $local_task) { - $controller = array($local_task, 'getPath'); - $arguments = $this->controllerResolver->getArguments($this->request, $controller); - return call_user_func_array($controller, $arguments); - } - - /** * Find all local tasks that appear on a named route. * * @param string $route_name @@ -240,14 +238,14 @@ public function getLocalTasksForRoute($route_name) { /** * Gets the render array for all local tasks. * - * @param string $route_name + * @param string $current_route_name * The route for which to make renderable local tasks. * * @return array * A render array as expected by theme_menu_local_tasks. */ - public function getTasksBuild($route_name) { - $tree = $this->getLocalTasksForRoute($route_name); + public function getTasksBuild($current_route_name) { + $tree = $this->getLocalTasksForRoute($current_route_name); $build = array(); // Collect all route names. @@ -257,31 +255,34 @@ public function getTasksBuild($route_name) { $route_names[] = $child->getRouteName(); } } - // Fetches all routes involved in the tree. + // Pre-fetch all routes involved in the tree. This reduces the number + // of SQL queries that would otherwise be triggered by the access manager. $routes = $route_names ? $this->routeProvider->getRoutesByNames($route_names) : array(); foreach ($tree as $level => $instances) { - foreach ($instances as $child) { - $path = $this->getPath($child); + foreach ($instances as $plugin_id => $child) { + // In order to get the Drupal path the base URL has to be stripped off. + $route_name = $child->getRouteName(); + $route_parameters = $child->getRouteParameters($this->request); + // Find out whether the user has access to the task. - $route = $routes[$child->getRouteName()]; - $map = array(); - // @todo - replace this call when we have a real service for it. - $access = menu_item_route_access($route, $path, $map); + $access = $this->accessManager->checkNamedRoute($route_name, $route_parameters); if ($access) { // Need to flag the list element as active for a tab for the current // route or if the plugin is set active (i.e. the parent tab). - $active = ($route_name == $child->getRouteName() || $child->getActive()); + $active = (($current_route_name == $route_name && (array_intersect_assoc($route_parameters, $this->request->attributes->all()) == $route_parameters)) || $child->getActive()); // @todo It might make sense to use menu link entities instead of // arrays. - $menu_link = array( + + $link = array( 'title' => $this->getTitle($child), - 'href' => $path, - 'localized_options' => $child->getOptions(), + 'route_name' => $route_name, + 'route_parameters' => $route_parameters, + 'localized_options' => $child->getOptions($this->request), ); - $build[$level][$path] = array( + $build[$level][$plugin_id] = array( '#theme' => 'menu_local_task', - '#link' => $menu_link, + '#link' => $link, '#active' => $active, '#weight' => $child->getWeight(), '#access' => $access, diff --git a/core/modules/comment/comment.local_tasks.yml b/core/modules/comment/comment.local_tasks.yml new file mode 100644 index 0000000..ee8d3bf --- /dev/null +++ b/core/modules/comment/comment.local_tasks.yml @@ -0,0 +1,15 @@ +comment_permalink_tab: + route_name: comment_permalink + title: 'View comment' + tab_root_id: comment_permalink_tab +comment_edit_page_tab: + route_name: comment_edit_page + title: 'Edit' + tab_root_id: comment_permalink_tab + weight: 0 +comment_confirm_delete_tab: + route_name: comment_confirm_delete + title: 'Delete' + tab_root_id: comment_permalink_tab + 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 43a2c8d..0f54856 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalTasksTest.php @@ -155,36 +155,41 @@ public function testPluginLocalTask() { )); // Ensure the view tab is active. - $result = $this->xpath('//ul[contains(@class, "tabs")]//a[contains(@class, "active")]'); + $result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a'); $this->assertEqual(1, count($result), 'There is just a single active tab.'); $this->assertEqual('View', (string) $result[0], 'The view tab is active.'); // Verify that local tasks in the second level appear. - - $this->drupalGet('menu-local-task-test/tasks/settings'); - $this->assertLocalTasks(array( + $sub_tasks = array( 'menu-local-task-test/tasks/settings/sub1', 'menu-local-task-test/tasks/settings/sub2', 'menu-local-task-test/tasks/settings/sub3', - ), 1); + 'menu-local-task-test/tasks/settings/derive1', + 'menu-local-task-test/tasks/settings/derive2', + ); + $this->drupalGet('menu-local-task-test/tasks/settings'); + $this->assertLocalTasks($sub_tasks, 1); - $result = $this->xpath('//ul[contains(@class, "tabs")]//a[contains(@class, "active")]'); + $result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a'); $this->assertEqual(1, count($result), 'There is just a single active tab.'); $this->assertEqual('Settings', (string) $result[0], 'The settings tab is active.'); - $this->drupalGet('menu-local-task-test/tasks/settings/sub1'); - $this->assertLocalTasks(array( - 'menu-local-task-test/tasks/settings/sub1', - 'menu-local-task-test/tasks/settings/sub2', - 'menu-local-task-test/tasks/settings/sub3', - ), 1); + $this->assertLocalTasks($sub_tasks, 1); $result = $this->xpath('//ul[contains(@class, "tabs")]//a[contains(@class, "active")]'); $this->assertEqual(2, count($result), 'There are tabs active on both levels.'); $this->assertEqual('Settings', (string) $result[0], 'The settings tab is active.'); $this->assertEqual('Dynamic title for TestTasksSettingsSub1', (string) $result[1], 'The sub1 tab is active.'); + $this->drupalGet('menu-local-task-test/tasks/settings/derive1'); + $this->assertLocalTasks($sub_tasks, 1); + + $result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]'); + $this->assertEqual(2, count($result), 'There are tabs active on both levels.'); + $this->assertEqual('Settings', (string) $result[0]->a, 'The settings tab is active.'); + $this->assertEqual('Derive 1', (string) $result[1]->a, 'The derive1 tab is active.'); + // Ensures that the local tasks contains the proper 'provider key' $definitions = $this->container->get('plugin.manager.menu.local_task')->getDefinitions(); $this->assertEqual($definitions['menu_local_task_test_tasks_view']['provider'], 'menu_test'); diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/Derivative/LocalTaskTest.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/Derivative/LocalTaskTest.php new file mode 100644 index 0000000..807c78d --- /dev/null +++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/Derivative/LocalTaskTest.php @@ -0,0 +1,26 @@ + 'Derive 1', 'derive2' => 'Derive 2') as $key => $title) { + $this->derivatives[$key] = $base_plugin_definition; + $this->derivatives[$key]['title'] = $title; + $this->derivatives[$key]['route_parameters'] = array('placeholder' => $key); + $this->derivatives[$key]['weight'] = $weight++; // ensure weights for testing. + } + return $this->derivatives; + } +} diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/TestControllers.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/TestControllers.php index c36a0ae..608e0c7 100644 --- a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/TestControllers.php +++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/TestControllers.php @@ -38,6 +38,13 @@ public function test2() { /** * Prints out test data. + */ + public function testDerived() { + return 'testDerived'; + } + + /** + * Prints out test data. * * @param string|null $placeholder * A placeholder for the return string. diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/menu_test.local_tasks.yml b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/menu_test.local_tasks.yml new file mode 100644 index 0000000..43b9de4 --- /dev/null +++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/menu_test.local_tasks.yml @@ -0,0 +1,8 @@ +menu_local_task_test_placeholder_sub1: + route_name: menu_local_task_test_placeholder_sub1 + title: 'placeholder sub1' + tab_root_id: menu_local_task_test_placeholder_sub1 +menu_local_task_test_placeholder_sub2: + route_name: menu_local_task_test_placeholder_sub2 + title: 'placeholder sub2' + tab_root_id: menu_local_task_test_placeholder_sub1 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 08eeaf1..a7cec6e 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 @@ -28,3 +28,10 @@ menu_local_task_test_tasks_settings_sub3: tab_root_id: menu_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 + tab_parent_id: menu_local_task_test_tasks_settings + derivative: Drupal\menu_test\Plugin\Derivative\LocalTaskTest + weight: 50 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 661a667..7823aed 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 @@ -117,6 +117,27 @@ menu_test.local_task_test_tasks_settings_sub3: requirements: _access: 'TRUE' +menu_test.local_task_test_tasks_settings_derived: + path: '/menu-local-task-test/tasks/settings/{placeholder}' + defaults: + _content: '\Drupal\menu_test\TestControllers::testDerived' + requirements: + _access: 'TRUE' + +menu_test.local_task_test_placeholder_sub1: + path: '/menu-local-task-test-dynamic/{placeholder}/sub1' + defaults: + _content: '\Drupal\menu_test\TestControllers::test1' + requirements: + _access: 'TRUE' + +menu_test.local_task_test_placeholder_sub2: + path: '/menu-local-task-test-dynamic/{placeholder}/sub2' + defaults: + _content: '\Drupal\menu_test\TestControllers::test1' + requirements: + _access: 'TRUE' + menu_test.optional_placeholder: path: '/menu-test/optional/{placeholder}' defaults: diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php new file mode 100644 index 0000000..6a7c8af --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php @@ -0,0 +1,278 @@ + 'local_task_default', + ); + + /** + * The mocked translator. + * + * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $stringTranslation; + + /** + * The mocked route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $routeProvider; + + public static function getInfo() { + return array( + 'name' => 'Local tasks default plugin.', + 'description' => 'Tests the local task default class.', + 'group' => 'Menu', + ); + } + + protected function setUp() { + parent::setUp(); + + $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface'); + $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + } + + /** + * Setups the local task default. + */ + protected function setupLocalTaskDefault() { + $this->localTaskBase = new LocalTaskDefault($this->config, $this->pluginId, $this->pluginDefinition, $this->stringTranslation, $this->routeProvider); + } + + /** + * Tests the getRouteParameters for a static route. + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getRouteParameters() + */ + public function testGetRouteParametersForStaticRoute() { + $this->pluginDefinition = array( + 'route_name' => 'test_route' + ); + + $this->routeProvider->expects($this->once()) + ->method('getRouteByName') + ->with('test_route') + ->will($this->returnValue(new Route('/test-route'))); + + $this->setupLocalTaskDefault(); + + $request = Request::create('/'); + $this->assertEquals(array(), $this->localTaskBase->getRouteParameters($request)); + } + + /** + * Tests the getRouteParameters for a route with the parameters in the plugin definition. + */ + public function testGetRouteParametersInPluginDefinitions() { + $this->pluginDefinition = array( + 'route_name' => 'test_route', + 'route_parameters' => array('parameter' => 'example') + ); + + $this->routeProvider->expects($this->once()) + ->method('getRouteByName') + ->with('test_route') + ->will($this->returnValue(new Route('/test-route/{parameter}'))); + + $this->setupLocalTaskDefault(); + + $request = new Request(); + + $this->assertEquals(array('parameter' => 'example'), $this->localTaskBase->getRouteParameters($request)); + } + + /** + * Tests the getRouteParameters method for a route with dynamic non upcasted parameters. + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getRouteParameters() + */ + public function testGetRouteParametersForDynamicRoute() { + $this->pluginDefinition = array( + 'route_name' => 'test_route' + ); + + $this->routeProvider->expects($this->once()) + ->method('getRouteByName') + ->with('test_route') + ->will($this->returnValue(new Route('/test-route/{parameter}'))); + + $this->setupLocalTaskDefault(); + + $request = new Request(array(), array(), array('parameter' => 'example')); + + $this->assertEquals(array('parameter' => 'example'), $this->localTaskBase->getRouteParameters($request)); + } + + /** + * Tests the getRouteParameters method for a route with upcasted parameters. + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getRouteParameters() + */ + public function testGetRouteParametersForDynamicRouteWithUpcastedParameters() { + $this->pluginDefinition = array( + 'route_name' => 'test_route' + ); + + $this->routeProvider->expects($this->once()) + ->method('getRouteByName') + ->with('test_route') + ->will($this->returnValue(new Route('/test-route/{parameter}'))); + + $this->setupLocalTaskDefault(); + + $request = new Request(); + $raw_variables = new ParameterBag(); + $raw_variables->set('parameter', 'example'); + $request->attributes->set('parameter', (object) array('example2')); + $request->attributes->set('_raw_variables', $raw_variables); + + $this->assertEquals(array('parameter' => 'example'), $this->localTaskBase->getRouteParameters($request)); + } + + /** + * Defines a test provider for getWeight() + * + * @see self::getWeight() + * + * @return array + * A list or test plugin definition and expected weight. + */ + public function providerTestGetWeight() { + return array( + array(array('weight' => 314), 314), + // Ensure that a default tab get a lower weight. + array( + array( + 'tab_root_id' => 'local_task_default', + 'id' => 'local_task_default' + ), + -10 + ), + array( + array( + 'tab_root_id' => 'local_task_example', + 'id' => 'local_task_default' + ), + 0 + ), + ); + } + + /** + * Tests the getWeight method. + * + * @dataProvider providerTestGetWeight + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getWeight() + */ + public function testGetWeight(array $plugin_definition, $expected_weight) { + $this->pluginDefinition = $plugin_definition; + $this->setupLocalTaskDefault(); + + $this->assertEquals($expected_weight, $this->localTaskBase->getWeight()); + } + + /** + * Tests getActive/setActive() method. + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getActive() + * @see \Drupal\Core\Menu\LocalTaskDefault::setActive() + */ + public function testActive() { + $this->setupLocalTaskDefault(); + + $this->assertFalse($this->localTaskBase->getActive()); + $this->localTaskBase->setActive(); + $this->assertTrue($this->localTaskBase->getActive()); + } + + /** + * Tests the getTitle method. + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getTitle() + */ + public function testGetTitle() { + $this->pluginDefinition['title'] = 'Example'; + $this->stringTranslation->expects($this->once()) + ->method('translate', $this->pluginDefinition['title']) + ->will($this->returnValue('Example translated')); + + $this->setupLocalTaskDefault(); + $this->assertEquals('Example translated', $this->localTaskBase->getTitle()); + } + + /** + * Tests the getOption method. + * + * @see \Drupal\Core\Menu\LocalTaskDefault::getOption() + */ + public function testGetOptions() { + $this->pluginDefinition['options'] = array( + 'attributes' => array('class' => array('example')), + ); + + $this->setupLocalTaskDefault(); + + $request = Request::create('/'); + $this->assertEquals($this->pluginDefinition['options'], $this->localTaskBase->getOptions($request)); + + $this->localTaskBase->setActive(TRUE); + + $this->assertEquals(array( + 'attributes' => array( + 'class' => array( + 'example', + 'active' + ) + ) + ), $this->localTaskBase->getOptions($request)); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php index 082a130..f10bb38 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php @@ -69,6 +69,13 @@ class LocalTaskManagerTest extends UnitTestCase { */ protected $cacheBackend; + /** + * The mocked access manager. + * + * @var \Drupal\Core\Access\AccessManager|\PHPUnit_Framework_MockObject_MockObject + */ + protected $accessManager; + public static function getInfo() { return array( 'name' => 'Local tasks manager.', @@ -89,6 +96,9 @@ 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->accessManager = $this->getMockBuilder('Drupal\Core\Access\AccessManager') + ->disableOriginalConstructor() + ->getMock(); $this->setupLocalTaskManager(); } @@ -208,24 +218,6 @@ public function testGetTitle() { } /** - * Tests the getPath method. - * - * @see \Drupal\system\Plugin\Type\MenuLocalTaskManager::getPath() - */ - public function testGetPath() { - $menu_local_task = $this->getMock('Drupal\Core\Menu\LocalTaskInterface'); - $menu_local_task->expects($this->once()) - ->method('getPath'); - - $this->controllerResolver->expects($this->once()) - ->method('getArguments') - ->with($this->request, array($menu_local_task, 'getPath')) - ->will($this->returnValue(array())); - - $this->manager->getPath($menu_local_task); - } - - /** * Setups the local task manager for the test. */ protected function setupLocalTaskManager() { @@ -243,6 +235,10 @@ protected function setupLocalTaskManager() { $property->setAccessible(TRUE); $property->setValue($this->manager, $this->request); + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'accessManager'); + $property->setAccessible(TRUE); + $property->setValue($this->manager, $this->accessManager); + $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'discovery'); $property->setAccessible(TRUE); $property->setValue($this->manager, $this->pluginDiscovery); @@ -274,14 +270,12 @@ protected function getLocalTaskFixtures() { 'route_name' => 'menu_local_task_test_tasks_settings', 'title' => 'Settings', 'tab_root_id' => 'menu_local_task_test_tasks_view', - 'class' => 'Drupal\menu_test\Plugin\Menu\MenuLocalTasksTestTasksSettings', ); $definitions['menu_local_task_test_tasks_edit'] = array( 'id' => 'menu_local_task_test_tasks_edit', 'route_name' => 'menu_local_task_test_tasks_edit', 'title' => 'Settings', 'tab_root_id' => 'menu_local_task_test_tasks_view', - 'class' => 'Drupal\menu_test\Plugin\Menu\MenuLocalTasksTestTasksEdit', 'weight' => 20, ); $definitions['menu_local_task_test_tasks_view'] = array( @@ -289,13 +283,13 @@ protected function getLocalTaskFixtures() { 'route_name' => 'menu_local_task_test_tasks_view', 'title' => 'Settings', 'tab_root_id' => 'menu_local_task_test_tasks_view', - 'class' => 'Drupal\menu_test\Plugin\Menu\MenuLocalTasksTestTasksView', ); // Add the defaults from the LocalTaskManager. foreach ($definitions as $id => &$info) { $info += array( 'id' => '', 'route_name' => '', + 'route_parameters' => array(), 'title' => '', 'tab_root_id' => '', 'tab_parent_id' => NULL,