diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index 9bd01c4..211f359 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -240,7 +240,7 @@ public function elementType($none_supported = FALSE, $default_empty = FALSE, $in } } if ($this->options['element_type']) { - return SafeMarkup::checkPlain($this->options['element_type']); + return $this->options['element_type']; } if ($default_empty) { @@ -268,7 +268,7 @@ public function elementLabelType($none_supported = FALSE, $default_empty = FALSE } } if ($this->options['element_label_type']) { - return SafeMarkup::checkPlain($this->options['element_label_type']); + return $this->options['element_label_type']; } if ($default_empty) { @@ -288,7 +288,7 @@ public function elementWrapperType($none_supported = FALSE, $default_empty = FAL } } if ($this->options['element_wrapper_type']) { - return SafeMarkup::checkPlain($this->options['element_wrapper_type']); + return $this->options['element_wrapper_type']; } if ($default_empty) { diff --git a/core/modules/views/src/Tests/ViewsEscapingTest.php b/core/modules/views/src/Tests/ViewsEscapingTest.php new file mode 100644 index 0000000..bde5fd5 --- /dev/null +++ b/core/modules/views/src/Tests/ViewsEscapingTest.php @@ -0,0 +1,61 @@ +enableViewsTestModule(); + } + + /** + * Tests for incorrectly escaped markup in the views-view-fields.html.twig. + */ + public function testViewsViewFieldsEscaping() { + \Drupal::service('theme_handler')->install(array('views_test_theme')); + + // Make base theme default then test for hook invocations. + $this->config('system.theme') + ->set('default', 'views_test_theme') + ->save(); + $this->assertEqual($this->config('system.theme')->get('default'), 'views_test_theme'); + + $this->drupalGet('test_page_display_200'); + $this->assertNoRaw('</'); + } + +} diff --git a/core/modules/views/templates/views-view-fields.html.twig b/core/modules/views/templates/views-view-fields.html.twig index a8aa00b..be8edc6 100644 --- a/core/modules/views/templates/views-view-fields.html.twig +++ b/core/modules/views/templates/views-view-fields.html.twig @@ -11,13 +11,15 @@ * - class: The safe class ID to use. * - handler: The Views field handler controlling this field. * - inline: Whether or not the field should be inline. - * - inline_html: Either div or span based on the 'inline' flag. - * - wrapper_prefix: A complete wrapper containing the inline_html to use. - * - wrapper_suffix: The closing tag for the wrapper. + * - wrapper_element: An HTML element for a wrapper. + * - wrapper_attributes: List of attributes for wrapper element. * - separator: An optional separator that may appear before a field. * - label: The field's label text. - * - label_html: The full HTML of the label to use including configured - * element type. + * - label_element: An HTML element for a label wrapper. + * - label_attributes: List of attributes for label wrapper. + * - label_suffix: Colon after the label. + * - element_type: An HTML element for the field content. + * - element_attributes: List of attributes for HTML element for field content. * - row: The raw result from the query, with all data it fetched. * * @see template_preprocess_views_view_fields() @@ -31,11 +33,24 @@ See http://api.drupal.org/api/function/theme_views_view_fields/8 for details. After copying this file to your theme's folder and customizing it, remove this HTML comment. --> -{% for field in fields %} +{% for field in fields -%} {{ field.separator }} - - {{ field.wrapper_prefix }} - {{ field.label_html }} + {%- if field.wrapper_element -%} + <{{ field.wrapper_element }}{{ field.wrapper_attributes }}> + {%- endif %} + {%- if field.label -%} + {%- if field.label_element -%} + <{{ field.label_element }}{{ field.label_attributes }}>{{ field.label }}{{ field.label_suffix }} + {%- else -%} + {{ field.label }}{{ field.label_suffix }} + {%- endif %} + {%- endif %} + {%- if field.element_type -%} + <{{ field.element_type }}{{ field.element_attributes }}>{{ field.content }} + {%- else -%} {{ field.content }} - {{ field.wrapper_suffix }} -{% endfor %} + {%- endif %} + {%- if field.wrapper_element -%} + + {%- endif %} +{%- endfor %} diff --git a/core/modules/views/tests/themes/views_test_theme/templates/views-view-fields.html.twig b/core/modules/views/tests/themes/views_test_theme/templates/views-view-fields.html.twig new file mode 100644 index 0000000..e5297f2 --- /dev/null +++ b/core/modules/views/tests/themes/views_test_theme/templates/views-view-fields.html.twig @@ -0,0 +1,48 @@ +{# +/** + * @file + * Theme override to display all the fields in a views row. + * + * Available variables: + * - view: The view in use. + * - fields: A list of fields, each one contains: + * - content: The output of the field. + * - raw: The raw data for the field, if it exists. This is NOT output safe. + * - class: The safe class ID to use. + * - handler: The Views field handler controlling this field. + * - inline: Whether or not the field should be inline. + * - wrapper_element: An HTML element for a wrapper. + * - wrapper_attributes: List of attributes for wrapper element. + * - separator: An optional separator that may appear before a field. + * - label: The field's label text. + * - label_element: An HTML element for a label wrapper. + * - label_attributes: List of attributes for label wrapper. + * - label_suffix: Colon after the label. + * - element_type: An HTML element for the field content. + * - element_attributes: List of attributes for HTML element for field content. + * - row: The raw result from the query, with all data it fetched. + * + * @see template_preprocess_views_view_fields() + */ +#} +{% for field in fields -%} + {{ field.separator }} + {%- if field.wrapper_element -%} + <{{ field.wrapper_element }}{{ field.wrapper_attributes }}> + {%- endif %} + {%- if field.label -%} + {%- if field.label_element -%} + <{{ field.label_element }}{{ field.label_attributes }}>{{ field.label }}{{ field.label_suffix }} + {%- else -%} + {{ field.label }}{{ field.label_suffix }} + {%- endif %} + {%- endif %} + {%- if field.element_type -%} + <{{ field.element_type }}{{ field.element_attributes }}>{{ field.content }} + {%- else -%} + {{ field.content }} + {%- endif %} + {%- if field.wrapper_element -%} + + {%- endif %} +{%- endfor %} diff --git a/core/modules/views/tests/themes/views_test_theme/views_test_theme.info.yml b/core/modules/views/tests/themes/views_test_theme/views_test_theme.info.yml new file mode 100644 index 0000000..b32f40a --- /dev/null +++ b/core/modules/views/tests/themes/views_test_theme/views_test_theme.info.yml @@ -0,0 +1,5 @@ +name: Views test theme +type: theme +description: Theme for testing Views functionality. +version: VERSION +core: 8.x diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index fbe962e..b214926 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -101,18 +101,7 @@ function template_preprocess_views_view_fields(&$variables) { if ($classes = $object->handler->elementClasses($row->index)) { $attributes['class'][] = $classes; } - $attributes = new Attribute($attributes); - - $pre = '<' . $object->element_type; - $pre .= $attributes; - $field_output = $pre . '>' . $field_output . 'element_type . '>'; - } - - // Protect ourselves somewhat for backward compatibility. This will - // prevent old templates from producing invalid HTML when no element type - // is selected. - if (empty($object->element_type)) { - $object->element_type = 'span'; + $object->element_attributes = new Attribute($attributes); } $object->content = $field_output; @@ -130,16 +119,14 @@ function template_preprocess_views_view_fields(&$variables) { $object->class = Html::cleanCssIdentifier($id); $previous_inline = $object->inline; - $object->inline_html = $object->handler->elementWrapperType(TRUE, TRUE); - if ($object->inline_html === '' && $variables['options']['default_field_elements']) { - $object->inline_html = $object->inline ? 'span' : 'div'; + // Set up field wrapper element. + $object->wrapper_element = $object->handler->elementWrapperType(TRUE, TRUE); + if ($object->wrapper_element === '' && $variables['options']['default_field_elements']) { + $object->wrapper_element = $object->inline ? 'span' : 'div'; } - // Set up the wrapper HTML. - $object->wrapper_prefix = ''; - $object->wrapper_suffix = ''; - - if ($object->inline_html) { + // Set up field wrapper attributes if field wrapper was set. + if ($object->wrapper_element) { $attributes = array(); if ($object->handler->options['element_default_classes']) { $attributes['class'][] = 'views-field'; @@ -149,26 +136,24 @@ function template_preprocess_views_view_fields(&$variables) { if ($classes = $object->handler->elementWrapperClasses($row->index)) { $attributes['class'][] = $classes; } - $attributes = new Attribute($attributes); - - $object->wrapper_prefix = '<' . $object->inline_html; - $object->wrapper_prefix .= $attributes; - $object->wrapper_prefix .= '>'; - $object->wrapper_suffix = 'inline_html . '>'; + $object->wrapper_attributes = new Attribute($attributes); } - // Set up the label for the value and the HTML to make it easier - // on the template. - $object->label = SafeMarkup::checkPlain($view->field[$id]->label()); - $object->label_html = ''; + // Set up field label + $object->label = $view->field[$id]->label(); + + // Set up field label wrapper and its attributes. if ($object->label) { - $object->label_html .= $object->label; + // Add a colon in a label suffix. if ($object->handler->options['element_label_colon']) { - $object->label_html .= ': '; + $object->label_suffix = ': '; } - $object->elementLabelType = $object->handler->elementLabelType(TRUE, !$variables['options']['default_field_elements']); - if ($object->elementLabelType) { + // Set up label HTML element. + $object->label_element = $object->handler->elementLabelType(TRUE, !$variables['options']['default_field_elements']); + + // Set up label attributes. + if ($object->label_element) { $attributes = array(); if ($object->handler->options['element_default_classes']) { $attributes['class'][] = 'views-label'; @@ -179,13 +164,7 @@ function template_preprocess_views_view_fields(&$variables) { if ($element_label_class) { $attributes['class'][] = $element_label_class; } - $attributes = new Attribute($attributes); - - $pre = '<' . $object->elementLabelType; - $pre .= $attributes; - $pre .= '>'; - - $object->label_html = $pre . $object->label_html . 'elementLabelType . '>'; + $object->label_attributes = new Attribute($attributes); } } @@ -219,12 +198,32 @@ function theme_views_view_fields($variables) { if (!empty($field->separator)) { $output .= $field->separator; } - - $output .= $field->wrapper_prefix; - $output .= $field->label_html; + $wrapper_element = SafeMarkup::checkPlain($field->wrapper_element); + if ($wrapper_element) { + $output .= '<' . $wrapper_element . $field->wrapper_attributes . '>'; + if (isset($field->label_element) && !empty($field->label_element)) { + $label_element = SafeMarkup::checkPlain($field->label_element); + $output .= '<' . $label_element . $field->label_attributes . '>'; + } + $output .= SafeMarkup::checkPlain($field->label); + if (isset($field->label_suffix) && isset($field->label_suffix)) { + $output .= $field->label_suffix; + } + if (isset($label_element)) { + $output .= ''; + } + } + $element_type = SafeMarkup::checkPlain($field->element_type); + if ($element_type) { + $output .= '<' . $element_type . $field->element_attributes . '>'; + } $output .= $field->content; - - $output .= $field->wrapper_suffix; + if ($element_type) { + $output .= ''; + } + if ($wrapper_element) { + $output .= ''; + } } return $output;