.../Drupal/Core/EventSubscriber/MainContentViewSubscriber.php | 11 ++++++++++- core/lib/Drupal/Core/Render/HtmlResponse.php | 4 +--- core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php | 2 ++ core/modules/smart_cache/smart_cache.info.yml | 2 +- core/modules/smart_cache/smart_cache.module | 5 ++--- .../src/PageCache/ResponsePolicy/DenyAdminRoutes.php | 5 ++++- .../system/src/Tests/Entity/EntityCacheTagsTestBase.php | 6 ++++-- core/modules/system/system.routing.yml | 2 +- 8 files changed, 25 insertions(+), 12 deletions(-) diff --git a/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php index 07dce6e..d1d44d6 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php @@ -7,6 +7,8 @@ namespace Drupal\Core\EventSubscriber; +use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -90,7 +92,14 @@ public function onViewRenderArray(GetResponseForControllerResultEvent $event) { $wrapper = isset($this->mainContentRenderers[$wrapper]) ? $wrapper : 'html'; $renderer = $this->classResolver->getInstanceFromDefinition($this->mainContentRenderers[$wrapper]); - $event->setResponse($renderer->renderResponse($result, $request, $this->routeMatch)); + $response = $renderer->renderResponse($result, $request, $this->routeMatch); + // The main content render array is rendered into a different Response + // object, depending on the specified wrapper format. + if ($response instanceof CacheableResponseInterface) { + $main_content_view_subscriber_cacheability = (new CacheableMetadata())->setCacheContexts(['url.query_args:' . static::WRAPPER_FORMAT]); + $response->addCacheableDependency($main_content_view_subscriber_cacheability); + } + $event->setResponse($response); } } diff --git a/core/lib/Drupal/Core/Render/HtmlResponse.php b/core/lib/Drupal/Core/Render/HtmlResponse.php index 4aea1e0..9ebb6fb 100644 --- a/core/lib/Drupal/Core/Render/HtmlResponse.php +++ b/core/lib/Drupal/Core/Render/HtmlResponse.php @@ -42,9 +42,7 @@ public function setContent($content) { $content = $content['#markup']; } - parent::setContent($content); - - return $this; + return parent::setContent($content); } } diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php index a5fcb1b..b3c08a6 100644 --- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php @@ -79,6 +79,8 @@ class HtmlRenderer implements MainContentRendererInterface { /** * The renderer configuration array. * + * @see sites/default/default.services.yml + * * @var array */ protected $rendererConfig; diff --git a/core/modules/smart_cache/smart_cache.info.yml b/core/modules/smart_cache/smart_cache.info.yml index 71778e1..cdac583 100644 --- a/core/modules/smart_cache/smart_cache.info.yml +++ b/core/modules/smart_cache/smart_cache.info.yml @@ -1,6 +1,6 @@ name: Smart Cache type: module -description: 'Caches the non-personalized parts of pages. Works well for websites of all sizes.' +description: 'Caches pages, minus the personalized parts. Works well for websites of all sizes.' package: Core version: VERSION core: 8.x diff --git a/core/modules/smart_cache/smart_cache.module b/core/modules/smart_cache/smart_cache.module index 0689334..cbd0f44 100644 --- a/core/modules/smart_cache/smart_cache.module +++ b/core/modules/smart_cache/smart_cache.module @@ -16,13 +16,12 @@ function smart_cache_help($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.smart_cache': $output = '

' . t('About') . '

'; - $output .= '

' . t('The Smart Cache module caches pages minus the personalized parts in the database. For more information, see the online documentation for the Smart Cache module.', ['!smartcache-documentation' => 'https://www.drupal.org/documentation/modules/smart_cache']) . '

'; + $output .= '

' . t('The Smart Cache module caches pages in the database, minus the personalized parts. For more information, see the online documentation for the Smart Cache module.', ['!smartcache-documentation' => 'https://www.drupal.org/documentation/modules/smart_cache']) . '

'; $output .= '

' . t('Uses') . '

'; $output .= '
'; $output .= '
' . t('Speeding up your site') . '
'; $output .= '
' . t('Pages are stored the first time they are requested if they are safe to cache, and then are reused. Personalized parts are excluded automatically. Depending on your site configuration and the complexity of particular pages, Smart Cache may significantly increase the speed of your site, even for authenticated users.') . '
'; - $output .= '
' . t('The module requires no configuration.') . '
'; - $output .= '
' . t('(Every part of the page contains metadata that allows Smart Cache to figure this out on its own.)') . '
'; + $output .= '
' . t('The module requires no configuration. Every part of the page contains metadata that allows Smart Cache to figure this out on its own.') . '
'; $output .= '
'; return $output; diff --git a/core/modules/smart_cache/src/PageCache/ResponsePolicy/DenyAdminRoutes.php b/core/modules/smart_cache/src/PageCache/ResponsePolicy/DenyAdminRoutes.php index 45485f5..b3458f8 100644 --- a/core/modules/smart_cache/src/PageCache/ResponsePolicy/DenyAdminRoutes.php +++ b/core/modules/smart_cache/src/PageCache/ResponsePolicy/DenyAdminRoutes.php @@ -15,7 +15,10 @@ /** * Cache policy for routes with the '_admin_route' option set. * - * This policy rule denies caching of responses generated for admin routes. + * This policy rule denies caching of responses generated for admin routes, + * because the cacheability metadata of most admin route responses is lacking, + * which would lead to stale content being shown and the site being perceived as + * broken. */ class DenyAdminRoutes implements ResponsePolicyInterface { diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php index 5c3aacf..243a620 100644 --- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php +++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php @@ -9,6 +9,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\EventSubscriber\MainContentViewSubscriber; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; @@ -337,6 +338,7 @@ public function testReferencedEntity() { // The default cache contexts for rendered entities. $default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions']; $entity_cache_contexts = $default_cache_contexts; + $page_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]); // Cache tags present on every rendered page. // 'user.permissions' is a required cache context, and responses that vary @@ -428,7 +430,7 @@ public function testReferencedEntity() { $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); - $this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); + $this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); $this->pass("Test listing containing referenced entity.", 'Debug'); @@ -438,7 +440,7 @@ public function testReferencedEntity() { $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); - $this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); + $this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); // Verify that after modifying the referenced entity, there is a cache miss diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index d9471da..7addd4b 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -456,7 +456,7 @@ system.db_update: op: 'info' options: _maintenance_access: TRUE - _admin_route: TRUE + no_cache: TRUE requirements: _access_system_update: 'TRUE'