diff --git a/core/core.services.yml b/core/core.services.yml index f941e33..5363020 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'] + arguments: ['@container.namespaces', '@controller_resolver', '@request', '@router.route_provider', '@module_handler', '@url_generator', '@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 d205ac0..7303d3f 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -969,7 +969,8 @@ function _menu_link_translate(&$item, $translate = FALSE) { */ function menu_item_route_access(Route $route, $href, &$map) { $request = Request::create('/' . $href); - $request->attributes->set('_system_path', $href); + $system_path = $href = \Drupal::service('path_processor_manager')->processInbound($href, $request); + $request->attributes->set('_system_path', $system_path); // Attempt to match this path to provide a fully built request to the // access checker. try { diff --git a/core/lib/Drupal/Core/Menu/LocalTaskBase.php b/core/lib/Drupal/Core/Menu/LocalTaskBase.php index 7568d2b..93a38a3 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskBase.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskBase.php @@ -26,13 +26,6 @@ protected $t; /** - * URL generator object. - * - * @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface - */ - protected $generator; - - /** * TRUE if this plugin is forced active for options attributes. * * @var bool @@ -50,13 +43,10 @@ * The plugin implementation definition. * @param \Drupal\Core\StringTranslation\Translator\TranslatorInterface $string_translation * The string translation object. - * @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $generator - * The url generator object. */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, TranslatorInterface $string_translation, UrlGeneratorInterface $generator) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, TranslatorInterface $string_translation) { // This is available for subclasses that need to translate a dynamic title. $this->t = $string_translation; - $this->generator = $generator; parent::__construct($configuration, $plugin_id, $plugin_definition); } @@ -68,8 +58,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('string_translation'), - $container->get('url_generator') + $container->get('string_translation') ); } @@ -81,6 +70,13 @@ public function getRouteName() { } /** + * Get the route parameters. + */ + public function getRouteParameters() { + return isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : array(); + } + + /** * {@inheritdoc} */ public function getTitle() { @@ -91,17 +87,8 @@ public function getTitle() { /** * {@inheritdoc} */ - public function getPath() { - // Subclasses may set a request into the generator or use any desired method - // to generate the path. - // @todo - use the new method from https://drupal.org/node/2031353 - $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 getRouteInformation() { + return array($this->getRouteName(), $this->getRouteParameters(), array()); } /** diff --git a/core/lib/Drupal/Core/Menu/LocalTaskInterface.php b/core/lib/Drupal/Core/Menu/LocalTaskInterface.php index b7f581d..f5d1539 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskInterface.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskInterface.php @@ -32,15 +32,16 @@ public function getRouteName(); public function getTitle(); /** - * Returns an internal Drupal path to use when creating the link for the tab. + * Returns the route information needed to render the local task. * * Subclasses may add optional arguments like NodeInterface $node = NULL that * will be supplied by the ControllerResolver. * - * @return string - * The path of this local task. + * @return array + * An array of the first element be the route name and the second an optional + * array of parameters. */ - public function getPath(); + public function getRouteInformation(); /** * Returns the weight of the local task. diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index 00837c5..25f1338 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -7,11 +7,13 @@ namespace Drupal\Core\Menu; +use Drupal\Core\Access\AccessManager; +use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\Routing\RouteProviderInterface; +use Drupal\Core\Routing\UrlGeneratorInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; /** * Manages discovery and instantiation of menu local task plugins. @@ -51,12 +53,26 @@ class LocalTaskManager extends DefaultPluginManager { protected $routeProvider; /** + * The URL generator. + * + * @var \Drupal\Core\Routing\UrlGeneratorInterface + */ + protected $urlGenerator; + + /** + * The access manager. + * + * @var \Drupal\Core\Access\AccessManager + */ + protected $accessManager; + + /** * Constructs a \Drupal\Core\Menu\LocalTaskManager object. * * @param \Traversable $namespaces * An object that implements \Traversable which contains the root paths * keyed by the corresponding namespace to look for plugin implementations, - * @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. @@ -64,12 +80,18 @@ class LocalTaskManager extends DefaultPluginManager { * The route provider to load routes by name. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler.u + * @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) { + public function __construct(\Traversable $namespaces, ControllerResolverInterface $controller_resolver, Request $request, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, UrlGeneratorInterface $url_generator, 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'); } @@ -89,16 +111,17 @@ public function getTitle(LocalTaskInterface $local_task) { } /** - * Gets the Drupal path for a 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 string - * The path. + * @return array + * An array of the first element be the route name and the second an optional + * array of parameters. */ - public function getPath(LocalTaskInterface $local_task) { - $controller = array($local_task, 'getPath'); + 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); } @@ -183,14 +206,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. @@ -205,18 +228,30 @@ public function getTasksBuild($route_name) { foreach ($tree as $level => $instances) { foreach ($instances as $child) { - $path = $this->getPath($child); + // In order to get the Drupal path the base URL has to be stripped off. + $route_information = $child->getRouteInformation(); + list($route_name, $route_parameters, $options) = $route_information; + $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->generateFromRoute($route_name, $route_parameters, $options); + // In order to get the Drupal path the base URL has to be stripped off. + $base_url = $this->urlGenerator->getContext()->getBaseUrl(); + if (!empty($base_url) && strpos($path, $base_url) === 0) { + $path = substr($path, strlen($base_url)); + } + $path = trim($path, '/'); + // 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 || $child->getActive()); // @todo It might make sense to use menu link entities instead of // arrays. + $menu_link = array( 'title' => $this->getTitle($child), 'href' => $path, diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php index 5d61a9c..05c6edd 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php @@ -62,6 +62,13 @@ class LocalTaskManagerTest extends UnitTestCase { */ protected $factory; + /** + * The mocked URL generator. + * + * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $urlGenerator; + public static function getInfo() { return array( 'name' => 'Local tasks manager.', @@ -81,6 +88,7 @@ protected function setUp() { $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->pluginDiscovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface'); $this->factory = $this->getMock('Drupal\Component\Plugin\Factory\FactoryInterface'); + $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); $this->setupLocalTaskManager(); } @@ -159,21 +167,21 @@ public function testGetTitle() { } /** - * Tests the getPath method. + * Tests the getRouteInformation method. * - * @see \Drupal\system\Plugin\Type\MenuLocalTaskManager::getPath() + * @see \Drupal\system\Plugin\Type\MenuLocalTaskManager::getRouteInformation() */ - public function testGetPath() { + public function testGetRouteInformation() { $menu_local_task = $this->getMock('Drupal\Core\Menu\LocalTaskInterface'); $menu_local_task->expects($this->once()) - ->method('getPath'); + ->method('getRouteInformation'); $this->controllerResolver->expects($this->once()) ->method('getArguments') - ->with($this->request, array($menu_local_task, 'getPath')) + ->with($this->request, array($menu_local_task, 'getRouteInformation')) ->will($this->returnValue(array())); - $this->manager->getPath($menu_local_task); + $this->manager->getRouteInformation($menu_local_task); } /** @@ -194,6 +202,10 @@ 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', 'discovery'); $property->setAccessible(TRUE); $property->setValue($this->manager, $this->pluginDiscovery);