Using Twig templates to generate the html output of the field is the standard in Drupal 8. Currently the module simply produces a string of html:

    $output =
      '<div class="' . (!empty($options['class'])? new HtmlEscapedText($options['class']) : '') . '">'
        . (empty($text)? '' : '<h3 class="iframe_title">' . (isset($options['html']) && $options['html'] ? $text : new HtmlEscapedText($text)) . '</h3>')
        . '<iframe src="' . htmlspecialchars(Url::fromUri($path, $options)->toString()) . '"'
          . $drupal_attributes->__toString()
          . $htmlid
        . '>'
        . t('Your browser does not support iframes. But You can use the following link.') . ' ' . Link::fromTextAndUrl('Link', Url::fromUri($path, $options_link))->toString()
        . '</iframe>'
      . '</div>'
    ;
    return $output;

Using a Twig template for this would make the output much easier to theme and override. It would also help the community to understand and give back to the module.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

marcvangend created an issue. See original summary.

bomoko’s picture

Status: Active » Needs review
FileSize
2.8 KB

I agree that having some control over the structure of the markup outputted would be really useful.
Here's a first attempt at adding a template - would love to get some feedback on this.
In particular, I'm unsure about whether I'm passing too much (or not enough) through to the template.

benellefimostfa’s picture

@bomoko the code seems to be okay for me, but let's see other opinions.
Thanks for your contribution.

marcvangend’s picture

@bomoko, thanks for your patch! No need to be unsure, you did great.

Your patch is a straight-forward translation of the PHP code into a twig template. You didn't add new variables or leave out existing ones, which is a good start. Now there are 2 things I think we should improve while we're at it.

1. Attributes

+++ b/templates/iframe.html.twig
@@ -0,0 +1,22 @@
+  <iframe src="{{ src }}" {{ attributes }} {{ htmlid }}>

In Drupal Twig files, an attribute object is used for all attributes on an HTML tag. It's not just for classes; the src and htmlid variables could all be part of a single attribute object. We could pass them to the template in a single array which looks something like this:

$attributes = [
  'class' => [
    'iframe'
  ],
  'id' => 'my-iframe-id',
  'src' => 'http://example.com',
];

2. Translation

+++ b/templates/iframe.html.twig
@@ -0,0 +1,22 @@
+    {{ 'Your browser does not support iframes, but you can use the following link:' | t }} {{ link('Link', src) }}

To use Drupal translations properly, the variable part of this message (the link) should be integrated in the translated string, because in another language it may not be at the end. I think it should look like this (untested code):

{% set iframe_link = link('Link', src) %}
{% trans %} Your browser does not support iframes, but you can use the following link: {{ iframe_link }} {% endtrans %}
bomoko’s picture

Assigned: Unassigned » bomoko
Status: Needs review » Needs work

Thanks so much for the feedback @marcvangend!
That's extremely useful.

Assigning this issue to myself - I'll submit a new patch with your suggestions ASAP.

bomoko’s picture

Assigned: bomoko » Unassigned
Status: Needs work » Needs review
FileSize
3.83 KB

Hi All

So I've made some changes to the patch as suggested by @marcvangend (thanks again).

1. I've bundled up all the attributes for the iFrame tag itself into the "attributes" variable.

2. I've removed the $options_link array from the iframe_iframe() - it was some code moved directly from the D7 version, and it adds the title to the link. If you look where it's being used in the current code, it's actually not doing anything (i.e Url::fromUri() doesn't actually recognize the structure of the data passed into it, and is essentially ignoring it). However, I've changed up the link that's generated in the twig template to do what that original code was trying to do, namely, add a "title" attribute to the link in the iFrame fallback text.

3. With regards to the embedding of the iframe_link variable as suggested in #4, it seems as though attempting to embed a variable generated by using the twig "link" function throws a warning - what seems to happen is that the render array for the link gets passed through to htmlspecialchars, rather than the rendered text. So I've gone with actually embedding the link in the string itself, and passing the appropriate variables in. This generates a translation string that looks as follows "Your browser does not support iframes, but you can use the following link: Link". Does this seem reasonable? Is there another approach I should be using?

Some notes/thoughts that I think might make sense to look at in other issues (since this is really about just getting a template working).
1 - I think that the code in iframe_iframe() can probably be simplified a little in places.
2 - The logic for the "class" attribute for the containing div and the iframe itself should be looked at (as it stands, it seems to attach itself both to the containing div and the iFrame - this is in the original code _and_ in my patch)
3 - The "fallback link" (the text and link between the iframe tags) is actually invalid html, according to the W3C validator. It might be a nice thing to add an option to either show/hide that text so that sites that need to pass the W3C validator _can_.

