diff --git a/core/core.services.yml b/core/core.services.yml
index 1368ac8..cf60a29 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -176,7 +176,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: ['@container.namespaces', '@controller_resolver', '@request', '@router.route_provider', '@module_handler', '@cache.cache', '@language_manager', '@url_generator', '@access_manager']
+ arguments: ['@container.namespaces', '@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 38fed60..6c8f04c 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 onece 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 . '';
}
/**
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskBase.php b/core/lib/Drupal/Core/Menu/LocalTaskBase.php
index 93a38a3..b1cd894 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskBase.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskBase.php
@@ -9,8 +9,10 @@
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
@@ -26,6 +28,13 @@
protected $t;
/**
+ * The route provider to load routes by name.
+ *
+ * @var \Drupal\Core\Routing\RouteProviderInterface
+ */
+ protected $routeProvider;
+
+ /**
* TRUE if this plugin is forced active for options attributes.
*
* @var bool
@@ -43,10 +52,13 @@
* The plugin implementation definition.
* @param \Drupal\Core\StringTranslation\Translator\TranslatorInterface $string_translation
* The string translation object.
+ * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+ * The route provider to load routes by name.
*/
- public function __construct(array $configuration, $plugin_id, array $plugin_definition, TranslatorInterface $string_translation) {
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, TranslatorInterface $string_translation, RouteProviderInterface $route_provider) {
// This is available for subclasses that need to translate a dynamic title.
$this->t = $string_translation;
+ $this->routeProvider = $route_provider;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
@@ -58,7 +70,8 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration,
$plugin_id,
$plugin_definition,
- $container->get('string_translation')
+ $container->get('string_translation'),
+ $container->get('router.route_provider')
);
}
@@ -70,10 +83,23 @@ public function getRouteName() {
}
/**
- * Get the route parameters.
+ * {@inheritdoc}
*/
- public function getRouteParameters() {
- return isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : array();
+ 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();
+ // The _raw_variables hold the original path strings in positions
+ // corresponidng to slug sint eh path patterns. For example, if the route
+ // path pattern is /node/{node} and the requested path is /node/2
+ // then $request_raw_variables is array('node' => 2).
+ $request_raw_variables = $request->attributes->get('_raw_variables');
+ foreach ($variables as $name) {
+ if (!isset($parameters[$name]) && isset($request_raw_variables[$name])) {
+ $parameters[$name] = $request_raw_variables[$name];
+ }
+ }
+ return $parameters;
}
/**
@@ -85,13 +111,6 @@ public function getTitle() {
}
/**
- * {@inheritdoc}
- */
- public function getRouteInformation() {
- return array($this->getRouteName(), $this->getRouteParameters(), array());
- }
-
- /**
* Returns the weight of the local task.
*
* @return int
@@ -114,7 +133,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 f5d1539..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,16 +34,17 @@ public function getRouteName();
public function getTitle();
/**
- * Returns the route information needed to render the local task.
+ * 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 array
- * An array of the first element be the route name and the second an optional
- * array of parameters.
+ * An array of parameter names and values.
+ *
+ * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
*/
- public function getRouteInformation();
+ public function getRouteParameters(Request $request);
/**
* Returns the weight of the local task.
@@ -52,14 +55,17 @@ public function getRouteInformation();
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 7e28c8d..349caf3 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -56,13 +56,6 @@ class LocalTaskManager extends DefaultPluginManager {
protected $routeProvider;
/**
- * The URL generator.
- *
- * @var \Drupal\Core\Routing\UrlGeneratorInterface
- */
- protected $urlGenerator;
-
- /**
* The access manager.
*
* @var \Drupal\Core\Access\AccessManager
@@ -87,17 +80,14 @@ class LocalTaskManager extends DefaultPluginManager {
* The cache backend.
* @param \Drupal\Core\Language\LanguageManager $language_manager
* The language manager.
- * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
- * The url generator.
* @param \Drupal\Core\Access\AccessManager $access_manager
* The access manager.
*/
- public function __construct(\Traversable $namespaces, ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager, UrlGeneratorInterface $url_generator, AccessManager $access_manager) {
+ public function __construct(\Traversable $namespaces, ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager, AccessManager $access_manager) {
parent::__construct('Plugin/Menu/LocalTask', $namespaces, array(), 'Drupal\Core\Annotation\Menu\LocalTask');
$this->controllerResolver = $controller_resolver;
$this->request = $request;
$this->routeProvider = $route_provider;
- $this->urlGenerator = $url_generator;
$this->accessManager = $access_manager;
$this->alterInfo($module_handler, 'local_tasks');
$this->setCacheBackend($cache, $language_manager, 'local_task', array('local_task' => TRUE));
@@ -119,22 +109,6 @@ public function getTitle(LocalTaskInterface $local_task) {
}
/**
- * Gets the route information for a local task.
- *
- * @param \Drupal\Core\Menu\LocalTaskInterface $local_task
- * The local task plugin instance to get the path for.
- *
- * @return array
- * An array of the first element be the route name and the second an optional
- * array of parameters.
- */
- public function getRouteInformation(LocalTaskInterface $local_task) {
- $controller = array($local_task, 'getRouteInformation');
- $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
@@ -245,18 +219,15 @@ public function getTasksBuild($current_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) {
+ foreach ($instances as $plugin_id => $child) {
// In order to get the Drupal path the base URL has to be stripped off.
- list($route_name, $route_parameters) = $child->getRouteInformation();
- $route_parameters = isset($route_parameters) ? (array) $route_parameters : array();
-
- // @todo On the longrun we should be able to use #type link instead.
- // so there would be no need for the URL generator here.
- $path = $this->urlGenerator->getPathFromRoute($route_name, $route_parameters);
+ $route_name = $child->getRouteName();
+ $route_parameters = $child->getRouteParameters($this->request);
// Find out whether the user has access to the task.
$access = $this->accessManager->checkNamedRoute($route_name, $route_parameters);
@@ -267,14 +238,15 @@ public function getTasksBuild($current_route_name) {
// @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/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
index 78a666f..ce5d08e 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
@@ -72,13 +72,6 @@ class LocalTaskManagerTest extends UnitTestCase {
protected $cacheBackend;
/**
- * The mocked URL generator.
- *
- * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
- */
- protected $urlGenerator;
-
- /**
* The mocked access manager.
*
* @var \Drupal\Core\Access\AccessManager|\PHPUnit_Framework_MockObject_MockObject
@@ -105,7 +98,6 @@ 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->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
$this->accessManager = $this->getMockBuilder('Drupal\Core\Access\AccessManager')
->disableOriginalConstructor()
->getMock();
@@ -228,24 +220,6 @@ public function testGetTitle() {
}
/**
- * Tests the getRouteInformation method.
- *
- * @see \Drupal\system\Plugin\Type\MenuLocalTaskManager::getRouteInformation()
- */
- public function testGetRouteInformation() {
- $menu_local_task = $this->getMock('Drupal\Core\Menu\LocalTaskInterface');
- $menu_local_task->expects($this->once())
- ->method('getRouteInformation');
-
- $this->controllerResolver->expects($this->once())
- ->method('getArguments')
- ->with($this->request, array($menu_local_task, 'getRouteInformation'))
- ->will($this->returnValue(array()));
-
- $this->manager->getRouteInformation($menu_local_task);
- }
-
- /**
* Setups the local task manager for the test.
*/
protected function setupLocalTaskManager() {
@@ -263,10 +237,6 @@ protected function setupLocalTaskManager() {
$property->setAccessible(TRUE);
$property->setValue($this->manager, $this->request);
- $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'urlGenerator');
- $property->setAccessible(TRUE);
- $property->setValue($this->manager, $this->urlGenerator);
-
$property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'accessManager');
$property->setAccessible(TRUE);
$property->setValue($this->manager, $this->accessManager);