core/lib/Drupal/Core/Access/AccessResult.php | 9 ++++++++- .../DefaultExceptionHtmlSubscriber.php | 5 ++++- .../EventSubscriber/MaintenanceModeSubscriber.php | 2 ++ .../Core/Render/MainContent/HtmlRenderer.php | 9 +++++++++ core/lib/Drupal/Core/Routing/AccessAwareRouter.php | 10 +++++++++- .../Core/Routing/AccessAwareRouterInterface.php | 5 +++++ core/modules/comment/src/CommentForm.php | 13 +++++++++++- .../comment/src/Tests/CommentAnonymousTest.php | 7 ++++--- .../contact/src/Access/ContactPageAccess.php | 2 +- .../contact/src/Controller/ContactController.php | 5 +++++ .../contact/src/Tests/ContactPersonalTest.php | 2 ++ .../src/Plugin/views/argument_default/Node.php | 2 +- .../modules/node/src/Tests/Views/FrontPageTest.php | 7 ++++--- .../system/config/install/system.performance.yml | 2 +- .../Tests/Cache/PageCacheTagsIntegrationTest.php | 2 ++ .../src/TwigThemeTestController.php | 1 + .../src/Plugin/views/argument_default/Tid.php | 2 +- .../src/Plugin/views/argument_default/User.php | 2 +- .../Plugin/views/argument/ArgumentPluginBase.php | 2 +- .../views/argument_default/QueryParameter.php | 2 +- .../src/Plugin/views/argument_default/Raw.php | 2 +- .../src/Plugin/views/filter/FilterPluginBase.php | 2 +- .../views/src/Plugin/views/sort/SortPluginBase.php | 2 +- core/modules/views/src/Tests/GlossaryTest.php | 3 ++- .../Drupal/Tests/Core/Access/AccessResultTest.php | 23 +++++++++++----------- .../Tests/Core/Routing/AccessAwareRouterTest.php | 23 +++++++++++++++++----- 26 files changed, 109 insertions(+), 37 deletions(-) diff --git a/core/lib/Drupal/Core/Access/AccessResult.php b/core/lib/Drupal/Core/Access/AccessResult.php index d515933..a6812eb 100644 --- a/core/lib/Drupal/Core/Access/AccessResult.php +++ b/core/lib/Drupal/Core/Access/AccessResult.php @@ -339,7 +339,14 @@ public function setCacheMaxAge($max_age) { * @return $this */ public function cachePerRole() { - $this->addCacheContexts(array('user.roles')); + $this->addCacheContexts(['user.roles']) + // @todo this is always needed when the 'user.roles' cache context is set: + // when caching per role, and something has changed in that role (e.g. + // a permission is granted or revoked), then the results for that role + // will change! To invalidate accordingly, we need this cache tag. + // This will be handled automatically once + // https://www.drupal.org/node/2428703 lands. + ->addCacheTags(['config:user_role_list']); return $this; } diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php index a98bf57..5861e85 100644 --- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php @@ -7,6 +7,7 @@ namespace Drupal\Core\EventSubscriber; +use Drupal\Core\Routing\AccessAwareRouterInterface; use Drupal\Core\Url; use Drupal\Core\Utility\Error; use Psr\Log\LoggerInterface; @@ -111,8 +112,10 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st } try { - // Persist the 'exception' attribute to the subrequest. + // Persist the 'exception' and access result attributes to the + // subrequest. $sub_request->attributes->set('exception', $request->attributes->get('exception')); + $sub_request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT)); // Carry over the session to the subrequest. if ($session = $request->getSession()) { diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php index bdd05c7..a32fa71 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php @@ -98,6 +98,8 @@ public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFa public function onKernelRequestMaintenance(GetResponseEvent $event) { $route_match = RouteMatch::createFromRequest($event->getRequest()); if ($this->maintenanceMode->applies($route_match)) { + // Don't cache maintenance mode pages. + \Drupal::service('page_cache_kill_switch')->trigger(); if (!$this->maintenanceMode->exempt($this->account)) { // Deliver the 503 page if the site is in maintenance mode and the // logged in user is not allowed to bypass it. diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php index 0bcdc5d..fca409d 100644 --- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php @@ -10,6 +10,7 @@ use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheableInterface; use Drupal\Core\Controller\TitleResolverInterface; use Drupal\Core\Display\PageVariantInterface; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -17,6 +18,7 @@ use Drupal\Core\Render\Renderer; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\RenderEvents; +use Drupal\Core\Routing\AccessAwareRouterInterface; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -145,6 +147,13 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // Set the generator in the HTTP header. list($version) = explode('.', \Drupal::VERSION, 2); + // Merge the request's access result cacheability metadata, if it has any. + $access_result = $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT); + if ($access_result instanceof CacheableInterface) { + $cache_contexts = Cache::mergeContexts($cache_contexts, $access_result->getCacheContexts()); + $cache_tags = Cache::mergeTags($cache_tags, $access_result->getCacheTags()); + } + return new Response($content, 200,[ 'X-Drupal-Cache-Tags' => implode(' ', $cache_tags), 'X-Drupal-Cache-Contexts' => implode(' ', $cache_contexts), diff --git a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php index 93a31c3..87e30a3 100644 --- a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php @@ -105,7 +105,15 @@ public function matchRequest(Request $request) { * The request to access check. */ protected function checkAccess(Request $request) { - if (!$this->accessManager->checkRequest($request, $this->account)) { + // The cacheability (if any) of this request's access check result must be + // applied to the response. + $access_result = $this->accessManager->checkRequest($request, $this->account, TRUE); + // Allow a master request to set the access result for a subrequest: if an + // access result attribute is already set, don't overwrite it. + if (!$request->attributes->has(AccessAwareRouterInterface::ACCESS_RESULT)) { + $request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $access_result); + } + if (!$access_result->isAllowed()) { throw new AccessDeniedHttpException(); } } diff --git a/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php b/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php index 34fa75a..b6cfe4a 100644 --- a/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouterInterface.php @@ -16,6 +16,11 @@ interface AccessAwareRouterInterface extends RouterInterface, RequestMatcherInterface { /** + * Attribute name of the access result for the request.. + */ + const ACCESS_RESULT = '_access_result'; + + /** * {@inheritdoc} * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php index 95629b2..92db76c 100644 --- a/core/modules/comment/src/CommentForm.php +++ b/core/modules/comment/src/CommentForm.php @@ -94,6 +94,12 @@ public function form(array $form, FormStateInterface $form_state) { $form['#attributes']['data-user-info-from-browser'] = TRUE; } + // Vary per role, because we check a permission above and attach an asset + // library only for authenticated users. + // @todo: change to 'user.roles:authenticated' once + // https://www.drupal.org/node/2432837 lands. + $form['#cache']['contexts'][] = 'user.roles'; + // If not replying to a comment, use our dedicated page callback for new // Comments on entities. if (!$comment->id() && !$comment->hasParentComment()) { @@ -210,7 +216,12 @@ public function form(array $form, FormStateInterface $form_state) { '#access' => $is_admin, ); - $form['#cache']['tags'] = Cache::mergeTags(isset($form['#cache']['tags']) ? $form['#cache']['tags'] : [], $config->getCacheTags()); + $form['#cache']['tags'] = Cache::mergeTags( + isset($form['#cache']['tags']) ? $form['#cache']['tags'] : [], + $config->getCacheTags(), + // The form depends on the field definition. + $field_definition->getConfig($entity->bundle())->getCacheTags() + ); return parent::form($form, $form_state, $comment); } diff --git a/core/modules/comment/src/Tests/CommentAnonymousTest.php b/core/modules/comment/src/Tests/CommentAnonymousTest.php index 8305651..8143819 100644 --- a/core/modules/comment/src/Tests/CommentAnonymousTest.php +++ b/core/modules/comment/src/Tests/CommentAnonymousTest.php @@ -35,7 +35,7 @@ protected function setUp() { */ function testAnonymous() { $this->drupalLogin($this->adminUser); - $this->setCommentAnonymous('0'); // Ensure that doesn't require contact info. + $this->setCommentAnonymous(COMMENT_ANONYMOUS_MAYNOT_CONTACT); $this->drupalLogout(); // Post anonymous comment without contact info. @@ -44,7 +44,7 @@ function testAnonymous() { // Allow contact info. $this->drupalLogin($this->adminUser); - $this->setCommentAnonymous('1'); + $this->setCommentAnonymous(COMMENT_ANONYMOUS_MAY_CONTACT); // Attempt to edit anonymous comment. $this->drupalGet('comment/' . $anonymous_comment1->id() . '/edit'); @@ -57,6 +57,7 @@ function testAnonymous() { $this->assertTrue($this->commentContactInfoAvailable(), 'Contact information available.'); // Check the presence of expected cache tags. + $this->assertCacheTag('config:field.field.node.article.comment'); $this->assertCacheTag('config:user.settings'); $anonymous_comment2 = $this->postComment($this->node, $this->randomMachineName(), $this->randomMachineName()); @@ -76,7 +77,7 @@ function testAnonymous() { // Require contact info. $this->drupalLogin($this->adminUser); - $this->setCommentAnonymous('2'); + $this->setCommentAnonymous(COMMENT_ANONYMOUS_MUST_CONTACT); $this->drupalLogout(); // Try to post comment with contact info (required). diff --git a/core/modules/contact/src/Access/ContactPageAccess.php b/core/modules/contact/src/Access/ContactPageAccess.php index 97d6b06..cb5630c 100644 --- a/core/modules/contact/src/Access/ContactPageAccess.php +++ b/core/modules/contact/src/Access/ContactPageAccess.php @@ -62,7 +62,7 @@ public function access(UserInterface $user, AccountInterface $account) { // Anonymous users cannot have contact forms. if ($contact_account->isAnonymous()) { - return AccessResult::forbidden(); + return AccessResult::forbidden()->cachePerRole(); } // Users may not contact themselves. diff --git a/core/modules/contact/src/Controller/ContactController.php b/core/modules/contact/src/Controller/ContactController.php index 648a064..8813cd9 100644 --- a/core/modules/contact/src/Controller/ContactController.php +++ b/core/modules/contact/src/Controller/ContactController.php @@ -106,6 +106,7 @@ public function contactSitePage(ContactFormInterface $contact_form = NULL) { $form = $this->entityFormBuilder()->getForm($message); $form['#title'] = String::checkPlain($contact_form->label()); + $form['#cache']['contexts'][] = 'user.roles'; $form['#cache']['tags'] = Cache::mergeTags(isset($form['#cache']['tags']) ? $form['#cache']['tags'] : [], $config->getCacheTags()); return $form; } @@ -141,6 +142,7 @@ public function contactPersonalPage(UserInterface $user) { $form = $this->entityFormBuilder()->getForm($message); $form['#title'] = $this->t('Contact @username', array('@username' => $user->getUsername())); + $form['#cache']['contexts'][] = 'user.roles'; return $form; } @@ -150,6 +152,9 @@ public function contactPersonalPage(UserInterface $user) { * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ protected function contactFloodControl() { + // Flood control is incompatible with page caching. + \Drupal::service('page_cache_kill_switch')->trigger(); + $limit = $this->config('contact.settings')->get('flood.limit'); $interval = $this->config('contact.settings')->get('flood.interval'); if (!$this->flood->isAllowed('contact', $limit, $interval)) { diff --git a/core/modules/contact/src/Tests/ContactPersonalTest.php b/core/modules/contact/src/Tests/ContactPersonalTest.php index a1229d1..ab84697 100644 --- a/core/modules/contact/src/Tests/ContactPersonalTest.php +++ b/core/modules/contact/src/Tests/ContactPersonalTest.php @@ -151,11 +151,13 @@ function testPersonalContactAccess() { // Test that anonymous users can access admin user's contact form. $this->drupalGet('user/' . $this->adminUser->id() . '/contact'); $this->assertResponse(200); + $this->assertCacheTag('config:user_role_list'); // Revoke the personal contact permission for the anonymous user. user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access user contact forms')); $this->drupalGet('user/' . $this->contactUser->id() . '/contact'); $this->assertResponse(403); + $this->assertCacheTag('config:user_role_list'); $this->drupalGet('user/' . $this->adminUser->id() . '/contact'); $this->assertResponse(403); diff --git a/core/modules/node/src/Plugin/views/argument_default/Node.php b/core/modules/node/src/Plugin/views/argument_default/Node.php index 4a61f03..e6d8343 100644 --- a/core/modules/node/src/Plugin/views/argument_default/Node.php +++ b/core/modules/node/src/Plugin/views/argument_default/Node.php @@ -83,7 +83,7 @@ public function isCacheable() { * {@inheritdoc} */ public function getCacheContexts() { - return ['cache.context.url']; + return ['url']; } } diff --git a/core/modules/node/src/Tests/Views/FrontPageTest.php b/core/modules/node/src/Tests/Views/FrontPageTest.php index 61fe5eb..ba86584 100644 --- a/core/modules/node/src/Tests/Views/FrontPageTest.php +++ b/core/modules/node/src/Tests/Views/FrontPageTest.php @@ -241,7 +241,7 @@ protected function assertFrontPageViewCacheTags($do_assert_views_caches) { $view = Views::getView('frontpage'); $view->setDisplay('page_1'); - $cache_contexts = ['node_view_grants', 'language']; + $cache_contexts = ['node_view_grants', 'language', 'user.roles']; // Test before there are any nodes. $empty_node_listing_cache_tags = [ @@ -257,7 +257,7 @@ protected function assertFrontPageViewCacheTags($do_assert_views_caches) { $this->assertPageCacheContextsAndTags( Url::fromRoute('view.frontpage.page_1'), $cache_contexts, - Cache::mergeTags($empty_node_listing_cache_tags, ['rendered']) + Cache::mergeTags($empty_node_listing_cache_tags, ['config:user_role_list', 'rendered']) ); // Create some nodes on the frontpage view. Add more than 10 nodes in order @@ -317,7 +317,7 @@ protected function assertFrontPageViewCacheTags($do_assert_views_caches) { $this->assertPageCacheContextsAndTags( Url::fromRoute('view.frontpage.page_1'), $cache_contexts, - Cache::mergeTags($first_page_output_cache_tags, ['rendered']) + Cache::mergeTags($first_page_output_cache_tags, ['config:user_role_list', 'rendered']) ); // Second page. @@ -331,6 +331,7 @@ protected function assertFrontPageViewCacheTags($do_assert_views_caches) { // The rest. 'config:filter.format.plain_text', 'config:views.view.frontpage', + 'config:user_role_list', 'node_list', 'node_view', 'user_view', diff --git a/core/modules/system/config/install/system.performance.yml b/core/modules/system/config/install/system.performance.yml index 1e75b4b..98f34f0 100644 --- a/core/modules/system/config/install/system.performance.yml +++ b/core/modules/system/config/install/system.performance.yml @@ -1,6 +1,6 @@ cache: page: - use_internal: false + use_internal: true max_age: 0 css: preprocess: true diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php index f4d30ec..7854d50 100644 --- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php +++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php @@ -101,6 +101,7 @@ function testPageCacheTags() { 'config:system.menu.footer', 'config:system.menu.main', 'config:system.site', + 'config:user_role_list', )); // Full node page 2. @@ -129,6 +130,7 @@ function testPageCacheTags() { 'config:system.site', 'comment_list', 'node_list', + 'config:user_role_list', 'config:views.view.comments_recent', )); } diff --git a/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php b/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php index ff49bd6..e935afb 100644 --- a/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php +++ b/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php @@ -72,6 +72,7 @@ public function fileUrlRender() { * Menu callback for testing the Twig registry loader. */ public function registryLoaderRender() { + \Drupal::service('page_cache_kill_switch')->trigger(); return array('#theme' => 'twig_registry_loader_test'); } diff --git a/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php b/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php index 5bd3f45..a8751d5 100644 --- a/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php +++ b/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php @@ -230,7 +230,7 @@ public function isCacheable() { * {@inheritdoc} */ public function getCacheContexts() { - return ['cache.context.url']; + return ['url']; } /** diff --git a/core/modules/user/src/Plugin/views/argument_default/User.php b/core/modules/user/src/Plugin/views/argument_default/User.php index e3d9644..269be4e 100644 --- a/core/modules/user/src/Plugin/views/argument_default/User.php +++ b/core/modules/user/src/Plugin/views/argument_default/User.php @@ -123,7 +123,7 @@ public function isCacheable() { * {@inheritdoc} */ public function getCacheContexts() { - return ['cache.context.url']; + return ['url']; } } diff --git a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php index 069276b..4e5469b 100644 --- a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php +++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php @@ -1208,7 +1208,7 @@ public function getCacheContexts() { // By definition arguments depends on the URL. // @todo Once contexts are properly injected into block views we could pull // the information from there. - $contexts[] = 'cache.context.url'; + $contexts[] = 'url'; // Asks all subplugins (argument defaults, argument validator and styles). if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheablePluginInterface) { diff --git a/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php b/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php index 337b722..feccd21 100644 --- a/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php +++ b/core/modules/views/src/Plugin/views/argument_default/QueryParameter.php @@ -95,7 +95,7 @@ public function isCacheable() { * {@inheritdoc} */ public function getCacheContexts() { - return ['cache.context.url']; + return ['url']; } } diff --git a/core/modules/views/src/Plugin/views/argument_default/Raw.php b/core/modules/views/src/Plugin/views/argument_default/Raw.php index 4239c50..40bddf3 100644 --- a/core/modules/views/src/Plugin/views/argument_default/Raw.php +++ b/core/modules/views/src/Plugin/views/argument_default/Raw.php @@ -124,7 +124,7 @@ public function isCacheable() { * {@inheritdoc} */ public function getCacheContexts() { - return ['cache.context.url']; + return ['url']; } } diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php index 7c104d8..f2e2d56 100644 --- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php +++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php @@ -1476,7 +1476,7 @@ public function getCacheContexts() { // input from GET parameters, which are part of the URL. Hence a view with // an exposed filter is cacheable per URL. if ($this->isExposed()) { - $cache_contexts[] = 'cache.context.url'; + $cache_contexts[] = 'url'; } return $cache_contexts; } diff --git a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php index 45b86be..8fe2b43 100644 --- a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php +++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php @@ -241,7 +241,7 @@ public function getCacheContexts() { $cache_contexts = []; // Exposed sorts use GET parameters, so it depends on the current URL. if ($this->isExposed()) { - $cache_contexts[] = 'cache.context.url'; + $cache_contexts[] = 'url'; } return $cache_contexts; } diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php index 36481a4..8328eec 100644 --- a/core/modules/views/src/Tests/GlossaryTest.php +++ b/core/modules/views/src/Tests/GlossaryTest.php @@ -86,7 +86,8 @@ public function testGlossaryView() { // Verify cache tags. $this->enablePageCaching(); - $this->assertPageCacheContextsAndTags(Url::fromRoute('view.glossary.page_1'), ['cache.context.url', 'node_view_grants', 'language'], [ + $this->assertPageCacheContextsAndTags(Url::fromRoute('view.glossary.page_1'), ['node_view_grants', 'language', 'url', 'user.roles'], [ + 'config:user_role_list', 'config:views.view.glossary', 'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(), 'node_list', diff --git a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php index 42d2d2a..92d22c5 100644 --- a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php +++ b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php @@ -325,7 +325,7 @@ public function testCacheMaxAge() { * @covers ::allowedIfHasPermission */ public function testCacheContexts() { - $verify = function (AccessResult $access, array $contexts) { + $verify = function (AccessResult $access, array $contexts, array $tags = []) { $this->assertFalse($access->isAllowed()); $this->assertFalse($access->isForbidden()); $this->assertTrue($access->isNeutral()); @@ -333,7 +333,7 @@ public function testCacheContexts() { $this->assertSame('default', $access->getCacheBin()); $this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge()); $this->assertSame($contexts, $access->getCacheContexts()); - $this->assertSame([], $access->getCacheTags()); + $this->assertSame($tags, $access->getCacheTags()); }; $access = AccessResult::neutral()->addCacheContexts(['foo']); @@ -357,10 +357,11 @@ public function testCacheContexts() { // ::cachePerRole() convenience method. $contexts = array('user.roles'); - $a = AccessResult::neutral()->addCacheContexts($contexts); - $verify($a, $contexts); + $tags = ['config:user_role_list']; + $a = AccessResult::neutral()->addCacheContexts($contexts)->addCacheTags($tags); + $verify($a, $contexts, $tags); $b = AccessResult::neutral()->cachePerRole(); - $verify($b, $contexts); + $verify($b, $contexts, $tags); $this->assertEquals($a, $b); // ::cachePerUser() convenience method. @@ -373,12 +374,12 @@ public function testCacheContexts() { // Both. $contexts = array('user', 'user.roles'); - $a = AccessResult::neutral()->addCacheContexts($contexts); - $verify($a, $contexts); + $a = AccessResult::neutral()->addCacheContexts($contexts)->addCacheTags($tags); + $verify($a, $contexts, $tags); $b = AccessResult::neutral()->cachePerRole()->cachePerUser(); - $verify($b, $contexts); + $verify($b, $contexts, $tags); $c = AccessResult::neutral()->cachePerUser()->cachePerRole(); - $verify($c, $contexts); + $verify($c, $contexts, $tags); $this->assertEquals($a, $b); $this->assertEquals($a, $c); @@ -393,7 +394,7 @@ public function testCacheContexts() { // Verify the object when using the ::allowedIfHasPermission() convenience // static method. $b = AccessResult::allowedIfHasPermission($account, 'may herd llamas'); - $verify($b, $contexts); + $verify($b, $contexts, $tags); } /** @@ -473,7 +474,7 @@ public function testInheritCacheability() { $this->assertTrue($access->inheritCacheability($other) instanceof AccessResult); $this->assertTrue($access->isCacheable()); $this->assertSame(['user.roles'], $access->getCacheContexts()); - $this->assertSame(['node:20011988'], $access->getCacheTags()); + $this->assertSame(['config:user_role_list', 'node:20011988'], $access->getCacheTags()); $this->assertSame('default', $access->getCacheBin()); $this->assertSame(1500, $access->getCacheMaxAge()); diff --git a/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php b/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php index 3e8676f..da58cd9 100644 --- a/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/AccessAwareRouterTest.php @@ -7,7 +7,9 @@ namespace Drupal\Tests\Core\Routing; +use Drupal\Core\Access\AccessResult; use Drupal\Core\Routing\AccessAwareRouter; +use Drupal\Core\Routing\AccessAwareRouterInterface; use Drupal\Tests\UnitTestCase; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; @@ -73,13 +75,18 @@ protected function setupRouter() { public function testMatchRequestAllowed() { $this->setupRouter(); $request = new Request(); + $access_result = AccessResult::allowed(); $this->accessManager->expects($this->once()) ->method('checkRequest') ->with($request) - ->will($this->returnValue(TRUE)); + ->willReturn($access_result); $parameters = $this->router->matchRequest($request); - $this->assertSame($request->attributes->all(), array(RouteObjectInterface::ROUTE_OBJECT => $this->route)); - $this->assertSame($parameters, array(RouteObjectInterface::ROUTE_OBJECT => $this->route)); + $expected = [ + RouteObjectInterface::ROUTE_OBJECT => $this->route, + AccessAwareRouterInterface::ACCESS_RESULT => $access_result, + ]; + $this->assertSame($expected, $request->attributes->all()); + $this->assertSame($expected, $parameters); } /** @@ -90,11 +97,17 @@ public function testMatchRequestAllowed() { public function testMatchRequestDenied() { $this->setupRouter(); $request = new Request(); + $access_result = AccessResult::forbidden(); $this->accessManager->expects($this->once()) ->method('checkRequest') ->with($request) - ->will($this->returnValue(FALSE)); - $this->router->matchRequest($request); + ->willReturn($access_result); + $parameters = $this->router->matchRequest($request); + $expected = [ + AccessAwareRouterInterface::ACCESS_RESULT => $access_result, + ]; + $this->assertSame($expected, $request->attributes->all()); + $this->assertSame($expected, $parameters); } /**