Issue #2179083 by Wim Leers, vijaycs85: Rendered menus (e.g. menu blocks) should set cache tags to inform the page cache.
parent
9fc89d90d8
commit
4022a54878
|
@ -3572,6 +3572,20 @@ function drupal_prepare_page($page) {
|
|||
// 'sidebar_first', 'footer', etc.
|
||||
drupal_alter('page', $page);
|
||||
|
||||
// The "main" and "secondary" menus are never part of the page-level render
|
||||
// array and therefore their cache tags will never bubble up into the page
|
||||
// cache, even though they should be. This happens because they're rendered
|
||||
// directly by the theme system.
|
||||
// @todo Remove this once https://drupal.org/node/1869476 lands.
|
||||
if (theme_get_setting('features.main_menu') && count(menu_main_menu())) {
|
||||
$main_links_source = _menu_get_links_source('main_links', 'main');
|
||||
$page['page_top']['#cache']['tags']['menu'][$main_links_source] = $main_links_source;
|
||||
}
|
||||
if (theme_get_setting('features.secondary_menu') && count(menu_secondary_menu())) {
|
||||
$secondary_links_source = _menu_get_links_source('secondary_links', 'account');
|
||||
$page['page_top']['#cache']['tags']['menu'][$secondary_links_source] = $secondary_links_source;
|
||||
}
|
||||
|
||||
// If no module has taken care of the main content, add it to the page now.
|
||||
// This allows the site to still be usable even if no modules that
|
||||
// control page regions (for example, the Block module) are enabled.
|
||||
|
|
|
@ -1047,6 +1047,9 @@ function menu_tree_output($tree) {
|
|||
// Add the theme wrapper for outer markup.
|
||||
// Allow menu-specific theme overrides.
|
||||
$build['#theme_wrappers'][] = 'menu_tree__' . strtr($data['link']['menu_name'], '-', '_');
|
||||
// Set cache tag.
|
||||
$menu_name = $data['link']['menu_name'];
|
||||
$build['#cache']['tags']['menu'][$menu_name] = $menu_name;
|
||||
}
|
||||
|
||||
return $build;
|
||||
|
@ -1705,10 +1708,7 @@ function menu_list_system_menus() {
|
|||
* Returns an array of links to be rendered as the Main menu.
|
||||
*/
|
||||
function menu_main_menu() {
|
||||
$config = \Drupal::config('menu.settings');
|
||||
$menu_enabled = \Drupal::moduleHandler()->moduleExists('menu');
|
||||
// When menu module is not enabled, we need a hardcoded default value.
|
||||
$main_links_source = $menu_enabled ? $config->get('main_links') : 'main';
|
||||
$main_links_source = _menu_get_links_source('main_links', 'main');
|
||||
return menu_navigation_links($main_links_source);
|
||||
}
|
||||
|
||||
|
@ -1716,11 +1716,8 @@ function menu_main_menu() {
|
|||
* Returns an array of links to be rendered as the Secondary links.
|
||||
*/
|
||||
function menu_secondary_menu() {
|
||||
$config = \Drupal::config('menu.settings');
|
||||
$menu_enabled = \Drupal::moduleHandler()->moduleExists('menu');
|
||||
// When menu module is not enabled, we need a hardcoded default value.
|
||||
$main_links_source = $menu_enabled ? $config->get('main_links') : 'main';
|
||||
$secondary_links_source = $menu_enabled ? $config->get('secondary_links') : 'account';
|
||||
$main_links_source = _menu_get_links_source('main_links', 'main');
|
||||
$secondary_links_source = _menu_get_links_source('secondary_links', 'account');
|
||||
|
||||
// If the secondary menu source is set as the primary menu, we display the
|
||||
// second level of the primary menu.
|
||||
|
@ -1732,6 +1729,22 @@ function menu_secondary_menu() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source of links of a menu.
|
||||
*
|
||||
* @param string $name
|
||||
* A string configuration key of menu link source.
|
||||
* @param string $default
|
||||
* Default menu name.
|
||||
*
|
||||
* @return string
|
||||
* Returns menu name, if exist
|
||||
*/
|
||||
function _menu_get_links_source($name, $default) {
|
||||
$config = \Drupal::config('menu.settings');
|
||||
return \Drupal::moduleHandler()->moduleExists('menu') ? $config->get($name) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of links for a navigation menu.
|
||||
*
|
||||
|
@ -2350,15 +2363,6 @@ function menu_get_active_trail() {
|
|||
return menu_set_active_trail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cached cached data for a single named menu.
|
||||
*/
|
||||
function menu_cache_clear($menu_name = 'tools') {
|
||||
Cache::deleteTags(array('menu' => $menu_name));
|
||||
// Also clear the menu system static caches.
|
||||
menu_reset_static_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached menu data.
|
||||
*
|
||||
|
|
|
@ -478,6 +478,66 @@ class MenuTest extends MenuWebTestBase {
|
|||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="menu-edit"><a href="' . base_path() . 'admin/structure/menu/manage/tools">Edit menu</a></li></ul>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cache tags are properly set and bubbled up to the page cache.
|
||||
*
|
||||
* Ensures that invalidation of the "menu:<menu name>" cache tags works.
|
||||
*/
|
||||
public function testMenuBlockPageCacheTags() {
|
||||
// Enable page caching.
|
||||
$config = \Drupal::config('system.performance');
|
||||
$config->set('cache.page.use_internal', 1);
|
||||
$config->set('cache.page.max_age', 300);
|
||||
$config->save();
|
||||
|
||||
// Create a Llama menu, add a link to it and place the corresponding block.
|
||||
$menu = entity_create('menu', array(
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'description' => 'Description text',
|
||||
));
|
||||
$menu->save();
|
||||
$menu_link = entity_create('menu_link', array(
|
||||
'link_path' => '<front>',
|
||||
'link_title' => 'Vicuña',
|
||||
'menu_name' => 'llama',
|
||||
));
|
||||
$menu_link->save();
|
||||
$block = $this->drupalPlaceBlock('system_menu_block:llama', array('label' => 'Llama', 'module' => 'system', 'region' => 'footer'));
|
||||
|
||||
// Prime the page cache.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
|
||||
// Verify a cache hit, but also the presence of the correct cache tags.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
$cid_parts = array(url('test-page', array('absolute' => TRUE)), 'html');
|
||||
$cid = sha1(implode(':', $cid_parts));
|
||||
$cache_entry = \Drupal::cache('page')->get($cid);
|
||||
$this->assertIdentical($cache_entry->tags, array('content:1', 'menu:llama'));
|
||||
|
||||
// The "Llama" menu is modified.
|
||||
$menu->label = 'Awesome llama';
|
||||
$menu->save();
|
||||
|
||||
// Verify that after the modified menu, there is a cache miss.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
|
||||
// Verify a cache hit.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
|
||||
// A link in the "Llama" menu is modified.
|
||||
$menu_link->link_title = 'Guanaco';
|
||||
$menu_link->save();
|
||||
|
||||
// Verify that after the modified menu link, there is a cache miss.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests menu link bundles.
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\menu_link\Entity;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Entity\Entity;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -446,9 +447,9 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($affected_menus as $menu_name) {
|
||||
menu_cache_clear($menu_name);
|
||||
}
|
||||
Cache::invalidateTags(array('menu' => array_keys($affected_menus)));
|
||||
// Also clear the menu system static caches.
|
||||
menu_reset_static_cache();
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
|
@ -523,10 +524,12 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
|
|||
// Check the has_children status of the parent.
|
||||
$storage_controller->updateParentalStatus($this);
|
||||
|
||||
menu_cache_clear($this->menu_name);
|
||||
Cache::invalidateTags(array('menu' => $this->menu_name));
|
||||
if (isset($this->original) && $this->menu_name != $this->original->menu_name) {
|
||||
menu_cache_clear($this->original->menu_name);
|
||||
Cache::invalidateTags(array('menu' => $this->original->menu_name));
|
||||
}
|
||||
// Also clear the menu system static caches.
|
||||
menu_reset_static_cache();
|
||||
|
||||
// Now clear the cache.
|
||||
_menu_clear_page_cache();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Entity;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\system\MenuInterface;
|
||||
|
@ -90,4 +91,22 @@ class Menu extends ConfigEntityBase implements MenuInterface {
|
|||
return (bool) $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
parent::postSave($storage_controller, $update);
|
||||
|
||||
Cache::invalidateTags(array('menu' => $this->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
parent::postDelete($storage_controller, $entities);
|
||||
|
||||
Cache::invalidateTags(array('menu' => array_keys($entities)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Cache\PageCacheTagsIntegrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Cache;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Enables the page cache and tests its cache tags in various scenarios.
|
||||
*
|
||||
* @see \Drupal\system\Tests\Bootstrap\PageCacheTest
|
||||
* @see \Drupal\node\Tests\NodePageCacheTest
|
||||
* @see \Drupal\menu\Tests\MenuTest::testMenuBlockPageCacheTags()
|
||||
*/
|
||||
class PageCacheTagsIntegrationTest extends WebTestBase {
|
||||
|
||||
protected $profile = 'standard';
|
||||
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Page cache tags integration test',
|
||||
'description' => 'Enable the page cache and test its cache tags in various scenarios.',
|
||||
'group' => 'Cache',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$config = \Drupal::config('system.performance');
|
||||
$config->set('cache.page.use_internal', 1);
|
||||
$config->set('cache.page.max_age', 300);
|
||||
$config->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cache tags are properly bubbled up to the page level.
|
||||
*/
|
||||
function testPageCacheTags() {
|
||||
// Create two nodes.
|
||||
$author_1 = $this->drupalCreateUser();
|
||||
$node_1_path = 'node/' . $this->drupalCreateNode(array(
|
||||
'uid' => $author_1->id(),
|
||||
'title' => 'Node 1',
|
||||
'body' => array(
|
||||
0 => array('value' => 'Body 1', 'format' => 'basic_html'),
|
||||
),
|
||||
'promote' => NODE_PROMOTED,
|
||||
))->id();
|
||||
$author_2 = $this->drupalCreateUser();
|
||||
$node_2_path = 'node/' . $this->drupalCreateNode(array(
|
||||
'uid' => $author_2->id(),
|
||||
'title' => 'Node 2',
|
||||
'body' => array(
|
||||
0 => array('value' => 'Body 2', 'format' => 'full_html'),
|
||||
),
|
||||
'promote' => NODE_PROMOTED,
|
||||
))->id();
|
||||
|
||||
// Full node page 1.
|
||||
$this->verifyPageCacheTags($node_1_path, array(
|
||||
'content:1',
|
||||
'user:' . $author_1->id(),
|
||||
'filter_format:basic_html',
|
||||
'menu:footer',
|
||||
'menu:main',
|
||||
));
|
||||
|
||||
// Full node page 2.
|
||||
$this->verifyPageCacheTags($node_2_path, array(
|
||||
'content:1',
|
||||
'user:' . $author_2->id(),
|
||||
'filter_format:full_html',
|
||||
'menu:footer',
|
||||
'menu:main',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills page cache for the given path, verify cache tags on page cache hit.
|
||||
*
|
||||
* @param $path
|
||||
* The Drupal page path to test.
|
||||
* @param $expected_tags
|
||||
* The expected cache tags for the page cache entry of the given $path.
|
||||
*/
|
||||
protected function verifyPageCacheTags($path, $expected_tags) {
|
||||
$this->drupalGet($path);
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
$this->drupalGet($path);
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
||||
$cid = sha1(implode(':', $cid_parts));
|
||||
$cache_entry = \Drupal::cache('page')->get($cid);
|
||||
$this->assertIdentical($cache_entry->tags, $expected_tags);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue