41

I have some html content, it comes from a external library so i can not change it, it has many <span> and <div> tags with attributes like: <span style="color: #0000ff;">, but when i use:

$render = array(
  '#markup' => $myhtmlcontent,
);

In a render array, Drupal strips the attributes, and it keeps just <span> without the styles.

So, how can I avoid Drupal changes what I pass as markup, which doesn't need any filter because the library makes it safe, or at last make it allow styles?

1
  • style attributes are always stripped out Commented Oct 25, 2016 at 21:26

4 Answers 4

35

Use inline template:

  return [
    '#type' => 'inline_template',
    '#template' => '{{ somecontent }}',
    '#context' => [
      'somecontent' => $somecontent
    ]
  ];

In your case without context, just straight up html.

You can also use the html_tag element for things like script or link and so on.

5
  • 3
    This filters out IFRAME tags.
    – joachim
    Commented May 2, 2016 at 14:40
  • 2
    And script and style tags Commented Oct 25, 2016 at 21:25
  • 10
    Try {{ variable | raw }} or <tag>{{variable}}</tag>
    – user21641
    Commented Oct 26, 2016 at 11:48
  • 3
    @IvanJaros That will just skip auto escaping the variable, but \Drupal\Component\Utility\Xss::filterAdmin() will still filter out tags like <script> and style attributes. The only way I have seen so far is to add a Javascript file as a library and add '#attached' => 'library' => "module_name/library_key_in_yml" to the above return. See drupal.stackexchange.com/questions/211078/… Commented Oct 29, 2016 at 23:04
  • Well, of course if you want to use script or any other "high risk" tags you are not supposed to do it via this method bud via theme function and template, duh.
    – user21641
    Commented Oct 30, 2016 at 11:46
34

In a render array, simply do this:

return [
  '#children' => $html,
];

EDIT: Alternatively use the method described by @nvahalik in the other answer, in combination with '#markup'. I think this is closer to the intended usage of the render API.

use Drupal\Core\Render\Markup;

[..]
return [
  '#markup' => Markup::create($html),
];

E.g. if the $html was produced with PHP's highlight_string(), it will contain style attributes which would be removed with #markup. But #children preserves them.

How to test this?

Go to devel/php (with devel module installed).

$elements = [
 ['#markup' => '<div style="font-weight: bold;">markup onionpowder</div>' . "\n"],
 ['#children' => '<div style="font-weight: bold;">children onionpowder</div>' . "\n"],
 ['#markup' => \Drupal\Core\Render\Markup::create('<div style="font-weight: bold;">safe markup onionpowder</div>' . "\n")],
];

var_export(drupal_render($elements)->__toString());

I personally get this output:

"""
'<div>markup onionpowder</div>\n
<div style="font-weight: bold;">children onionpowder</div>\n
<div style="font-weight: bold;">safe markup onionpowder</div>\n
'
"""

(The "onionpowder" allows to set conditional break points in the render functions)

But perhaps it depends on the Drupal version?

Also it can happen that this will be (perhaps unintentionally) processed again in a different point in the pipeline.

Technical notes

See https://git.drupalcode.org/project/drupal/blob/8.8.x/core/lib/Drupal/Core/Render/Renderer.php#L184

The '#markup' is filtered in ->ensureMarkupIsSafe(), which is called from within ->doRender(). The same does not apply to '#children' in the version of Drupal I was using (It's a local test site, cannot update atm).

8
  • Yes - it worked for me. instead #markup use some other keywords like #scode or #children or #customname
    – Manikandan
    Commented Aug 10, 2019 at 12:11
  • any 'style' attribute is still stripped out of that too (Drupal core 8.8.1) Commented Dec 29, 2019 at 12:56
  • @WilliamTurrell Interesting. Do you know which code is responsible for this filtering?
    – donquixote
    Commented Dec 30, 2019 at 20:18
  • @donquixote Xss:filterAdmin (see the comments on answer by user21641), and there's a note here (under 'Render Arrays', #markup). Note, I was having this problem when writing a Field Formatter, even using external template files for the output, - which I didn't expect, so I worked around it by overwriting the views-view-fields template too & putting the style attribute there. Commented Dec 30, 2019 at 21:18
  • @WilliamTurrell Your links are all about #markup, not #children.
    – donquixote
    Commented Dec 30, 2019 at 21:23
29

As documented in Render API overview, in Drupal 8 a #markup value is passed through \Drupal\Component\Utility\Xss::filterAdmin(), which strips known XSS vectors while allowing a permissive list of HTML tags that are not XSS vectors. You can use #allowed_tags to set the list of allowed tags, but that would not stop Drupal from stripping attributes (for example, the style attribute).

$output['audio_controller'] = array(
  '#markup' => '<audio src="//www.example.com/resources/sample.mp3" controls><p>Your browser does not support HTML5 audio tags.</p></audio>',
  '#allowed_tags' => ['audio'],
);

Since in your case you are passing complex HTML markup, you should use #type or #theme. In this case, #type seems a better solution.

You could also implement a new type, with a render element plugin, but if that is the only case where you need it, then implementing a render element plugin is probably excessive.

2
  • You can also do '#allowed_tags' => TRUE, instead of specifying each one. Commented Feb 19, 2023 at 13:17
  • 2
    @rockstardev It's better to specify the allowed tags if possible, so that you don't open yourself to as many security risks.
    – mbomb007
    Commented Apr 12, 2023 at 13:37
14

If you want to add plain Markup to render arrays, you can create it using \Drupal\Core\Render\Markup. This will allow you to put raw HTML into something. For instance, here's how it's used in the Permission Report module to put HTML into tables:

$rows[] = [
  [
    'data' => $this->l($meta['title'], new Url('permission_report.permission', ['user_permission' => $perm], $options)),
  ],
  ['data' => $users_having_role],
  ['data' => Markup::create(implode(', ', $display_roles))],
];
2
  • 4
    I know this is two years down the line but thanks. For beginners let me simplify it: use \Drupal\Core\Render\Markup $HeaderSettingsIcon = Markup::create('<i class="fa fa-cogs"></i>'); Commented Aug 19, 2017 at 21:01
  • Even if I pass a style attribute to Markup::create, whilst it's still there in Markup::create's output, it gets stripped out again further up the render array. (I'm trying to generate a CSS background-image on a div dynamically). Commented Dec 29, 2019 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.