From 041511a78abbf1b41e09beaf0c54a0c56176ba96 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Sun, 4 Jan 2015 14:03:11 +0000 Subject: [PATCH] Issue #2397727 by jibran, dawehner: Remove the SafeMarkup::set() call in field/Field.php --- .../field/src/Plugin/views/field/Field.php | 49 ++-- .../src/Tests/Views/HandlerFieldFieldTest.php | 20 ++ .../views/field/FieldHandlerInterface.php | 268 ++++++++++++++++++ .../Plugin/views/field/FieldPluginBase.php | 121 ++------ .../field/MultiItemsFieldHandlerInterface.php | 53 ++++ .../src/Plugin/views/field/PrerenderList.php | 28 +- 6 files changed, 399 insertions(+), 140 deletions(-) create mode 100644 core/modules/views/src/Plugin/views/field/FieldHandlerInterface.php create mode 100644 core/modules/views/src/Plugin/views/field/MultiItemsFieldHandlerInterface.php diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php index 7fdc28bcbe..afb643d33b 100644 --- a/core/modules/field/src/Plugin/views/field/Field.php +++ b/core/modules/field/src/Plugin/views/field/Field.php @@ -20,10 +20,12 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Render\Element; +use Drupal\Core\Render\RendererInterface; use Drupal\Core\Session\AccountInterface; use Drupal\views\Plugin\CacheablePluginInterface; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\field\FieldPluginBase; +use Drupal\views\Plugin\views\field\MultiItemsFieldHandlerInterface; use Drupal\views\ResultRow; use Drupal\views\ViewExecutable; use Drupal\views\Views; @@ -36,7 +38,7 @@ * * @ViewsField("field") */ -class Field extends FieldPluginBase implements CacheablePluginInterface { +class Field extends FieldPluginBase implements CacheablePluginInterface, MultiItemsFieldHandlerInterface { /** * An array to store field renderable arrays for use by renderItems(). @@ -112,6 +114,13 @@ class Field extends FieldPluginBase implements CacheablePluginInterface { */ protected $languageManager; + /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + /** * Constructs a \Drupal\field\Plugin\views\field\Field object. * @@ -127,13 +136,17 @@ class Field extends FieldPluginBase implements CacheablePluginInterface { * The field formatter plugin manager. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. + * */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, LanguageManagerInterface $language_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, LanguageManagerInterface $language_manager, RendererInterface $renderer) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->entityManager = $entity_manager; $this->formatterPluginManager = $formatter_plugin_manager; $this->languageManager = $language_manager; + $this->renderer = $renderer; } /** @@ -146,7 +159,8 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('entity.manager'), $container->get('plugin.manager.field.formatter'), - $container->get('language_manager') + $container->get('language_manager'), + $container->get('renderer') ); } @@ -689,34 +703,25 @@ public function submitGroupByForm(&$form, FormStateInterface $form_state) { * When using advanced render, each possible item in the list is rendered * individually. Then the items are all pasted together. */ - protected function renderItems($items) { + public function renderItems($items) { if (!empty($items)) { - $output = ''; - if (!$this->options['group_rows']) { - foreach ($items as $item) { - $output .= SafeMarkup::escape($item); - } - return SafeMarkup::set($output); - } - if ($this->options['multi_type'] == 'separator') { - $output = ''; - $separator = ''; - $escaped_separator = Xss::filterAdmin($this->options['separator']); - foreach ($items as $item) { - $output .= $separator . SafeMarkup::escape($item); - $separator = $escaped_separator; - } - return SafeMarkup::set($output); + if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) { + $separator = $this->options['multi_type'] == 'separator' ? SafeMarkup::checkAdminXss($this->options['separator']) : ''; + $build = [ + '#type' => 'inline_template', + '#template' => '{{ items | safe_join(separator) }}', + '#context' => ['separator' => $separator, 'items' => $items], + ]; } else { - $item_list = array( + $build = array( '#theme' => 'item_list', '#items' => $items, '#title' => NULL, '#list_type' => $this->options['multi_type'], ); - return drupal_render($item_list); } + return $this->renderer->render($build); } } diff --git a/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php index f3bd30a905..c017dedd84 100644 --- a/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php +++ b/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php @@ -288,6 +288,26 @@ public function _testMultipleFieldRender() { } $this->assertEqual($rendered_field, implode(':', $items), 'Make sure that the amount of items is limited.'); } + $view->destroy(); + + // Test separator with HTML, ensure it is escaped. + $this->prepareView($view); + $view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE; + $view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3; + $view->displayHandlers->get('default')->options['fields'][$field_name]['separator'] = '

test

'; + $this->executeView($view); + + for ($i = 0; $i < 3; $i++) { + $rendered_field = $view->style_plugin->getField($i, $field_name); + $items = []; + $pure_items = $this->nodes[$i]->{$field_name}->getValue(); + $pure_items = array_splice($pure_items, 0, 3); + foreach ($pure_items as $j => $item) { + $items[] = $pure_items[$j]['value']; + } + $this->assertEqual($rendered_field, implode('

test

', $items), 'Make sure that the amount of items is limited.'); + } + $view->destroy(); } } diff --git a/core/modules/views/src/Plugin/views/field/FieldHandlerInterface.php b/core/modules/views/src/Plugin/views/field/FieldHandlerInterface.php new file mode 100644 index 0000000000..b85676e9fb --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/FieldHandlerInterface.php @@ -0,0 +1,268 @@ +themeFunctions() as #theme. + * + * @param \Drupal\views\ResultRow $values + * Holds single row of a view's result set. + * + * @return string|false + * Returns rendered output of the given theme implementation. + */ + function theme(ResultRow $values); + +} diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index 4459ac2ef5..b2b6b4c610 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -47,11 +47,7 @@ * @ingroup views_plugins * @see plugin_api */ - -/** - * Base field handler that has no options and renders an unformatted field. - */ -abstract class FieldPluginBase extends HandlerBase { +abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterface { /** * Indicator of the renderText() method for rendering a single item. @@ -197,7 +193,7 @@ protected function addAdditionalFields($fields = NULL) { } /** - * Called to determine what to tell the clicksorter. + * {@inheritdoc} */ public function clickSort($order) { if (isset($this->field_alias)) { @@ -209,18 +205,14 @@ public function clickSort($order) { } /** - * Determine if this field is click sortable. - * - * @return bool - * The value of 'click sortable' from the plugin definition, this defaults - * to TRUE if not set. + * {@inheritdoc} */ public function clickSortable() { return isset($this->definition['click sortable']) ? $this->definition['click sortable'] : TRUE; } /** - * Get this field's label. + * {@inheritdoc} */ public function label() { if (!isset($this->options['label'])) { @@ -230,7 +222,7 @@ public function label() { } /** - * Return an HTML element based upon the field's element type. + * {@inheritdoc} */ public function elementType($none_supported = FALSE, $default_empty = FALSE, $inline = FALSE) { if ($none_supported) { @@ -258,7 +250,7 @@ public function elementType($none_supported = FALSE, $default_empty = FALSE, $in } /** - * Return an HTML element for the label based upon the field's element type. + * {@inheritdoc} */ public function elementLabelType($none_supported = FALSE, $default_empty = FALSE) { if ($none_supported) { @@ -278,7 +270,7 @@ public function elementLabelType($none_supported = FALSE, $default_empty = FALSE } /** - * Return an HTML element for the wrapper based upon the field's element type. + * {@inheritdoc} */ public function elementWrapperType($none_supported = FALSE, $default_empty = FALSE) { if ($none_supported) { @@ -298,11 +290,7 @@ public function elementWrapperType($none_supported = FALSE, $default_empty = FAL } /** - * Provide a list of elements valid for field HTML. - * - * This function can be overridden by fields that want more or fewer - * elements available, though this seems like it would be an incredibly - * rare occurrence. + * {@inheritdoc} */ public function getElements() { static $elements = NULL; @@ -319,7 +307,7 @@ public function getElements() { } /** - * Return the class of the field. + * {@inheritdoc} */ public function elementClasses($row_index = NULL) { $classes = explode(' ', $this->options['element_class']); @@ -331,10 +319,7 @@ public function elementClasses($row_index = NULL) { } /** - * Replace a value with tokens from the last field. - * - * This function actually figures out which field was last and uses its - * tokens so they will all be available. + * {@inheritdoc} */ public function tokenizeValue($value, $row_index = NULL) { if (strpos($value, '[') !== FALSE || strpos($value, '!') !== FALSE || strpos($value, '%') !== FALSE) { @@ -369,7 +354,7 @@ public function tokenizeValue($value, $row_index = NULL) { } /** - * Return the class of the field's label. + * {@inheritdoc} */ public function elementLabelClasses($row_index = NULL) { $classes = explode(' ', $this->options['element_label_class']); @@ -381,7 +366,7 @@ public function elementLabelClasses($row_index = NULL) { } /** - * Return the class of the field's wrapper. + * {@inheritdoc} */ public function elementWrapperClasses($row_index = NULL) { $classes = explode(' ', $this->options['element_wrapper_class']); @@ -393,13 +378,7 @@ public function elementWrapperClasses($row_index = NULL) { } /** - * Gets the entity matching the current row and relationship. - * - * @param \Drupal\views\ResultRow $values - * An object containing all retrieved values. - * - * @return \Drupal\Core\Entity\EntityInterface - * Returns the entity matching the values. + * {@inheritdoc} */ public function getEntity(ResultRow $values) { $relationship_id = $this->options['relationship']; @@ -412,15 +391,7 @@ public function getEntity(ResultRow $values) { } /** - * Get the value that's supposed to be rendered. - * - * This api exists so that other modules can easy set the values of the field - * without having the need to change the render method as well. - * - * @param \Drupal\views\ResultRow $values - * An object containing all retrieved values. - * @param string $field - * Optional name of the field where the value is stored. + * {@inheritdoc} */ public function getValue(ResultRow $values, $field = NULL) { $alias = isset($field) ? $this->aliases[$field] : $this->field_alias; @@ -430,11 +401,7 @@ public function getValue(ResultRow $values, $field = NULL) { } /** - * Determines if this field will be available as an option to group the result - * by in the style settings. - * - * @return bool - * TRUE if this field handler is groupable, otherwise FALSE. + * {@inheritdoc} */ public function useStringGroupBy() { return TRUE; @@ -1119,21 +1086,12 @@ public function adminSummary() { } /** - * Run before any fields are rendered. - * - * This gives the handlers some time to set up before any handler has - * been rendered. - * - * @param \Drupal\views\ResultRow[] $values - * An array of all ResultRow objects returned from the query. + * {@inheritdoc} */ public function preRender(&$values) { } /** - * Renders the field. - * - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. + * {@inheritdoc} */ public function render(ResultRow $values) { $value = $this->getValue($values); @@ -1141,16 +1099,10 @@ public function render(ResultRow $values) { } /** - * Render a field using advanced settings. - * - * This renders a field normally, then decides if render-as-link and - * text-replacement rendering is necessary. - * - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. + * {@inheritdoc} */ public function advancedRender(ResultRow $values) { - if ($this->allowAdvancedRender() && method_exists($this, 'render_item')) { + if ($this->allowAdvancedRender() && $this instanceof MultiItemsFieldHandlerInterface) { $raw_items = $this->getItems($values); // If there are no items, set the original value to NULL. if (empty($raw_items)) { @@ -1168,7 +1120,7 @@ public function advancedRender(ResultRow $values) { if ($this->allowAdvancedRender()) { $tokens = NULL; - if (method_exists($this, 'render_item')) { + if ($this instanceof MultiItemsFieldHandlerInterface) { $items = array(); foreach ($raw_items as $count => $item) { $value = $this->render_item($count, $item); @@ -1215,17 +1167,7 @@ public function advancedRender(ResultRow $values) { } /** - * Checks if a field value is empty. - * - * @param $value - * The field value. - * @param bool $empty_zero - * Whether or not this field is configured to consider 0 as empty. - * @param bool $no_skip_empty - * Whether or not to use empty() to check the value. - * - * @return bool - * TRUE if the value is considered empty, FALSE otherwise. + * {@inheritdoc} */ public function isValueEmpty($value, $empty_zero, $no_skip_empty = TRUE) { if (!isset($value)) { @@ -1242,10 +1184,7 @@ public function isValueEmpty($value, $empty_zero, $no_skip_empty = TRUE) { } /** - * Perform an advanced text render for the item. - * - * This is separated out as some fields may render lists, and this allows - * each item to be handled individually. + * {@inheritdoc} */ public function renderText($alter) { $value = $this->last_render; @@ -1332,7 +1271,7 @@ protected function renderAltered($alter, $tokens) { } /** - * Trim the field down to the specified length. + * {@inheritdoc} */ public function renderTrimText($alter, $value) { if (!empty($alter['strip_tags'])) { @@ -1516,11 +1455,7 @@ protected function renderAsLink($alter, $text, $tokens) { } /** - * Get the 'render' tokens to use for advanced rendering. - * - * This runs through all of the fields and arguments that - * are available and gets their values. This will then be - * used in one giant str_replace(). + * {@inheritdoc} */ public function getRenderTokens($item) { $tokens = array(); @@ -1647,13 +1582,7 @@ protected function addSelfTokens(&$tokens, $item) { } protected function documentSelfTokens(&$tokens) { } /** - * Pass values to $this->getRenderer()->render() using $this->themeFunctions() as #theme. - * - * @param \Drupal\views\ResultRow $values - * Holds single row of a view's result set. - * - * @return string|false - * Returns rendered output of the given theme implementation. + * {@inheritdoc} */ function theme(ResultRow $values) { $build = array( diff --git a/core/modules/views/src/Plugin/views/field/MultiItemsFieldHandlerInterface.php b/core/modules/views/src/Plugin/views/field/MultiItemsFieldHandlerInterface.php new file mode 100644 index 0000000000..cb07251751 --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/MultiItemsFieldHandlerInterface.php @@ -0,0 +1,53 @@ +items * * @ingroup views_field_handlers - * - * @ViewsField("prerender_list") */ -class PrerenderList extends FieldPluginBase { +abstract class PrerenderList extends FieldPluginBase implements MultiItemsFieldHandlerInterface { /** * Stores all items which are used to render the items. @@ -68,12 +67,9 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { } /** - * Render all items in this field together. - * - * When using advanced render, each possible item in the list is rendered - * individually. Then the items are all pasted together. + * {@inheritdoc} */ - protected function renderItems($items) { + public function renderItems($items) { if (!empty($items)) { if ($this->options['type'] == 'separator') { return implode($this->sanitizeValue($this->options['separator'], 'xss_admin'), $items); @@ -91,7 +87,7 @@ protected function renderItems($items) { } /** - * Return an array of items for the field. + * {@inheritdoc} * * Items should be stored in the result array, if possible, as an array * with 'value' as the actual displayable value of the item, plus @@ -100,7 +96,7 @@ protected function renderItems($items) { * is to be made. Additionally, items that might be turned into tokens * should also be in this array. */ - public function getItems($values) { + public function getItems(ResultRow $values) { $field = $this->getValue($values); if (!empty($this->items[$field])) { return $this->items[$field]; @@ -109,16 +105,4 @@ public function getItems($values) { return array(); } - /** - * Determine if advanced rendering is allowed. - * - * By default, advanced rendering will NOT be allowed if the class - * inheriting from this does not implement a 'renderItems' method. - */ - protected function allowAdvancedRender() { - // Note that the advanced render bits also use the presence of - // this method to determine if it needs to render items as a list. - return method_exists($this, 'render_item'); - } - } -- 2.22.2