Notifications: Add htmlMessage for opt-in HTML rendering in toasts - #23152
Conversation
|
Hi there @andrejd22, thank you for this contribution! 👍 While we wait for one of the Core Collaborators team to have a look at your work, we wanted to let you know about that we have a checklist for some of the things we will consider during review:
Don't worry if you got something wrong. We like to think of a pull request as the start of a conversation, we're happy to provide guidance on improving your contribution. If you realize that you might want to make some changes then you can do that by adding new commits to the branch you created for this work and pushing new commits. They should then automatically show up as updates to this pull request. Thanks, from your friendly Umbraco GitHub bot 🤖 🙂 |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates notification rendering to allow HTML content inside toast notifications by using Lit’s unsafeHTML directive.
Changes:
- Render default notification
data.messageas HTML viaunsafeHTML. - Render peek-error notification message as HTML when the message is a string, otherwise render the template as-is.
- Add
unsafeHTMLimports in relevant notification elements.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Umbraco.Web.UI.Client/src/packages/core/notification/layouts/default/notification-layout-default.element.ts | Switches default notification message rendering from text interpolation to unsafeHTML. |
| src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error-notification.element.ts | Uses unsafeHTML for string messages while preserving non-string/template rendering. |
iOvergaard
left a comment
There was a problem hiding this comment.
This is a good idea, but we need to sanitize the HTML first. Towards that purpose, you can import the sanitizeHTML function and use that.
import { sanitizeHTML } from '@umbraco-cms/backoffice/utils';
const sanitizedMessage = sanitizeHTML(this.#message);
return unsafeHTML(sanitizedMessage);|
@iOvergaard added |
… HTML Rendering every notification message as HTML changed the plain-text contract of UmbNotificationDefaultData.message for all existing callers: angle-bracket text was silently stripped by sanitization, and sanitized markup (links, images) from user-controlled strings could still render. Instead, add an explicit htmlMessage field (string | TemplateResult, mirroring UmbConfirmModalData.content) that takes precedence over message. String values are sanitized before rendering; TemplateResults render as-is since Lit escapes bindings. The backend EventMessage path opts in via the API interceptor, so server-sent notifications can contain links. message keeps rendering as plain text everywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The notification container announces data.message via textContent, so passing the raw EventMessage HTML there would have screen readers read the literal markup. Extract the text with an inert DOMParser document (nothing executes or loads) and keep the HTML in htmlMessage only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@iOvergaard Is it possible to have this in Umbraco v17? |
It's a new feature, so generally I'd say no. It depends on how stable it is; that is probably the most I can promise at this point. |
Allows notifications/toasts to render HTML via a new opt-in
htmlMessagefield, so backendEventMessages can contain clickable links (the original goal of this PR).How it works
UmbNotificationDefaultData.htmlMessage?: string | TemplateResult— takes precedence overmessage. String values are sanitized (DOMPurify) before rendering;TemplateResults render as-is since Lit escapes their bindings. MirrorsUmbConfirmModalData.content.messagekeeps its plain-text contract — no behavioural change for existingpeek()/stay()call sites or package developers.EventMessages (theumb-notificationsheader) opt in via the API interceptor and render throughumb-peek-error-notification. The plainmessageis kept markup-free because it is read aloud by the screen-reader announcer.docs/security.md; unit tests added for both notification layouts.Why not render all messages as HTML (the original approach): sanitization silently strips angle-bracket plain text (e.g.
IEnumerable<T>→IEnumerable), and sanitized markup from user-controlled strings (links, images, styled elements) would still render — a phishing/UI-spoofing vector. Making HTML opt-in scopes that trust decision to deliberate call sites.Testing
EventMessagefrom the server containing HTML, e.g.<a href="/umbraco">a link</a>→ the toast renders a clickable link (scripts/event handlers are stripped).Taken over and reworked by HQ — thanks @andrejd22 for the contribution and the original implementation!