Loading core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php +4 −2 Original line number Diff line number Diff line Loading @@ -83,16 +83,18 @@ public function build(RouteMatchInterface $route_match) { } $breadcrumb = $builder->build($route_match); if ($breadcrumb instanceof Breadcrumb) { $context['builder'] = $builder; $breadcrumb->addCacheableDependency($cacheable_metadata); break; } else { throw new \UnexpectedValueException('Invalid breadcrumb returned by ' . get_class($builder) . '::build().'); } } // Ensure all collected cacheability is applied. $breadcrumb->addCacheableDependency($cacheable_metadata); // Allow modules to alter the breadcrumb. $this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context); Loading core/modules/system/tests/modules/menu_test/menu_test.routing.yml +8 −0 Original line number Diff line number Diff line Loading @@ -577,3 +577,11 @@ menu_test.breadcrumb3: _title: 'Normal title' requirements: _access: 'TRUE' menu_test.skippable-breadcrumb: path: '/menu-test/skippable-breadcrumb' defaults: _controller: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback' _title: 'Normal title' requirements: _access: 'TRUE' core/modules/system/tests/modules/menu_test/src/MenuTestServiceProvider.php 0 → 100644 +28 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\menu_test; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ServiceModifierInterface; use Symfony\Component\DependencyInjection\Reference; /** * Decorate core's default path-based breadcrumb builder when it is available. */ class MenuTestServiceProvider implements ServiceModifierInterface { /** * {@inheritdoc} */ public function alter(ContainerBuilder $container): void { if ($container->has('system.breadcrumb.default')) { $container->register('menu_test.breadcrumb.default', SkippablePathBasedBreadcrumbBuilder::class) ->setDecoratedService('system.breadcrumb.default') ->addArgument(new Reference('menu_test.breadcrumb.default.inner')) ->addArgument(new Reference('request_stack')); } } } core/modules/system/tests/modules/menu_test/src/SkippablePathBasedBreadcrumbBuilder.php 0 → 100644 +40 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\menu_test; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\HttpFoundation\RequestStack; /** * A path-based breadcrumb builder can be skipped from applying. */ class SkippablePathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { public function __construct( protected BreadcrumbBuilderInterface $pathBasedBreadcrumbBuilder, protected RequestStack $requestStack, ) {} /** * {@inheritdoc} */ public function applies(RouteMatchInterface $route_match, ?CacheableMetadata $cacheable_metadata = NULL): bool { $query_arg = 'menu_test_skip_breadcrumbs'; $cacheable_metadata?->addCacheContexts(['url.query_args:' . $query_arg]); // Apply unless the query argument is present. return !$this->requestStack->getCurrentRequest()->query->has($query_arg); } /** * {@inheritdoc} */ public function build(RouteMatchInterface $route_match): Breadcrumb { return $this->pathBasedBreadcrumbBuilder->build($route_match); } } core/modules/system/tests/src/Functional/Menu/BreadcrumbTest.php +9 −0 Original line number Diff line number Diff line Loading @@ -388,6 +388,15 @@ public function testBreadCrumbs(): void { $this->drupalGet('menu-test/breadcrumb1/breadcrumb2/breadcrumb3'); $this->assertSession()->responseContains('<script>alert(12);</script>'); $this->assertSession()->assertEscaped('<script>alert(123);</script>'); // Assert that the breadcrumb cacheability is respected after not applying. $this->assertBreadcrumb(Url::fromRoute('menu_test.skippable-breadcrumb', [], [ 'query' => [ 'menu_test_skip_breadcrumbs' => 'yes', ], ]), []); $trail = $home + ['menu-test' => 'Menu test root']; $this->assertBreadcrumb(Url::fromRoute('menu_test.skippable-breadcrumb'), $trail); } /** Loading Loading
core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php +4 −2 Original line number Diff line number Diff line Loading @@ -83,16 +83,18 @@ public function build(RouteMatchInterface $route_match) { } $breadcrumb = $builder->build($route_match); if ($breadcrumb instanceof Breadcrumb) { $context['builder'] = $builder; $breadcrumb->addCacheableDependency($cacheable_metadata); break; } else { throw new \UnexpectedValueException('Invalid breadcrumb returned by ' . get_class($builder) . '::build().'); } } // Ensure all collected cacheability is applied. $breadcrumb->addCacheableDependency($cacheable_metadata); // Allow modules to alter the breadcrumb. $this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context); Loading
core/modules/system/tests/modules/menu_test/menu_test.routing.yml +8 −0 Original line number Diff line number Diff line Loading @@ -577,3 +577,11 @@ menu_test.breadcrumb3: _title: 'Normal title' requirements: _access: 'TRUE' menu_test.skippable-breadcrumb: path: '/menu-test/skippable-breadcrumb' defaults: _controller: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback' _title: 'Normal title' requirements: _access: 'TRUE'
core/modules/system/tests/modules/menu_test/src/MenuTestServiceProvider.php 0 → 100644 +28 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\menu_test; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ServiceModifierInterface; use Symfony\Component\DependencyInjection\Reference; /** * Decorate core's default path-based breadcrumb builder when it is available. */ class MenuTestServiceProvider implements ServiceModifierInterface { /** * {@inheritdoc} */ public function alter(ContainerBuilder $container): void { if ($container->has('system.breadcrumb.default')) { $container->register('menu_test.breadcrumb.default', SkippablePathBasedBreadcrumbBuilder::class) ->setDecoratedService('system.breadcrumb.default') ->addArgument(new Reference('menu_test.breadcrumb.default.inner')) ->addArgument(new Reference('request_stack')); } } }
core/modules/system/tests/modules/menu_test/src/SkippablePathBasedBreadcrumbBuilder.php 0 → 100644 +40 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\menu_test; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\HttpFoundation\RequestStack; /** * A path-based breadcrumb builder can be skipped from applying. */ class SkippablePathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface { public function __construct( protected BreadcrumbBuilderInterface $pathBasedBreadcrumbBuilder, protected RequestStack $requestStack, ) {} /** * {@inheritdoc} */ public function applies(RouteMatchInterface $route_match, ?CacheableMetadata $cacheable_metadata = NULL): bool { $query_arg = 'menu_test_skip_breadcrumbs'; $cacheable_metadata?->addCacheContexts(['url.query_args:' . $query_arg]); // Apply unless the query argument is present. return !$this->requestStack->getCurrentRequest()->query->has($query_arg); } /** * {@inheritdoc} */ public function build(RouteMatchInterface $route_match): Breadcrumb { return $this->pathBasedBreadcrumbBuilder->build($route_match); } }
core/modules/system/tests/src/Functional/Menu/BreadcrumbTest.php +9 −0 Original line number Diff line number Diff line Loading @@ -388,6 +388,15 @@ public function testBreadCrumbs(): void { $this->drupalGet('menu-test/breadcrumb1/breadcrumb2/breadcrumb3'); $this->assertSession()->responseContains('<script>alert(12);</script>'); $this->assertSession()->assertEscaped('<script>alert(123);</script>'); // Assert that the breadcrumb cacheability is respected after not applying. $this->assertBreadcrumb(Url::fromRoute('menu_test.skippable-breadcrumb', [], [ 'query' => [ 'menu_test_skip_breadcrumbs' => 'yes', ], ]), []); $trail = $home + ['menu-test' => 'Menu test root']; $this->assertBreadcrumb(Url::fromRoute('menu_test.skippable-breadcrumb'), $trail); } /** Loading