diff --git a/core/includes/common.inc b/core/includes/common.inc index be31bf8..c1dd7c9 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3373,13 +3373,13 @@ function drupal_render_children(&$element, $children_keys = NULL) { if ($children_keys === NULL) { $children_keys = Element::children($element); } - $markups = array(); + $output = ''; foreach ($children_keys as $key) { if (!empty($element[$key])) { - $markups[] = drupal_render($element[$key]); + $output .= drupal_render($element[$key]); } } - return SafeMarkup::implode('', $markups); + return SafeMarkup::set($output); } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 5b40372..68debb9 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -2018,7 +2018,13 @@ function template_preprocess_html(&$variables) { } $variables['head_title_array'] = $head_title; - $variables['head_title'] = SafeMarkup::implode(' | ', $head_title); + $output = ''; + $separator = ''; + foreach ($head_title as $item) { + $output .= $separator . SafeMarkup::escape($item); + $separator = ' | '; + } + $variables['head_title'] = SafeMarkup::set($output); // @todo Remove drupal_*_html_head() and refactor accordingly. $html_heads = drupal_get_html_head(FALSE); diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php index 4043da3..871cf74 100644 --- a/core/lib/Drupal/Component/Utility/SafeMarkup.php +++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php @@ -31,13 +31,13 @@ class SafeMarkup { * markup; instead, use the appropriate * @link sanitization sanitization functions @endlink or the * @link theme_render theme and render systems @endlink so that the output - * can be themed and altered properly. + * can be themed, escaped, and altered properly. * * This marks strings as secure for the entire page render, not just the code - * or element that set it. Therefore, only complete strings should be + * or element that set it. Therefore, only valid HTML should be * marked as safe (never partial markup). For example, you should never do: * @code - * SafeMarkup::set("'"); + * SafeMarkup::set("<"); * @endcode * or: * @code @@ -81,33 +81,6 @@ public static function isSafe($string, $strategy = 'html') { } /** - * Safely implodes strings by escaping any that are not known to be safe. - * - * @param string $delimiter - * The delimiter to use when imploding. - * @param array $array - * An array of the strings to implode. - * @param string $strategy - * The escaping strategy. See SafeMarkup::set(). Defaults to 'html'. - * - * @return \Drupal\Component\Utility\SafeMarkup|string - * The imploded string, which is now also marked as safe. - */ - public static function implode($delimiter, array $array, $strategy = 'html') { - $array[] = $delimiter; - foreach ($array as $key => $string) { - if (!(isset(static::$safeStrings[(string) $string][$strategy]) || - isset(static::$safeStrings[(string) $string]['all']))) { - $array[$key] = String::checkPlain($string); - } - } - // Recover the delimiter. - $delimiter = array_pop($array); - static::set($delimiter, $strategy); - return static::set(implode($delimiter, $array), $strategy); - } - - /** * Adds previously retrieved known safe strings to the safe string list. * * This is useful for the batch and form APIs, where it is important to @@ -135,6 +108,20 @@ public static function setMultiple(array $safe_strings) { } /** + * Encodes special characters in a plain-text string for display as HTML. + * + * @param $string + * A string. + * + * @return string + * The escaped string. If $string was already set as safe with + * SafeString::set, it won't be escaped again. + */ + public static function escape($string) { + return static::isSafe($string) ? $string : String::checkPlain($string); + } + + /** * Retrieves all strings currently marked as safe. * * This is useful for the batch and form APIs, where it is important to diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php index 6a168d2..c33a070 100644 --- a/core/modules/field/src/Plugin/views/field/Field.php +++ b/core/modules/field/src/Plugin/views/field/Field.php @@ -688,12 +688,22 @@ public function submitGroupByForm(&$form, &$form_state) { */ protected function renderItems($items) { if (!empty($items)) { + $output = ''; if (!$this->options['group_rows']) { - return SafeMarkup::implode('', $items); + foreach ($items as $item) { + $output .= SafeMarkup::escape($item); + } + return SafeMarkup::set($output); } - if ($this->options['multi_type'] == 'separator') { - return SafeMarkup::implode(Xss::filterAdmin($this->options['separator']), $items); + $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); } else { $item_list = array( diff --git a/core/modules/field_ui/src/DisplayOverviewBase.php b/core/modules/field_ui/src/DisplayOverviewBase.php index bb2d114..f450313 100644 --- a/core/modules/field_ui/src/DisplayOverviewBase.php +++ b/core/modules/field_ui/src/DisplayOverviewBase.php @@ -404,8 +404,14 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent $this->alterSettingsSummary($summary, $plugin, $field_definition); if (!empty($summary)) { + $summary_escaped = ''; + $separator = ''; + foreach ($summary as $summary_item) { + $summary_escaped .= $separator . SafeMarkup::escape($summary_item); + $separator = '
'; + } $field_row['settings_summary'] = array( - '#markup' => '
' . SafeMarkup::implode('
', $summary) . '
', + '#markup' => SafeMarkup::set('
' . $summary_escaped . '
'), '#cell_attributes' => array('class' => array('field-plugin-summary-cell')), ); } diff --git a/core/modules/field_ui/src/FieldConfigListBuilder.php b/core/modules/field_ui/src/FieldConfigListBuilder.php index 74b2310..4a00229 100644 --- a/core/modules/field_ui/src/FieldConfigListBuilder.php +++ b/core/modules/field_ui/src/FieldConfigListBuilder.php @@ -8,7 +8,6 @@ namespace Drupal\field_ui; use Drupal\Component\Utility\SafeMarkup; -use Drupal\Component\Utility\String; use Drupal\Core\Config\Entity\ConfigEntityListBuilder; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityManagerInterface; @@ -119,7 +118,13 @@ public function buildRow(EntityInterface $field) { $usage[] = $this->bundles[$field->entity_type][$bundle]['label']; } } - $row['data']['usage'] = SafeMarkup::implode(', ', $usage); + $usage_escaped = ''; + $separator = ''; + foreach ($usage as $usage_item) { + $usage_escaped .= $separator . SafeMarkup::escape($usage_item); + $separator = ', '; + } + $row['data']['usage'] = SafeMarkup::set($usage_escaped); return $row; } diff --git a/core/modules/file/file.module b/core/modules/file/file.module index ce47280..e91a572 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -931,10 +931,10 @@ function file_save_upload($form_field_name, $validators = array(), $destination '#theme' => 'item_list', '#items' => $errors, ); - $message = SafeMarkup::implode('', array($message, drupal_render($item_list))); + $message = SafeMarkup::set($message . drupal_render($item_list)); } else { - $message = SafeMarkup::implode('', array($message, ' ', array_pop($errors))); + $message = SafeMarkup::set($message . ' ' . SafeMarkup::escape(array_pop($errors))); } drupal_set_message($message, 'error'); $files[$i] = FALSE; diff --git a/core/modules/node/src/Plugin/Search/NodeSearch.php b/core/modules/node/src/Plugin/Search/NodeSearch.php index 1be527d..f8e76a8 100644 --- a/core/modules/node/src/Plugin/Search/NodeSearch.php +++ b/core/modules/node/src/Plugin/Search/NodeSearch.php @@ -269,10 +269,10 @@ public function execute() { unset($build['#theme']); // Fetch comment count for snippet. - $node->rendered = SafeMarkup::implode(' ', array( - drupal_render($build), - $this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)), - )); + $node->rendered = SafeMarkup::set( + drupal_render($build) . ' ' . + SafeMarkup::escape($this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode))) + ); $extra = $this->moduleHandler->invokeAll('node_search_result', array($node, $item->langcode)); diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 4d79262..5bfcc2f 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -462,7 +462,7 @@ function rdf_preprocess_comment(&$variables) { $created_metadata_markup = drupal_render($rdf_metadata); // Appends the markup to the created variable and the submitted variable // which are both available in comment.html.twig. - $variables['created'] = SafeMarkup::implode('', array($variables['created'], $created_metadata_markup)); + $variables['created'] = SafeMarkup::set(SafeMarkup::escape($variables['created']) . $created_metadata_markup); $variables['submitted'] = SafeMarkup::set($variables['submitted'] . $created_metadata_markup); } $title_mapping = $mapping->getPreparedFieldMapping('subject'); diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module index bc1f77f..0c93cb9 100644 --- a/core/modules/responsive_image/responsive_image.module +++ b/core/modules/responsive_image/responsive_image.module @@ -259,7 +259,7 @@ function theme_responsive_image($variables) { } $output[] = ''; - return SafeMarkup::implode("\n", $output); + return SafeMarkup::set(implode("\n", $output)); } } diff --git a/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module b/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module index cc2526a..75ddd3d 100644 --- a/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module +++ b/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module @@ -16,5 +16,5 @@ */ function search_embedded_form_preprocess_search_result(&$variables) { $form = \Drupal::formBuilder()->getForm('Drupal\search_embedded_form\Form\SearchEmbeddedForm'); - $variables['snippet'] = SafeMarkup::implode('', array($variables['snippet'] , drupal_render($form))); + $variables['snippet'] = SafeMarkup::set(SafeMarkup::escape($variables['snippet']) . drupal_render($form)); } diff --git a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc index 667bdd6..6964668 100644 --- a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc +++ b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc @@ -83,7 +83,7 @@ function _batch_test_finished_helper($batch_id, $success, $results, $operations) $messages = array("results for batch $batch_id"); if ($results) { foreach ($results as $op => $op_results) { - $messages[] = 'op '. $op . ': processed ' . count($op_results) . ' elements'; + $messages[] = 'op '. SafeMarkup::escape($op) . ': processed ' . count($op_results) . ' elements'; } } else { @@ -96,9 +96,7 @@ function _batch_test_finished_helper($batch_id, $success, $results, $operations) $messages[] = t('An error occurred while processing @op with arguments:
@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE))); } - // The BR tag is safe as a delimiter. - SafeMarkup::set('
'); - drupal_set_message(SafeMarkup::implode('
', $messages)); + drupal_set_message(SafeMarkup::set(implode('
', $messages))); } /** diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 9f4e6ba..922a42f 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -543,7 +543,7 @@ function template_preprocess_views_view_table(&$variables) { '#style' => $initial, ); $markup = drupal_render($tablesort_indicator); - $label = SafeMarkup::implode('', array($label, $markup)); + $label = SafeMarkup::set($label . $markup); } $query['order'] = $field; @@ -634,7 +634,7 @@ function template_preprocess_views_view_table(&$variables) { $field_output = $handler->getField($num, $field); $element_type = $fields[$field]->elementType(TRUE, TRUE); if ($element_type) { - $field_output = SafeMarkup::implode('', array(SafeMarkup::set('<' . $element_type . '>'), $field_output, SafeMarkup::set(''))); + $field_output = SafeMarkup::set('<' . $element_type . '>' . SafeMarkup::escape($field_output) . ''); } // Only bother with separators and stuff if the field shows up. @@ -642,13 +642,17 @@ function template_preprocess_views_view_table(&$variables) { // Place the field into the column, along with an optional separator. if (!empty($column_reference['content'])) { if (!empty($options['info'][$column]['separator'])) { - $column_reference['content'] = SafeMarkup::implode('', array($column_reference['content'], Xss::filterAdmin($options['info'][$column]['separator']))); + $safe_content = SafeMarkup::escape($column_reference['content']); + $safe_separator = Xss::filterAdmin($options['info'][$column]['separator']); + $column_reference['content'] = SafeMarkup::set($safe_content . $safe_separator); } } else { $column_reference['content'] = ''; } - $column_reference['content'] = SafeMarkup::implode('', array($column_reference['content'], $field_output)); + $safe_content = SafeMarkup::escape($column_reference['content']); + $safe_field_output = SafeMarkup::escape($field_output); + $column_reference['content'] = SafeMarkup::set($safe_content . $safe_field_output); } } $column_reference['attributes'] = new Attribute($column_reference['attributes']); diff --git a/core/modules/views_ui/src/Controller/ViewsUIController.php b/core/modules/views_ui/src/Controller/ViewsUIController.php index 8ad44fd..0138266 100644 --- a/core/modules/views_ui/src/Controller/ViewsUIController.php +++ b/core/modules/views_ui/src/Controller/ViewsUIController.php @@ -93,7 +93,7 @@ public function reportFields() { foreach ($views as $view) { $rows[$field_name]['data'][1][] = $this->l($view, 'views_ui.edit', array('view' => $view)); } - $rows[$field_name]['data'][1] = SafeMarkup::implode(', ', $rows[$field_name]['data'][1]); + $rows[$field_name]['data'][1] = SafeMarkup::set(implode(', ', $rows[$field_name]['data'][1])); } // Sort rows by field name. @@ -121,7 +121,7 @@ public function reportPlugins() { foreach ($row['views'] as $row_name => $view) { $row['views'][$row_name] = $this->l($view, 'views_ui.edit', array('view' => $view)); } - $row['views'] = SafeMarkup::implode(', ', $row['views']); + $row['views'] = SafeMarkup::set(implode(', ', $row['views'])); } // Sort rows by field name. diff --git a/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php index 191a2aa..7321712 100644 --- a/core/modules/views_ui/src/ViewListBuilder.php +++ b/core/modules/views_ui/src/ViewListBuilder.php @@ -81,6 +81,12 @@ public function load() { */ public function buildRow(EntityInterface $view) { $row = parent::buildRow($view); + $display_paths = ''; + $separator = ''; + foreach ($this->getDisplayPaths($view) as $display_path) { + $display_paths .= $separator . SafeMarkup::escape($display_path); + $separator = ', '; + } return array( 'data' => array( 'view_name' => array( @@ -97,7 +103,7 @@ public function buildRow(EntityInterface $view) { 'class' => array('views-table-filter-text-source'), ), 'tag' => $view->get('tag'), - 'path' => SafeMarkup::implode(', ', $this->getDisplayPaths($view)), + 'path' => SafeMarkup::set($display_paths), 'operations' => $row['operations'], ), 'title' => $this->t('Machine name: @name', array('@name' => $view->id())), diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc index 6fe4d76..efa54d9 100644 --- a/core/modules/views_ui/views_ui.theme.inc +++ b/core/modules/views_ui/views_ui.theme.inc @@ -89,7 +89,19 @@ function template_preprocess_views_ui_display_tab_bucket(&$variables) { */ function template_preprocess_views_ui_view_info(&$variables) { $variables['title'] = $variables['view']->label(); - $variables['displays'] = empty($variables['displays']) ? t('None') : SafeMarkup::set(format_plural(count($variables['displays']), 'Display', 'Displays') . ': ' . '' . SafeMarkup::implode(', ', $variables['displays']) . ''); + if (empty($variables['displays'])) { + $displays = t('None'); + } + else { + $displays = format_plural(count($variables['displays']), 'Display', 'Displays') . ': '; + $separator = ''; + foreach ($variables['displays'] as $displays_item) { + $displays .= $separator . SafeMarkup::escape($displays_item); + $separator = ', '; + } + $displays = SafeMarkup::set($displays . ''); + } + $variables['displays'] = $displays; } /** diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php index 7aeb2b4..bb82c90 100644 --- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php @@ -8,7 +8,6 @@ namespace Drupal\Tests\Component\Utility; use Drupal\Component\Utility\SafeMarkup; -use Drupal\Component\Utility\String; use Drupal\Tests\UnitTestCase; /** @@ -78,43 +77,6 @@ public function testStrategy() { } /** - * Tests string formatting with SafeMarkup::implode(). - * - * @dataProvider providerImplode - * - * @param string $delimiter - * The delimiter to run through SafeMarkup::implode(). - * @param string $args - * The arguments to pass into SafeMarkup::implode(). - * @param string $expected - * The expected result from calling the function. - * @param string $message - * The message to display as output to the test. - * - * @covers ::implode() - */ - public function testImplode($delimiter, $args, $expected, $message) { - $result = SafeMarkup::implode($delimiter, $args); - $this->assertEquals($expected, $result, $message); - $this->assertTrue(SafeMarkup::isSafe($result), 'The return value has been marked as safe'); - } - - /** - * Data provider for testImplode(). - * - * @see testImplode() - */ - public function providerImplode() { - $tests[] = array('|', array('Simple text'), 'Simple text', 'SafeMarkup::implode() leaves simple text alone.'); - $tests[] = array(' | ', array('Escaped text: