diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php index ac3cb9e98d5cb8f4e49ea275b95a7a0d490371d0..51c830809a1f2904efde6edcc9df7d6f5fe8d15b 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php @@ -214,6 +214,8 @@ public function alterRoutes(RouteCollection $collection) { // Ensure that we don't override a route which is already controlled by // views. if (!$route->hasDefault('view_id') && ('/' . $view_path == $route_path)) { + $parameters = $route->compile()->getPathVariables(); + // @todo Figure out whether we need to merge some settings (like // requirements). @@ -223,6 +225,24 @@ public function alterRoutes(RouteCollection $collection) { $view_id = $this->view->storage->id(); $display_id = $this->display['id']; $route = $this->getRoute($view_id, $display_id); + + $path = $route->getPath(); + // Load the argument IDs from the view executable. + $view_arguments = (array) $this->view->argument; + $argument_ids = array_keys($view_arguments); + + // Replace the path with the original parameter names and add a mapping. + $argument_map = array(); + // We assume that the numeric ids of the parameters match the one from + // the view argument handlers. + foreach ($parameters as $position => $parameter_name) { + $path = str_replace('arg_' . $argument_ids[$position], $parameter_name, $path); + $argument_map['arg_' . $argument_ids[$position]] = $parameter_name; + } + // Set the corrected path and the mapping to the route object. + $route->setDefault('_view_argument_map', $argument_map); + $route->setPath($path); + $collection->add($name, $route); $view_route_names[$view_id . '.' . $display_id] = $name; } diff --git a/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php b/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php index 8da43f02095739860ec0434908843c44c1478b4c..6fb4a7cd948587785f610f280ec32a19b1fba6c9 100644 --- a/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php +++ b/core/modules/views/lib/Drupal/views/Routing/ViewPageController.php @@ -7,6 +7,7 @@ namespace Drupal\views\Routing; +use Drupal\Component\Utility\String; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\views\ViewExecutableFactory; @@ -65,15 +66,37 @@ public function handle(Request $request) { $entity = $this->storageController->load($view_id); if (empty($entity)) { - throw new NotFoundHttpException(format_string('Page controller for view %id requested, but view was not found.', array('%id' => $view_id))); + throw new NotFoundHttpException(String::format('Page controller for view %id requested, but view was not found.', array('%id' => $view_id))); } $view = $this->executableFactory->get($entity); $view->setDisplay($display_id); $view->initHandlers(); $args = array(); + $map = $request->attributes->get('_view_argument_map', array()); foreach (array_keys((array) $view->argument) as $argument_id) { - $arg = $request->attributes->get('arg_' . $argument_id); + + // Allow parameters be pulled from the request. + // The map stores the actual name of the parameter in the request. Views + // which override existing controller, use for example 'node' instead of + // arg_nid as name. + $attribute = 'arg_' . $argument_id; + if (isset($map[$attribute])) { + $attribute = $map[$attribute]; + + // First try to get from the original values then on the not converted + // ones. + if ($request->attributes->has('_raw_variables')) { + $arg = $request->attributes->get('_raw_variables')->get($attribute); + } + else { + $arg = $request->attributes->get($attribute); + } + } + else { + $arg = $request->attributes->get($attribute); + } + if (isset($arg)) { $args[] = $arg; } diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php deleted file mode 100644 index d4806f0d346fa64116cb393145de1c10d8484753..0000000000000000000000000000000000000000 --- a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php +++ /dev/null @@ -1,94 +0,0 @@ - 'View page controller test', - 'description' => 'Tests views page controller.', - 'group' => 'Views' - ); - } - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('router', 'menu_router')); - - $this->pageController = new ViewPageController($this->container->get('entity.manager')->getStorageController('view'), new ViewExecutableFactory()); - } - - /** - * Tests the page controller. - */ - public function testPageController() { - $this->assertTrue($this->pageController instanceof ViewPageController, 'Ensure the right class is stored in the container'); - - // Pass in a non existent view. - $random_view_id = $this->randomName(); - - $request = new Request(); - $request->attributes->set('view_id', $random_view_id); - $request->attributes->set('display_id', 'default'); - try { - $this->pageController->handle($request); - $this->fail('No exception thrown on non-existing view.'); - } - - catch (NotFoundHttpException $e) { - $this->pass('Exception thrown when view was not found'); - } - - $request->attributes->set('view_id', 'test_page_view'); - $output = $this->pageController->handle($request); - $this->assertTrue(is_array($output)); - $this->assertEqual($output['#view']->storage->id, 'test_page_view', 'The right view was executed.'); - - $request->attributes->set('display_id', 'page_1'); - $output = $this->pageController->handle($request); - $this->assertTrue($output instanceof Response, 'Ensure the page display returns a response object.'); - } - -} diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php index e7f15a8d09788cae17cbd919ff875dd9e9417cc7..2e2395e3cb7709a61879f7512e6e1e54187e4885 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php @@ -114,8 +114,8 @@ public function testCollectRoutes() { */ public function testAlterRoute() { $collection = new RouteCollection(); - $collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController'))); - $route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController')); + $collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content'))); + $route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content')); $collection->add('test_route_2', $route_2); list($view) = $this->setupViewExecutableAccessPlugin(); @@ -145,6 +145,42 @@ public function testAlterRoute() { $this->assertSame($collection->get('test_route_2'), $route_2); } + /** + * Tests alter routes with parameters in the overriding route. + */ + public function testAlterRoutesWithParameters() { + $collection = new RouteCollection(); + $collection->add('test_route', new Route('test_route/{parameter}', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content'))); + + list($view) = $this->setupViewExecutableAccessPlugin(); + + // Manually setup an argument handler. + $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $view->argument['test_id'] = $argument; + + $display = array(); + $display['display_plugin'] = 'page'; + $display['id'] = 'page_1'; + $display['display_options'] = array( + 'path' => 'test_route/%', + ); + $this->pathPlugin->initDisplay($view, $display); + + $view_route_names = $this->pathPlugin->alterRoutes($collection); + $this->assertEquals(array('test_id.page_1' => 'test_route'), $view_route_names); + + // Ensure that the test_route is overridden. + $route = $collection->get('test_route'); + $this->assertInstanceOf('\Symfony\Component\Routing\Route', $route); + $this->assertEquals('test_id', $route->getDefault('view_id')); + $this->assertEquals('page_1', $route->getDefault('display_id')); + // Ensure that the path did not changed and placeholders are respected. + $this->assertEquals('/test_route/{parameter}', $route->getPath()); + $this->assertEquals(array('arg_test_id' => 'parameter'), $route->getDefault('_view_argument_map')); + } + /** * Returns some mocked view entity, view executable, and access plugin. */ diff --git a/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php b/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5bcf07a2421f8b2680bf4e1a21d62e6a7914db76 --- /dev/null +++ b/core/modules/views/tests/Drupal/views/Tests/Routing/ViewPageControllerTest.php @@ -0,0 +1,265 @@ + 'View page controller test', + 'description' => 'Tests views page controller.', + 'group' => 'Views' + ); + } + + protected function setUp() { + $this->storageController = $this->getMockBuilder('Drupal\views\ViewStorageController') + ->disableOriginalConstructor() + ->getMock(); + $this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->pageController = new ViewPageController($this->storageController, $this->executableFactory); + } + + /** + * Tests the page controller. + */ + public function testPageController() { + $view = $this->getMock('Drupal\views\ViewStorageInterface'); + + $this->storageController->expects($this->once()) + ->method('load') + ->with('test_page_view') + ->will($this->returnValue($view)); + + $executable = $this->getMockBuilder('Drupal\views\ViewExecutable') + ->disableOriginalConstructor() + ->getMock(); + $executable->expects($this->once()) + ->method('setDisplay') + ->with('default'); + $executable->expects($this->once()) + ->method('initHandlers'); + $executable->expects($this->once()) + ->method('executeDisplay') + ->with('default', array()) + ->will($this->returnValue(array('#markup' => 'example output'))); + + $this->executableFactory->staticExpects($this->any()) + ->method('get') + ->with($view) + ->will($this->returnValue($executable)); + + $request = new Request(); + $request->attributes->set('view_id', 'test_page_view'); + $request->attributes->set('display_id', 'default'); + + $output = $this->pageController->handle($request); + $this->assertInternalType('array', $output); + $this->assertEquals(array('#markup' => 'example output'), $output); + } + + /** + * Tests the page controller with arguments on a non overridden page view. + */ + public function testHandleWithArgumentsWithoutOverridden() { + $view = $this->getMock('Drupal\views\ViewStorageInterface'); + + $this->storageController->expects($this->once()) + ->method('load') + ->with('test_page_view') + ->will($this->returnValue($view)); + + $executable = $this->getMockBuilder('Drupal\views\ViewExecutable') + ->disableOriginalConstructor() + ->getMock(); + $executable->expects($this->once()) + ->method('setDisplay') + ->with('page_1'); + $executable->expects($this->once()) + ->method('initHandlers'); + + // Manually setup a argument handler. + $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $executable->argument['test_id'] = $argument; + + $executable->expects($this->once()) + ->method('executeDisplay') + ->with('page_1', array('test-argument')); + + $this->executableFactory->staticExpects($this->any()) + ->method('get') + ->with($view) + ->will($this->returnValue($executable)); + + $request = new Request(); + $request->attributes->set('view_id', 'test_page_view'); + $request->attributes->set('display_id', 'page_1'); + // Add the argument to the request. + $request->attributes->set('arg_test_id', 'test-argument'); + + $this->pageController->handle($request); + } + + /** + * Tests the page controller with arguments of a overridden page view. + * + * Note: This test does not care about upcasting for now. + */ + public function testHandleWithArgumentsOnOveriddenRoute() { + $view = $this->getMock('Drupal\views\ViewStorageInterface'); + + $this->storageController->expects($this->once()) + ->method('load') + ->with('test_page_view') + ->will($this->returnValue($view)); + + $executable = $this->getMockBuilder('Drupal\views\ViewExecutable') + ->disableOriginalConstructor() + ->getMock(); + $executable->expects($this->once()) + ->method('setDisplay') + ->with('page_1'); + $executable->expects($this->once()) + ->method('initHandlers'); + + // Manually setup a argument handler. + $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $executable->argument['test_id'] = $argument; + + $executable->expects($this->once()) + ->method('executeDisplay') + ->with('page_1', array('test-argument')); + + $this->executableFactory->staticExpects($this->any()) + ->method('get') + ->with($view) + ->will($this->returnValue($executable)); + + $request = new Request(); + $request->attributes->set('view_id', 'test_page_view'); + $request->attributes->set('display_id', 'page_1'); + // Add the argument to the request. + $request->attributes->set('parameter', 'test-argument'); + $request->attributes->set('_view_argument_map', array( + 'arg_test_id' => 'parameter', + )); + + $this->pageController->handle($request); + } + + /** + * Tests the page controller with arguments of a overridden page view. + * + * This test care about upcasted values and ensures that the raw variables + * are pulled in. + */ + public function testHandleWithArgumentsOnOveriddenRouteWithUpcasting() { + $view = $this->getMock('Drupal\views\ViewStorageInterface'); + + $this->storageController->expects($this->once()) + ->method('load') + ->with('test_page_view') + ->will($this->returnValue($view)); + + $executable = $this->getMockBuilder('Drupal\views\ViewExecutable') + ->disableOriginalConstructor() + ->getMock(); + $executable->expects($this->once()) + ->method('setDisplay') + ->with('page_1'); + $executable->expects($this->once()) + ->method('initHandlers'); + + // Manually setup a argument handler. + $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $executable->argument['test_id'] = $argument; + + $executable->expects($this->once()) + ->method('executeDisplay') + ->with('page_1', array('example_id')); + + $this->executableFactory->staticExpects($this->any()) + ->method('get') + ->with($view) + ->will($this->returnValue($executable)); + + $request = new Request(); + $request->attributes->set('view_id', 'test_page_view'); + $request->attributes->set('display_id', 'page_1'); + // Add the argument to the request. + $request->attributes->set('test_entity', $this->getMock('Drupal\Core\Entity\EntityInterface')); + $raw_variables = new ParameterBag(array('test_entity' => 'example_id')); + $request->attributes->set('_raw_variables', $raw_variables); + + $request->attributes->set('_view_argument_map', array( + 'arg_test_id' => 'test_entity', + )); + + $this->pageController->handle($request); + } + + /** + * Tests handle with a non existing view. + * + * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public function testHandleWithNotExistingView() { + // Pass in a non existent view. + $random_view_id = $this->randomName(); + + $request = new Request(); + $request->attributes->set('view_id', $random_view_id); + $request->attributes->set('display_id', 'default'); + $this->pageController->handle($request); + } + +}