core/includes/common.inc | 8 +- core/lib/Drupal/Core/Cache/CacheableHelper.php | 89 ++++++++++++++ core/modules/block/block.module | 92 ++------------- core/modules/block/lib/Drupal/block/BlockBase.php | 56 ++++++++- .../lib/Drupal/block/BlockPluginInterface.php | 3 +- .../block/lib/Drupal/block/BlockViewBuilder.php | 97 +++++++++++++--- .../block/lib/Drupal/block/Entity/Block.php | 27 +++++ .../Drupal/block/Plugin/views/display/Block.php | 46 -------- .../lib/Drupal/block/Tests/BlockCacheTest.php | 12 +- .../lib/Drupal/block/Tests/BlockInterfaceTest.php | 12 +- .../Drupal/block/Tests/BlockRenderOrderTest.php | 7 +- .../Drupal/block/Tests/BlockStorageUnitTest.php | 4 +- .../block/lib/Drupal/block/Tests/BlockTest.php | 122 ++++++++++++++++---- .../Drupal/block/Tests/Views/DisplayBlockTest.php | 19 +-- .../tests/Drupal/block/Tests/BlockBaseTest.php | 5 - .../block_test/config/block.block.test_block.yml | 1 - .../block_test/Plugin/Block/TestCacheBlock.php | 12 +- .../block_test/Plugin/Block/TestXSSTitleBlock.php | 12 -- .../book/Plugin/Block/BookNavigationBlock.php | 9 +- core/modules/forum/forum.module | 17 --- .../forum/Plugin/Block/ActiveTopicsBlock.php | 8 +- .../Drupal/forum/Plugin/Block/ForumBlockBase.php | 34 +++++- .../Drupal/forum/Plugin/Block/NewTopicsBlock.php | 9 +- .../language/Plugin/Derivative/LanguageBlock.php | 1 - core/modules/menu/menu.module | 2 +- .../lib/Drupal/simpletest/WebTestBase.php | 4 + .../Drupal/system/Plugin/Block/SystemHelpBlock.php | 20 ++++ .../Drupal/system/Plugin/Block/SystemMainBlock.php | 20 ++++ .../Drupal/system/Plugin/Block/SystemMenuBlock.php | 8 +- .../system/Plugin/Derivative/SystemMenuBlock.php | 1 - core/modules/system/system.module | 24 ++++ .../lib/Drupal/views/Plugin/Block/ViewsBlock.php | 28 ++++- .../Drupal/views/Plugin/Derivative/ViewsBlock.php | 1 - .../Plugin/Derivative/ViewsExposedFilterBlock.php | 1 - .../Plugin/views/display/DisplayPluginBase.php | 1 - .../views/Tests/Plugin/Block/ViewsBlockTest.php | 3 - .../minimal/config/block.block.stark_admin.yml | 1 - .../minimal/config/block.block.stark_login.yml | 1 - .../minimal/config/block.block.stark_tools.yml | 1 - .../config/block.block.bartik_breadcrumbs.yml | 1 - .../standard/config/block.block.bartik_content.yml | 1 - .../standard/config/block.block.bartik_footer.yml | 1 - .../standard/config/block.block.bartik_help.yml | 1 - .../standard/config/block.block.bartik_login.yml | 1 - .../standard/config/block.block.bartik_powered.yml | 1 - .../standard/config/block.block.bartik_search.yml | 1 - .../standard/config/block.block.bartik_tools.yml | 1 - .../config/block.block.seven_breadcrumbs.yml | 1 - .../standard/config/block.block.seven_content.yml | 1 - .../standard/config/block.block.seven_help.yml | 1 - .../standard/config/block.block.seven_login.yml | 1 - 51 files changed, 545 insertions(+), 285 deletions(-) diff --git a/core/includes/common.inc b/core/includes/common.inc index b97cc5e..6a17040 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Url; use Drupal\Component\Utility\Xss; use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheableHelper; use Drupal\Core\Language\Language; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; @@ -4482,9 +4483,14 @@ function drupal_render_cid_create($elements) { return $elements['#cache']['cid']; } elseif (isset($elements['#cache']['keys'])) { + // Add cache context keys when constants are used in the 'keys' parameter. + $cacheable_helper = new CacheableHelper; + $keys = $cacheable_helper->addCacheContextsToKeys($elements['#cache']['keys']); + $granularity = isset($elements['#cache']['granularity']) ? $elements['#cache']['granularity'] : NULL; // Merge in additional cache ID parts based provided by drupal_render_cid_parts(). - $cid_parts = array_merge($elements['#cache']['keys'], drupal_render_cid_parts($granularity)); + $cid_parts = array_merge($keys, drupal_render_cid_parts($granularity)); + return implode(':', $cid_parts); } return FALSE; diff --git a/core/lib/Drupal/Core/Cache/CacheableHelper.php b/core/lib/Drupal/Core/Cache/CacheableHelper.php new file mode 100644 index 0000000..be1f402 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/CacheableHelper.php @@ -0,0 +1,89 @@ +preExecute(); + $keys = array((string) $query, $query->getArguments()); + return hash('sha256', serialize($keys)); + } + + /** + * Converts cache contexts to string representations of the context. + * + * @param $keys + * An array of cache keys that may or may not contain cache contexts. + * @return array + * A copy of the input, with cache contexts converted. + */ + function addCacheContextsToKeys($keys) { + $keys_with_contexts = array(); + foreach ($keys as $key) { + $keys_with_contexts[] = $this->getContext($key) ?: $key; + } + return $keys_with_contexts; + } + + /** + * Provides the string representaton of a cache context. + * + * @todo Document this properly once the input arguments are decided on, + * assuming the reuse of existing cache constants is temporary. + * + * @return string + * The string representaton of a cache context. + */ + protected function getContext($context) { + switch ($context) { + case DRUPAL_CACHE_PER_PAGE: + // @todo: Make this use the request properly. + return $this->currentPath(); + case DRUPAL_CACHE_PER_USER: + return "u." . $this->currentUser()->id(); + case DRUPAL_CACHE_PER_ROLE: + return 'r.' . implode(',', $this->currentUser()->getRoles()); + default: + return FALSE; + } + } + + /** + * @return \Drupal\Core\Session\AccountInterface + * The current user. + */ + protected function currentUser() { + return \Drupal::currentUser(); + } + + /** + * @return string + * The current path. + */ + protected function currentPath() { + global $base_root; + return $base_root . request_uri(); + } + +} diff --git a/core/modules/block/block.module b/core/modules/block/block.module index ba915a9..f9d54cd 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -193,70 +193,13 @@ function block_page_build(&$page) { function block_get_blocks_by_region($region) { $build = array(); if ($list = block_list($region)) { - $build = _block_get_renderable_region($list); - } - return $build; -} - -/** - * Gets an array of blocks suitable for drupal_render(). - * - * @param $list - * A list of blocks such as that returned by block_list(). - * - * @return - * A renderable array. - */ -function _block_get_renderable_region($list = array()) { - $build = array(); - // Block caching is not compatible with node_access modules. We also - // preserve the submission of forms in blocks, by fetching from cache - // only if the request method is 'GET' (or 'HEAD'). User 1 being out of - // the regular 'roles define permissions' schema, it brings too many - // chances of having unwanted output get in the cache and later be served - // to other users. We therefore exclude user 1 from block caching. - $not_cacheable = \Drupal::currentUser()->id() == 1 || - count(\Drupal::moduleHandler()->getImplementations('node_grants')) || - !\Drupal::request()->isMethodSafe(); - - foreach ($list as $key => $block) { - $settings = $block->get('settings'); - if ($not_cacheable || in_array($settings['cache'], array(DRUPAL_NO_CACHE, DRUPAL_CACHE_CUSTOM))) { - // Non-cached blocks get built immediately. + foreach ($list as $key => $block) { if ($block->access()) { $build[$key] = entity_view($block, 'block'); } } - else { - $build[$key] = array( - '#block' => $block, - '#weight' => $block->get('weight'), - '#pre_render' => array('_block_get_renderable_block'), - '#cache' => array( - 'keys' => array($key, $settings['module']), - 'granularity' => $settings['cache'], - 'bin' => 'block', - 'tags' => array('content' => TRUE), - ), - ); - } - - // Add contextual links for this block; skip the main content block, since - // contextual links are basically output as tabs/local tasks already. Also - // skip the help block, since we assume that most users do not need or want - // to perform contextual actions on the help block, and the links needlessly - // draw attention on it. - if (isset($build[$key]) && !in_array($block->get('plugin'), array('system_help_block', 'system_main_block'))) { - $build[$key]['#contextual_links']['block'] = array( - 'route_parameters' => array('block' => $key), - ); - - // If there are any nested contextual links, move them to the top level. - if (isset($build[$key]['content']['#contextual_links'])) { - $build[$key]['#contextual_links'] += $build[$key]['content']['#contextual_links']; - unset($build[$key]['content']['#contextual_links']); - } - } + // block_list() already returned the blocks in sorted order. + $build['#sorted'] = TRUE; } return $build; } @@ -383,9 +326,7 @@ function block_list($region) { $blocks[$region] = array(); } - uasort($blocks[$region], function($first, $second) { - return $first->weight === $second->weight ? 0 : ($first->weight < $second->weight ? -1 : 1); - }); + uasort($blocks[$region], 'Drupal\block\Entity\Block::sort'); return $blocks[$region]; } @@ -406,26 +347,6 @@ function block_load($entity_id) { } /** - * Builds the content and label for a block. - * - * For cacheable blocks, this is called during #pre_render. - * - * @param $element - * A renderable array. - * - * @return - * A renderable array. - */ -function _block_get_renderable_block($element) { - $block = $element['#block']; - // Don't bother to build blocks that aren't accessible. - if ($element['#access'] = $block->access()) { - $element += entity_view($block, 'block'); - } - return $element; -} - -/** * Implements hook_rebuild(). */ function block_rebuild() { @@ -497,6 +418,11 @@ function template_preprocess_block(&$variables) { $variables['derivative_plugin_id'] = $variables['elements']['#derivative_plugin_id']; $variables['label'] = !empty($variables['configuration']['label_display']) ? $variables['configuration']['label'] : ''; $variables['content'] = $variables['elements']['content']; + // A block's label is configuration: it is static. Allow dynamic labels to be + // set in the render array. + if (isset($variables['elements']['content']['#block_label']) && !empty($variables['configuration']['label_display'])) { + $variables['label'] = $variables['elements']['content']['#block_label']; + } $variables['attributes']['class'][] = 'block'; $variables['attributes']['class'][] = drupal_html_class('block-' . $variables['configuration']['module']); diff --git a/core/modules/block/lib/Drupal/block/BlockBase.php b/core/modules/block/lib/Drupal/block/BlockBase.php index 48e469b..59ea159 100644 --- a/core/modules/block/lib/Drupal/block/BlockBase.php +++ b/core/modules/block/lib/Drupal/block/BlockBase.php @@ -11,6 +11,7 @@ use Drupal\block\BlockInterface; use Drupal\Component\Utility\Unicode; use Drupal\Core\Language\Language; +use Drupal\Core\Cache\CacheableInterface; use Drupal\Core\Session\AccountInterface; /** @@ -32,7 +33,9 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi 'label' => '', 'module' => $plugin_definition['module'], 'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE, - 'cache' => DRUPAL_NO_CACHE, + 'cache' => array( + 'max_age' => 0, + ), ); } @@ -108,7 +111,13 @@ public function buildConfigurationForm(array $form, array &$form_state) { '#default_value' => ($this->configuration['label_display'] === BlockInterface::BLOCK_LABEL_VISIBLE), '#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE, ); - + $form['cache']['max_age'] = array( + '#type' => 'select', + '#title' => t('Cache: Max age'), + '#description' => t('Choose how long this block may be cached. The default of 0 seconds means it will never be cached.'), + '#default_value' => $this->configuration['cache']['max_age'], + '#options' => drupal_map_assoc(array(0, 60, 300, 1800, 3600, 21600, 86400, 604800, 2592000, 31536000, 315360000), 'format_interval'), + ); // Add plugin-specific settings for this block type. $form += $this->blockForm($form, $form_state); return $form; @@ -152,6 +161,7 @@ public function submitConfigurationForm(array &$form, array &$form_state) { $this->configuration['label'] = $form_state['values']['label']; $this->configuration['label_display'] = $form_state['values']['label_display']; $this->configuration['module'] = $form_state['values']['module']; + $this->configuration['cache'] = $form_state['values']['cache']; $this->blockSubmit($form, $form_state); } } @@ -185,4 +195,46 @@ public function getMachineNameSuggestion() { return $transliterated; } + /** + * {@inheritdoc} + */ + public function getCacheKeys() { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getCacheTags() { + // If a block plugin's output changes, then it must be able to invalidate a + // cache tag that affects all instances of this block: across themes and + // across regions. + $block_plugin_cache_tag = str_replace(':', '__', $this->getPluginID()); + return array('block_plugin' => array($block_plugin_cache_tag)); + } + + /** + * {@inheritdoc} + */ + public function getCacheBin() { + return 'block'; + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + return (int)$this->configuration['cache']['max_age']; + } + + /** + * {@inheritdoc} + */ + public function isCacheable() { + // Similar to the page cache, a block is cacheable if it has a max age. + // Blocks that should never be cached can override this method to simply + // return FALSE. + return (bool)$this->configuration['cache']['max_age'] > 0; + } + } diff --git a/core/modules/block/lib/Drupal/block/BlockPluginInterface.php b/core/modules/block/lib/Drupal/block/BlockPluginInterface.php index 047efd9..4f6bdc2 100644 --- a/core/modules/block/lib/Drupal/block/BlockPluginInterface.php +++ b/core/modules/block/lib/Drupal/block/BlockPluginInterface.php @@ -7,6 +7,7 @@ namespace Drupal\block; +use Drupal\Core\Cache\CacheableInterface; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Core\Plugin\PluginFormInterface; @@ -20,7 +21,7 @@ * brif references to the important components that are not coupled to the * interface. */ -interface BlockPluginInterface extends ConfigurablePluginInterface, PluginFormInterface, PluginInspectionInterface { +interface BlockPluginInterface extends ConfigurablePluginInterface, PluginFormInterface, PluginInspectionInterface, CacheableInterface { /** * Indicates whether the block should be shown. diff --git a/core/modules/block/lib/Drupal/block/BlockViewBuilder.php b/core/modules/block/lib/Drupal/block/BlockViewBuilder.php index 73cabaa..2c04f93 100644 --- a/core/modules/block/lib/Drupal/block/BlockViewBuilder.php +++ b/core/modules/block/lib/Drupal/block/BlockViewBuilder.php @@ -7,6 +7,8 @@ namespace Drupal\block; +use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityViewBuilder; use Drupal\Core\Entity\EntityViewBuilderInterface; use Drupal\Core\Entity\EntityInterface; @@ -41,34 +43,97 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la $plugin_id = $plugin->getPluginId(); $base_id = $plugin->getBasePluginId(); $derivative_id = $plugin->getDerivativeId(); + $configuration = $plugin->getConfiguration(); - if ($content = $plugin->build()) { - $configuration = $plugin->getConfiguration(); - $build[$entity_id] = array( - '#theme' => 'block', - 'content' => $content, - '#configuration' => $configuration, - '#plugin_id' => $plugin_id, - '#base_plugin_id' => $base_id, - '#derivative_plugin_id' => $derivative_id, + $build[$entity_id] = array( + '#theme' => 'block', + '#weight' => $entity->get('weight'), + '#configuration' => $configuration, + '#plugin_id' => $plugin_id, + '#base_plugin_id' => $base_id, + '#derivative_plugin_id' => $derivative_id, + // All blocks get a "Configure block" contextual link. + '#contextual_links' => array( + 'block' => array( + 'route_parameters' => array('block' => $entity->id()), + ), + ), + // @todo Remove after fixing http://drupal.org/node/1989568. + '#block' => $entity, + ); + $build[$entity_id]['#configuration']['label'] = check_plain($configuration['label']); + + if ($plugin->isCacheable()) { + // Generic cache keys, with the block plugin's custom keys appended + // (usually cache context keys like DRUPAL_CACHE_PER_ROLE). + $default_cache_keys = array('entity_view', 'block', $entity->id(), $entity->langcode); + $default_cache_tags = array( + 'content' => TRUE, + 'block_view' => TRUE, + 'block' => array($entity->id()), + ); + $build[$entity_id] += array( + '#pre_render' => array( + array($this, 'buildBlock'), + ), + '#cache' => array( + 'keys' => array_merge($default_cache_keys, $plugin->getCacheKeys()), + 'bin' => $plugin->getCacheBin(), + 'tags' => NestedArray::mergeDeep($default_cache_tags, $plugin->getCacheTags()), + 'expire' => REQUEST_TIME + $plugin->getCacheMaxAge(), + ), ); - $build[$entity_id]['#configuration']['label'] = check_plain($configuration['label']); } else { - $build[$entity_id] = array(); + $build[$entity_id] = $this->buildBlock($build[$entity_id]); } + } + return $build; + } - $this->moduleHandler()->alter(array('block_view', "block_view_$base_id"), $build[$entity_id], $plugin); + /** + * #pre_render callback for building a block. + * + * Renders the content using the provided block plugin, and then: + * - if there is no content, aborts rendering, and makes sure the block won't + * be rendered. + * - if there is content, moves the contextual links from the block content to + * the block itself, and invokes the alter hooks. + */ + public function buildBlock($build) { + $plugin = $build['#block']->getPlugin(); + $content = $plugin->build(); - // @todo Remove after fixing http://drupal.org/node/1989568. - $build[$entity_id]['#block'] = $entity; + if (!empty($content)) { + $build['content'] = $content; + + // If there are any nested contextual links, move them to the top level. + if (isset($build['content']['#contextual_links'])) { + $build['#contextual_links'] += $build['content']['#contextual_links']; + unset($build['content']['#contextual_links']); + } + + $base_id = $plugin->getBasePluginId(); + $this->moduleHandler()->alter(array('block_view', "block_view_$base_id"), $build, $plugin); + + return $build; + } + else { + // Abort rendering. + return array('#access' => FALSE, '#printed' => TRUE); } - return $build; } /** * {@inheritdoc} */ - public function resetCache(array $ids = NULL) { } + public function resetCache(array $entities = NULL) { + if (isset($entities)) { + Cache::invalidateTags(array('block' => array_keys($entities))); + } + else { + Cache::invalidateTags(array('block_view' => TRUE)); + } + } } diff --git a/core/modules/block/lib/Drupal/block/Entity/Block.php b/core/modules/block/lib/Drupal/block/Entity/Block.php index dc20929..51436bf 100644 --- a/core/modules/block/lib/Drupal/block/Entity/Block.php +++ b/core/modules/block/lib/Drupal/block/Entity/Block.php @@ -7,6 +7,7 @@ namespace Drupal\block\Entity; +use Drupal\Core\Cache\Cache; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\block\BlockPluginBag; use Drupal\block\BlockInterface; @@ -159,6 +160,32 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { } /** + * {@inheritdoc} + */ + public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) { + parent::postSave($storage_controller, $update); + + if ($update) { + Cache::invalidateTags(array('block' => $this->id())); + } + // When placing a new block, invalidate all cache entries for this theme, + // since any page that uses this theme might be affected. + else { + // @todo Replace with theme cache tag: https://drupal.org/node/2185617 + Cache::invalidateTags(array('content' => TRUE)); + } + } + + /** + * {@inheritdoc} + */ + public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) { + parent::postDelete($storage_controller, $entities); + + Cache::invalidateTags(array('block' => array_keys($entities))); + } + + /** * Sorts active blocks by weight; sorts inactive blocks by name. */ public static function sort($a, $b) { diff --git a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php index 657b635..94bea95 100644 --- a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php +++ b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php @@ -46,7 +46,6 @@ protected function defineOptions() { $options['block_description'] = array('default' => '', 'translatable' => TRUE); $options['block_category'] = array('default' => 'Lists (Views)', 'translatable' => TRUE); - $options['block_caching'] = array('default' => DRUPAL_NO_CACHE); $options['block_hide_empty'] = array('default' => FALSE); $options['allow'] = array( @@ -131,13 +130,6 @@ public function optionsSummary(&$categories, &$options) { 'value' => empty($filtered_allow) ? t('None') : t('Items per page'), ); - $types = $this->blockCachingModes(); - $options['block_caching'] = array( - 'category' => 'other', - 'title' => t('Block caching'), - 'value' => $types[$this->getCacheType()], - ); - $options['block_hide_empty'] = array( 'category' => 'other', 'title' => t('Hide block if the view output is empty'), @@ -146,33 +138,6 @@ public function optionsSummary(&$categories, &$options) { } /** - * Provide a list of core's block caching modes. - */ - protected function blockCachingModes() { - return array( - DRUPAL_NO_CACHE => t('Do not cache'), - DRUPAL_CACHE_GLOBAL => t('Cache once for everything (global)'), - DRUPAL_CACHE_PER_PAGE => t('Per page'), - DRUPAL_CACHE_PER_ROLE => t('Per role'), - DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE => t('Per role per page'), - DRUPAL_CACHE_PER_USER => t('Per user'), - DRUPAL_CACHE_PER_USER | DRUPAL_CACHE_PER_PAGE => t('Per user per page'), - ); - } - - /** - * Provide a single method to figure caching type, keeping a sensible default - * for when it's unset. - */ - public function getCacheType() { - $cache_type = $this->getOption('block_caching'); - if (empty($cache_type)) { - $cache_type = DRUPAL_NO_CACHE; - } - return $cache_type; - } - - /** * Provide the default form for setting options. */ public function buildOptionsForm(&$form, &$form_state) { @@ -196,16 +161,6 @@ public function buildOptionsForm(&$form, &$form_state) { '#default_value' => $this->getOption('block_category'), ); break; - case 'block_caching': - $form['#title'] .= t('Block caching type'); - - $form['block_caching'] = array( - '#type' => 'radios', - '#description' => t("This sets the default status for Drupal's built-in block caching method; this requires that caching be turned on in block administration, and be careful because you have little control over when this cache is flushed."), - '#options' => $this->blockCachingModes(), - '#default_value' => $this->getCacheType(), - ); - break; case 'block_hide_empty': $form['#title'] .= t('Block empty settings'); @@ -251,7 +206,6 @@ public function submitOptionsForm(&$form, &$form_state) { switch ($form_state['section']) { case 'block_description': case 'block_category': - case 'block_caching': case 'allow': case 'block_hide_empty': $this->setOption($form_state['section'], $form_state['values'][$form_state['section']]); diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php index 6206dbb..bdc706c 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockCacheTest.php @@ -127,14 +127,16 @@ function testCacheGlobal() { } /** - * Test DRUPAL_NO_CACHE. + * Test non-cacheable block. */ function testNoCache() { - $this->setCacheMode(DRUPAL_NO_CACHE); + $this->block->getPlugin()->setConfigurationValue('cache', array('max_age' => 0)); + $this->block->save(); + $current_content = $this->randomName(); \Drupal::state()->set('block_test.content', $current_content); - // If DRUPAL_NO_CACHE has no effect, the next request would be cached. + // If max_age = 0 has no effect, the next request would be cached. $this->drupalGet(''); $this->assertText($current_content, 'Block content displays.'); @@ -198,7 +200,9 @@ function testCachePerPage() { * Private helper method to set the test block's cache mode. */ private function setCacheMode($cache_mode) { - $this->block->getPlugin()->setConfigurationValue('cache', $cache_mode); + $block = $this->block->getPlugin(); + $block->setConfigurationValue('cache', array('max_age' => 600)); + $block->setConfigurationValue('test_cache_contexts', array($cache_mode)); $this->block->save(); } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php index 3f22caa..e8b0f85 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php @@ -48,7 +48,9 @@ public function testBlockInterface() { 'display_message' => 'no message set', 'module' => 'block_test', 'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE, - 'cache' => DRUPAL_NO_CACHE, + 'cache' => array( + 'max_age' => 0, + ), ); // Initial configuration of the block at construction time. $display_block = $manager->createInstance('test_block_instantiation', $configuration); @@ -83,6 +85,14 @@ public function testBlockInterface() { '#default_value' => TRUE, '#return_value' => 'visible', ), + 'cache' => array( + 'max_age' => array( + '#type' => 'select', + '#title' => t('Cache: Max age'), + '#default_value' => 0, + '#options' => drupal_map_assoc(array(0, 60, 300, 1800, 3600, 21600, 86400, 604800, 2592000, 31536000, 315360000), 'format_interval'), + ), + ), 'display_message' => array( '#type' => 'textfield', '#title' => t('Display message'), diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockRenderOrderTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockRenderOrderTest.php index da6202e..dbbd614 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockRenderOrderTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockRenderOrderTest.php @@ -48,21 +48,24 @@ function testBlockRenderOrder() { 'stark_powered' => array( 'weight' => '-3', 'id' => 'stark_powered', + 'label' => 'Test block A', ), 'stark_by' => array( 'weight' => '3', 'id' => 'stark_by', + 'label' => 'Test block C', ), 'stark_drupal' => array( 'weight' => '3', 'id' => 'stark_drupal', + 'label' => 'Test block B', ), ); // Place the test blocks. foreach ($test_blocks as $test_block) { $this->drupalPlaceBlock('system_powered_by_block', array( - 'label' => 'Test Block', + 'label' => $test_block['label'], 'region' => $region, 'weight' => $test_block['weight'], 'id' => $test_block['id'], @@ -81,6 +84,6 @@ function testBlockRenderOrder() { } } $this->assertTrue($position['stark_powered'] < $position['stark_by'], 'Blocks with different weight are rendered in the correct order.'); - $this->assertTrue($position['stark_drupal'] < $position['stark_by'], 'Blocks with identical weight are rendered in reverse alphabetical order.'); + $this->assertTrue($position['stark_drupal'] < $position['stark_by'], 'Blocks with identical weight are rendered in alphabetical order.'); } } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php index e88f0ba..9188d17 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php @@ -99,10 +99,12 @@ protected function createTests() { 'region' => '-1', 'plugin' => 'test_html_id', 'settings' => array( - 'cache' => 1, 'label' => '', 'module' => 'block_test', 'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE, + 'cache' => array( + 'max_age' => 0, + ), ), 'visibility' => NULL, ); diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php index af49957..fc061cf 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php @@ -7,6 +7,7 @@ namespace Drupal\block\Tests; +use Drupal\Core\Cache\Cache; use Drupal\simpletest\WebTestBase; /** @@ -247,35 +248,106 @@ function moveBlockToRegion(array $block, $region) { } /** - * Test _block_rehash(). + * Test that cache tags are properly set and bubbled up to the page cache. + * + * Verify that invalidation of these cache tags works: + * - "block:" + * - "block_plugin:" */ - function testBlockRehash() { - \Drupal::moduleHandler()->install(array('block_test')); - $this->assertTrue(module_exists('block_test'), 'Test block module enabled.'); - - // Clear the block cache to load the block_test module's block definitions. - $this->container->get('plugin.manager.block')->clearCachedDefinitions(); - - // Add a test block. - $block = array(); - $block['id'] = 'test_cache'; - $block['theme'] = \Drupal::config('system.theme')->get('default'); - $block['region'] = 'header'; - $block = $this->drupalPlaceBlock('test_cache', array('region' => 'header')); + public function testBlockCacheTags() { + // The page cache only works for anonymous users. + $this->drupalLogout(); - // Our test block's caching should default to DRUPAL_CACHE_PER_ROLE. - $settings = $block->get('settings'); - $this->assertEqual($settings['cache'], DRUPAL_CACHE_PER_ROLE, 'Test block cache mode defaults to DRUPAL_CACHE_PER_ROLE.'); + // Enable page caching. + $config = \Drupal::config('system.performance'); + $config->set('cache.page.use_internal', 1); + $config->set('cache.page.max_age', 300); + $config->save(); + + // Place the "Powered by Drupal" block. + $block = $this->drupalPlaceBlock('system_powered_by_block', array('id' => 'powered', 'cache' => array('max_age' => 315360000))); + + // Prime the page cache. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + + // Verify a cache hit, but also the presence of the correct cache tags in + // both the page and block caches. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + $cid_parts = array(url('', array('absolute' => TRUE)), 'html'); + $cid = sha1(implode(':', $cid_parts)); + $cache_entry = \Drupal::cache('page')->get($cid); + $expected_cache_tags = array( + 'content:1', + 'block_view:1', + 'block:powered', + 'block_plugin:system_powered_by_block', + ); + $this->assertIdentical($cache_entry->tags, $expected_cache_tags); + $cache_entry = \Drupal::cache('block')->get('entity_view:block:powered:en:stark'); + $this->assertIdentical($cache_entry->tags, $expected_cache_tags); - // Disable caching for this block. - $block->getPlugin()->setConfigurationValue('cache', DRUPAL_NO_CACHE); + // The "Powered by Drupal" block is modified; verify a cache miss. + $block->set('region', 'content'); $block->save(); - // Flushing all caches should call _block_rehash(). - $this->resetAll(); - // Verify that block is updated with the new caching mode. - $block = entity_load('block', $block->id()); - $settings = $block->get('settings'); - $this->assertEqual($settings['cache'], DRUPAL_NO_CACHE, "Test block's database entry updated to DRUPAL_NO_CACHE."); + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + + // Now we should have a cache hit again. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + + // Place the "Powered by Drupal" block another time; verify a cache miss. + $block_2 = $this->drupalPlaceBlock('system_powered_by_block', array('id' => 'powered-2', 'cache' => array('max_age' => 315360000))); + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + + // Verify a cache hit, but also the presence of the correct cache tags. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + $cid_parts = array(url('', array('absolute' => TRUE)), 'html'); + $cid = sha1(implode(':', $cid_parts)); + $cache_entry = \Drupal::cache('page')->get($cid); + $expected_cache_tags = array( + 'content:1', + 'block_view:1', + 'block:powered-2', + 'block:powered', + 'block_plugin:system_powered_by_block', + ); + $this->assertEqual($cache_entry->tags, $expected_cache_tags); + $expected_cache_tags = array( + 'content:1', + 'block_view:1', + 'block:powered', + 'block_plugin:system_powered_by_block', + ); + $cache_entry = \Drupal::cache('block')->get('entity_view:block:powered:en:stark'); + $this->assertIdentical($cache_entry->tags, $expected_cache_tags); + $expected_cache_tags = array( + 'content:1', + 'block_view:1', + 'block:powered-2', + 'block_plugin:system_powered_by_block', + ); + $cache_entry = \Drupal::cache('block')->get('entity_view:block:powered-2:en:stark'); + $this->assertIdentical($cache_entry->tags, $expected_cache_tags); + + // The plugin providing the "Powered by Drupal" block is modified; verify a + // cache miss. + Cache::invalidateTags(array('block_plugin:system_powered_by_block')); + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + + // Now we should have a cache hit again. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + + // Delete the "Powered by Drupal" blocks; verify a cache miss. + entity_delete_multiple('block', array('powered', 'powered-2')); + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); } } diff --git a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php index b86a1ea..00fb33c 100644 --- a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php @@ -175,19 +175,6 @@ protected function testDeleteBlockDisplay() { } /** - * Tests views block plugin definitions. - */ - public function testViewsBlockPlugins() { - // Ensures that the cache setting gets to the block settings. - $instance = $this->container->get('plugin.manager.block')->createInstance('views_block:test_view_block2-block_2'); - $configuration = $instance->getConfiguration(); - $this->assertEqual($configuration['cache'], DRUPAL_NO_CACHE); - $instance = $this->container->get('plugin.manager.block')->createInstance('views_block:test_view_block2-block_3'); - $configuration = $instance->getConfiguration(); - $this->assertEqual($configuration['cache'], DRUPAL_CACHE_PER_USER); - } - - /** * Test the block form for a Views block. */ public function testViewsBlockForm() { @@ -281,19 +268,23 @@ public function testBlockRendering() { public function testBlockContextualLinks() { $this->drupalLogin($this->drupalCreateUser(array('administer views', 'access contextual links', 'administer blocks'))); $block = $this->drupalPlaceBlock('views_block:test_view_block-block_1'); + $cached_block = $this->drupalPlaceBlock('views_block:test_view_block-block_1', array('cache' => array('max_age' => 3600))); $this->drupalGet('test-page'); $id = 'block:block=' . $block->id() . ':|views_ui_edit:view=test_view_block:location=block&name=test_view_block&display_id=block_1'; + $cached_id = 'block:block=' . $cached_block->id() . ':|views_ui_edit:view=test_view_block:location=block&name=test_view_block&display_id=block_1'; // @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder() $this->assertRaw(' $id)) . '>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id))); + $this->assertRaw(' $cached_id)) . '>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $cached_id))); // Get server-rendered contextual links. // @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks() - $post = array('ids[0]' => $id); + $post = array('ids[0]' => $id, 'ids[1]' => $cached_id); $response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page'))); $this->assertResponse(200); $json = drupal_json_decode($response); $this->assertIdentical($json[$id], ''); + $this->assertIdentical($json[$cached_id], ''); } } diff --git a/core/modules/block/tests/Drupal/block/Tests/BlockBaseTest.php b/core/modules/block/tests/Drupal/block/Tests/BlockBaseTest.php index f0d04ec..8d96c33 100644 --- a/core/modules/block/tests/Drupal/block/Tests/BlockBaseTest.php +++ b/core/modules/block/tests/Drupal/block/Tests/BlockBaseTest.php @@ -12,11 +12,6 @@ use Drupal\Core\Transliteration\PHPTransliteration; use Drupal\Tests\UnitTestCase; -// @todo Remove once the constants are replaced with constants on classes. -if (!defined('DRUPAL_NO_CACHE')) { - define('DRUPAL_NO_CACHE', -1); -} - /** * Tests the base block plugin. * diff --git a/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml b/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml index bb65174..7f929c3 100644 --- a/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml +++ b/core/modules/block/tests/modules/block_test/config/block.block.test_block.yml @@ -9,7 +9,6 @@ settings: label: 'Test block html id' module: block_test label_display: 'hidden' - cache: 1 visibility: path: visibility: 0 diff --git a/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestCacheBlock.php b/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestCacheBlock.php index 2fa6122..ad8dab1 100644 --- a/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestCacheBlock.php +++ b/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestCacheBlock.php @@ -21,22 +21,18 @@ class TestCacheBlock extends BlockBase { /** * {@inheritdoc} - * - * Sets a different caching strategy for testing purposes. */ - public function defaultConfiguration() { + public function build() { return array( - 'cache' => DRUPAL_CACHE_PER_ROLE, + '#markup' => \Drupal::state()->get('block_test.content'), ); } /** * {@inheritdoc} */ - public function build() { - return array( - '#children' => \Drupal::state()->get('block_test.content'), - ); + public function getCacheKeys() { + return $this->configuration['test_cache_contexts']; } } diff --git a/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestXSSTitleBlock.php b/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestXSSTitleBlock.php index 156abf9..3146b59 100644 --- a/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestXSSTitleBlock.php +++ b/core/modules/block/tests/modules/block_test/lib/Drupal/block_test/Plugin/Block/TestXSSTitleBlock.php @@ -16,16 +16,4 @@ * ) */ class TestXSSTitleBlock extends TestCacheBlock { - - /** - * {@inheritdoc} - * - * Sets a different caching strategy for testing purposes. - */ - public function defaultConfiguration() { - return array( - 'cache' => DRUPAL_NO_CACHE, - ); - } - } diff --git a/core/modules/book/lib/Drupal/book/Plugin/Block/BookNavigationBlock.php b/core/modules/book/lib/Drupal/book/Plugin/Block/BookNavigationBlock.php index 3fd3f64..1a28758 100644 --- a/core/modules/book/lib/Drupal/book/Plugin/Block/BookNavigationBlock.php +++ b/core/modules/book/lib/Drupal/book/Plugin/Block/BookNavigationBlock.php @@ -25,7 +25,6 @@ class BookNavigationBlock extends BlockBase { */ public function defaultConfiguration() { return array( - 'cache' => DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE, 'block_mode' => "all pages", ); } @@ -61,6 +60,7 @@ public function blockSubmit($form, &$form_state) { */ public function build() { $current_bid = 0; + if ($node = menu_get_object()) { $current_bid = empty($node->book['bid']) ? 0 : $node->book['bid']; } @@ -116,4 +116,11 @@ public function build() { return array(); } + /** + * {@inheritdoc} + */ + public function getCacheKeys() { + return array(DRUPAL_CACHE_PER_PAGE, DRUPAL_CACHE_PER_ROLE); + } + } diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 2fb5036..df822d0 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -575,23 +575,6 @@ function forum_form_node_form_alter(&$form, &$form_state, $form_id) { } /** - * Render API callback: Lists nodes based on the element's #query property. - * - * This function can be used as a #pre_render callback. - * - * @see \Drupal\forum\Plugin\block\block\NewTopicsBlock::build() - * @see \Drupal\forum\Plugin\block\block\ActiveTopicsBlock::build() - */ -function forum_block_view_pre_render($elements) { - $result = $elements['#query']->execute(); - if ($node_title_list = node_title_list($result)) { - $elements['forum_list'] = $node_title_list; - $elements['forum_more'] = array('#theme' => 'more_link', '#url' => 'forum', '#title' => t('Read the latest forum topics.')); - } - return $elements; -} - -/** * Implements hook_preprocess_HOOK() for block templates. */ function forum_preprocess_block(&$variables) { diff --git a/core/modules/forum/lib/Drupal/forum/Plugin/Block/ActiveTopicsBlock.php b/core/modules/forum/lib/Drupal/forum/Plugin/Block/ActiveTopicsBlock.php index d83f736..133529a 100644 --- a/core/modules/forum/lib/Drupal/forum/Plugin/Block/ActiveTopicsBlock.php +++ b/core/modules/forum/lib/Drupal/forum/Plugin/Block/ActiveTopicsBlock.php @@ -21,17 +21,13 @@ class ActiveTopicsBlock extends ForumBlockBase { /** * {@inheritdoc} */ - public function build() { - $query = db_select('forum_index', 'f') + protected function buildForumQuery() { + return db_select('forum_index', 'f') ->fields('f') ->addTag('node_access') ->addMetaData('base_table', 'forum_index') ->orderBy('f.last_comment_timestamp', 'DESC') ->range(0, $this->configuration['block_count']); - - return array( - drupal_render_cache_by_query($query, 'forum_block_view'), - ); } } diff --git a/core/modules/forum/lib/Drupal/forum/Plugin/Block/ForumBlockBase.php b/core/modules/forum/lib/Drupal/forum/Plugin/Block/ForumBlockBase.php index c2ad994..26ed640 100644 --- a/core/modules/forum/lib/Drupal/forum/Plugin/Block/ForumBlockBase.php +++ b/core/modules/forum/lib/Drupal/forum/Plugin/Block/ForumBlockBase.php @@ -9,6 +9,7 @@ use Drupal\block\BlockBase; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Cache\CacheableHelper; /** * Provides a base class for Forum blocks. @@ -18,9 +19,32 @@ /** * {@inheritdoc} */ + public function build() { + $result = $this->buildForumQuery()->execute(); + if ($node_title_list = node_title_list($result)) { + $elements['forum_list'] = $node_title_list; + $elements['forum_more'] = array( + '#theme' => 'more_link', + '#url' => 'forum', + '#title' => t('Read the latest forum topics.') + ); + } + return $elements; + } + + /** + * Builds the select query to use for this forum block. + * + * @return \Drupal\Core\Database\Query\Select + * A Select object. + */ + abstract protected function buildForumQuery(); + + /** + * {@inheritdoc} + */ public function defaultConfiguration() { return array( - 'cache' => DRUPAL_CACHE_CUSTOM, 'properties' => array( 'administrative' => TRUE, ), @@ -55,4 +79,12 @@ public function blockSubmit($form, &$form_state) { $this->configuration['block_count'] = $form_state['values']['block_count']; } + /** + * {@inheritdoc} + */ + public function getCacheKeys() { + $cacheable_helper = new CacheableHelper; + return array($this->cacheKeyFromQuery($this->buildForumQuery())); + } + } diff --git a/core/modules/forum/lib/Drupal/forum/Plugin/Block/NewTopicsBlock.php b/core/modules/forum/lib/Drupal/forum/Plugin/Block/NewTopicsBlock.php index 8b75a83..635dab4 100644 --- a/core/modules/forum/lib/Drupal/forum/Plugin/Block/NewTopicsBlock.php +++ b/core/modules/forum/lib/Drupal/forum/Plugin/Block/NewTopicsBlock.php @@ -21,17 +21,12 @@ class NewTopicsBlock extends ForumBlockBase { /** * {@inheritdoc} */ - public function build() { - $query = db_select('forum_index', 'f') + protected function buildForumQuery() { + return db_select('forum_index', 'f') ->fields('f') ->addTag('node_access') ->addMetaData('base_table', 'forum_index') ->orderBy('f.created', 'DESC') ->range(0, $this->configuration['block_count']); - - return array( - drupal_render_cache_by_query($query, 'forum_block_view'), - ); } - } diff --git a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php index acf276f..0de6dcf 100644 --- a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php +++ b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php @@ -27,7 +27,6 @@ public function getDerivativeDefinitions(array $base_plugin_definition) { foreach ($configurable_types as $type) { $this->derivatives[$type] = $base_plugin_definition; $this->derivatives[$type]['admin_label'] = t('Language switcher (!type)', array('!type' => $info[$type]['name'])); - $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE; } // If there is just one configurable type then change the title of the // block. diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module index 64a171b..e849a3a 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -335,7 +335,7 @@ function menu_block_view_system_menu_block_alter(array &$build, BlockPluginInter $menu_name = $block->getDerivativeId(); if (isset($menus[$menu_name]) && isset($build['content'])) { foreach (element_children($build['content']) as $key) { - $build['content']['#contextual_links']['menu'] = array( + $build['#contextual_links']['menu'] = array( 'route_parameters' => array('menu' => $build['content'][$key]['#original_link']['menu_name']), ); } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index fff0ace..744fc5f 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -357,6 +357,7 @@ protected function drupalCreateContentType(array $values = array()) { * - region: 'sidebar_first'. * - theme: The default theme. * - visibility: Empty array. + * - cache: array('max_age' => 0). * * @return \Drupal\block\Entity\Block * The block entity. @@ -373,6 +374,9 @@ protected function drupalPlaceBlock($plugin_id, array $settings = array()) { 'label' => $this->randomName(8), 'visibility' => array(), 'weight' => 0, + 'cache' => array( + 'max_age' => 0, + ), ); foreach (array('region', 'id', 'theme', 'plugin', 'visibility', 'weight') as $key) { $values[$key] = $settings[$key]; diff --git a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php index f669e61..9f1e73b 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php +++ b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php @@ -117,4 +117,24 @@ public function build() { ); } + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, array &$form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + // The help block is never cacheable. + $form['cache']['#access'] = FALSE; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function isCacheable() { + // The help block is never cacheable. + return FALSE; + } + } diff --git a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMainBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMainBlock.php index a64951a..56b22b0 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMainBlock.php +++ b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMainBlock.php @@ -28,4 +28,24 @@ public function build() { ); } + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, array &$form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + // The main content block is never cacheable. + $form['cache']['#access'] = FALSE; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function isCacheable() { + // The main content block is never cacheable. + return FALSE; + } + } diff --git a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMenuBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMenuBlock.php index 8298193..f44e533 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMenuBlock.php +++ b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemMenuBlock.php @@ -8,7 +8,6 @@ namespace Drupal\system\Plugin\Block; use Drupal\block\BlockBase; -use Drupal\Core\Session\AccountInterface; /** * Provides a generic Menu block. @@ -30,4 +29,11 @@ public function build() { return menu_tree($menu); } + /** + * {@inheritdoc} + */ + public function getCacheKeys() { + return array(DRUPAL_CACHE_PER_PAGE, DRUPAL_CACHE_PER_ROLE); + } + } diff --git a/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php index 5542808..0c1d37f 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php +++ b/core/modules/system/lib/Drupal/system/Plugin/Derivative/SystemMenuBlock.php @@ -52,7 +52,6 @@ public function getDerivativeDefinitions(array $base_plugin_definition) { foreach ($this->menuStorage->loadMultiple() as $menu => $entity) { $this->derivatives[$menu] = $base_plugin_definition; $this->derivatives[$menu]['admin_label'] = $entity->label(); - $this->derivatives[$menu]['cache'] = DRUPAL_NO_CACHE; } return $this->derivatives; } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index cc647a2..bc10db2 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -9,6 +9,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Language\Language; use Drupal\Core\Utility\ModuleInfo; +use Drupal\block\BlockPluginInterface; use Drupal\menu_link\MenuLinkInterface; use Drupal\user\UserInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -3173,3 +3174,26 @@ function system_admin_paths() { ); return $paths; } + +/** + * Implements hook_block_view_BASE_BLOCK_ID_alter(). + */ +function system_block_view_system_main_block_alter(array &$build, BlockPluginInterface $block) { + // Remove contextual links on the main content block, since contextual links + // are basically output as tabs/local tasks already. + if (isset($build['#contextual_links'])) { + unset($build['#contextual_links']); + } +} + +/** + * Implements hook_block_view_BASE_BLOCK_ID_alter(). + */ +function system_block_view_system_help_block_alter(array &$build, BlockPluginInterface $block) { + // Remove contextual links on the help block, since we assume that most users + // don't need or want to perform contextual actions on the help block, and the + // links needlessly draw atention to it. + if (isset($build['#contextual_links'])) { + unset($build['#contextual_links']); + } +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php index 482c25f..7b32708 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php @@ -29,13 +29,11 @@ public function build() { $this->view->display_handler->preBlockBuild($this); if ($output = $this->view->executeDisplay($this->displayID)) { - // Set the label to the title configured in the view. - if (empty($this->configuration['views_label'])) { - $this->configuration['label'] = Xss::filterAdmin($this->view->getTitle()); - } - else { - $this->configuration['label'] = $this->configuration['views_label']; + // Override the label to the dynamic title configured in the view. + if (empty($this->configuration['views_label']) && $this->view->getTitle()) { + $output['#block_label'] = Xss::filterAdmin($this->view->getTitle()); } + // Before returning the block output, convert it to a renderable array // with contextual links. $this->addContextualLinks($output); @@ -48,6 +46,20 @@ public function build() { /** * {@inheritdoc} */ + public function getConfiguration() { + $configuration = parent::getConfiguration(); + + // Set the label to the static title configured in the view. + if (!empty($configuration['views_label'])) { + $configuration['label'] = $configuration['views_label']; + } + + return $configuration; + } + + /** + * {@inheritdoc} + */ public function defaultConfiguration() { $settings = parent::defaultConfiguration(); @@ -101,4 +113,8 @@ public function getMachineNameSuggestion() { return 'views_block__' . $this->view->storage->id() . '_' . $this->view->current_display; } + public function cacheKeys() { + return array($this->getMachineNameSuggestion()); + } + } diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php index 30d242a..c8ba106 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsBlock.php @@ -102,7 +102,6 @@ public function getDerivativeDefinitions(array $base_plugin_definition) { $this->derivatives[$delta] = array( 'category' => $display->getOption('block_category'), 'admin_label' => $desc, - 'cache' => $display->getCacheType() ); $this->derivatives[$delta] += $base_plugin_definition; } diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsExposedFilterBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsExposedFilterBlock.php index 96bdd4c..a85efa3 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsExposedFilterBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsExposedFilterBlock.php @@ -93,7 +93,6 @@ public function getDerivativeDefinitions(array $base_plugin_definition) { $desc = t('Exposed form: @view-@display_id', array('@view' => $view->id(), '@display_id' => $display->display['id'])); $this->derivatives[$delta] = array( 'admin_label' => $desc, - 'cache' => DRUPAL_NO_CACHE, ); $this->derivatives[$delta] += $base_plugin_definition; } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php index e371f7d..ec98caf 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php @@ -2653,7 +2653,6 @@ public function getSpecialBlocks() { $blocks[$delta] = array( 'info' => $desc, - 'cache' => DRUPAL_NO_CACHE, ); } diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php index 15d4d6e..d0a4d37 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php @@ -15,9 +15,6 @@ if (!defined('BLOCK_LABEL_VISIBLE')) { define('BLOCK_LABEL_VISIBLE', 'visible'); } -if (!defined('DRUPAL_NO_CACHE')) { - define('DRUPAL_NO_CACHE', -1); -} /** * Tests the views block plugin. diff --git a/core/profiles/minimal/config/block.block.stark_admin.yml b/core/profiles/minimal/config/block.block.stark_admin.yml index aeded26..46c0d48 100644 --- a/core/profiles/minimal/config/block.block.stark_admin.yml +++ b/core/profiles/minimal/config/block.block.stark_admin.yml @@ -9,7 +9,6 @@ settings: label: Administration module: system label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/minimal/config/block.block.stark_login.yml b/core/profiles/minimal/config/block.block.stark_login.yml index 7ca1d45..3bba541 100644 --- a/core/profiles/minimal/config/block.block.stark_login.yml +++ b/core/profiles/minimal/config/block.block.stark_login.yml @@ -9,7 +9,6 @@ settings: label: 'User login' module: user label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/minimal/config/block.block.stark_tools.yml b/core/profiles/minimal/config/block.block.stark_tools.yml index 0d1fb7b..bfbb8b0 100644 --- a/core/profiles/minimal/config/block.block.stark_tools.yml +++ b/core/profiles/minimal/config/block.block.stark_tools.yml @@ -9,7 +9,6 @@ settings: label: Tools module: system label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml b/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml index 21d410d..af4921c 100644 --- a/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml +++ b/core/profiles/standard/config/block.block.bartik_breadcrumbs.yml @@ -10,7 +10,6 @@ settings: label: Breadcrumbs module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_content.yml b/core/profiles/standard/config/block.block.bartik_content.yml index 0098966..3c0bb56 100644 --- a/core/profiles/standard/config/block.block.bartik_content.yml +++ b/core/profiles/standard/config/block.block.bartik_content.yml @@ -10,7 +10,6 @@ settings: label: 'Main page content' module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_footer.yml b/core/profiles/standard/config/block.block.bartik_footer.yml index 7578cb6..cd2b779 100644 --- a/core/profiles/standard/config/block.block.bartik_footer.yml +++ b/core/profiles/standard/config/block.block.bartik_footer.yml @@ -10,7 +10,6 @@ settings: label: 'Footer menu' module: system label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_help.yml b/core/profiles/standard/config/block.block.bartik_help.yml index a79e8b9..3889578 100644 --- a/core/profiles/standard/config/block.block.bartik_help.yml +++ b/core/profiles/standard/config/block.block.bartik_help.yml @@ -10,7 +10,6 @@ settings: label: 'System Help' module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_login.yml b/core/profiles/standard/config/block.block.bartik_login.yml index 47aa159..cad6514 100644 --- a/core/profiles/standard/config/block.block.bartik_login.yml +++ b/core/profiles/standard/config/block.block.bartik_login.yml @@ -10,7 +10,6 @@ settings: label: 'User login' module: user label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_powered.yml b/core/profiles/standard/config/block.block.bartik_powered.yml index 24754e7..0970c81 100644 --- a/core/profiles/standard/config/block.block.bartik_powered.yml +++ b/core/profiles/standard/config/block.block.bartik_powered.yml @@ -10,7 +10,6 @@ settings: label: 'Powered by Drupal' module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_search.yml b/core/profiles/standard/config/block.block.bartik_search.yml index c1a6942..6860fc3 100644 --- a/core/profiles/standard/config/block.block.bartik_search.yml +++ b/core/profiles/standard/config/block.block.bartik_search.yml @@ -10,7 +10,6 @@ settings: label: Search module: search label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.bartik_tools.yml b/core/profiles/standard/config/block.block.bartik_tools.yml index 5850d61..917c1e3 100644 --- a/core/profiles/standard/config/block.block.bartik_tools.yml +++ b/core/profiles/standard/config/block.block.bartik_tools.yml @@ -10,7 +10,6 @@ settings: label: Tools module: system label_display: visible - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.seven_breadcrumbs.yml b/core/profiles/standard/config/block.block.seven_breadcrumbs.yml index 092fa26..2fa13e7 100644 --- a/core/profiles/standard/config/block.block.seven_breadcrumbs.yml +++ b/core/profiles/standard/config/block.block.seven_breadcrumbs.yml @@ -10,7 +10,6 @@ settings: label: Breadcrumbs module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.seven_content.yml b/core/profiles/standard/config/block.block.seven_content.yml index e1591e5..6e23969 100644 --- a/core/profiles/standard/config/block.block.seven_content.yml +++ b/core/profiles/standard/config/block.block.seven_content.yml @@ -10,7 +10,6 @@ settings: label: 'Main page content' module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.seven_help.yml b/core/profiles/standard/config/block.block.seven_help.yml index 5e39acf..b79273a 100644 --- a/core/profiles/standard/config/block.block.seven_help.yml +++ b/core/profiles/standard/config/block.block.seven_help.yml @@ -10,7 +10,6 @@ settings: label: 'System Help' module: system label_display: '0' - cache: -1 visibility: path: visibility: 0 diff --git a/core/profiles/standard/config/block.block.seven_login.yml b/core/profiles/standard/config/block.block.seven_login.yml index f76a568..cceccaa 100644 --- a/core/profiles/standard/config/block.block.seven_login.yml +++ b/core/profiles/standard/config/block.block.seven_login.yml @@ -10,7 +10,6 @@ settings: label: 'User login' module: user label_display: visible - cache: -1 visibility: path: visibility: 0