Issue #3272985 by recrit, grasmash, catch, smustgrave, xjm: RSS Feed header reverts to text/html when cached

merge-requests/5101/merge
catch 2023-11-27 23:03:43 +00:00
parent 9cb09532f0
commit b20ec5185c
5 changed files with 82 additions and 12 deletions

View File

@ -116,9 +116,47 @@ class Feed extends PathPluginBase implements ResponseDisplayPluginInterface {
$cache_metadata = CacheableMetadata::createFromRenderArray($build); $cache_metadata = CacheableMetadata::createFromRenderArray($build);
$response->addCacheableDependency($cache_metadata); $response->addCacheableDependency($cache_metadata);
// Set the HTTP headers and status code on the response if any bubbled.
if (!empty($build['#attached']['http_header'])) {
static::setHeaders($response, $build['#attached']['http_header']);
}
return $response; return $response;
} }
/**
* Sets headers on a response object.
*
* @param \Drupal\Core\Cache\CacheableResponse $response
* The HTML response to update.
* @param array $headers
* The headers to set, as an array. The items in this array should be as
* follows:
* - The header name.
* - The header value.
* - (optional) Whether to replace a current value with the new one, or add
* it to the others. If the value is not replaced, it will be appended,
* resulting in a header like this: 'Header: value1,value2'.
*
* @see \Drupal\Core\Render\HtmlResponseAttachmentsProcessor::setHeaders()
*/
protected static function setHeaders(CacheableResponse $response, array $headers): void {
foreach ($headers as $values) {
$name = $values[0];
$value = $values[1];
$replace = !empty($values[2]);
// Drupal treats the HTTP response status code like a header, even though
// it really is not.
if (strtolower($name) === 'status') {
$response->setStatusCode($value);
}
else {
$response->headers->set($name, $value, $replace);
}
}
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -67,6 +67,11 @@ class Opml extends StylePluginBase {
'#view' => $this->view, '#view' => $this->view,
'#options' => $this->options, '#options' => $this->options,
'#rows' => $rows, '#rows' => $rows,
'#attached' => [
'http_header' => [
['Content-Type', 'text/xml; charset=utf-8'],
],
],
]; ];
unset($this->view->row_index); unset($this->view->row_index);
return $build; return $build;

View File

@ -132,6 +132,11 @@ class Rss extends StylePluginBase {
'#view' => $this->view, '#view' => $this->view,
'#options' => $this->options, '#options' => $this->options,
'#rows' => $rows, '#rows' => $rows,
'#attached' => [
'http_header' => [
['Content-Type', 'application/rss+xml; charset=utf-8'],
],
],
]; ];
unset($this->view->row_index); unset($this->view->row_index);
return $build; return $build;

View File

@ -204,4 +204,38 @@ class DisplayFeedTest extends ViewTestBase {
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
} }
/**
* Tests the cacheability of the feed display.
*/
public function testFeedCacheability(): void {
// Test as an anonymous user.
$this->drupalLogout();
// Set the page cache max age to a value greater than zero.
$config = $this->config('system.performance');
$config->set('cache.page.max_age', 300);
$config->save();
// Uninstall all page cache modules that could cache the HTTP response
// headers.
\Drupal::service('module_installer')->uninstall([
'page_cache',
'dynamic_page_cache',
]);
// Reset all so that the config and module changes are active.
$this->resetAll();
$url = 'test-feed-display.xml';
$this->drupalGet($url);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public');
$this->assertSession()->responseHeaderEquals('Content-Type', 'application/rss+xml; charset=utf-8');
// Visit the page again to get the cached response.
$this->drupalGet($url);
$this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public');
$this->assertSession()->responseHeaderEquals('Content-Type', 'application/rss+xml; charset=utf-8');
}
} }

View File

@ -928,12 +928,6 @@ function template_preprocess_views_view_rss(&$variables) {
$variables['namespaces'] = new Attribute($style->namespaces); $variables['namespaces'] = new Attribute($style->namespaces);
$variables['items'] = $items; $variables['items'] = $items;
$variables['channel_elements'] = $style->channel_elements; $variables['channel_elements'] = $style->channel_elements;
// During live preview we don't want to output the header since the contents
// of the feed are being displayed inside a normal HTML page.
if (empty($variables['view']->live_preview)) {
$variables['view']->getResponse()->headers->set('Content-Type', 'application/rss+xml; charset=utf-8');
}
} }
/** /**
@ -995,12 +989,6 @@ function template_preprocess_views_view_opml(&$variables) {
$variables['title'] = $title; $variables['title'] = $title;
$variables['items'] = $items; $variables['items'] = $items;
$variables['updated'] = gmdate(DATE_RFC2822, \Drupal::time()->getRequestTime()); $variables['updated'] = gmdate(DATE_RFC2822, \Drupal::time()->getRequestTime());
// During live preview we don't want to output the header since the contents
// of the feed are being displayed inside a normal HTML page.
if (empty($variables['view']->live_preview)) {
$variables['view']->getResponse()->headers->set('Content-Type', 'text/xml; charset=utf-8');
}
} }
/** /**