Currently an option-type element (radio, checkbox, select) descending from Plugin\WebformElement\OptionsBase can be displayed as:
a) raw, which is the value as an HTML string,
b) description, which is the option's description as sanitized HTML
c) value, which is the escaped (plain text) option' text.
d) custom
Option c if used with reasonable html (em tag with class) results in escaped HTML displayed to the user. I thought maybe I could be clever an use a token of the value format with custom, but it results in a fatal recursion loop.
PROPOSAL: Add a variant of 'value' which performs the same logic but uses a '#markup' render element (i.e. calls Xss::filterAdmin()), rather than '#plain_text'
USE CASE: I need to color-code the various options of a radio button. This works fine for form display, but not for submission display. The only alternative that I can think of would be to subclass Radio, which seems like a very big hammer for a very small nail.
| Comment | File | Size | Author |
|---|---|---|---|
| #19 | 3112230-19.patch | 574 bytes | jrockowitz |
| #12 | radio_value_display_markup-3112230.patch | 960 bytes | danchadwick |
| #6 | webform.webform.issue_3112224.yml | 4.72 KB | jrockowitz |
Comments
Comment #2
jrockowitz commentedAdding an 'html' format to option related elements seems like a reasonable request.
At the same time, a 'custom' format (ie Twig) could be used to render the desired HTML output. Using a custom format would even allow a simple string to stored in the database while applying the color formatting to rendered output. Personally, I think this is a better approach because it separates the element's data from the element's presentation.
Comment #3
danchadwick commentedI'm a twig newbie, but I don't think I can get what I want with a custom twig template.
{{ value | raw }} gives the item's value rather than the option text.
{{ item.value | raw }} gives the HTML escaped option text (i.e. displays HTML tags as if they were text)
{{ item.description | raw }} gives the item's description, and there is none ... so nothing
{{ item.raw | raw }} gives the item's value without escaping.
What I need is a nonexistant option like {{ item.option | raw }}.
This format is dynamic. It is applied to the webform as an override, based upon the source entity.
Another option would be to just change the #plain_text to #markup. This would interpret any HTML in the item's option text as HTML, but I doubt that's unintended. It would make the item consistent with the form display. There is a small chance this would be backwards-compatibility breaking, although it's a little hard to see someone using HTML and not wanting it interpreted as such.
Comment #4
jrockowitz commentedI would not be surprised if someone had an option value set to "< 20" and "> 20" which would be affected by using #markup
Can you share an example element and maybe I can help get the Twig markup to work as expected.
Comment #5
danchadwick commentedThanks for the help.
I have a WebformHandler::alterElements() which uses other data from the source entity to set the element's options, including (optionally) color formatting. This is then used in the form during data entry and should be used when displaying the submission as well. Currently using "value" I get something like this displayed (i.e. this literally, not this as the underlying html, with Run Group in bold):
So the element's raw value is
1, option 1's text is<em class="plain rg_red">Advanced (A)</em>, and option 1's description is not set.The other options have different text and either no em tag or an em tag with different classes.
My problem was that when I tried to refer to {{ item.value }} it was already escaped. If there were something like {{ item.option }} that would work.
Comment #6
jrockowitz commentedThe attached example and below starts to show how you can manipulate arrays in Twig and render custom HTML markup. You could probably move the array on #element['#custom_value'] and use this in Twig.
Comment #7
jrockowitz commentedYou could also use
{{ item.raw|raw }}Comment #8
danchadwick commentedThanks. Yuck.
Re #6. Clever. But each webform submission has different options (number of items, text of the item, color (if any) of the item) based upon data in the source entity. To make the form work, I would have to keep the alternations I currently make to the element. But then I'd have to also custom generate at run time the twig template, creating
colors. Maybe I should use twig to generate a twig template. :p. Seem like double the work and way too hard when all I really need is'#markup'Re #7, this would change the value stored in the database, so it's a non-starter.
If you're open to a patch to add "HTML Value" as an additional format to raw, value, description, and custom, I'll create it. This would also harmonize OptionBase items with other elements like Likert that use a '#item_list'.
Comment #9
jrockowitz commentedI think this challenge can be solved via Twig. The below example moves the HTML option out of the Twig template.
Comment #10
danchadwick commentedRe #9. Again, thanks for the help. I do see that this workaround could work. Of course, I'd have to generate at runtime the options_html array (since it is not known at webform-creation time). And that would be in addition to generating the options, which I already do in elementAlter().
BUT: I just realized that the argument against just changing
'#plain_text'to'#markup'doesn't hold water because if someone inadvertently used HTML in the option, it would not display in the form. I made the above change and it made the element submission display match what the element form appearance in every test case:Therefore I conclude that the change from plain_text to markup is both safe (since no existing webform could possibly be made worse) and an improvement (since it harmonizes the display of the form and submission result).
:)
Comment #11
jrockowitz commentedChanging #plain_text' to '#markup' will impact plain text emails. I am not comfortable making this type of change.
Your requirements seem very custom and already require some custom code to implement, there I think the solution from #9 seems like a decent workaround.
Comment #12
danchadwick commentedIf the webform handler is not configured to sent HTML, then formatTextItem (not formatHtmlItem) is called, which is not affected by the proposed change. Still, a webform handler could be configured to sent HTML even though the mail system either doesn't support it (which is a user error), or the user has the preference to receive plain text. (I'm not sure if this option is still in Drupal 8 or not.)
So now limiting the discussion to handlers sending HTML email, I think the proposed change only affects plain text and HTML emails in a positive way.
CASE 1: Intentional valid HTML.
- Today, the desired HTML will be escaped and sent as unintended HTML gibberish for both plain text and HTML emails.
- After the proposed change, the desired HTML will be sent as valid HTML in HTML emails. For plain text emails, it depends upon how the mail system is configured. If it used an HTML-capable mail system (such as the SwiftMailer I use), that will strip off the HTML, as is desired. If it uses a mail system that doesn't support HTML, the plain text email will contain gibberish, just as it does today.
SUMMARY: Proposed change is better in some cases and the same in others.
CASE 2: Unintentional invalid HTML
- Today, the inadvertent HTML will be escaped. If the email is sent as either HTML or plain text by an HTML-capable mail system, it will be received as desired. If it is sent by a plain-text only mail system, it will be received as HTML-encoded gibberish.
- After the proposed change, invalid HTML will be sent as HTML and received as invalid HTML. Modern email clients display this just as a browser does -- as plain text as desired. If sent has plain text, it will be received and displayed as intended (text, not HTML).
SUMMARY: Proposed change is better in some cases and the same in others.
CASE 3: Unintentional valid HTML (very unlikely because it will be displayed wrong in the form)
- Today the HTML will be escaped. It will be sent both as plain text and HTML as desired, but remember the form was broken so I'm not sure how we got this far.
- After the change, the unescaped HTML will be received properly as plain text but will be displayed in HTML text in the same way as it was displayed on the form (i.e. wrong).
SUMMARY: Proposed change makes emails and submission consistent with how the form is displayed. Even though not desired by webform author, the author's error is now more readily apparent because the author is more likely to see the submissions and emails than the form.
While my use case is more esoteric than others, I think the desire to include basic HTML in options-based elements is common. Making this work out-of-the-box, without having to resort to quite tricky twig templates would be an improvement for everyone.
I'm also happy to offer a patch to add add the option of an HTML "value" (i.e. option text). Or I can live with composer patching webform if need be. I'm posting up a proposed patch to see what the automated tests think.
Comment #13
jrockowitz commentedMy other problem is we are encouraging people to add HTML markup into option values and text which are not supported by select menus.
I am pretty sure this change will cause regressions. For example, people do use '<Any>' as the option value or text.
Comment #14
danchadwick commentedA (very good) reason to choose a Radio element over a select is because it does support HTML.
It cannot cause a regression with
'<Any>'because that is case 3, "unintentional valid html", which is hopelessly broken. It display nothing on the form, therefore no existing webforms could possible have this. I just tested and confirmed this.I carefully considered all the permutations for possible regressions/behavior and concluded this change is only for the better. But it's your module. If you disagree, I will just add a patch to composer (after I figure out how to have one composer.json for production and another for development/staging -- argh).
If you really don't want this, and don't want a patch to either change #plain_text to #markup or to add 'HTML Value' as an option, please just close this as closed "won't fix" or "works as designed" and I'll move on. I do not want to waste your valuable time. And again, thank you for your wonderful and generous work on this module.
Comment #15
jrockowitz commentedFor now, I just want to mark this postponed so that other people can weigh-in on the ticket. I am probably more comfortable committing this to Webform 8.x-6x. For now, please use the patch via composer.
Comment #16
danchadwick commented+1. Thank you for both your time and efforts. It is appreciated.
Comment #17
danchadwick commentedI found that rather than patching webform, this can be easily achieved in the theme layer. I would like to leave this as postponed because I think this would be a good enhancement for 8.x-6.x. You can also implement theme_preprocess_webform_element_base_html and check the element's type.
Comment #18
jrockowitz commentedComment #19
jrockowitz commentedComment #20
jrockowitz commentedComment #22
danchadwick commentedI apologize for the slow attention. I like the code, but maybe you want to remove the comment, which was intended for my internal use?
Comment #23
mrinalini9 commentedComment #24
mrinalini9 commentedComment #25
jrockowitz commentedFixed via #3144022: [Webform 6.x] Cleanup options value display