Add-on: Notifications (io.jmix.notifications) · Version: 2.8.2 · Module: jmix-notifications-flowui
Summary
InAppNotification already models an HTML content type — ContentType.HTML (text/html; charset=UTF-8) — and the public sending API accepts it (Notification.builder()…withContentType(ContentType.HTML)). However, when a recipient opens a notification, io.jmix.notificationsflowui.NotificationDialogs#createReadNotificationDialog always puts the body into a read-only TextArea. As a result, HTML notifications are displayed as raw markup (literal <p>, <a>, … tags) instead of formatted, rich content with clickable links.
The source already flags this as planned work — there is a // todo also support RichTextArea comment at exactly this location (and a related // use RichTextEditor if ContentType is HTML in CreateNotificationDialog).
Current behavior
createReadNotificationDialog renders the body unconditionally as plain text:
// todo also support RichTextArea
TextArea textArea = new TextArea();
textArea.setValue(reloadedNotification.getBody());
textArea.setReadOnly(true);
textArea.setSizeFull();
...
layout.add(textArea, actionsLayout);
Expected behavior
When InAppNotification.getContentType() equals ContentType.HTML.getValue(), the read dialog should render the body as formatted HTML (links clickable, basic formatting visible). Plain-text notifications (the default) render exactly as today.
Use case
We send in-app notifications with formatted bodies (links, emphasis, lists) generated from templates. Because body rendering is neither content-type-aware nor extensible, every project must subclass NotificationDialogs as @Primary and post-process the built dialog to swap the TextArea for a com.vaadin.flow.component.Html. That workaround relies on @Internal API and on the dialog's internal component structure, so it can break on upgrade. Native support removes the per-project hack.
Proposed change
Make the body component content-type-aware, factored into a protected method so apps can customize further. Minimal and backward-compatible (only ContentType.HTML changes; plain text is untouched).
New imports (note: com.vaadin.flow.component.Component is referenced fully-qualified, matching the existing usage in this class which already imports org.springframework.stereotype.Component):
import com.vaadin.flow.component.Html;
import com.vaadin.flow.component.orderedlayout.Scroller;
import io.jmix.notifications.entity.ContentType;
import org.springframework.lang.Nullable;
In createReadNotificationDialog(...) — replace the TextArea block and the layout.add(...) call:
VerticalLayout layout = new VerticalLayout();
layout.setPadding(false);
layout.setSizeFull();
- // todo also support RichTextArea
- TextArea textArea = new TextArea();
- textArea.setValue(reloadedNotification.getBody());
- textArea.setReadOnly(true);
- textArea.setSizeFull();
+ com.vaadin.flow.component.Component bodyComponent = createBodyComponent(reloadedNotification);
HorizontalLayout actionsLayout = new HorizontalLayout();
...
- layout.add(textArea, actionsLayout);
+ layout.add(bodyComponent, actionsLayout);
dialog.add(layout);
return dialog;
}
New methods:
/**
* Creates a component to display the notification body. {@link ContentType#HTML} content is
* rendered as rich HTML, any other content type is shown as plain text in a read-only text area.
*
* @param notification notification to display
* @return body component
*/
protected com.vaadin.flow.component.Component createBodyComponent(InAppNotification notification) {
if (ContentType.HTML.getValue().equals(notification.getContentType())) {
Scroller scroller = new Scroller(new Html(wrapInRootElement(notification.getBody())));
scroller.setSizeFull();
return scroller;
}
TextArea textArea = new TextArea();
textArea.setValue(notification.getBody());
textArea.setReadOnly(true);
textArea.setSizeFull();
return textArea;
}
/**
* Wraps the body in a single root {@code <div>} element, as required by the Vaadin
* {@link Html} component.
*
* @param body notification body, may be {@code null}
* @return HTML string with a single root element
*/
protected String wrapInRootElement(@Nullable String body) {
return "<div>" + (body == null ? "" : body) + "</div>";
}
Backward compatibility
The default (ContentType.PLAIN) path is unchanged — same read-only TextArea. Only notifications explicitly created with ContentType.HTML change rendering. The logic sits in protected methods, so applications can override createBodyComponent (e.g. to plug in a sanitizer or a RichTextArea).
Security consideration
com.vaadin.flow.component.Html renders the supplied markup as-is; ensuring the content is safe is the application's responsibility. Today HTML bodies are produced programmatically (the create dialog hardcodes ContentType.PLAIN), so the input is trusted. If/when the create dialog gains an HTML editor (per the // use RichTextEditor if ContentType is HTML TODO), stored bodies become user-authored and rendering them raw would be a stored-XSS surface. Recommendation: sanitize HTML content (e.g. a JSoup safelist) before rendering, ideally via a configurable/overridable hook, or restrict HTML notifications to trusted senders.
Current workaround
A @Primary subclass of NotificationDialogs that calls super.createReadNotificationDialog(...) and then walks the returned dialog's component tree to replace the TextArea with a Scroller(new Html(...)) when the content type is HTML. Functional, but depends on @Internal API and the dialog's internal structure.
inapp-notifications-html-feature-request.md
Add-on: Notifications (
io.jmix.notifications) · Version: 2.8.2 · Module:jmix-notifications-flowuiSummary
InAppNotificationalready models an HTML content type —ContentType.HTML(text/html; charset=UTF-8) — and the public sending API accepts it (Notification.builder()…withContentType(ContentType.HTML)). However, when a recipient opens a notification,io.jmix.notificationsflowui.NotificationDialogs#createReadNotificationDialogalways puts the body into a read-onlyTextArea. As a result, HTML notifications are displayed as raw markup (literal<p>,<a>, … tags) instead of formatted, rich content with clickable links.The source already flags this as planned work — there is a
// todo also support RichTextAreacomment at exactly this location (and a related// use RichTextEditor if ContentType is HTMLinCreateNotificationDialog).Current behavior
createReadNotificationDialogrenders the body unconditionally as plain text:Expected behavior
When
InAppNotification.getContentType()equalsContentType.HTML.getValue(), the read dialog should render the body as formatted HTML (links clickable, basic formatting visible). Plain-text notifications (the default) render exactly as today.Use case
We send in-app notifications with formatted bodies (links, emphasis, lists) generated from templates. Because body rendering is neither content-type-aware nor extensible, every project must subclass
NotificationDialogsas@Primaryand post-process the built dialog to swap theTextAreafor acom.vaadin.flow.component.Html. That workaround relies on@InternalAPI and on the dialog's internal component structure, so it can break on upgrade. Native support removes the per-project hack.Proposed change
Make the body component content-type-aware, factored into a
protectedmethod so apps can customize further. Minimal and backward-compatible (onlyContentType.HTMLchanges; plain text is untouched).New imports (note:
com.vaadin.flow.component.Componentis referenced fully-qualified, matching the existing usage in this class which already importsorg.springframework.stereotype.Component):In
createReadNotificationDialog(...)— replace theTextAreablock and thelayout.add(...)call:VerticalLayout layout = new VerticalLayout(); layout.setPadding(false); layout.setSizeFull(); - // todo also support RichTextArea - TextArea textArea = new TextArea(); - textArea.setValue(reloadedNotification.getBody()); - textArea.setReadOnly(true); - textArea.setSizeFull(); + com.vaadin.flow.component.Component bodyComponent = createBodyComponent(reloadedNotification); HorizontalLayout actionsLayout = new HorizontalLayout(); ... - layout.add(textArea, actionsLayout); + layout.add(bodyComponent, actionsLayout); dialog.add(layout); return dialog; }New methods:
Backward compatibility
The default (
ContentType.PLAIN) path is unchanged — same read-onlyTextArea. Only notifications explicitly created withContentType.HTMLchange rendering. The logic sits inprotectedmethods, so applications can overridecreateBodyComponent(e.g. to plug in a sanitizer or aRichTextArea).Security consideration
com.vaadin.flow.component.Htmlrenders the supplied markup as-is; ensuring the content is safe is the application's responsibility. Today HTML bodies are produced programmatically (the create dialog hardcodesContentType.PLAIN), so the input is trusted. If/when the create dialog gains an HTML editor (per the// use RichTextEditor if ContentType is HTMLTODO), stored bodies become user-authored and rendering them raw would be a stored-XSS surface. Recommendation: sanitize HTML content (e.g. a JSoup safelist) before rendering, ideally via a configurable/overridable hook, or restrict HTML notifications to trusted senders.Current workaround
A
@Primarysubclass ofNotificationDialogsthat callssuper.createReadNotificationDialog(...)and then walks the returned dialog's component tree to replace theTextAreawith aScroller(new Html(...))when the content type is HTML. Functional, but depends on@InternalAPI and the dialog's internal structure.inapp-notifications-html-feature-request.md