core/core.services.yml | 8 +- .../Core/Render/MetadataBubblingUrlGenerator.php | 142 -------------------- .../cache_contexts_safeguards.info.yml | 7 + .../src/CacheContextsSafeguardsServiceProvider.php | 51 ++++++++ .../src/CacheabilityBubblingNodeGrantStorage.php | 124 ++++++++++++++++++ .../src/MetadataBubblingUrlGenerator.php | 143 +++++++++++++++++++++ core/modules/node/node.services.yml | 7 +- .../src/CacheabilityBubblingNodeGrantStorage.php | 122 ------------------ core/profiles/minimal/minimal.info.yml | 1 + core/profiles/standard/standard.info.yml | 1 + core/profiles/testing/testing.info.yml | 1 + 11 files changed, 330 insertions(+), 277 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index ed1341c..589d574 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -745,15 +745,9 @@ services: arguments: ['@route_filter.lazy_collector'] tags: - { name: event_subscriber } - url_generator.non_bubbling: + url_generator: class: Drupal\Core\Routing\UrlGenerator arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%'] - public: false - calls: - - [setContext, ['@?router.request_context']] - url_generator: - class: Drupal\Core\Render\MetadataBubblingUrlGenerator - arguments: ['@url_generator.non_bubbling', '@renderer'] calls: - [setContext, ['@?router.request_context']] redirect.destination: diff --git a/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php b/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php deleted file mode 100644 index 3a4535e..0000000 --- a/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php +++ /dev/null @@ -1,142 +0,0 @@ -urlGenerator = $url_generator; - $this->renderer = $renderer; - } - - /** - * {@inheritdoc} - */ - public function setContext(SymfonyRequestContext $context) { - $this->urlGenerator->setContext($context); - } - - /** - * {@inheritdoc} - */ - public function getContext() { - return $this->urlGenerator->getContext(); - } - - /** - * {@inheritdoc} - */ - public function getPathFromRoute($name, $parameters = array()) { - return $this->urlGenerator->getPathFromRoute($name, $parameters); - } - - /** - * Bubbles the bubbleable metadata to the current render context. - * - * @param \Drupal\Core\GeneratedUrl $generated_url - * The generated URL whose bubbleable metadata to bubble. - * @param array $options - * (optional) The URL options. Defaults to none. - */ - protected function bubble(GeneratedUrl $generated_url, array $options = []) { - // Bubbling metadata makes sense only if the code is executed inside a - // render context. All code running outside controllers has no render - // context by default, so URLs used there are not supposed to affect the - // response cacheability. - if ($this->renderer->hasRenderContext()) { - $build = []; - $generated_url->applyTo($build); - $this->renderer->render($build); - } - } - - /** - * {@inheritdoc} - */ - public function generate($name, $parameters = array(), $absolute = FALSE) { - $options['absolute'] = $absolute; - $generated_url = $this->generateFromRoute($name, $parameters, $options, TRUE); - $this->bubble($generated_url); - return $generated_url->getGeneratedUrl(); - } - - /** - * {@inheritdoc} - */ - public function generateFromRoute($name, $parameters = array(), $options = array(), $collect_bubbleable_metadata = FALSE) { - $generated_url = $this->urlGenerator->generateFromRoute($name, $parameters, $options, TRUE); - if (!$collect_bubbleable_metadata) { - $this->bubble($generated_url, $options); - } - return $collect_bubbleable_metadata ? $generated_url : $generated_url->getGeneratedUrl(); - } - - /** - * {@inheritdoc} - */ - public function generateFromPath($path = NULL, $options = array(), $collect_bubbleable_metadata = FALSE) { - $generated_url = $this->urlGenerator->generateFromPath($path, $options, TRUE); - if (!$collect_bubbleable_metadata) { - $this->bubble($generated_url, $options); - } - return $collect_bubbleable_metadata ? $generated_url : $generated_url->getGeneratedUrl(); - } - - /** - * {@inheritdoc} - */ - public function supports($name) { - return $this->urlGenerator->supports($name); - } - - /** - * {@inheritdoc} - */ - public function getRouteDebugMessage($name, array $parameters = array()) { - return $this->urlGenerator->getRouteDebugMessage($name, $parameters); - } - -} diff --git a/core/modules/cache_context_safeguards/cache_contexts_safeguards.info.yml b/core/modules/cache_context_safeguards/cache_contexts_safeguards.info.yml new file mode 100644 index 0000000..4059828 --- /dev/null +++ b/core/modules/cache_context_safeguards/cache_contexts_safeguards.info.yml @@ -0,0 +1,7 @@ +name: Cache Contexts Safeguards +type: module +description: "…" +package: Core +core: 8.x +hidden: true +version: VERSION diff --git a/core/modules/cache_context_safeguards/src/CacheContextsSafeguardsServiceProvider.php b/core/modules/cache_context_safeguards/src/CacheContextsSafeguardsServiceProvider.php new file mode 100644 index 0000000..6a19502 --- /dev/null +++ b/core/modules/cache_context_safeguards/src/CacheContextsSafeguardsServiceProvider.php @@ -0,0 +1,51 @@ +has('url_generator')) { + $container->setDefinition('url_generator.non_bubbling', $container->getDefinition('url_generator')) + ->setPublic(FALSE); + $container->register('url_generator') + ->setClass('\Drupal\cache_contexts_safeguards\MetadataBubblingUrlGenerator') + ->setArguments([ + new Reference('url_generator.non_bubbling'), + new Reference('renderer') + ]) + ->addMethodCall('setContext', [new Reference('router.request_context', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]); + } + + // Decorate node storage. + if ($container->has('node.grant_storage')) { + $container->setDefinition('node.grant_storage.non_bubbling', $container->getDefinition('node.grant_storage')) + ->setPublic(FALSE); + $container->register('node.grant_storage') + ->setClass('\Drupal\cache_contexts_safeguards\CacheabilityBubblingNodeGrantStorage') + ->addArgument(new Reference('node.grant_storage.non_bubbling')) + ->addMethodCall('setContainer', [new Reference('service_container')]); + } + } + +} + diff --git a/core/modules/cache_context_safeguards/src/CacheabilityBubblingNodeGrantStorage.php b/core/modules/cache_context_safeguards/src/CacheabilityBubblingNodeGrantStorage.php new file mode 100644 index 0000000..a536f4f --- /dev/null +++ b/core/modules/cache_context_safeguards/src/CacheabilityBubblingNodeGrantStorage.php @@ -0,0 +1,124 @@ +nodeGrantStorage = $node_grant_storage; + } + + /** + * {@inheritdoc} + */ + public function access(NodeInterface $node, $operation, $langcode, AccountInterface $account) { + return $this->nodeGrantStorage->access($node, $operation, $langcode, $account); + } + + /** + * {@inheritdoc} + */ + public function checkAll(AccountInterface $account) { + return $this->nodeGrantStorage->checkAll($account); + } + + /** + * {@inheritdoc} + */ + public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table) { + // Bubble the 'user.node_grants:$op' cache context to the current render + // context. + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + if ($renderer->hasRenderContext()) { + $build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]]; + $renderer->render($build); + } + + return $this->nodeGrantStorage->alterQuery($query, $tables, $op, $account, $base_table); + } + + /** + * {@inheritdoc} + */ + public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE) { + return $this->nodeGrantStorage->write($node, $grants, $realm, $delete); + } + + /** + * {@inheritdoc} + */ + public function delete() { + return $this->nodeGrantStorage->delete(); + } + + /** + * {@inheritdoc} + */ + public function writeDefault() { + return $this->nodeGrantStorage->writeDefault(); + } + + /** + * {@inheritdoc} + */ + public function count() { + return $this->nodeGrantStorage->count(); + } + + /** + * {@inheritdoc} + */ + public function deleteNodeRecords(array $nids) { + return $this->nodeGrantStorage->deleteNodeRecords($nids); + } + +} diff --git a/core/modules/cache_context_safeguards/src/MetadataBubblingUrlGenerator.php b/core/modules/cache_context_safeguards/src/MetadataBubblingUrlGenerator.php new file mode 100644 index 0000000..a62fe93 --- /dev/null +++ b/core/modules/cache_context_safeguards/src/MetadataBubblingUrlGenerator.php @@ -0,0 +1,143 @@ +urlGenerator = $url_generator; + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public function setContext(SymfonyRequestContext $context) { + $this->urlGenerator->setContext($context); + } + + /** + * {@inheritdoc} + */ + public function getContext() { + return $this->urlGenerator->getContext(); + } + + /** + * {@inheritdoc} + */ + public function getPathFromRoute($name, $parameters = array()) { + return $this->urlGenerator->getPathFromRoute($name, $parameters); + } + + /** + * Bubbles the bubbleable metadata to the current render context. + * + * @param \Drupal\Core\GeneratedUrl $generated_url + * The generated URL whose bubbleable metadata to bubble. + * @param array $options + * (optional) The URL options. Defaults to none. + */ + protected function bubble(GeneratedUrl $generated_url, array $options = []) { + // Bubbling metadata makes sense only if the code is executed inside a + // render context. All code running outside controllers has no render + // context by default, so URLs used there are not supposed to affect the + // response cacheability. + if ($this->renderer->hasRenderContext()) { + $build = []; + $generated_url->applyTo($build); + $this->renderer->render($build); + } + } + + /** + * {@inheritdoc} + */ + public function generate($name, $parameters = array(), $absolute = FALSE) { + $options['absolute'] = $absolute; + $generated_url = $this->generateFromRoute($name, $parameters, $options, TRUE); + $this->bubble($generated_url); + return $generated_url->getGeneratedUrl(); + } + + /** + * {@inheritdoc} + */ + public function generateFromRoute($name, $parameters = array(), $options = array(), $collect_bubbleable_metadata = FALSE) { + $generated_url = $this->urlGenerator->generateFromRoute($name, $parameters, $options, TRUE); + if (!$collect_bubbleable_metadata) { + $this->bubble($generated_url, $options); + } + return $collect_bubbleable_metadata ? $generated_url : $generated_url->getGeneratedUrl(); + } + + /** + * {@inheritdoc} + */ + public function generateFromPath($path = NULL, $options = array(), $collect_bubbleable_metadata = FALSE) { + $generated_url = $this->urlGenerator->generateFromPath($path, $options, TRUE); + if (!$collect_bubbleable_metadata) { + $this->bubble($generated_url, $options); + } + return $collect_bubbleable_metadata ? $generated_url : $generated_url->getGeneratedUrl(); + } + + /** + * {@inheritdoc} + */ + public function supports($name) { + return $this->urlGenerator->supports($name); + } + + /** + * {@inheritdoc} + */ + public function getRouteDebugMessage($name, array $parameters = array()) { + return $this->urlGenerator->getRouteDebugMessage($name, $parameters); + } + +} diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml index fcf2866..2ff32c3 100644 --- a/core/modules/node/node.services.yml +++ b/core/modules/node/node.services.yml @@ -3,16 +3,11 @@ services: class: Drupal\node\Routing\RouteSubscriber tags: - { name: event_subscriber } - node.grant_storage.non_bubbling: + node.grant_storage: class: Drupal\node\NodeGrantDatabaseStorage arguments: ['@database', '@module_handler', '@language_manager'] - public: false tags: - { name: backend_overridable } - node.grant_storage: - class: Drupal\node\CacheabilityBubblingNodeGrantStorage - parent: container.trait - arguments: ['@node.grant_storage.non_bubbling'] access_check.node.revision: class: Drupal\node\Access\NodeRevisionAccessCheck arguments: ['@entity.manager'] diff --git a/core/modules/node/src/CacheabilityBubblingNodeGrantStorage.php b/core/modules/node/src/CacheabilityBubblingNodeGrantStorage.php deleted file mode 100644 index 60b6d90..0000000 --- a/core/modules/node/src/CacheabilityBubblingNodeGrantStorage.php +++ /dev/null @@ -1,122 +0,0 @@ -nodeGrantStorage = $node_grant_storage; - } - - /** - * {@inheritdoc} - */ - public function access(NodeInterface $node, $operation, $langcode, AccountInterface $account) { - return $this->nodeGrantStorage->access($node, $operation, $langcode, $account); - } - - /** - * {@inheritdoc} - */ - public function checkAll(AccountInterface $account) { - return $this->nodeGrantStorage->checkAll($account); - } - - /** - * {@inheritdoc} - */ - public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table) { - // Bubble the 'user.node_grants:$op' cache context to the current render - // context. - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - if ($renderer->hasRenderContext()) { - $build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]]; - $renderer->render($build); - } - - return $this->nodeGrantStorage->alterQuery($query, $tables, $op, $account, $base_table); - } - - /** - * {@inheritdoc} - */ - public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE) { - return $this->nodeGrantStorage->write($node, $grants, $realm, $delete); - } - - /** - * {@inheritdoc} - */ - public function delete() { - return $this->nodeGrantStorage->delete(); - } - - /** - * {@inheritdoc} - */ - public function writeDefault() { - return $this->nodeGrantStorage->writeDefault(); - } - - /** - * {@inheritdoc} - */ - public function count() { - return $this->nodeGrantStorage->count(); - } - - /** - * {@inheritdoc} - */ - public function deleteNodeRecords(array $nids) { - return $this->nodeGrantStorage->deleteNodeRecords($nids); - } - -} diff --git a/core/profiles/minimal/minimal.info.yml b/core/profiles/minimal/minimal.info.yml index 206b8e7..b94b9f0 100644 --- a/core/profiles/minimal/minimal.info.yml +++ b/core/profiles/minimal/minimal.info.yml @@ -8,5 +8,6 @@ dependencies: - block - dblog - page_cache + - cache_context_safeguards themes: - stark diff --git a/core/profiles/standard/standard.info.yml b/core/profiles/standard/standard.info.yml index a356ae8..1c82dcd 100644 --- a/core/profiles/standard/standard.info.yml +++ b/core/profiles/standard/standard.info.yml @@ -37,6 +37,7 @@ dependencies: - views - views_ui - tour + - cache_context_safeguards themes: - bartik - seven diff --git a/core/profiles/testing/testing.info.yml b/core/profiles/testing/testing.info.yml index 5ded376..835dc6c 100644 --- a/core/profiles/testing/testing.info.yml +++ b/core/profiles/testing/testing.info.yml @@ -8,6 +8,7 @@ dependencies: # Enable page_cache in testing, to ensure that as many tests as possible run # with page caching enabled. - page_cache + - cache_contexts_safeguards # @todo: Remove this in https://www.drupal.org/node/2352949 themes: - classy