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);