public function testAvailableContextLabels() { $container = $this->getMockContainer(); $container->expects($this->once())->method("get")->with("cache_context.foo")->will($this->returnValue(new FooCacheContext())); $cache_contexts = new CacheContexts($container, $this->getContextsFixture()); $labels = $cache_contexts->getLabels(); $expected = array("cache_context.foo" => "Foo"); $this->assertEquals($expected, $labels); }
/** * {@inheritdoc} * * The entire HTML: takes a #type 'page' and wraps it in a #type 'html'. */ public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) { list($page, $title) = $this->prepare($main_content, $request, $route_match); if (!isset($page['#type']) || $page['#type'] !== 'page') { throw new \LogicException('Must be #type page'); } $page['#title'] = $title; // Now render the rendered page.html.twig template inside the html.html.twig // template, and use the bubbled #attached metadata from $page to ensure we // load all attached assets. $html = ['#type' => 'html', 'page' => $page]; $html += element_info('html'); // The special page regions will appear directly in html.html.twig, not in // page.html.twig, hence add them here, just before rendering html.html.twig. $this->buildPageTopAndBottom($html); // The three parts of rendered markup in html.html.twig (page_top, page and // page_bottom) must be rendered with drupal_render_root(), so that their // #post_render_cache callbacks are executed (which may attach additional // assets). // html.html.twig must be able to render the final list of attached assets, // and hence may not execute any #post_render_cache_callbacks (because they // might add yet more assets to be attached), and therefore it must be // rendered with drupal_render(), not drupal_render_root(). $this->renderer->render($html['page'], TRUE); if (isset($html['page_top'])) { $this->renderer->render($html['page_top'], TRUE); } if (isset($html['page_bottom'])) { $this->renderer->render($html['page_bottom'], TRUE); } $content = $this->renderer->render($html); // Expose the cache contexts and cache tags associated with this page in a // X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags header respectively. Also // associate the "rendered" cache tag. This allows us to invalidate the // entire render cache, regardless of the cache bin. $cache_contexts = []; $cache_tags = ['rendered']; $cache_max_age = Cache::PERMANENT; foreach (['page_top', 'page', 'page_bottom'] as $region) { if (isset($html[$region])) { $cache_contexts = Cache::mergeContexts($cache_contexts, $html[$region]['#cache']['contexts']); $cache_tags = Cache::mergeTags($cache_tags, $html[$region]['#cache']['tags']); $cache_max_age = Cache::mergeMaxAges($cache_max_age, $html[$region]['#cache']['max-age']); } } // Set the generator in the HTTP header. list($version) = explode('.', \Drupal::VERSION, 2); $response = new Response($content, 200, ['X-Drupal-Cache-Tags' => implode(' ', $cache_tags), 'X-Drupal-Cache-Contexts' => implode(' ', $this->cacheContexts->optimizeTokens($cache_contexts)), 'X-Generator' => 'Drupal ' . $version . ' (https://www.drupal.org)']); // If an explicit non-infinite max-age is specified by a part of the page, // respect that by applying it to the response's headers. if ($cache_max_age !== Cache::PERMANENT) { $response->setMaxAge($cache_max_age); } return $response; }
/** * Creates the cache ID for a renderable element. * * This creates the cache ID string, either by returning the #cache['cid'] * property if present or by building the cache ID out of the #cache['keys'] + * #cache['contexts']. * * @param array $elements * A renderable array. * * @return string * The cache ID string, or FALSE if the element may not be cached. */ protected function createCacheID(array $elements) { // If the maximum age is zero, then caching is effectively prohibited. if (isset($elements['#cache']['max-age']) && $elements['#cache']['max-age'] === 0) { return FALSE; } if (isset($elements['#cache']['cid'])) { return $elements['#cache']['cid']; } elseif (isset($elements['#cache']['keys'])) { $cid_parts = $elements['#cache']['keys']; if (isset($elements['#cache']['contexts'])) { $contexts = $this->cacheContexts->convertTokensToKeys($elements['#cache']['contexts']); $cid_parts = array_merge($cid_parts, $contexts); } return implode(':', $cid_parts); } return FALSE; }
public function testAvailableContextLabels() { $container = $this->getMockContainer(); $cache_contexts = new CacheContexts($container, $this->getContextsFixture()); $labels = $cache_contexts->getLabels(); $expected = array("foo" => "Foo"); $this->assertEquals($expected, $labels); }
/** * {@inheritdoc} * * Creates a generic configuration form for all block types. Individual * block plugins can add elements to this form by overriding * BlockBase::blockForm(). Most block plugins should not override this * method unless they need to alter the generic form elements. * * @see \Drupal\Core\Block\BlockBase::blockForm() */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $definition = $this->getPluginDefinition(); $form['provider'] = array('#type' => 'value', '#value' => $definition['provider']); $form['admin_label'] = array('#type' => 'item', '#title' => $this->t('Block description'), '#markup' => String::checkPlain($definition['admin_label'])); $form['label'] = array('#type' => 'textfield', '#title' => $this->t('Title'), '#maxlength' => 255, '#default_value' => $this->label(), '#required' => TRUE); $form['label_display'] = array('#type' => 'checkbox', '#title' => $this->t('Display title'), '#default_value' => $this->configuration['label_display'] === BlockInterface::BLOCK_LABEL_VISIBLE, '#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE); // Identical options to the ones for page caching. // @see \Drupal\system\Form\PerformanceForm::buildForm() $period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400); $period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($period, $period)); $period[0] = '<' . $this->t('no caching') . '>'; $period[\Drupal\Core\Cache\Cache::PERMANENT] = $this->t('Forever'); $form['cache'] = array('#type' => 'details', '#title' => $this->t('Cache settings')); $form['cache']['max_age'] = array('#type' => 'select', '#title' => $this->t('Maximum age'), '#description' => $this->t('The maximum time this block may be cached.'), '#default_value' => $this->configuration['cache']['max_age'], '#options' => $period); $contexts = \Drupal::service("cache_contexts")->getLabels(); // Blocks are always rendered in the "per language" and "per theme" cache // contexts. No need to show those options to the end user. unset($contexts['languages']); unset($contexts['theme']); $form['cache']['contexts'] = array('#type' => 'checkboxes', '#title' => $this->t('Vary by context'), '#description' => $this->t('The contexts this cached block must be varied by. <em>All</em> blocks are varied by language and theme.'), '#default_value' => $this->configuration['cache']['contexts'], '#options' => $contexts, '#states' => array('disabled' => array(':input[name="settings[cache][max_age]"]' => array('value' => (string) 0)))); if (count($this->getRequiredCacheContexts()) > 0) { // Remove the required cache contexts from the list of contexts a user can // choose to modify by: they must always be applied. $context_labels = array(); $all_contexts = \Drupal::service("cache_contexts")->getLabels(TRUE); foreach (CacheContexts::parseTokens($this->getRequiredCacheContexts()) as $context) { $context_id = $context[0]; $context_labels[] = $all_contexts[$context_id]; unset($form['cache']['contexts']['#options'][$context_id]); } $required_context_list = implode(', ', $context_labels); $form['cache']['contexts']['#description'] .= ' ' . $this->t('This block is <em>always</em> varied by the following contexts: %required-context-list.', array('%required-context-list' => $required_context_list)); } // Add plugin-specific settings for this block type. $form += $this->blockForm($form, $form_state); return $form; }