Issue #3482691 by james.williams, arunkumark, kristiaanvandeneynde, smustgrave: BreadcrumbManager ignores cacheability when no builders apply
parent
e6b0b85ee8
commit
78bbe4db98
|
@ -83,16 +83,18 @@ class BreadcrumbManager implements ChainBreadcrumbBuilderInterface {
|
|||
}
|
||||
|
||||
$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);
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?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'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?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);
|
||||
}
|
||||
|
||||
}
|
|
@ -388,6 +388,15 @@ class BreadcrumbTest extends BrowserTestBase {
|
|||
$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…
Reference in New Issue