diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py
index bfd55135e85..33d8c6c2dcc 100644
--- a/addons/digest/models/digest.py
+++ b/addons/digest/models/digest.py
@@ -144,7 +144,8 @@ class Digest(models.Model):
'tips': self._compute_tips(user.company_id, user, tips_count=tips_count, consumed=consum_tips),
'preferences': self._compute_preferences(user.company_id, user),
},
- post_process=True
+ post_process=True,
+ options={'preserve_comments': True}
)[self.id]
full_mail = self.env['mail.render.mixin']._render_encapsulate(
'digest.digest_mail_layout',
diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py
index 27eec614618..b24c9d3d357 100644
--- a/addons/mail/models/mail_render_mixin.py
+++ b/addons/mail/models/mail_render_mixin.py
@@ -276,8 +276,12 @@ class MailRenderMixin(models.AbstractModel):
for record in self.env[model].browse(res_ids):
variables['object'] = record
try:
- render_result = self.env['ir.qweb']._render(html.fragment_fromstring(
- template_src, create_parent='div'), variables, raise_on_code=is_restricted)
+ render_result = self.env['ir.qweb']._render(
+ html.fragment_fromstring(template_src, create_parent='div'),
+ variables,
+ raise_on_code=is_restricted,
+ **(options or {})
+ )
# remove the rendered tag
that was added in order to wrap potentially multiples nodes into one.
render_result = render_result[5:-6]
except QWebCodeFound:
@@ -329,7 +333,7 @@ class MailRenderMixin(models.AbstractModel):
for record in self.env[model].browse(res_ids):
variables['object'] = record
try:
- render_result = view._render(variables, engine='ir.qweb', minimal_qcontext=True)
+ render_result = view._render(variables, engine='ir.qweb', minimal_qcontext=True, options=options)
except Exception as e:
_logger.info("Failed to render template : %s (%d)", template_src, view.id, exc_info=True)
raise UserError(_("Failed to render template : %(xml_id)s (%(view_id)d)",
diff --git a/addons/mail/tests/test_mail_composer.py b/addons/mail/tests/test_mail_composer.py
index 8dd6c5614f1..f99e83cfe1b 100644
--- a/addons/mail/tests/test_mail_composer.py
+++ b/addons/mail/tests/test_mail_composer.py
@@ -47,14 +47,9 @@ class TestMailComposer(MailCommon):
values = mail_compose_message.get_mail_values(self.partner_employee.ids)
- self.assertIn("""
""",
+ self.assertIn(self.body_html,
values[self.partner_employee.id]['body_html'],
- 'We must remove comments')
+ 'We must preserve (mso) comments in email html')
@users('employee')
def test_mail_mass_mode_compose_with_mso(self):
@@ -74,11 +69,6 @@ class TestMailComposer(MailCommon):
values = composer.get_mail_values(self.partner_employee.ids)
- self.assertIn("""
""",
+ self.assertIn(self.body_html,
values[self.partner_employee.id]['body_html'],
- 'We must remove comments')
+ 'We must preserve (mso) comments in email html')
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 5ef54670f28..a91b0c4bd4c 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -598,7 +598,8 @@ class MailComposer(models.TransientModel):
res_ids = [res_ids]
subjects = self._render_field('subject', res_ids, options={"render_safe": True})
- bodies = self._render_field('body', res_ids, post_process=True)
+ # We want to preserve comments in emails so as to keep mso conditionals
+ bodies = self._render_field('body', res_ids, post_process=True, options={'preserve_comments': self.composition_mode == 'mass_mail'})
emails_from = self._render_field('email_from', res_ids)
replies_to = self._render_field('reply_to', res_ids)
default_recipients = {}
diff --git a/addons/web_editor/models/ir_ui_view.py b/addons/web_editor/models/ir_ui_view.py
index 52bab6a98dd..ee7d3457751 100644
--- a/addons/web_editor/models/ir_ui_view.py
+++ b/addons/web_editor/models/ir_ui_view.py
@@ -18,7 +18,7 @@ EDITING_ATTRIBUTES = ['data-oe-model', 'data-oe-id', 'data-oe-field', 'data-oe-x
class IrUiView(models.Model):
_inherit = 'ir.ui.view'
- def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
+ def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False, options=None):
if values and values.get('editable'):
try:
self.check_access_rights('write')
@@ -26,7 +26,7 @@ class IrUiView(models.Model):
except AccessError:
values['editable'] = False
- return super(IrUiView, self)._render(values=values, engine=engine, minimal_qcontext=minimal_qcontext)
+ return super(IrUiView, self)._render(values=values, engine=engine, minimal_qcontext=minimal_qcontext, options=options)
#------------------------------------------------------
# Save from html
diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py
index 58aed3a3c4e..169c202314e 100644
--- a/odoo/addons/base/models/ir_ui_view.py
+++ b/odoo/addons/base/models/ir_ui_view.py
@@ -1974,13 +1974,13 @@ actual arch.
def _render_template(self, template, values=None, engine='ir.qweb'):
return self.browse(self.get_view_id(template))._render(values, engine)
- def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False):
+ def _render(self, values=None, engine='ir.qweb', minimal_qcontext=False, options=None):
assert isinstance(self.id, int)
qcontext = dict() if minimal_qcontext else self._prepare_qcontext()
qcontext.update(values or {})
- return self.env[engine]._render(self.id, qcontext)
+ return self.env[engine]._render(self.id, qcontext, **(options or {}))
@api.model
def _prepare_qcontext(self):
diff --git a/odoo/addons/base/models/qweb.py b/odoo/addons/base/models/qweb.py
index cd10dbfb026..313352ac6a6 100644
--- a/odoo/addons/base/models/qweb.py
+++ b/odoo/addons/base/models/qweb.py
@@ -951,7 +951,10 @@ class QWeb(object):
body = []
if el.getchildren():
for item in el:
- if not isinstance(item, etree._Comment):
+ if isinstance(item, etree._Comment):
+ if options.get('preserve_comments'):
+ self._appendText("" % item.text, options)
+ else:
body.extend(self._compile_node(item, options, indent))
# comments can also contains tail text
if item.tail is not None: