Commit 8980243b authored by catch's avatar catch

Issue #2076283 by dawehner, pwolanin, neclimdul: Allow local task plugins on...

Issue #2076283 by dawehner, pwolanin, neclimdul: Allow local task plugins on paths with a dynamic value (like a node).
parent a1346c8e
......@@ -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()
......
......@@ -1738,8 +1738,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 '<li' . (!empty($variables['element']['#active']) ? ' class="active"' : '') . '>' . l($link_text, $link['href'], $link['localized_options']) . '</li>';
return '<li' . (!empty($variables['element']['#active']) ? ' class="active"' : '') . '>' . $a_tag . '</li>';
}
/**
......@@ -1979,6 +1986,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
......@@ -2164,16 +2185,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();
......
......@@ -9,9 +9,10 @@
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
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'])) {
......
......@@ -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.
......
......@@ -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;
......@@ -78,10 +81,17 @@ 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));
}
......@@ -131,21 +144,6 @@ public function getTitle(LocalTaskInterface $local_task) {
return call_user_func_array($controller, $arguments);
}
/**
* 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.
*
......@@ -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,
......
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
......@@ -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');
......
<?php
/**
* Contains \Drupal\menu_test\Plugin\Derivative\LocalTaskTest
*/
namespace Drupal\menu_test\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DerivativeBase;
class LocalTaskTest extends DerivativeBase {
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions(array $base_plugin_definition) {
$weight = $base_plugin_definition['weight'];
foreach (array('derive1' => '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;
}
}
......@@ -36,6 +36,13 @@ public function test2() {
return 'test2';
}
/**
* Prints out test data.
*/
public function testDerived() {
return 'testDerived';
}
/**
* Prints out test data.
*
......
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
......@@ -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
......@@ -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:
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Menu\LocalTaskDefaultTest.
*/
namespace Drupal\Tests\Core\Menu;
use Drupal\Core\Menu\LocalTaskDefault;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Tests the local task default class.
*
* @see \Drupal\Core\Menu\LocalTaskDefaultTest
*/
class LocalTaskDefaultTest extends UnitTestCase {
/**
* The tested local task default plugin.
*
* @var \Drupal\Core\Menu\LocalTaskDefault
*/
protected $localTaskBase;
/**
* The used plugin configuration.
*
* @var array
*/
protected $config = array();
/**
* The used plugin ID.
*
* @var string
*/
protected $pluginId = 'local_task_default';
/**
* The used plugin definition.
*
* @var array
*/
protected $pluginDefinition = array(
'id' => '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'));