fix(fusion_helpdesk): render message bodies as HTML, not escaped text

The OWL dialog used <t t-out="m.body"/> on message bodies, but t-out
escapes plain strings — it only renders raw when the value is a Markup
instance. Bodies arrive over JSON-RPC as plain strings (Markup is a
client-side type, doesn't cross the wire), so the customer was seeing
literal "<p>This has been fixed.</p>" in the thread instead of the
rendered HTML.

Wrap incoming bodies in `markup()` at the boundary (openTicket +
sendReply call sites) so the template renders them as the sanitised
HTML the central chatter already produced. Trust is fine — the body is
sanitised server-side by mail.thread before it ever leaves nexa.

Bumps fusion_helpdesk to 19.0.1.7.1.
This commit is contained in:
gsinghpal
2026-05-27 11:40:17 -04:00
parent d7ec91b0f1
commit e596723ba5
2 changed files with 16 additions and 3 deletions

View File

@@ -3,7 +3,7 @@
# License OPL-1 (Odoo Proprietary License v1.0)
{
'name': 'Fusion Helpdesk Reporter',
'version': '19.0.1.7.0',
'version': '19.0.1.7.1',
'category': 'Productivity',
'summary': 'One-click in-app bug reporting & feature requesting — '
'auto-creates a helpdesk.ticket on a central Odoo Helpdesk.',

View File

@@ -11,13 +11,25 @@
// Tickets are NOT copied locally — every list/thread/reply is a live call
// to the central Helpdesk, scoped server-side to the logged-in user.
import { Component, useState, onWillStart } from "@odoo/owl";
import { Component, markup, useState, onWillStart } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { rpc } from "@web/core/network/rpc";
import { user } from "@web/core/user";
import { useService } from "@web/core/utils/hooks";
import { _t } from "@web/core/l10n/translation";
// Wrap message bodies in `markup()` so the OWL template's `t-out` renders them
// as HTML instead of escaping `<p>…</p>` literally. Bodies come from central as
// plain JSON strings (Markup doesn't survive JSON-RPC) but are already sanitised
// server-side by Odoo's mail.thread, so we trust them. Mutates the list in place
// because that's what the existing call sites assign straight to state.
function _markupBodies(messages) {
for (const m of messages || []) {
m.body = markup(m.body || "");
}
return messages || [];
}
const MAX_BYTES_PER_FILE = 10 * 1024 * 1024; // 10 MB hard cap per file
export class FusionHelpdeskDialog extends Component {
@@ -193,6 +205,7 @@ export class FusionHelpdeskDialog extends Component {
this.state.threadError = res.message || _t("Could not open this ticket.");
return;
}
_markupBodies(res.ticket.messages);
this.state.current = res.ticket;
this.state.tab = "thread";
// The ticket is now seen server-side; clear its unread flag locally.
@@ -227,7 +240,7 @@ export class FusionHelpdeskDialog extends Component {
if (!res.ok) {
this.state.threadError = res.message || _t("Could not send your reply.");
} else {
this.state.current.messages = res.messages || this.state.current.messages;
this.state.current.messages = _markupBodies(res.messages) || this.state.current.messages;
this.state.replyBody = "";
this.notification.add(_t("Reply sent."), { type: "success" });
}