Commit 3251139c authored by catch's avatar catch

Issue #2378789 by Wim Leers: Views output cache is broken

parent 186a80cb
......@@ -7,6 +7,7 @@
namespace Drupal\views\Plugin\views\cache;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\views\Plugin\views\PluginBase;
use Drupal\Core\Database\Query\Select;
......@@ -142,8 +143,8 @@ public function cacheSet($type) {
\Drupal::cache($this->resultsBin)->set($this->generateResultsKey(), $data, $this->cacheSetExpire($type), $this->getCacheTags());
break;
case 'output':
$this->gatherHeaders($this->view->display_handler->output);
$this->storage['output'] = drupal_render($this->view->display_handler->output);
$this->gatherRenderMetadata($this->view->display_handler->output);
\Drupal::cache($this->outputBin)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), $this->getCacheTags());
break;
}
......@@ -178,9 +179,13 @@ public function cacheGet($type) {
if (!$cutoff || $cache->created > $cutoff) {
$this->storage = $cache->data;
$this->restoreHeaders();
$this->restoreRenderMetadata();
$this->view->display_handler->output = array(
'#attached' => &$this->view->element['#attached'],
'#cache' => [
'tags' => &$this->view->element['#cache']['tags'],
],
'#post_render_cache' => &$this->view->element['#post_render_cache'],
'#markup' => $cache->data['output'],
);
......@@ -226,60 +231,28 @@ public function postRender(&$output) { }
/**
* Start caching the html head.
*
* This takes a snapshot of the current system state so that we don't
* duplicate it. Later on, when gatherHeaders() is run, this information
* will be removed so that we don't hold onto it.
*
* @see _drupal_add_html_head()
*/
public function cacheStart() {
$this->storage['head'] = _drupal_add_html_head();
}
public function cacheStart() { }
/**
* Gather the JS/CSS from the render array and the html head from band data.
* Gather bubbleable render metadata from the render array.
*
* @param array $render_array
* The view render array to collect data from.
*/
protected function gatherHeaders(array $render_array = []) {
// Simple replacement for head
if (isset($this->storage['head'])) {
$this->storage['head'] = str_replace($this->storage['head'], '', _drupal_add_html_head());
}
else {
$this->storage['head'] = '';
}
$this->storage['css'] = $render_array['#attached']['css'];
$this->storage['js'] = $render_array['#attached']['js'];
protected function gatherRenderMetadata(array $render_array = []) {
$this->storage['attachments'] = $render_array['#attached'];
$this->storage['postRenderCache'] = $render_array['#post_render_cache'];
$this->storage['cacheTags'] = $render_array['#cache']['tags'];
}
/**
* Restore out of band data saved to cache. Copied from Panels.
* Restore bubbleable render metadata.
*/
public function restoreHeaders() {
if (!empty($this->storage['head'])) {
_drupal_add_html_head($this->storage['head']);
}
if (!empty($this->storage['css'])) {
foreach ($this->storage['css'] as $args) {
$this->view->element['#attached']['css'][] = $args;
}
}
if (!empty($this->storage['js'])) {
foreach ($this->storage['js'] as $key => $args) {
if ($key !== 'settings') {
$this->view->element['#attached']['js'][] = $args;
}
else {
foreach ($args as $setting) {
$this->view->element['#attached']['js']['setting'][] = $setting;
}
}
}
}
public function restoreRenderMetadata() {
$this->view->element['#attached'] = drupal_merge_attached($this->view->element['#attached'], $this->storage['attachments']);
$this->view->element['#cache']['tags'] = Cache::mergeTags(isset($this->view->element['#cache']['tags']) ? $this->view->element['#cache']['tags'] : [], $this->storage['cacheTags']);
$this->view->element['#post_render_cache'] = NestedArray::mergeDeep(isset($this->view->element['#post_render_cache']) ? $this->view->element['#post_render_cache'] : [], $this->storage['postRenderCache']);
}
/**
......
......@@ -147,39 +147,14 @@ function testHeaderStorage() {
$view->setDisplay();
$output = $view->preview();
drupal_render($output);
$this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
$css_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.css';
$js_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.js';
$this->assertTrue(in_array($css_path, $output['#attached']['css']), 'Make sure the css is added for cached views.');
$this->assertTrue(in_array($js_path, $output['#attached']['js']), 'Make sure the js is added for cached views.');
$this->assertTrue(['views_test_data:1'], $output['#cache']['tags']);
$this->assertTrue(['views_test_data_post_render_cache' => [['foo' => 'bar']]], $output['#post_render_cache']);
$this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
// Now add some css/jss before running the view.
// Make sure that this css is not added when running the cached view.
$view->storage->set('id', 'test_cache_header_storage_2');
$attached = array(
'#attached' => array(
'css' => array(
drupal_get_path('module', 'system') . '/css/system.maintenance.css' => array(),
),
'js' => array(
drupal_get_path('module', 'user') . '/user.permissions.js' => array(),
),
),
);
drupal_render($attached);
drupal_process_attached($attached);
$view->destroy();
$output = $view->preview();
drupal_render($output);
$this->assertTrue(empty($output['#attached']['css']), 'The view does not have attached CSS.');
$this->assertTrue(empty($output['#attached']['js']), 'The view does not have attached JS.');
$view->destroy();
$output = $view->preview();
drupal_render($output);
$this->assertTrue(empty($output['#attached']['css']), 'The cached view does not have attached CSS.');
$this->assertTrue(empty($output['#attached']['js']), 'The cached view does not have attached JS.');
}
/**
......
test:
css:
component:
views_cache.test.css: {}
js:
views_cache.test.js: {}
......@@ -48,13 +48,24 @@ function views_test_data_views_pre_render(ViewExecutable $view) {
if (isset($view) && ($view->storage->id() == 'test_cache_header_storage')) {
$path = drupal_get_path('module', 'views_test_data');
$view->element['#attached']['library'][] = 'views_test_data/test';
$view->element['#attached']['js'][] = "$path/views_cache.test.js";
$view->element['#attached']['css'][] = "$path/views_cache.test.css";
$view->element['#cache']['tags'][] = 'views_test_data:1';
$view->element['#post_render_cache']['views_test_data_post_render_cache'][] = ['foo' => 'bar'];
$view->build_info['pre_render_called'] = TRUE;
}
}
/**
* #post_render_cache callback; for testing purposes only.
*/
function views_test_data_post_render_cache(array $element, array $context) {
// No-op.
return $element;
}
/**
* Implements hook_views_post_render().
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment