diff --git a/core/modules/aggregator/src/Plugin/views/row/Rss.php b/core/modules/aggregator/src/Plugin/views/row/Rss.php
index b34bb8feead8..3dc20ebac3b1 100644
--- a/core/modules/aggregator/src/Plugin/views/row/Rss.php
+++ b/core/modules/aggregator/src/Plugin/views/row/Rss.php
@@ -50,7 +50,6 @@ class Rss extends RssPluginBase {
$item = new \stdClass();
foreach ($entity as $name => $field) {
- // views_view_row_rss takes care about the escaping.
$item->{$name} = $field->value;
}
diff --git a/core/modules/comment/src/Plugin/views/row/Rss.php b/core/modules/comment/src/Plugin/views/row/Rss.php
index 85eacaa38c6d..f4e62c72ec17 100644
--- a/core/modules/comment/src/Plugin/views/row/Rss.php
+++ b/core/modules/comment/src/Plugin/views/row/Rss.php
@@ -84,8 +84,6 @@ class Rss extends RssPluginBase {
return;
}
- $description_build = [];
-
$comment->link = $comment->url('canonical', array('absolute' => TRUE));
$comment->rss_namespaces = array();
$comment->rss_elements = array(
@@ -113,13 +111,11 @@ class Rss extends RssPluginBase {
$this->view->style_plugin->namespaces = array_merge($this->view->style_plugin->namespaces, $comment->rss_namespaces);
}
+ $item = new \stdClass();
if ($view_mode != 'title') {
// We render comment contents.
- $description_build = $build;
+ $item->description = $build;
}
-
- $item = new \stdClass();
- $item->description = $description_build;
$item->title = $comment->label();
$item->link = $comment->link;
// Provide a reference so that the render call in
diff --git a/core/modules/node/src/Plugin/views/row/Rss.php b/core/modules/node/src/Plugin/views/row/Rss.php
index ae2b8712d51f..3f2deeef2711 100644
--- a/core/modules/node/src/Plugin/views/row/Rss.php
+++ b/core/modules/node/src/Plugin/views/row/Rss.php
@@ -108,8 +108,6 @@ class Rss extends RssPluginBase {
return;
}
- $description_build = [];
-
$node->link = $node->url('canonical', array('absolute' => TRUE));
$node->rss_namespaces = array();
$node->rss_elements = array(
@@ -149,13 +147,11 @@ class Rss extends RssPluginBase {
$this->view->style_plugin->namespaces += $xml_rdf_namespaces;
}
+ $item = new \stdClass();
if ($display_mode != 'title') {
// We render node contents.
- $description_build = $build;
+ $item->description = $build;
}
-
- $item = new \stdClass();
- $item->description = $description_build;
$item->title = $node->label();
$item->link = $node->link;
// Provide a reference so that the render call in
diff --git a/core/modules/views/src/Plugin/views/row/RssFields.php b/core/modules/views/src/Plugin/views/row/RssFields.php
index 25a75d1da003..5fa91cc672a0 100644
--- a/core/modules/views/src/Plugin/views/row/RssFields.php
+++ b/core/modules/views/src/Plugin/views/row/RssFields.php
@@ -147,8 +147,10 @@ class RssFields extends RowPluginBase {
// @todo Views should expect and store a leading /. See:
// https://www.drupal.org/node/2423913
$item->link = Url::fromUserInput('/' . $this->getField($row_index, $this->options['link_field']))->setAbsolute()->toString();
+
$field = $this->getField($row_index, $this->options['description_field']);
$item->description = is_array($field) ? $field : ['#markup' => $field];
+
$item->elements = array(
array('key' => 'pubDate', 'value' => $this->getField($row_index, $this->options['date_field'])),
array(
diff --git a/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php b/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php
index 3f2cf21822bb..8032af333758 100644
--- a/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php
+++ b/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php
@@ -48,9 +48,13 @@ class DisplayFeedTest extends PluginTestBase {
// Verify a title with HTML entities is properly escaped.
$node_title = 'This "cool" & "neat" article\'s title';
- $node = $this->drupalCreateNode(array(
- 'title' => $node_title
- ));
+ $node = $this->drupalCreateNode([
+ 'title' => $node_title,
+ 'body' => [0 => [
+ 'value' => 'A paragraph',
+ 'format' => filter_default_format(),
+ ]],
+ ]);
// Test the site name setting.
$site_name = $this->randomMachineName();
@@ -60,6 +64,8 @@ class DisplayFeedTest extends PluginTestBase {
$result = $this->xpath('//title');
$this->assertEqual($result[0], $site_name, 'The site title is used for the feed title.');
$this->assertEqual($result[1], $node_title, 'Node title with HTML entities displays correctly.');
+ // Verify HTML is properly escaped in the description field.
+ $this->assertRaw('<p>A paragraph</p>');
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_display_feed');
$display = &$view->getDisplay('feed_1');
@@ -101,12 +107,18 @@ class DisplayFeedTest extends PluginTestBase {
// Verify a title with HTML entities is properly escaped.
$node_title = 'This "cool" & "neat" article\'s title';
$this->drupalCreateNode(array(
- 'title' => $node_title
+ 'title' => $node_title,
+ 'body' => [0 => [
+ 'value' => 'A paragraph',
+ 'format' => filter_default_format(),
+ ]],
));
$this->drupalGet('test-feed-display-fields.xml');
$result = $this->xpath('//title/a');
$this->assertEqual($result[0], $node_title, 'Node title with HTML entities displays correctly.');
+ // Verify HTML is properly escaped in the description field.
+ $this->assertRaw('<p>A paragraph</p>');
}
/**
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_display_feed.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_display_feed.yml
index 2c35726c6077..26b3074405e1 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_display_feed.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_display_feed.yml
@@ -1,8 +1,12 @@
langcode: en
status: true
dependencies:
+ config:
+ - core.entity_view_mode.node.teaser
+ - field.storage.node.body
module:
- node
+ - text
- user
id: test_display_feed
label: test_display_feed
@@ -41,6 +45,68 @@ display:
plugin_id: field
entity_type: node
entity_field: title
+ body:
+ id: body
+ table: node__body
+ field: body
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: false
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: text_default
+ settings: { }
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ plugin_id: field
filters:
status:
expose:
@@ -75,10 +141,21 @@ display:
style:
type: default
title: test_display_feed
+ display_extenders: { }
display_plugin: default
display_title: Master
id: default
position: 0
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url.query_args
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: -1
+ tags:
+ - 'config:field.storage.node.body'
feed_1:
display_options:
displays: { }
@@ -90,10 +167,20 @@ display:
style:
type: rss
sitename_title: true
+ display_extenders: { }
display_plugin: feed
display_title: Feed
id: feed_1
position: 0
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: -1
+ tags:
+ - 'config:field.storage.node.body'
feed_2:
display_options:
displays: { }
@@ -105,7 +192,7 @@ display:
options:
title_field: title
link_field: title
- description_field: title
+ description_field: body
creator_field: title
date_field: title
guid_field_options:
@@ -115,14 +202,35 @@ display:
type: rss
sitename_title: true
display_description: ''
+ display_extenders: { }
display_plugin: feed
display_title: 'Feed with Fields'
id: feed_2
position: 0
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: -1
+ tags:
+ - 'config:field.storage.node.body'
page:
display_options:
path: test-feed-display
+ display_extenders: { }
display_plugin: page
- display_title: Page
+ display_title: 'Page'
id: page
position: 0
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url.query_args
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: -1
+ tags:
+ - 'config:field.storage.node.body'
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 96d4c7ba0ced..6a0a141d30c4 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -863,11 +863,14 @@ function template_preprocess_views_view_row_rss(&$variables) {
$variables['title'] = $item->title;
$variables['link'] = $item->link;
- /** @var \Drupal\Core\Render\RendererInterface $renderer */
- $renderer = \Drupal::service('renderer');
- // We render the item description. It might contain entities, which attach rss
- // elements via hook_entity_view, see comment_entity_view().
- $variables['description'] = is_array($item->description) ? $renderer->render($item->description) : $item->description;
+ // The description is the only place where we should find HTML.
+ // @see https://validator.w3.org/feed/docs/rss2.html#hrelementsOfLtitemgt
+ // If we have a render array, render it here and pass the result to the
+ // template, letting Twig autoescape it.
+ if (isset($item->description) && is_array($item->description)) {
+ $variables['description'] = (string) \Drupal::service('renderer')->render($item->description);
+ }
+
$variables['item_elements'] = array();
foreach ($item->elements as $element) {
if (isset($element['attributes']) && is_array($element['attributes'])) {