diff --git a/core/lib/Drupal/Core/Render/ReadOnlyRenderArray.php b/core/lib/Drupal/Core/Render/ProtectedRenderArray.php similarity index 85% rename from core/lib/Drupal/Core/Render/ReadOnlyRenderArray.php rename to core/lib/Drupal/Core/Render/ProtectedRenderArray.php index b17a680..7e675c4 100644 --- a/core/lib/Drupal/Core/Render/ReadOnlyRenderArray.php +++ b/core/lib/Drupal/Core/Render/ProtectedRenderArray.php @@ -7,10 +7,10 @@ * * @ingroup utility */ -class ReadOnlyRenderArray extends \ArrayObject implements RenderableInterface { +class ProtectedRenderArray extends \ArrayObject implements RenderableInterface { /** - * ReadOnlyRenderArray constructor. + * ProtectedRenderArray constructor. * * @param array $input * The render array to wrap into a read-only object. @@ -32,7 +32,7 @@ public function toRenderable() { * @param mixed $index * The index with the value. * - * @return \Drupal\Core\Render\ReadOnlyRenderArray|mixed + * @return \Drupal\Core\Render\ProtectedRenderArray|mixed */ public function __get($index) { return $this->offsetGet($index); @@ -44,7 +44,7 @@ public function __get($index) { public function offsetGet($index) { $value = parent::offsetGet($index); if (is_array($value) && !empty($value)) { - $value = new ReadOnlyRenderArray($value); + $value = new ProtectedRenderArray($value); } return $value; } diff --git a/core/lib/Drupal/Core/Render/RenderArrayIterator.php b/core/lib/Drupal/Core/Render/RenderArrayIterator.php index 55a4302..e7ffded 100644 --- a/core/lib/Drupal/Core/Render/RenderArrayIterator.php +++ b/core/lib/Drupal/Core/Render/RenderArrayIterator.php @@ -15,7 +15,7 @@ class RenderArrayIterator extends \ArrayIterator { public function offsetGet($index) { $value = parent::offsetGet($index); if (is_array($value) && !empty($value)) { - $value = new ReadOnlyRenderArray($value); + $value = new ProtectedRenderArray($value); } return $value; } @@ -26,7 +26,7 @@ public function offsetGet($index) { public function current() { $value = parent::current(); if (is_array($value) && !empty($value)) { - $value = new ReadOnlyRenderArray($value); + $value = new ProtectedRenderArray($value); } return $value; } diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index 584c600..91ddcc1 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -4,7 +4,7 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Render\Markup; -use Drupal\Core\Render\ReadOnlyRenderArray; +use Drupal\Core\Render\ProtectedRenderArray; use Drupal\Core\State\StateInterface; /** @@ -155,7 +155,7 @@ public function wrapContext(&$context) { if (is_array($context)) { foreach ($context as $key => $value) { if (is_array($context[$key]) && !empty($context[$key])) { - $context[$key] = new ReadOnlyRenderArray($context[$key]); + $context[$key] = new ProtectedRenderArray($context[$key]); } } } @@ -168,12 +168,12 @@ public function wrapContext(&$context) { * An array of parameters. */ public function unWrapContext(&$context) { - if (is_object($context) && $context instanceof ReadOnlyRenderArray) { + if (is_object($context) && $context instanceof ProtectedRenderArray) { $context = $context->getArrayCopy(); } if (is_array($context)) { foreach ($context as $key => $value) { - if (is_object($context[$key]) && $context[$key] instanceof ReadOnlyRenderArray) { + if (is_object($context[$key]) && $context[$key] instanceof ProtectedRenderArray) { $context[$key] = $context[$key]->getArrayCopy(); } } diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index d0d8779..014c2e3 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -9,7 +9,7 @@ use Drupal\Core\Render\AttachmentsInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\Markup; -use Drupal\Core\Render\ReadOnlyRenderArray; +use Drupal\Core\Render\ProtectedRenderArray; use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\UrlGeneratorInterface; @@ -248,7 +248,7 @@ public function getPath($name, $parameters = [], $options = []) { * (optional) An associative array of additional options. The 'absolute' * option is forced to be TRUE. * - * @return \Drupal\Core\Render\ReadOnlyRenderArray + * @return \Drupal\Core\Render\ProtectedRenderArray * The generated absolute URL for the given route. * * @todo Add an option for scheme-relative URLs. @@ -261,7 +261,7 @@ public function getUrl($name, $parameters = [], $options = []) { // Return as render array, so we can bubble the bubbleable metadata. $build = ['#markup' => $generated_url->getGeneratedUrl()]; $generated_url->applyTo($build); - return new ReadOnlyRenderArray($build); + return new ProtectedRenderArray($build); } /** @@ -274,7 +274,7 @@ public function getUrl($name, $parameters = [], $options = []) { * @param array|\Drupal\Core\Template\Attribute $attributes * An optional array or Attribute object of link attributes. * - * @return \Drupal\Core\Render\ReadOnlyRenderArray + * @return \Drupal\Core\Render\ProtectedRenderArray * A render array representing a link to the given URL. */ public function getLink($text, $url, $attributes = []) { @@ -301,7 +301,7 @@ public function getLink($text, $url, $attributes = []) { '#title' => $text, '#url' => $url, ]; - return new ReadOnlyRenderArray($build); + return new ProtectedRenderArray($build); } /** diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index e0a173a..60391ff 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -7,9 +7,10 @@ use Drupal\comment\CommentInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; +use Drupal\Component\Utility\Xss; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Render\ReadOnlyRenderArray; +use Drupal\Core\Render\Markup; use Drupal\Core\Url; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; @@ -546,7 +547,9 @@ function template_preprocess_forum_list(&$variables) { $row = 0; // Sanitize each forum so that the template can safely print the data. foreach ($variables['forums'] as $id => $forum) { - $variables['forums'][$id]->description = new ReadOnlyRenderArray(['#markup' => $forum->description->value]); + // Work-around the fact that this is an object instead of a render array + // by setting the property to a filtered Markup object so it's not escaped. + $variables['forums'][$id]->description = Markup::create(Xss::filter($forum->description->value)); $variables['forums'][$id]->link = forum_uri($forum); $variables['forums'][$id]->name = $forum->label(); $variables['forums'][$id]->is_container = !empty($forum->forum_container->value); diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php index 49169c4..4024a8a 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php @@ -3,7 +3,7 @@ namespace Drupal\Tests\Core\Template; use Drupal\Core\GeneratedLink; -use Drupal\Core\Render\ReadOnlyRenderArray; +use Drupal\Core\Render\ProtectedRenderArray; use Drupal\Core\Render\RenderableInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Template\Loader\StringLoader; @@ -219,7 +219,7 @@ public function testSafeJoin() { $markup->__toString()->willReturn('will be markup'); $markup = $markup->reveal(); - $items = new ReadOnlyRenderArray([ + $items = new ProtectedRenderArray([ 'will be escaped', $markup, ['#markup' => 'will be rendered'], @@ -228,7 +228,7 @@ public function testSafeJoin() { $this->assertEquals('<em>will be escaped</em>
will be markup
will be rendered', $result); // Ensure safe_join Twig filter supports Traversable variables. - $items = new ReadOnlyRenderArray([ + $items = new ProtectedRenderArray([ 'will be escaped', $markup, ['#markup' => 'will be rendered'],