diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index e56dcae..420437c 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -365,6 +365,12 @@ protected function viewsTokenReplace($text, $tokens) { if (strpos($token, '{{') !== FALSE) { // Twig wants a token replacement array stripped of curly-brackets. $token = trim(str_replace(array('{', '}'), '', $token)); + + // We need to validate tokens are valid Twig variables. Twig uses the + // same variable naming rules as PHP. + // @see http://php.net/manual/en/language.variables.basics.php + assert('preg_match(\'/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/\', $token) === 1', 'Tokens need to be valid Twig variables.'); + $twig_tokens[$token] = $replacement; } else { diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php index 98f1a59..e4e3b0a 100644 --- a/core/modules/views/src/Plugin/views/field/Field.php +++ b/core/modules/views/src/Plugin/views/field/Field.php @@ -913,7 +913,6 @@ protected function addSelfTokens(&$tokens, $item) { // Use \Drupal\Component\Utility\Xss::filterAdmin() because it's user data // and we can't be sure it is safe. We know nothing about the data, // though, so we can't really do much else. - if (isset($item['raw'])) { $raw = $item['raw']; diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index f5bc20c..6107eb5 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -1676,7 +1676,8 @@ protected function getTokenValuesRecursive(array $array, array $parent_keys = ar * fields as a list. For example, the field that displays all terms * on a node might have tokens for the tid and the term. * - * By convention, tokens should follow the format of {{ token__subtoken }} + * By convention, tokens should follow the format of {{ token + * subtoken }} * where token is the field ID and subtoken is the field. If the * field ID is terms, then the tokens might be {{ terms__tid }} and * {{ terms__name }}. diff --git a/core/modules/views/src/Tests/Plugin/PluginBaseTest.php b/core/modules/views/src/Tests/Plugin/PluginBaseTest.php new file mode 100644 index 0000000..f4b5c2d --- /dev/null +++ b/core/modules/views/src/Tests/Plugin/PluginBaseTest.php @@ -0,0 +1,61 @@ +testPluginBase = new TestPluginBase(); + } + + /** + * Test that the token replacement in views works correctly. + */ + public function testViewsTokenReplace() { + $text = '{{ langcode__value }} means {{ langcode }}'; + $tokens = ['{{ langcode }}' => SafeString::create('English'), '{{ langcode__value }}' => 'en']; + + $result = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function () use ($text, $tokens) { + return $this->testPluginBase->viewsTokenReplace($text, $tokens); + }); + + $this->assertIdentical($result, 'en means English'); + } + +} + +/** + * Helper class for using the PluginBase abstract class. + */ +class TestPluginBase extends PluginBase { + + public function __construct() { + parent::__construct([], '', []); + } + + public function viewsTokenReplace($text, $tokens) { + return parent::viewsTokenReplace($text, $tokens); + } + +} diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldTest.php index 74d5d3d..0f8feef 100644 --- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldTest.php +++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldTest.php @@ -46,7 +46,7 @@ public function getTestValue() { * Overrides Drupal\views\Plugin\views\field\FieldPluginBase::addSelfTokens(). */ protected function addSelfTokens(&$tokens, $item) { - $tokens['[test-token]'] = $this->getTestValue(); + $tokens['[test__token]'] = $this->getTestValue(); } /**