diff --git a/addons/calendar/models/calendar_attendee.py b/addons/calendar/models/calendar_attendee.py index 9df8051e061..0506fad587a 100644 --- a/addons/calendar/models/calendar_attendee.py +++ b/addons/calendar/models/calendar_attendee.py @@ -123,8 +123,7 @@ class Attendee(models.Model): body = mail_template._render_field( 'body_html', attendee.ids, - compute_lang=True, - post_process=True)[attendee.id] + compute_lang=True)[attendee.id] subject = mail_template._render_field( 'subject', attendee.ids, diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py index 735a78f4be3..354d3a7d59d 100644 --- a/addons/digest/models/digest.py +++ b/addons/digest/models/digest.py @@ -165,8 +165,10 @@ class Digest(models.Model): 'tips': self._compute_tips(user.company_id, user, tips_count=tips_count, consumed=consume_tips), 'preferences': self._compute_preferences(user.company_id, user), }, - post_process=True, - options={'preserve_comments': True} + options={ + 'preserve_comments': True, + 'post_process': True, + }, )[self.id] full_mail = self.env['mail.render.mixin']._render_encapsulate( 'digest.digest_mail_layout', @@ -288,7 +290,15 @@ class Digest(models.Model): '|', ('group_id', 'in', user.groups_id.ids), ('group_id', '=', False) ], limit=tips_count) tip_descriptions = [ - tools.html_sanitize(self.env['mail.render.mixin'].sudo()._render_template(tip.tip_description, 'digest.tip', tip.ids, post_process=True, engine="qweb")[tip.id]) + tools.html_sanitize( + self.env['mail.render.mixin'].sudo()._render_template( + tip.tip_description, + 'digest.tip', + tip.ids, + engine="qweb", + options={'post_process': True}, + )[tip.id] + ) for tip in tips ] if consumed: diff --git a/addons/mail/models/mail_composer_mixin.py b/addons/mail/models/mail_composer_mixin.py index 9619eae88f8..a17b5941199 100644 --- a/addons/mail/models/mail_composer_mixin.py +++ b/addons/mail/models/mail_composer_mixin.py @@ -22,7 +22,9 @@ class MailComposerMixin(models.AbstractModel): # Content subject = fields.Char('Subject', compute='_compute_subject', readonly=False, store=True) - body = fields.Html('Contents', compute='_compute_body', render_engine='qweb', store=True, readonly=False, sanitize=False) + body = fields.Html( + 'Contents', compute='_compute_body', readonly=False, store=True, + render_engine='qweb', render_options={'post_process': True}, sanitize=False) template_id = fields.Many2one('mail.template', 'Mail Template', domain="[('model', '=', render_model)]") # Access is_mail_template_editor = fields.Boolean('Is Editor', compute='_compute_is_mail_template_editor') diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py index 75929065faa..cfec4651007 100644 --- a/addons/mail/models/mail_render_mixin.py +++ b/addons/mail/models/mail_render_mixin.py @@ -284,7 +284,8 @@ class MailRenderMixin(models.AbstractModel): :param dict add_context: additional context to give to renderer. It allows to add or update values to base rendering context generated by ``MailRenderMixin._render_eval_context()``; - :param dict options: options for rendering (not used currently); + :param dict options: options for rendering propagated to IrQweb render + (see docstring for available options); :return dict: {res_id: string of rendered template based on record} """ @@ -337,7 +338,8 @@ class MailRenderMixin(models.AbstractModel): :param dict add_context: additional context to give to renderer. It allows to add or update values to base rendering context generated by ``MailRenderMixin._render_eval_context()``; - :param dict options: options for rendering (not used currently); + :param dict options: options for rendering propagated to IrQweb render + (see docstring for available options); :return dict: {res_id: string of rendered template based on record} """ @@ -378,7 +380,8 @@ class MailRenderMixin(models.AbstractModel): :param dict add_context: additional context to give to renderer. It allows to add or update values to base rendering context generated by ``MailRenderMixin._render_inline_template_eval_context()``; - :param dict options: options for rendering; + :param dict options: options for rendering (no options available + currently); :return dict: {res_id: string of rendered template based on record} """ @@ -434,7 +437,7 @@ class MailRenderMixin(models.AbstractModel): @api.model def _render_template(self, template_src, model, res_ids, engine='inline_template', - add_context=None, options=None, post_process=False): + add_context=None, options=None): """ Render the given string on records designed by model / res_ids using the given rendering engine. Possible engine are small_web, qweb, or qweb_view. @@ -449,12 +452,20 @@ class MailRenderMixin(models.AbstractModel): :param dict add_context: additional context to give to renderer. It allows to add or update values to base rendering context generated by ``MailRenderMixin._render__eval_context()``; - :param dict options: options for rendering; - :param boolean post_process: perform a post processing on rendered result - (notably html links management). See``_render_template_postprocess``; + :param dict options: options for rendering. Use in this method and also + propagated to rendering sub-methods. May contain notably + + boolean post_process: perform a post processing on rendered result + (notably html links management). See``_render_template_postprocess``; + boolean preserve_comments: if set, comments are preserved. Default + behavior is to remove them. It is used notably for browser-specific + code implemented like comments; :return dict: {res_id: string of rendered template based on record} """ + if options is None: + options = {} + if not isinstance(res_ids, (list, tuple)): raise ValueError(_('Template rendering should be called only using on a list of IDs.')) if engine not in ('inline_template', 'qweb', 'qweb_view'): @@ -470,7 +481,7 @@ class MailRenderMixin(models.AbstractModel): rendered = self._render_template_inline_template(template_src, model, res_ids, add_context=add_context, options=options) - if post_process: + if options.get('post_process'): rendered = self._render_template_postprocess(rendered) return rendered @@ -523,7 +534,7 @@ class MailRenderMixin(models.AbstractModel): def _render_field(self, field, res_ids, engine='inline_template', compute_lang=False, set_lang=False, - add_context=None, options=None, post_process=False): + add_context=None, options=None): """ Given some record ids, render a template located on field on all records. ``field`` should be a field of self (i.e. ``body_html`` on ``mail.template``). res_ids are record IDs linked to ``model`` field @@ -542,9 +553,16 @@ class MailRenderMixin(models.AbstractModel): ``compute_lang`` is False; :param dict add_context: additional context to give to renderer; - :param dict options: options for rendering; - :param boolean post_process: perform a post processing on rendered result - (notably html links management). See``_render_template_postprocess``); + :param dict options: options for rendering. Use in this method and also + propagated to rendering sub-methods. Base values come from the field + (coming from ``render_options`` parameter) and are updated by this + optional dictionary. May contain notably + + boolean post_process: perform a post processing on rendered result + (notably html links management). See``_render_template_postprocess``; + boolean preserve_comments: if set, comments are preserved. Default + behavior is to remove them. It is used notably for browser-specific + code implemented like comments; :return dict: {res_id: string of rendered template based on record} """ @@ -568,7 +586,6 @@ class MailRenderMixin(models.AbstractModel): # rendering options engine = getattr(self._fields[field], 'render_engine', engine) options.update(**getattr(self._fields[field], 'render_options', {})) - post_process = options.get('post_process') or post_process return dict( (res_id, rendered) @@ -580,6 +597,5 @@ class MailRenderMixin(models.AbstractModel): engine=engine, add_context=add_context, options=options, - post_process=post_process ).items() ) diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 920647e98c7..7825c182cd4 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -56,7 +56,9 @@ class MailTemplate(models.Model): email_cc = fields.Char('Cc', help="Carbon copy recipients (placeholders may be used here)") reply_to = fields.Char('Reply To', help="Email address to which replies will be redirected when sending emails in mass; only used when the reply is not logged in the original discussion thread.") # content - body_html = fields.Html('Body', render_engine='qweb', translate=True, prefetch=True, sanitize=False) + body_html = fields.Html( + 'Body', render_engine='qweb', render_options={'post_process': True}, + prefetch=True, translate=True, sanitize=False) attachment_ids = fields.Many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id', 'attachment_id', 'Attachments', help="You may attach files to this template, to be added to all " @@ -246,8 +248,7 @@ class MailTemplate(models.Model): for lang, (template, template_res_ids) in self._classify_per_lang(res_ids).items(): for field in fields: generated_field_values = template._render_field( - field, template_res_ids, - post_process=(field == 'body_html') + field, template_res_ids ) for res_id, field_value in generated_field_values.items(): results.setdefault(res_id, dict())[field] = field_value diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 3469f43d754..8b69f6a0f36 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -89,7 +89,9 @@ class MailComposer(models.TransientModel): # content subject = fields.Char('Subject', compute=False) - body = fields.Html('Contents', render_engine='qweb', compute=False, default='', sanitize_style=True) + body = fields.Html( + 'Contents', render_engine='qweb', render_options={'post_process': True}, + compute=False, default='', sanitize_style=True) parent_id = fields.Many2one( 'mail.message', 'Parent Message', ondelete='set null') template_id = fields.Many2one('mail.template', 'Use template', domain="[('model', '=', model)]") @@ -658,7 +660,7 @@ class MailComposer(models.TransientModel): subjects = self._render_field('subject', res_ids) # 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'}) + bodies = self._render_field('body', res_ids, 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/mass_mailing/controllers/main.py b/addons/mass_mailing/controllers/main.py index 05401375eb1..2b3cfd32fc7 100644 --- a/addons/mass_mailing/controllers/main.py +++ b/addons/mass_mailing/controllers/main.py @@ -167,7 +167,13 @@ class MassMailController(http.Controller): if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)) and not request.env.user.has_group('mass_mailing.group_mass_mailing_user'): raise exceptions.AccessDenied() - html_markupsafe = mailing._render_field('body_html', [res_id])[res_id] + # do not force lang, will simply use user context + html_markupsafe = mailing._render_field( + 'body_html', + [res_id], + compute_lang=False, + options={'post_process': False} + )[res_id] # Update generic URLs (without parameters) to final ones html_markupsafe = html_markupsafe.replace('/unsubscribe_from_list', mailing._get_unsubscribe_url(email, res_id)) diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 4fc7b40989a..d95f295c1b3 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -73,6 +73,7 @@ class MassMailing(models.Model): 'Subject', required=True, translate=False) preview = fields.Char( 'Preview', translate=False, + render_engine='inline_template', render_options={'post_process': True}, help='Catchy preview sentence that encourages recipients to open this email.\n' 'In most inboxes, this is displayed next to the subject.\n' 'Keep it empty if you prefer the first characters of your email content to appear instead.') @@ -104,9 +105,9 @@ class MassMailing(models.Model): help="Date at which the mailing was or will be sent.") # don't translate 'body_arch', the translations are only on 'body_html' body_arch = fields.Html(string='Body', translate=False, sanitize=False) - body_html = fields.Html(string='Body converted to be sent by mail', render_engine='qweb', sanitize=False) - - # used to determine if the mail body is empty + body_html = fields.Html( + string='Body converted to be sent by mail', sanitize=False, + render_engine='qweb', render_options={'post_process': True}) is_body_empty = fields.Boolean(compute="_compute_is_body_empty") attachment_ids = fields.Many2many( 'ir.attachment', 'mass_mailing_ir_attachments_rel', diff --git a/addons/mass_mailing/wizard/mailing_mailing_test.py b/addons/mass_mailing/wizard/mailing_mailing_test.py index 0006295a9ca..b00684fb8c6 100644 --- a/addons/mass_mailing/wizard/mailing_mailing_test.py +++ b/addons/mass_mailing/wizard/mailing_mailing_test.py @@ -37,10 +37,11 @@ class TestMassMailing(models.TransientModel): # Downside: Qweb syntax is only tested when there is atleast one record of the mailing's model if record: # Returns a proper error if there is a syntax error with Qweb - body = mailing._render_field('body_html', record.ids, post_process=True)[record.id] - preview = mailing._render_field('preview', record.ids, post_process=True)[record.id] + # do not force lang, will simply use user context + body = mailing._render_field('body_html', record.ids, compute_lang=False)[record.id] + preview = mailing._render_field('preview', record.ids, compute_lang=False)[record.id] full_body = mailing._prepend_preview(Markup(body), preview) - subject = mailing._render_field('subject', record.ids)[record.id] + subject = mailing._render_field('subject', record.ids, compute_lang=False)[record.id] else: full_body = mailing._prepend_preview(mailing.body_html, mailing.preview) subject = mailing.subject diff --git a/addons/sale/wizard/sale_order_cancel.py b/addons/sale/wizard/sale_order_cancel.py index ab445a81758..ac4eb391f90 100644 --- a/addons/sale/wizard/sale_order_cancel.py +++ b/addons/sale/wizard/sale_order_cancel.py @@ -66,7 +66,7 @@ class SaleOrderCancel(models.TransientModel): wizard.template_id.subject, 'sale.order', [wizard.order_id.id], - post_process=True, + options={'post_process': True}, )[wizard.order_id.id] @api.depends('order_id') @@ -77,7 +77,7 @@ class SaleOrderCancel(models.TransientModel): wizard.template_id.body_html, 'sale.order', [wizard.order_id.id], - post_process=True, + options={'post_process': True}, engine='qweb', )[wizard.order_id.id] diff --git a/addons/survey/wizard/survey_invite.py b/addons/survey/wizard/survey_invite.py index f0814ea8bf3..9c7e89514b6 100644 --- a/addons/survey/wizard/survey_invite.py +++ b/addons/survey/wizard/survey_invite.py @@ -210,7 +210,7 @@ class SurveyInvite(models.TransientModel): def _send_mail(self, answer): """ Create mail specific for recipient containing notably its access token """ subject = self._render_field('subject', answer.ids)[answer.id] - body = self._render_field('body', answer.ids, post_process=True)[answer.id] + body = self._render_field('body', answer.ids)[answer.id] # post the message mail_values = { 'attachment_ids': [(4, att.id) for att in self.attachment_ids], diff --git a/addons/website_slides/wizard/slide_channel_invite.py b/addons/website_slides/wizard/slide_channel_invite.py index be8ef6490c2..d4e32cfbcd8 100644 --- a/addons/website_slides/wizard/slide_channel_invite.py +++ b/addons/website_slides/wizard/slide_channel_invite.py @@ -67,7 +67,7 @@ class SlideChannelInvite(models.TransientModel): def _prepare_mail_values(self, slide_channel_partner): """ Create mail specific for recipient """ subject = self._render_field('subject', slide_channel_partner.ids)[slide_channel_partner.id] - body = self._render_field('body', slide_channel_partner.ids, post_process=True)[slide_channel_partner.id] + body = self._render_field('body', slide_channel_partner.ids)[slide_channel_partner.id] # post the message mail_values = { 'email_from': self.env.user.email_formatted,