/** * Adds in the current user as a context. * * @param \Drupal\page_manager\Event\PageManagerContextEvent $event * The page entity context event. */ public function onPageContext(PageManagerContextEvent $event) { $request = $this->requestStack->getCurrentRequest(); $page = $event->getPage(); $routes = $this->routeProvider->getRoutesByPattern($page->getPath())->all(); $route = reset($routes); if ($route && ($route_contexts = $route->getOption('parameters'))) { foreach ($route_contexts as $route_context_name => $route_context) { // Skip this parameter. if ($route_context_name == 'page_manager_page_variant' || $route_context_name == 'page_manager_page') { continue; } $context_name = $this->t('{@name} from route', ['@name' => $route_context_name]); if ($request->attributes->has($route_context_name)) { $value = $request->attributes->get($route_context_name); } else { // @todo Find a way to add in a fake value for configuration. $value = NULL; } $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); $context = new Context(new ContextDefinition($route_context['type'], $context_name, FALSE), $value); $context->addCacheableDependency($cacheability); $page->addContext($route_context_name, $context); } } }
/** * Prints a page listing various types of help. * * The page has sections defined by \Drupal\help\HelpSectionPluginInterface * plugins. * * @return array * A render array for the help page. */ public function helpMain() { $output = []; // We are checking permissions, so add the user.permissions cache context. $cacheability = new CacheableMetadata(); $cacheability->addCacheContexts(['user.permissions']); $plugins = $this->helpManager->getDefinitions(); $cacheability->addCacheableDependency($this->helpManager); foreach ($plugins as $plugin_id => $plugin_definition) { // Check the provided permission. if (!empty($plugin_definition['permission']) && !$this->currentuser()->hasPermission($plugin_definition['permission'])) { continue; } // Add the section to the page. /** @var \Drupal\help\HelpSectionPluginInterface $plugin */ $plugin = $this->helpManager->createInstance($plugin_id); $this_output = ['#theme' => 'help_section', '#title' => $plugin->getTitle(), '#description' => $plugin->getDescription(), '#empty' => $this->t('There is currently nothing in this section.'), '#links' => []]; $links = $plugin->listTopics(); if (is_array($links) && count($links)) { $this_output['#links'] = $links; } $cacheability->addCacheableDependency($plugin); $output[$plugin_id] = $this_output; } $cacheability->applyTo($output); return $output; }
/** * {@inheritdoc} */ public function getRuntimeContexts(array $unqualified_context_ids) { // Add a context for each language type. $language_types = $this->languageManager->getLanguageTypes(); $info = $this->languageManager->getDefinedLanguageTypesInfo(); if ($unqualified_context_ids) { foreach ($unqualified_context_ids as $unqualified_context_id) { if (array_search($unqualified_context_id, $language_types) === FALSE) { unset($language_types[$unqualified_context_id]); } } } $result = []; foreach ($language_types as $type_key) { if (isset($info[$type_key]['name'])) { $context = new Context(new ContextDefinition('language', $info[$type_key]['name'])); $context->setContextValue($this->languageManager->getCurrentLanguage($type_key)); $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['languages:' . $type_key]); $context->addCacheableDependency($cacheability); $result[$type_key] = $context; } } return $result; }
/** * Adds a cache tag if the 'user.permissions' cache context is present. * * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event * The event to process. */ public function onRespond(FilterResponseEvent $event) { if (!$event->isMasterRequest()) { return; } if (!$this->currentUser->isAnonymous()) { return; } $response = $event->getResponse(); if (!$response instanceof CacheableResponseInterface) { return; } // The 'user.permissions' cache context ensures that if the permissions for // a role are modified, users are not served stale render cache content. // But, when entire responses are cached in reverse proxies, the value for // the cache context is never calculated, causing the stale response to not // be invalidated. Therefore, when varying by permissions and the current // user is the anonymous user, also add the cache tag for the 'anonymous' // role. if (in_array('user.permissions', $response->getCacheableMetadata()->getCacheContexts())) { $per_permissions_response_for_anon = new CacheableMetadata(); $per_permissions_response_for_anon->setCacheTags(['config:user.role.anonymous']); $response->addCacheableDependency($per_permissions_response_for_anon); } }
/** * {@inheritdoc} */ public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { if ($route_name === '<current>') { if ($current_route = $this->routeMatch->getRouteObject()) { $requirements = $current_route->getRequirements(); // Setting _method and _schema is deprecated since 2.7. Using // setMethods() and setSchemes() are now the recommended ways. unset($requirements['_method']); unset($requirements['_schema']); $route->setRequirements($requirements); $route->setPath($current_route->getPath()); $route->setSchemes($current_route->getSchemes()); $route->setMethods($current_route->getMethods()); $route->setOptions($current_route->getOptions()); $route->setDefaults($current_route->getDefaults()); $parameters = array_merge($parameters, $this->routeMatch->getRawParameters()->all()); if ($cacheable_metadata) { $cacheable_metadata->addCacheContexts(['route']); } } else { // If we have no current route match available, point to the frontpage. $route->setPath('/'); } } }
/** * {@inheritdoc} */ public function getCacheableMetadata($menu_name = NULL) { if (!$menu_name) { throw new \LogicException('No menu name provided for menu.active_trails cache context.'); } $cacheable_metadata = new CacheableMetadata(); return $cacheable_metadata->setCacheTags(["config:system.menu.{$menu_name}"]); }
/** * {@inheritdoc} */ public function getCacheableMetadata() { // Since this depends on State this can change at any time and is not // cacheable. $metadata = new CacheableMetadata(); $metadata->setCacheMaxAge(0); return $metadata; }
/** * {@inheritdoc} */ public function getCacheableMetadata($name) { $metadata = new CacheableMetadata(); if ($name === 'block.block.config_override_test') { $metadata->setCacheContexts(['config_override_integration_test'])->setCacheTags(['config_override_integration_test_tag']); } return $metadata; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); $metadata = new CacheableMetadata(); $metadata->addCacheTags(['merge:tag']); $metadata->addCacheContexts(['user.permissions']); $result = $result->merge($metadata); return $result; }
/** * {@inheritdoc} */ public function getCacheableMetadata() { $cacheable_metadata = new CacheableMetadata(); $tags = []; foreach ($this->user->getRoles() as $rid) { $tags[] = "config:user.role.{$rid}"; } return $cacheable_metadata->setCacheTags($tags); }
/** * {@inheritdoc} */ public function addCacheableDependency($dependency) { // A trait doesn't have a constructor, so initialize the cacheability // metadata if that hasn't happened yet. if (!isset($this->cacheabilityMetadata)) { $this->cacheabilityMetadata = new CacheableMetadata(); } $this->cacheabilityMetadata = $this->cacheabilityMetadata->merge(CacheableMetadata::createFromObject($dependency)); return $this; }
/** * {@inheritdoc} */ public function getRuntimeContexts(array $unqualified_context_ids) { $current_user = $this->userStorage->load($this->account->id()); $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')), $current_user); $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['user']); $context->addCacheableDependency($cacheability); $result = ['current_user' => $context]; return $result; }
/** * Adds in the current user as a context. * * @param \Drupal\page_manager\Event\PageManagerContextEvent $event * The page entity context event. */ public function onPageContext(PageManagerContextEvent $event) { $id = $this->account->id(); $current_user = $this->userStorage->load($id); $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')), $current_user); $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['user']); $context->addCacheableDependency($cacheability); $event->getPage()->addContext('current_user', $context); }
/** * {@inheritdoc} */ public function getRuntimeContexts(array $unqualified_context_ids) { $current_user = $this->userStorage->load($this->account->id()); $context1 = new Context(new ContextDefinition('entity:user', 'User 1'), $current_user); $context2 = new Context(new ContextDefinition('entity:user', 'User 2'), $current_user); $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['user']); $context1->addCacheableDependency($cacheability); $context2->addCacheableDependency($cacheability); return ['user1' => $context1, 'user2' => $context2]; }
/** * {@inheritdoc} */ public function getCacheableMetadata() { $cacheable_metadata = new CacheableMetadata(); // The permissions hash changes when: // - a user is updated to have different roles; $tags = ['user:' . $this->user->id()]; // - a role is updated to have different permissions. foreach ($this->user->getRoles() as $rid) { $tags[] = "config:user.role.{$rid}"; } return $cacheable_metadata->setCacheTags($tags); }
/** * {@inheritdoc} */ public function getRuntimeContexts(array $unqualified_context_ids) { // Load the current domain. $current_domain = $this->negotiator->getActiveDomain(); // Set the context. $context = new Context(new ContextDefinition('entity:domain', $this->t('Active domain')), $current_domain); // Allow caching. $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['url.site']); $context->addCacheableDependency($cacheability); // Prepare the result. $result = ['current_domain' => $context]; return $result; }
/** * {@inheritdoc} */ public function getCacheableMetadata($operation = NULL) { $cacheable_metadata = new CacheableMetadata(); if (!\Drupal::moduleHandler()->getImplementations('node_grants')) { return $cacheable_metadata; } // The node grants may change if the user is updated. (The max-age is set to // zero below, but sites may override this cache context, and change it to a // non-zero value. In such cases, this cache tag is needed for correctness.) $cacheable_metadata->setCacheTags(['user:' . $this->user->id()]); // If the site is using node grants, this cache context can not be // optimized. return $cacheable_metadata->setCacheMaxAge(0); }
/** * Sets the '4xx-response' cache tag on 4xx responses. * * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event * The event to process. */ public function onRespond(FilterResponseEvent $event) { if (!$event->isMasterRequest()) { return; } $response = $event->getResponse(); if (!$response instanceof CacheableResponseInterface) { return; } if ($response->isClientError()) { $http_4xx_response_cacheability = new CacheableMetadata(); $http_4xx_response_cacheability->setCacheTags(['4xx-response']); $response->addCacheableDependency($http_4xx_response_cacheability); } }
/** * Handles a page request for our forced login route. */ public function forceLogin() { // TODO: What if CAS is not configured? need to handle that case. $query_params = $this->requestStack->getCurrentRequest()->query->all(); $cas_login_url = $this->casHelper->getServerLoginUrl($query_params); $this->casHelper->log("Cas forced login route, redirecting to: {$cas_login_url}"); // This response is OK to cache, but since the redirect URL is dependent on // the configured server settings, we need to add some cache metadata tied // to the settings. $cacheable_metadata = new CacheableMetadata(); $cacheable_metadata->addCacheTags(array('config:cas.settings')); $response = TrustedRedirectResponse::create($cas_login_url, 302); $response->addCacheableDependency($cacheable_metadata); return $response; }
/** * Creates a bubbleable metadata object with values taken from a render array. * * @param array $build * A render array. * * @return static */ public static function createFromRenderArray(array $build) { $meta = parent::createFromRenderArray($build); $meta->attached = isset($build['#attached']) ? $build['#attached'] : []; $meta->postRenderCache = isset($build['#post_render_cache']) ? $build['#post_render_cache'] : []; return $meta; }
/** * {@inheritdoc} */ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) { $active_theme = $this->themeManager->getActiveTheme(); // Build an array of the region names in the right order. $empty = array_fill_keys($active_theme->getRegions(), array()); $full = array(); foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) { /** @var \Drupal\block\BlockInterface $block */ $access = $block->access('view', NULL, TRUE); $region = $block->getRegion(); if (!isset($cacheable_metadata[$region])) { $cacheable_metadata[$region] = CacheableMetadata::createFromObject($access); } else { $cacheable_metadata[$region] = $cacheable_metadata[$region]->merge(CacheableMetadata::createFromObject($access)); } // Set the contexts on the block before checking access. if ($access->isAllowed()) { $full[$region][$block_id] = $block; } } // Merge it with the actual values to maintain the region ordering. $assignments = array_intersect_key(array_merge($empty, $full), $empty); foreach ($assignments as &$assignment) { // Suppress errors because PHPUnit will indirectly modify the contents, // triggering https://bugs.php.net/bug.php?id=50688. @uasort($assignment, 'Drupal\\block\\Entity\\Block::sort'); } return $assignments; }
/** * {@inheritdoc} * * @see ::prepareView() * @see ::getEntitiestoView() */ public function view(FieldItemListInterface $items, $langcode = NULL) { $elements = parent::view($items, $langcode); $field_level_access_cacheability = new CacheableMetadata(); // Try to map the cacheability of the access result that was set at // _accessCacheability in getEntitiesToView() to the corresponding render // subtree. If no such subtree is found, then merge it with the field-level // access cacheability. foreach ($items as $delta => $item) { // Ignore items for which access cacheability could not be determined in // prepareView(). if (!empty($item->_accessCacheability)) { if (isset($elements[$delta])) { CacheableMetadata::createFromRenderArray($elements[$delta])->merge($item->_accessCacheability)->applyTo($elements[$delta]); } else { $field_level_access_cacheability = $field_level_access_cacheability->merge($item->_accessCacheability); } } } // Apply the cacheability metadata for the inaccessible entities and the // entities for which the corresponding render subtree could not be found. // This causes the field to be rendered (and cached) according to the cache // contexts by which the access results vary, to ensure only users with // access to this field can view it. It also tags this field with the cache // tags on which the access results depend, to ensure users that cannot view // this field at the moment will gain access once any of those cache tags // are invalidated. $field_level_access_cacheability->applyTo($elements); return $elements; }
/** * {@inheritdoc} */ public function getCacheableMetadata() { // The book active trail depends on the node and data attached to it. // That information is however not stored as part of the node. $cacheable_metadata = new CacheableMetadata(); if ($node = $this->requestStack->getCurrentRequest()->get('node')) { // If the node is part of a book then we can use the cache tag for that // book. If not, then it can't be optimized away. if (!empty($node->book['bid'])) { $cacheable_metadata->addCacheTags(['bid:' . $node->book['bid']]); } else { $cacheable_metadata->setCacheMaxAge(0); } } return $cacheable_metadata; }
/** * Test the forcedLogin redirect. * * @covers ::forceLogin * @covers ::__construct */ public function testForceLogin() { $request = $this->getMock('\\Symfony\\Component\\HttpFoundation\\Request'); $query = $this->getMock('\\Symfony\\Component\\HttpFoundation\\ParameterBag'); $request->query = $query; $this->requestStack->expects($this->once())->method('getCurrentRequest')->will($this->returnValue($request)); $parameters = array('returnto' => 'node/1', 'foo' => 'bar'); $query->expects($this->once())->method('all')->will($this->returnValue($parameters)); $this->casHelper->expects($this->once())->method('getServerLoginUrl')->with($this->equalTo($parameters))->will($this->returnValue('https://example.com')); $cacheableMetadata = new CacheableMetadata(); $cacheableMetadata->addCacheTags(array('config:cas.settings')); $expected_response = new TrustedRedirectResponse('https://example.com', 302); $expected_response->addCacheableDependency($cacheableMetadata); $force_login_controller = new ForceLoginController($this->casHelper, $this->requestStack); $response = $force_login_controller->forceLogin(); $this->assertEquals($expected_response, $response); }
/** * {@inheritdoc} */ public function getRuntimeContexts(array $unqualified_context_ids) { $result = []; $context = new Context(new ContextDefinition('entity:node', NULL, FALSE)); if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) { if ($node = $this->routeMatch->getParameter('node')) { $context->setContextValue($node); } } elseif ($this->routeMatch->getRouteName() == 'node.add') { $node_type = $this->routeMatch->getParameter('node_type'); $context->setContextValue(Node::create(array('type' => $node_type->id()))); } $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); $context->addCacheableDependency($cacheability); $result['node'] = $context; return $result; }
/** * {@inheritdoc} */ public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { if ($route_name === '<current>') { if ($current_route = $this->routeMatch->getRouteObject()) { $route->setPath($current_route->getPath()); $route->setRequirements($current_route->getRequirements()); $route->setOptions($current_route->getOptions()); $route->setDefaults($current_route->getDefaults()); $parameters = array_merge($parameters, $this->routeMatch->getRawParameters()->all()); if ($cacheable_metadata) { $cacheable_metadata->addCacheContexts(['route']); } } else { // If we have no current route match available, point to the frontpage. $route->setPath('/'); } } }
/** * {@inheritdoc} */ public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { if ($route->hasRequirement('_csrf_token')) { $path = ltrim($route->getPath(), '/'); // Replace the path parameters with values from the parameters array. foreach ($parameters as $param => $value) { $path = str_replace("{{$param}}", $value, $path); } // Adding this to the parameters means it will get merged into the query // string when the route is compiled. $parameters['token'] = $this->csrfToken->get($path); if ($cacheable_metadata) { // Tokens are per user and per session, so not cacheable. // @todo Improve in https://www.drupal.org/node/2351015. $cacheable_metadata->setCacheMaxAge(0); } } }
/** * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound(). */ public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { // Rewrite user/uid to user/username. if (preg_match('!^/user/([0-9]+)(/.*)?!', $path, $matches)) { if ($account = User::load($matches[1])) { $matches += array(2 => ''); $path = '/user/' . $account->getUsername() . $matches[2]; if ($cacheable_metadata) { $cacheable_metadata->addCacheTags($account->getCacheTags()); } } } // Rewrite forum/ to community/. if ($path == '/forum' || strpos($path, '/forum/') === 0) { $path = '/community' . substr($path, 5); } return $path; }
/** * {@inheritdoc} */ public function getCacheableMetadata($operation = NULL) { $cacheable_metadata = new CacheableMetadata(); if (!\Drupal::moduleHandler()->getImplementations('node_grants')) { return $cacheable_metadata; } // If the site is using node grants, this cache context can not be // optimized. return $cacheable_metadata->setCacheMaxAge(0); }
/** * {@inheritdoc} */ public function getRuntimeContexts(array $unqualified_context_ids) { $result = []; $context_definition = new ContextDefinition('entity:rdf_entity:asset_release', NULL, FALSE); $value = NULL; if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['rdf_entity'])) { /** @var \Drupal\rdf_entity\RdfInterface $asset_release */ if ($asset_release = $this->routeMatch->getParameter('rdf_entity')) { if ($asset_release->bundle() == 'asset_release') { $value = $asset_release; } } } $cacheability = new CacheableMetadata(); $cacheability->setCacheContexts(['route']); $context = new Context($context_definition, $value); $context->addCacheableDependency($cacheability); $result['asset_release'] = $context; return $result; }