From e1432afd9cfbd148e651fe13cb4459593e72e32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 28 May 2020 13:28:56 +0000 Subject: [PATCH] [IMP] mail: add first lightweight support of qweb in rendering mixin PURPOSE Make digest email and tips more appealing. The goals of these tips are * to encourage the adoption of other apps (Did you know ?); * to make Odoo look more fun (Fun tips and tricks, young and dynamic style); * to show social proof and increase trust (emphasis on already existing projects / customers to); SPECIFICATIONS In this commit we simply add a first support of tools to render qweb templates like rendering jinja. Instead of giving the template to render the xml id of the view is passed. This code is meant to be improved in a near future with new use cases that will soon arise. LINKS Task ID 2197417 PR odoo/odoo#51619 --- addons/mail/models/mail_render_mixin.py | 83 +++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py index a0d633538dc..ddd32659a03 100644 --- a/addons/mail/models/mail_render_mixin.py +++ b/addons/mail/models/mail_render_mixin.py @@ -183,10 +183,74 @@ class MailRenderMixin(models.AbstractModel): return html + @api.model + def _render_encapsulate(self, layout_xmlid, html, add_context=None, context_record=None): + try: + template = self.env.ref(layout_xmlid, raise_if_not_found=True) + except ValueError: + _logger.warning('QWeb template %s not found when rendering encapsulation template.' % (layout_xmlid)) + else: + record_name = context_record.display_name if context_record else '' + model_description = self.env['ir.model']._get(context_record._name).display_name if context_record else False + template_ctx = { + 'body': html, + 'record_name': record_name, + 'model_description': model_description, + 'company': context_record['company_id'] if (context_record and 'company_id' in context_record) else self.env.company, + 'record': context_record, + } + if add_context: + template_ctx.update(**add_context) + + html = template._render(template_ctx, engine='ir.qweb', minimal_qcontext=True) + html = self.env['mail.render.mixin']._replace_local_links(html) + return html + # ------------------------------------------------------------ # RENDERING # ------------------------------------------------------------ + @api.model + def _render_qweb_eval_context(self): + """ Prepare qweb evaluation context, containing for all rendering + + * ``user``: current user browse record; + * ``ctx```: current context; + * various formatting tools; + """ + render_context = { + 'format_date': lambda date, date_format=False, lang_code=False: format_date(self.env, date, date_format, lang_code), + 'format_datetime': lambda dt, tz=False, dt_format=False, lang_code=False: format_datetime(self.env, dt, tz, dt_format, lang_code), + 'format_amount': lambda amount, currency, lang_code=False: tools.format_amount(self.env, amount, currency, lang_code), + 'format_duration': lambda value: tools.format_duration(value), + 'user': self.env.user, + 'ctx': self._context, + } + return render_context + + @api.model + def _render_template_qweb(self, template_src, model, res_ids, add_context=None): + view = self.env.ref(template_src, raise_if_not_found=False) or self.env['ir.ui.view'] + results = dict.fromkeys(res_ids, u"") + if not view: + return results + + # prepare template variables + variables = self._render_qweb_eval_context() + if add_context: + variables.update(**add_context) + + for record in self.env[model].browse(res_ids): + variables['object'] = record + try: + render_result = view._render(variables, engine='ir.qweb', minimal_qcontext=True) + 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 : %s (%d)") % (template_src, view.id)) + results[record.id] = render_result + + return results + @api.model def _render_jinja_eval_context(self): """ Prepare jinja evaluation context, containing for all rendering @@ -207,7 +271,7 @@ class MailRenderMixin(models.AbstractModel): return render_context @api.model - def _render_template_jinja(self, template_txt, model, res_ids): + def _render_template_jinja(self, template_txt, model, res_ids, add_context=None): """ Render a string-based template on records given by a model and a list of IDs, using jinja. @@ -238,6 +302,9 @@ class MailRenderMixin(models.AbstractModel): # prepare template variables variables = self._render_jinja_eval_context() + if add_context: + variables.update(**add_context) + # TDE CHECKME # records = self.env[model].browse(it for it in res_ids if it) # filter to avoid browsing [None] if any(r is None for r in res_ids): @@ -271,11 +338,12 @@ class MailRenderMixin(models.AbstractModel): return rendered @api.model - def _render_template(self, template_txt, model, res_ids, engine='jinja', post_process=False): + def _render_template(self, template_src, model, res_ids, engine='jinja', add_context=None, post_process=False): """ Render the given string on records designed by model / res_ids using the given rendering engine. Currently only jinja is supported. - :param str template_txt: template text to render + :param str template_src: template text to render (jinja) or xml id of view (qweb) + this could be cleaned but hey, we are in a rush :param str model: model name of records on which we want to perform rendering :param list res_ids: list of ids of records (all belonging to same model) :param string engine: jinja @@ -286,10 +354,13 @@ class MailRenderMixin(models.AbstractModel): """ if not isinstance(res_ids, (list, tuple)): raise ValueError(_('Template rendering should be called only using on a list of IDs.')) - if engine != 'jinja': - raise ValueError(_('Template rendering supports only jinja.')) + if engine not in ('jinja', 'qweb'): + raise ValueError(_('Template rendering supports only jinja or qweb.')) - rendered = self._render_template_jinja(template_txt, model, res_ids) + if engine == 'qweb': + rendered = self._render_template_qweb(template_src, model, res_ids, add_context=add_context) + else: + rendered = self._render_template_jinja(template_src, model, res_ids, add_context=add_context) if post_process: rendered = self._render_template_postprocess(rendered)