flocondetoile’s picture

Status: Needs review » Reviewed & tested by the community

Patch #6 works fine. And this is a feature really useful

- we can easily override the template if necessary
- we can preprocess the theme function to add whatever we need
- easier maintenance
- use the twig power :-)

Looks like RTBC.

neffets’s picture

Unsanitized variables ({{ variable|raw }} in Twig templates or !variable in translation strings) are deprecated for Drupal 8 translations and won't work in latest 8.x Drupal versions. The translation API description is a little outdated.

For the use case, we may just add the HTML link tag to the translation string and make the link URL itself the variable. Unfortunately, the twig trans tag doesn't support that kind of variable yet. (core issue.) So we should use the t filter instead:

{{ 'A <a href=":url">link</a> example in a translation.'|t({ ':url': url }) }}

The above example matches the translation strings:

en: A <a href=":url">link</a> example in a translation.

de: Ein Beispiel-<a href=":url">Link</a> in einer Übersetzung.

flocondetoile’s picture

Status: Reviewed & tested by the community » Needs work
flocondetoile’s picture

FileSize
5.49 KB

Attached a new patch based on comments in #8.

And with some improvments.
- return directly the render array instead of render it.
- using the attribtes.class instead of the oprions array in the twig template
- added the fallback link into the variables passed to the template.

flocondetoile’s picture

Status: Needs work » Needs review
marcvangend’s picture

+++ b/templates/iframe.html.twig
@@ -0,0 +1,22 @@
+    {{ "Your browser does not support iframes, but you can use the following link @link"|t({ '@link': link }) }}

Neffets wrote in #8 that unescaped variables like "!link" are deprecated. I get that, but I do not understand what "For the use case, we may just add the HTML link tag to the translation string and make the link URL itself the variable" means. Are you saying we can use "@link" here without the risk of escaped HTML?

flocondetoile’s picture

Source : https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Rend...

@variable: When the placeholder replacement value is:

A string, the replaced value in the returned string will be sanitized using \Drupal\Component\Utility\Html::escape().
A MarkupInterface object, the replaced value in the returned string will not be sanitized.
A MarkupInterface object cast to a string, the replaced value in the returned string be forcibly sanitized using \Drupal\Component\Utility\Html::escape().

$this->placeholderFormat('This will force HTML-escaping of the replacement value: @text', ['@text' => (string) $safe_string_interface_object));

Use this placeholder as the default choice for anything displayed on the site, but not within HTML attributes, JavaScript, or CSS. Doing so is a security risk.

So yes, using @link here is safe. It's how I understand this ^^
And the text used for the link is not a user input.

Edit: Hum. I have a doubt.

May be should be better to use:

$this
  ->placeholderFormat('<a href=":url">@variable</a>', [
  ':url' => $url,
  '@variable' => $variable,
]);
flocondetoile’s picture

FileSize
5.33 KB

Back to <a href=":url">Link</a> used in the template.

Thanks

marcvangend’s picture

Status: Needs review » Needs work
+++ b/templates/iframe.html.twig
@@ -0,0 +1,21 @@
+  <iframe {{ attributes }}>
+    {{ 'Your browser does not support iframes, but you can use the following <a href=":url">Link</a>'|t({ ':url': src }) }}
+  </iframe>

Let's also take care of accessibility here. WCAG Success Criterion 2.4.9 says you cannot have non-descriptive link texts such as "Click here" (see https://www.w3.org/TR/2016/NOTE-WCAG20-TECHS-20161007/F84). In this template we have an iframe title available, so why not do something like this:

{{ 'Your browser does not support iframes, but you can visit <a href=":url">@text</a>'|t({ ':url': src, '@text': text }) }}
marcvangend’s picture

FileSize
5.33 KB

New (untested) patch attached.

bomoko’s picture

Status: Needs work » Needs review
FileSize
5.52 KB

Great stuff, @marcvangend (and everyone else). Alas, the patch doesn't apply directly anymore.
I've rerolled it here, haven't added anything though - just getting it working.

marcvangend’s picture

Status: Needs review » Reviewed & tested by the community

Thanks @bomoko. The patch applies cleanly and overriding the template works as expected. All feedback from previous comments has been processed. Back to RTBC.

  • neffets committed 93a004c on 8.x-1.x
    Issue #2861387 by bomoko, flocondetoile, marcvangend: Use a Twig...
neffets’s picture

Status: Reviewed & tested by the community » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

lslinnet’s picture

For those looking for why this was removed from the latest version you can find the followup issue at https://www.drupal.org/project/iframe/issues/3099249

lslinnet’s picture

[deleted] - duplicate due to 500 error :(