diff --git a/src/Routing/VariantRouteFilter.php b/src/Routing/VariantRouteFilter.php index 31e3eb3..1afa918 100644 --- a/src/Routing/VariantRouteFilter.php +++ b/src/Routing/VariantRouteFilter.php @@ -13,6 +13,7 @@ use Drupal\Core\ParamConverter\ParamNotConvertedException; use Drupal\Core\Path\CurrentPathStack; use Drupal\Core\Routing\RouteFilterInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Route; @@ -94,14 +95,25 @@ public function filter(RouteCollection $collection, Request $request) { // If this page manager route isn't the one selected, remove it. if ($variant_route_name !== $name) { + unset($routes[$name]); $collection->remove($name); } - // If the selected route has a base route, move that to the end. - elseif ($base_route_name !== $name && ($base_route = $collection->get($base_route_name))) { - $collection->add($base_route_name, $base_route); + // If the selected route has a base route, remove it. + elseif ($base_route_name !== $name) { + unset($routes[$base_route_name]); + $collection->remove($base_route_name); } } + // Iterate over the routes, using the base_route_name if one is specified. + // All routes must be removed and re-added to the collection to ensure the + // original order is kept. + foreach ($routes as $name => $route) { + $base_route_name = $route->getDefault('base_route_name') ?: $name; + $collection->remove($name); + $collection->add($base_route_name, $route); + } + return $collection; } @@ -125,6 +137,8 @@ protected function getVariantRouteName(array $routes, Request $request) { } if ($attributes = $this->getRequestAttributes($route, $name, $request)) { + // Use the base route name if available. + $attributes[RouteObjectInterface::ROUTE_NAME] = $route->getDefault('base_route_name') ?: $name; // Add the enhanced attributes to the request. $request->attributes->add($attributes); $this->requestStack->push($request); @@ -147,8 +161,8 @@ protected function getVariantRouteName(array $routes, Request $request) { * Sort callback for routes based on the variant weight. */ protected function routeWeightSort(Route $a, Route $b) { - $a_weight = $a->getDefault('page_manager_page_variant_weight'); - $b_weight = $b->getDefault('page_manager_page_variant_weight'); + $a_weight = $a->getDefault('page_manager_page_variant_weight') ?: PHP_INT_MAX; + $b_weight = $b->getDefault('page_manager_page_variant_weight') ?: PHP_INT_MAX; return ($a_weight < $b_weight) ? -1 : 1; } diff --git a/src/Tests/PageManagerTranslationIntegrationTest.php b/src/Tests/PageManagerTranslationIntegrationTest.php new file mode 100644 index 0000000..7ba06dc --- /dev/null +++ b/src/Tests/PageManagerTranslationIntegrationTest.php @@ -0,0 +1,83 @@ +drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + } + + /** + * {@inheritdoc} + */ + protected function getTranslatorPermissions() { + return array_merge(parent::getTranslatorPermissions(), ['administer pages', 'administer pages']); + } + + /** + * Tests that overriding the node page does not prevent translation. + */ + public function testNode() { + $this->drupalPlaceBlock('local_tasks_block'); + $this->drupalPlaceBlock('page_title_block'); + + $node = $this->drupalCreateNode(['type' => 'article']); + $this->drupalGet('node/' . $node->id()); + $this->assertResponse(200); + $this->assertText($node->label()); + $this->clickLink('Translate'); + $this->assertResponse(200); + + // Create a new variant. + $http_status_variant = PageVariant::create([ + 'variant' => 'http_status_code', + 'label' => 'HTTP status code', + 'id' => 'http_status_code', + 'page' => 'node_view', + ]); + $http_status_variant->getVariantPlugin()->setConfiguration(['status_code' => 200]); + $http_status_variant->save(); + $this->triggerRouterRebuild(); + + $this->drupalGet('node/' . $node->id()); + $this->assertResponse(200); + $this->clickLink('Translate'); + $this->assertResponse(200); + } + +} diff --git a/tests/src/Kernel/PageManagerRoutingTest.php b/tests/src/Kernel/PageManagerRoutingTest.php index ad10689..74c5971 100644 --- a/tests/src/Kernel/PageManagerRoutingTest.php +++ b/tests/src/Kernel/PageManagerRoutingTest.php @@ -122,11 +122,11 @@ public function providerTestRouteFilter() { $data = []; $data['custom'] = [ '/custom/entity_test/1', - 'page_manager.page_view_custom_entity_test_view_custom_entity_test_view_variant', + 'page_manager.page_view_custom_entity_test_view', ]; $data['no_format'] = [ '/entity_test/1', - 'page_manager.page_view_entity_test_view_entity_test_view_variant', + 'entity.entity_test.canonical', ]; $data['format_added_after'] = [ '/entity_test/1?_format=json', diff --git a/tests/src/Unit/VariantRouteFilterTest.php b/tests/src/Unit/VariantRouteFilterTest.php index 387eb59..916916a 100644 --- a/tests/src/Unit/VariantRouteFilterTest.php +++ b/tests/src/Unit/VariantRouteFilterTest.php @@ -259,7 +259,7 @@ public function testFilterAllowedAccessSecondRoute() { $defaults2 = [ 'page_manager_page_variant' => 'variant_2', 'page_manager_page_variant_weight' => 2, - 'base_route_name' => 'invalid', + 'base_route_name' => 'base_route_name_for_selected_route', ]; $route1 = new Route('/path/with/{slug}', $defaults1); $route2 = new Route('/path/with/{slug}', $defaults2); @@ -277,11 +277,11 @@ public function testFilterAllowedAccessSecondRoute() { $this->pageVariantStorage->load('variant_2')->willReturn($page_variant2->reveal())->shouldBeCalled(); $result = $this->routeFilter->filter($route_collection, $request); - $expected = ['route_2' => $route2]; + $expected = ['base_route_name_for_selected_route' => $route2]; $this->assertSame($expected, $result->all()); $expected_attributes = $defaults2 + [ '_route_object' => $route2, - '_route' => 'route_2', + '_route' => 'base_route_name_for_selected_route', ]; $this->assertSame($expected_attributes, $request->attributes->all()); } @@ -328,11 +328,11 @@ public function testFilterAllowedAccessFirstRoute() { $this->pageVariantStorage->load('variant1')->willReturn($page_variant1->reveal())->shouldBeCalled(); $result = $this->routeFilter->filter($route_collection, $request); - $expected = ['route_2' => $route2, 'route_4' => $route4, 'route_1' => $route1]; + $expected = ['route_1' => $route2, 'route_4' => $route4]; $this->assertSame($expected, $result->all()); $expected_attributes = $defaults2 + [ '_route_object' => $route2, - '_route' => 'route_2', + '_route' => 'route_1', ]; $this->assertSame($expected_attributes, $request->attributes->all()); } @@ -418,6 +418,38 @@ public function testFilterRequestAttributesException() { } /** + * @covers ::filter + */ + public function testFilterPreservingBaseRouteName() { + $route_collection = new RouteCollection(); + $request = new Request(); + + // Add routes in different order to also test order preserving + $route1 = new Route('/path/with/{slug}', ['page_manager_page_variant' => 'variant1', 'base_route_name' => 'preserved_route_name']); + $route2 = new Route('/path/with/{slug}', ['page_manager_page_variant' => 'variant2']); + $route3 = new Route('/path/with/{slug}', []); + $route4 = new Route('/path/with/{slug}', []); + $route_collection->add('route_4', $route4); + $route_collection->add('route_3', $route3); + $route_collection->add('route_1', $route1); + $route_collection->add('route_2', $route2); + + $page_variant1 = $this->prophesize(PageVariantInterface::class); + $page_variant1->access('view')->willReturn(TRUE); + $page_variant2 = $this->prophesize(PageVariantInterface::class); + $page_variant2->access('view')->willReturn(FALSE); + + $this->currentPath->getPath($request)->willReturn(''); + $this->pageVariantStorage->load('variant1')->willReturn($page_variant1->reveal())->shouldBeCalled(); + $this->pageVariantStorage->load('variant2')->shouldNotBeCalled(); + + $result = $this->routeFilter->filter($route_collection, $request); + + $expected = ['preserved_route_name' => $route1, 'route_4' => $route4, 'route_3' => $route3]; + $this->assertSame($expected, $result->all()); + } + + /** * @covers ::getRequestAttributes */ public function testGetRequestAttributes() {