From 1ae8fcdae338385d73178183d8ea7b75622b0fb3 Mon Sep 17 00:00:00 2001 From: Daniel Palumbo Date: Wed, 12 Aug 2026 08:02:55 +0200 Subject: [PATCH] [T3274] FIX: double-escaped QWeb entities crashing mail template rendering Several mail_template rows have their ">"/"<"/'"'/"'" QWeb comparison/string-literal operators double-escaped (e.g. "&gt;" instead of ">"). Odoo 18's stricter QWeb compiler evaluates the still-escaped "&gt;" text as a Python operator and crashes - found via the "Donation - Thank You Letter" template (T3315), but confirmed present in 76 mail_template rows in compassion_18, 45 of which have no owning module at all (created directly via the UI), so a per-module migration could never reach them. Placed in partner_communication since it's a shared dependency of every affected module and guaranteed installed wherever any of these templates live. Purely mechanical string unescaping, safe regardless of which module (if any) owns the row. --- partner_communication/__manifest__.py | 2 +- .../migrations/18.0.1.0.2/post-migration.py | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 partner_communication/migrations/18.0.1.0.2/post-migration.py diff --git a/partner_communication/__manifest__.py b/partner_communication/__manifest__.py index b0d020061..44b156191 100644 --- a/partner_communication/__manifest__.py +++ b/partner_communication/__manifest__.py @@ -30,7 +30,7 @@ # pylint: disable=C8101 { "name": "Partner Communication", - "version": "18.0.1.0.1", + "version": "18.0.1.0.2", "category": "Other", "author": "Compassion Switzerland", "license": "AGPL-3", diff --git a/partner_communication/migrations/18.0.1.0.2/post-migration.py b/partner_communication/migrations/18.0.1.0.2/post-migration.py new file mode 100644 index 000000000..5d113a955 --- /dev/null +++ b/partner_communication/migrations/18.0.1.0.2/post-migration.py @@ -0,0 +1,59 @@ +import json +import logging + +from openupgradelib import openupgrade + +_logger = logging.getLogger(__name__) + +# T3274: found while investigating why sponsorship onboarding communications +# crashed - the "Donation - Thank You Letter" template had its ">"/"<"/'"'/"'" +# QWeb comparison/string-literal operators double-escaped +# (e.g. "&gt;" instead of ">"), so Odoo 18's stricter QWeb compiler +# tried to evaluate the still-escaped "&gt;" text as a Python operator +# and crashed. This is a purely mechanical unescape, safe to apply to any +# mail_template regardless of which module (if any) owns it - many of the +# affected templates were created directly via the UI and have no owning +# module at all, so a per-module migration could never reach them. +ENTITY_FIXES = ( + ("&gt;", ">"), + ("&lt;", "<"), + ("&quot;", """), + ("&apos;", "'"), +) + + +def _fix_body(body): + if not body: + return body + for broken, fixed in ENTITY_FIXES: + body = body.replace(broken, fixed) + return body + + +@openupgrade.migrate() +def migrate(env, version): + env.cr.execute( + """ + SELECT id, body_html FROM mail_template + WHERE body_html::text LIKE '%&gt;%' + OR body_html::text LIKE '%&lt;%' + OR body_html::text LIKE '%&quot;%' + OR body_html::text LIKE '%&apos;%' + """ + ) + rows = env.cr.fetchall() + fixed_count = 0 + for template_id, body_html in rows: + fixed = {lang: _fix_body(body) for lang, body in body_html.items()} + if fixed != body_html: + env.cr.execute( + "UPDATE mail_template SET body_html = %s WHERE id = %s", + (json.dumps(fixed), template_id), + ) + fixed_count += 1 + _logger.info( + "T3274: unescaped double-escaped >/</"/' entities " + "in %s/%s mail_template rows", + fixed_count, + len(rows), + )