diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 99e807a..4cec15b 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -136,6 +136,7 @@ public function getFilters() { // Replace twig's escape filter with our own. new \Twig_SimpleFilter('drupal_escape', [$this, 'escapeFilter'], array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), + new \Twig_SimpleFilter('drupal_escape_placeholder', [$this, 'escapePlaceholder'], array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), // Implements safe joining. // @todo Make that the default for |join? Upstream issue: @@ -351,6 +352,28 @@ public function attachLibrary($library) { } /** + * Provides a placeholder wrapper around ::escapeFilter. + * + * @param \Twig_Environment $env + * A Twig_Environment instance. + * @param mixed $arg + * The value to be escaped. + * @param string $strategy + * The escaping strategy. Defaults to 'html'. + * @param string $charset + * The charset. + * @param bool $autoescape + * Whether the function is called by the auto-escaping feature (TRUE) or by + * the developer (FALSE). + * + * @return string|null + * The escaped, rendered output, or NULL if there is no valid output. + */ + public function escapePlaceholder(\Twig_Environment $env, $arg, $strategy = 'html', $charset = NULL, $autoescape = FALSE) { + return '' . $this->escapeFilter($env, $arg, $strategy, $charset, $autoescape) . ''; + } + + /** * Overrides twig_escape_filter(). * * Replacement function for Twig's escape filter. diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php index a08ec2d..585013f 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php +++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php @@ -19,9 +19,19 @@ class TwigNodeVisitor implements \Twig_NodeVisitorInterface { /** + * Defines whether we are in a trans node. + * + * @var bool + */ + protected $inTransNode = FALSE; + + /** * {@inheritdoc} */ function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) { + if ($node instanceof TwigNodeTrans) { + $this->inTransNode = TRUE; + } return $node; } @@ -29,6 +39,10 @@ function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) { * {@inheritdoc} */ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) { + if ($node instanceof TwigNodeTrans) { + $this->inTransNode = FALSE; + } + // We use this to inject a call to render_var -> TwigExtension->renderVar() // before anything is printed. if ($node instanceof \Twig_Node_Print) { @@ -47,6 +61,13 @@ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) { // Change the 'escape' filter to our own 'drupal_escape' filter. else if ($node instanceof \Twig_Node_Expression_Filter) { $name = $node->getNode('filter')->getAttribute('value'); + if (!$this->inTransNode && 'placeholder' === $name) { + // Use our own escape filter that is SafeMarkup aware. + $node->getNode('filter')->setAttribute('value', 'drupal_escape_placeholder'); + + // Store that we have a filter active already that knows how to deal with render arrays. + $this->skipRenderVarFunction = TRUE; + } if ('escape' == $name || 'e' == $name) { // Use our own escape filter that is SafeMarkup aware. $node->getNode('filter')->setAttribute('value', 'drupal_escape');