26

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?

  • style attributes are always stripped out – Yzmir Ramirez Oct 25 '16 at 21:26
28

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.

  • 2
    This filters out IFRAME tags. – joachim May 2 '16 at 14:40
  • 1
    And script and style tags – Yzmir Ramirez Oct 25 '16 at 21:25
  • 8
    Try {{ variable | raw }} or <tag>{{variable}}</tag> – user21641 Oct 26 '16 at 11:48
  • 1
    @IvanJaros That will just skip auto escaping the variable , but the ` \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/… – Yzmir Ramirez Oct 29 '16 at 23:04
  • 1
    Anyone know if this the only/best way of doing HTML comments? e.g. <!-- Hello, World! --> (nb: as mentioned above you need '| raw') – William Turrell Aug 2 '18 at 11:58
19

As documented in Render API overview, in Drupal 8 the string passed to #markup 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 (e.g. style).

$output['filtered_string'] = array(
  '#markup' => '<em>This is filtered</em>',
  '#allowed_tags' => ['strong'],
);

Since in your case you are passing complex HTML markup, you should use #type (as suggested from Ivan Jaros or use #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.

14

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).

  • Yes - it worked for me. instead #markup use some other keywords like #scode or #children or #customname – Manikandan Aug 10 '19 at 12:11
  • any 'style' attribute is still stripped out of that too (Drupal core 8.8.1) – William Turrell Dec 29 '19 at 12:56
  • @WilliamTurrell Interesting. Do you know which code is responsible for this filtering? – donquixote Dec 30 '19 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. – William Turrell Dec 30 '19 at 21:18
  • @WilliamTurrell Your links are all about #markup, not #children. – donquixote Dec 30 '19 at 21:23
12

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))],
];
  • 3
    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>'); – Ngatia Frankline Aug 19 '17 at 21:01
  • @NgatiaFrankline, is there a way to offer you my gratitude? – dresh Dec 10 '18 at 18:43
  • I did this : $form['first_name'] = array( '#type' => 'textfield', '#title' => t('Your First Name :'), '#required' => TRUE, '#attributes' => array( 'class' => array('form-control req'), 'name'=>array('first_name')), '#label_attributes' => array('class' => array('mandatory')), '#children' => Markup::create('<span class="error-text">Please enter the valid First Name</span>'), ); – dresh Dec 10 '18 at 18:45
  • 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). – William Turrell Dec 29 '19 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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