diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index 81ac493..4204c61 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -15,6 +15,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginBase as ComponentPluginBase; use Drupal\Core\Render\Element; +use Drupal\Core\Render\RenderContext; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\ViewExecutable; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -390,6 +391,7 @@ protected function viewsTokenReplace($text, $tokens) { // Use the unfiltered text for the Twig template, then filter the output. // Otherwise, Xss::filterAdmin could remove valid Twig syntax before the // template is parsed. + $build = array( '#type' => 'inline_template', '#template' => $text, @@ -401,7 +403,7 @@ function ($children, $elements) { ], ); - return (string) $this->getRenderer()->render($build); + return (string) $this->getRenderer()->renderPlain($build); } } diff --git a/core/modules/views/src/Tests/Handler/FieldKernelTest.php b/core/modules/views/src/Tests/Handler/FieldKernelTest.php index 1265d66..aabb813 100644 --- a/core/modules/views/src/Tests/Handler/FieldKernelTest.php +++ b/core/modules/views/src/Tests/Handler/FieldKernelTest.php @@ -193,8 +193,22 @@ public function testArgumentTokens() { return $name_field_0->advancedRender($row); }); - $this->assertNotEqual('Muh', (string) $output); - $this->assertEqual('', (string) $output, 'Ensure that the preprocess function is not executed.'); + $this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE); + $this->assertEqual('%1 !1', (string) $output, "Ensure that old style placeholders aren't replaced"); + + // This time use new style tokens but ensure that we still don't allow + // arbitrary code execution. + + $name_field_0->options['alter']['alter_text'] = TRUE; + $name_field_0->options['alter']['text'] = '{{ arguments.name }} {{ raw_arguments.name }}'; + + $row = $view->result[0]; + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { + return $name_field_0->advancedRender($row); + }); + + $this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE); + $this->assertEqual(' ', (string) $output, "Ensure that old style placeholders aren't replaced"); } /** diff --git a/core/modules/views/src/Tests/Update/ArgumentPlaceholderUpdatePathTest.php b/core/modules/views/src/Tests/Update/ArgumentPlaceholderUpdatePathTest.php new file mode 100644 index 0000000..f93d83b --- /dev/null +++ b/core/modules/views/src/Tests/Update/ArgumentPlaceholderUpdatePathTest.php @@ -0,0 +1,49 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../tests/fixtures/update/argument-placeholder.php' + ]; + } + + public function testArgumentPlaceholderUpdate() { + $this->runUpdates(); + $view = View::load('test_token_view'); + + $data = $view->toArray(); + $this->assertEqual('{{ argument.nid }}-test-class', $data['displays']['default']['display_options']['style']['options']['col_class_custom']); + $this->assertEqual('{{ argument.nid }}-test-class', $data['displays']['default']['display_options']['style']['options']['row_class_custom']); + $this->assertEqual('{{ argument.nid }}-custom-text', $data['displays']['default']['display_options']['fields']['title']['alter']['text']); + $this->assertEqual('test_token_view {{ argument.nid }}', $data['displays']['default']['display_options']['title']); + $this->assertEqual('{{ argument.nid }}-custom', $data['displays']['default']['display_options']['header']['area_text_custom']['content']); + $this->assertEqual('{{ argument.nid }}-text', $data['displays']['default']['display_options']['footer']['area']['content']['value']); + $this->assertEqual("Displaying @start - @end of @total\n\n{{ argument.nid }}-result", $data['displays']['default']['display_options']['empty']['result']['content']); + $this->assertEqual('{{ argument.nid }}-title', $data['displays']['default']['display_options']['empty']['title']['title']); + $this->assertEqual('{{ argument.nid }} title', $data['displays']['default']['display_options']['arguments']['nid']['title']); + $this->assertEqual('{{ argument.nid }} title', $data['displays']['default']['display_options']['arguments']['nid']['exception']['title']); + $this->assertEqual('{{ argument.nid }}-more-text', $data['displays']['default']['display_options']['use_more_text']); + $this->assertEqual('{{ argument.nid }}-custom-url', $data['displays']['default']['display_options']['link_url']); + } + +} diff --git a/core/modules/views/tests/fixtures/update/argument-placeholder.php b/core/modules/views/tests/fixtures/update/argument-placeholder.php new file mode 100644 index 0000000..2a277be --- /dev/null +++ b/core/modules/views/tests/fixtures/update/argument-placeholder.php @@ -0,0 +1,19 @@ +insert('config') + ->fields(array( + 'collection', + 'name', + 'data', + )) + ->values(array( + 'collection' => '', + 'name' => 'views.view.test_token_view', + 'data' => serialize(\Drupal\Component\Serialization\Yaml::decode(file_get_contents('core/modules/views/tests/modules/views_test_config/test_views/views.view.test_token_view.yml'))), + )) + ->fields([ + 'collection' => '', + ]) + ->execute(); diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.module b/core/modules/views/tests/modules/views_test_data/views_test_data.module index 4a2c1ff..ecf65b1 100644 --- a/core/modules/views/tests/modules/views_test_data/views_test_data.module +++ b/core/modules/views/tests/modules/views_test_data/views_test_data.module @@ -111,7 +111,16 @@ function template_preprocess_views_view_mapping_test(&$variables) { } } +/** + * Test pre_render function. + * + * @param array $element + * A render array + * + * @return array + * The changed render array. + */ function views_test_data_test_pre_render_function($element) { - $element['#markup'] = 'Muh'; + $element['#markup'] = 'views_test_data_test_pre_render_function executed'; return $element; } diff --git a/core/modules/views/tests/src/Unit/Plugin/area/EntityTest.php b/core/modules/views/tests/src/Unit/Plugin/area/EntityTest.php index 322ea02..0c3ff91 100644 --- a/core/modules/views/tests/src/Unit/Plugin/area/EntityTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/area/EntityTest.php @@ -130,8 +130,8 @@ protected function setupEntityManager() { */ public function providerTestTokens() { return [ - ['{{ arguments.test1 }}', 5], - ['{{ raw_arguments.test2 }}', 6], + ['{{ raw_arguments.test1 }}', 5], + ['{{ arguments.test2 }}', 6], ['{{ test_render_token }}', 7], ['{{ test:global_token }}', 8], ]; diff --git a/core/modules/views/views.install b/core/modules/views/views.install index 1e1498f..ddeaa03 100644 --- a/core/modules/views/views.install +++ b/core/modules/views/views.install @@ -123,7 +123,9 @@ function views_update_8002() { $config_factory = \Drupal::configFactory(); foreach ($config_factory->listAll('views.view.') as $view_config_name) { $view = $config_factory->getEditable($view_config_name); + $displays = $view->get('display'); + $argument_map_per_display = _views_update_argument_map($displays); $changed = FALSE; @@ -131,17 +133,30 @@ function views_update_8002() { foreach ($displays as $display_name => &$display) { if (!empty($display['display_options']['fields'])) { foreach ($display['display_options']['fields'] as $field_name => &$field) { - $token_values = ['path', 'alt', 'link_class', 'rel', 'target', 'query', 'fragment', 'prefix', 'suffix', 'more_link_text', 'more_link_path', 'link_attributes']; + $token_values = [ + 'path', + 'alt', + 'link_class', + 'rel', + 'target', + 'query', + 'fragment', + 'prefix', + 'suffix', + 'more_link_text', + 'more_link_path', + 'link_attributes' + ]; foreach ($token_values as $token_name) { - if (isset($field['alter'][$token_name])) { + if (!empty($field['alter'][$token_name])) { if (is_array($field['alter'][$token_name])) { foreach (array_keys($field['alter'][$token_name]) as $key) { - $field['alter'][$token_name][$key] = _views_update_8002_token_update($field['alter'][$token_name][$key]); + $field['alter'][$token_name][$key] = _views_update_8002_token_update($field['alter'][$token_name][$key], $argument_map_per_display[$display_name]); } } else { - $field['alter'][$token_name] = _views_update_8002_token_update($field['alter'][$token_name]); + $field['alter'][$token_name] = _views_update_8002_token_update($field['alter'][$token_name], $argument_map_per_display[$display_name]); } $changed = TRUE; } @@ -158,16 +173,16 @@ function views_update_8002() { foreach ($display['display_options'][$area_type] as &$area) { switch ($area['plugin_id']) { case 'title': - $area['title'] = _views_update_8002_token_update($area['title']); + $area['title'] = _views_update_8002_token_update($area['title'], $argument_map_per_display[$display_name]); break; case 'result': - $area['content'] = _views_update_8002_token_update($area['content']); + $area['content'] = _views_update_8002_token_update($area['content'], $argument_map_per_display[$display_name]); break; case 'text': - $area['content']['value'] = _views_update_8002_token_update($area['content']['value']); + $area['content']['value'] = _views_update_8002_token_update($area['content']['value'], $argument_map_per_display[$display_name]); break; case 'text_custom': - $area['content'] = _views_update_8002_token_update($area['content']); + $area['content'] = _views_update_8002_token_update($area['content'], $argument_map_per_display[$display_name]); break; } } @@ -181,10 +196,10 @@ function views_update_8002() { if (!empty($display['display_options']['arguments'])) { foreach ($display['display_options']['arguments'] as &$argument) { if (isset($argument['exception']['title'])) { - $argument['exception']['title'] = _views_update_8002_token_update($argument['exception']['title']); + $argument['exception']['title'] = _views_update_8002_token_update($argument['exception']['title'], $argument_map_per_display[$display_name]); } if (isset($argument['title'])) { - $argument['title'] = _views_update_8002_token_update($argument['title']); + $argument['title'] = _views_update_8002_token_update($argument['title'], $argument_map_per_display[$display_name]); } } } @@ -194,13 +209,13 @@ function views_update_8002() { // Update the more link text and more link URL. foreach ($displays as $display_name => &$display) { if (!empty($display['display_options']['title'])) { - $display['display_options']['title'] = _views_update_8002_token_update($display['display_options']['title']); + $display['display_options']['title'] = _views_update_8002_token_update($display['display_options']['title'], $argument_map_per_display[$display_name]); } if (!empty($display['display_options']['use_more_text'])) { - $display['display_options']['use_more_text'] = _views_update_8002_token_update($display['display_options']['use_more_text']); + $display['display_options']['use_more_text'] = _views_update_8002_token_update($display['display_options']['use_more_text'], $argument_map_per_display[$display_name]); } if (!empty($display['display_options']['link_url'])) { - $display['display_options']['link_url'] = _views_update_8002_token_update($display['display_options']['link_url']); + $display['display_options']['link_url'] = _views_update_8002_token_update($display['display_options']['link_url'], $argument_map_per_display[$display_name]); } } @@ -209,22 +224,22 @@ function views_update_8002() { foreach ($displays as $display_name => &$display) { if (!empty($display['display_options']['style'])) { if (!empty($display['display_options']['style']['row_class'])) { - $display['display_options']['style']['row_class'] = _views_update_8002_token_update($display['display_options']['style']['row_class']); + $display['display_options']['style']['row_class'] = _views_update_8002_token_update($display['display_options']['style']['row_class'], $argument_map_per_display[$display_name]); } if (!empty($display['display_options']['style']['col_class_custom'])) { - $display['display_options']['style']['col_class_custom'] = _views_update_8002_token_update($display['display_options']['style']['col_class_custom']); + $display['display_options']['style']['col_class_custom'] = _views_update_8002_token_update($display['display_options']['style']['col_class_custom'], $argument_map_per_display[$display_name]); } if (!empty($display['display_options']['style']['row_class_custom'])) { - $display['display_options']['style']['row_class_custom'] = _views_update_8002_token_update($display['display_options']['style']['row_class_custom']); + $display['display_options']['style']['row_class_custom'] = _views_update_8002_token_update($display['display_options']['style']['row_class_custom'], $argument_map_per_display[$display_name]); } if (!empty($display['display_options']['style']['description'])) { - $display['display_options']['style']['description'] = _views_update_8002_token_update($display['display_options']['style']['description']); + $display['display_options']['style']['description'] = _views_update_8002_token_update($display['display_options']['style']['description'], $argument_map_per_display[$display_name]); } } } - if ($changed) { + $view->set('display', $displays); $view->save(TRUE); } } @@ -240,13 +255,45 @@ function views_update_8002() { * @return string * The updated value. */ -function _views_update_8002_token_update($text) { - preg_replace_callback('/%(\d)/', function ($arg1) { return "{{ arguments.{$arg1[1]} }}"; }, $text); - preg_replace_callback('/!(\d)/', function ($arg1) { return "{{ raw_arguments.{$arg1[1]} }}"; }, $text); +function _views_update_8002_token_update($text, array $argument_map) { + $text = preg_replace_callback('/%(\d)/', function ($match) use ($argument_map) { + return "{{ arguments.{$argument_map[$match[1]]} }}"; + }, $text); + $text = preg_replace_callback('/!(\d)/', function ($match) use ($argument_map) { + return "{{ raw_arguments.{$argument_map[$match[1]]} }}"; + }, $text); return $text; } /** + * Updates argument maps. + * + * @param array $displays + * + * @return array + * The argument map. + */ +function _views_update_argument_map($displays) { + $argument_map = []; + foreach ($displays as $display_id => $display) { + $argument_map[$display_id] = []; + if (isset($display['display_options']['arguments'])) { + foreach (array_keys($display['display_options']['arguments']) as $number => $name) { + $argument_map[$display_id][$number + 1] = $name; + } + } + elseif (isset($displays['default']['display_options']['arguments'])) { + foreach (array_keys($displays['default']['display_options']['arguments']) as $number => $name) { + $argument_map[$display_id][$number + 1] = $name; + } + } + } + + return $argument_map; +} + +/** * @} End of "addtogroup updates-8.0.0-beta". */ +