From 8a026e26c6ae25e3fdb5369a7fa3343d70fab782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 15 Feb 2018 17:24:19 +0100 Subject: [PATCH] [IMP] mail: update _notify API Purpose of this commit is to allow to give additional values when rendering notification emails and to make some parameters explicit. This commit updates the _notify API. It allow to propagate a dictionary containing values given to the rendering of the notification template. This allow to have more complex templates and/or to control the rendering depending on some parameters. Indeed currently only some fixed values and values coming form the message to notify are given to the rendering context. First use will be soon when pushing notification emails not necessarily linked to documents. In this process some values will be given by the caller and not only by the message context anymore. This commit also moves some context key used to control the notification process directly in values given to the _notify method. It allows to make those parameters explicit and rely less on context. The _notify method now takes a values dictionary that may contain * add_sign: add user signature to notification email, default is True. It replaces the ``mail_notify_user_signature`` context key; * mail_auto_delete: auto delete send emails, default is True. It replaces the ``mail_auto_delete`` context key; * other values are given to the context used to render the notification template, allowing customization. It allows to remove some context switch notably in the composer. A values dictionary is computed and given to message_post and then propagated to the notification mechanism. --- addons/crm/models/crm_lead.py | 4 +-- addons/event/models/event.py | 4 +-- .../hr_recruitment/models/hr_recruitment.py | 4 +-- addons/mail/models/mail_message.py | 4 +-- addons/mail/models/mail_thread.py | 12 +++---- addons/mail/models/res_partner.py | 32 ++++++++++++------- addons/mail/wizard/invite.py | 2 +- addons/mail/wizard/mail_compose_message.py | 11 +++---- addons/project/models/project.py | 4 +-- .../test_mail/tests/test_message_compose.py | 5 +-- .../website_event_track/models/event_track.py | 4 +-- 11 files changed, 45 insertions(+), 41 deletions(-) diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index 8c2371e2c6c..d0365ed2e12 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -1193,7 +1193,7 @@ class Lead(models.Model): update_vals[key] = res.group(2).lower() return super(Lead, self).message_update(msg_dict, update_vals=update_vals) - def _message_post_after_hook(self, message, values, notif_layout): + def _message_post_after_hook(self, message, values, notif_layout, notif_values): if self.email_from and not self.partner_id: # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using @@ -1204,7 +1204,7 @@ class Lead(models.Model): ('partner_id', '=', False), ('email_from', '=', new_partner.email), ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id}) - return super(Lead, self)._message_post_after_hook(message, values, notif_layout) + return super(Lead, self)._message_post_after_hook(message, values, notif_layout, notif_values) @api.multi def message_partner_info_from_emails(self, emails, link_mail=False): diff --git a/addons/event/models/event.py b/addons/event/models/event.py index aab8f6596a6..02b1c1a3489 100644 --- a/addons/event/models/event.py +++ b/addons/event/models/event.py @@ -449,7 +449,7 @@ class EventRegistration(models.Model): pass return recipients - def _message_post_after_hook(self, message, values, notif_layout): + def _message_post_after_hook(self, message, values, notif_layout, notif_values): if self.email and not self.partner_id: # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using @@ -461,7 +461,7 @@ class EventRegistration(models.Model): ('email', '=', new_partner.email), ('state', 'not in', ['cancel']), ]).write({'partner_id': new_partner.id}) - return super(EventRegistration, self)._message_post_after_hook(message, values, notif_layout) + return super(EventRegistration, self)._message_post_after_hook(message, values, notif_layout, notif_values) @api.multi def action_send_badge_email(self): diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index d88bc0b9072..17c728b96c7 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -377,7 +377,7 @@ class Applicant(models.Model): defaults.update(custom_values) return super(Applicant, self).message_new(msg, custom_values=defaults) - def _message_post_after_hook(self, message, values, notif_layout): + def _message_post_after_hook(self, message, values, notif_layout, notif_values): if self.email_from and not self.partner_id: # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using @@ -388,7 +388,7 @@ class Applicant(models.Model): ('partner_id', '=', False), ('email_from', '=', new_partner.email), ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id}) - return super(Applicant, self)._message_post_after_hook(message, values, notif_layout) + return super(Applicant, self)._message_post_after_hook(message, values, notif_layout, notif_values) @api.multi def create_employee_from_applicant(self): diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 724f78ed827..f0872b1b475 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -796,7 +796,7 @@ class Message(models.Model): #------------------------------------------------------ @api.multi - def _notify(self, layout=False, force_send=False, send_after_commit=True, user_signature=True): + def _notify(self, layout=False, force_send=False, send_after_commit=True, values=None): """ Compute recipients to notify based on specified recipients and document followers. Delegate notification to partners to send emails and bus notifications and to channels to broadcast messages on channels """ @@ -847,7 +847,7 @@ class Message(models.Model): ('id', 'in', (partners_sudo - notif_partners).ids), ('channel_ids', 'in', email_channels.ids), ('email', '!=', self_sudo.author_id.email or self_sudo.email_from), - ])._notify(self, layout=layout, force_send=force_send, send_after_commit=send_after_commit, user_signature=user_signature) + ])._notify(self, layout=layout, force_send=force_send, send_after_commit=send_after_commit, values=values) notif_partners._notify_by_chat(self) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 955d9ac4479..392106a0547 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -67,12 +67,8 @@ class MailThread(models.AbstractModel): creating messages - ``tracking_disable``: at create and write, perform no MailThread features (auto subscription, tracking, post, ...) - - ``mail_auto_delete``: auto delete mail notifications; True by default - (technical hack for templates) - ``mail_notify_force_send``: if less than 50 email notifications to send, send them directly instead of using the queue; True by default - - ``mail_notify_user_signature``: add the current user signature in - email notifications; True by default ''' _name = 'mail.thread' _description = 'Email Thread' @@ -1788,7 +1784,7 @@ class MailThread(models.AbstractModel): def message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, content_subtype='html', - notif_layout=False, **kwargs): + notif_layout=False, notif_values=None, **kwargs): """ Post a new message in an existing thread, returning the new mail.message ID. :param int thread_id: thread ID to post into, or list with one ID; @@ -1913,10 +1909,10 @@ class MailThread(models.AbstractModel): # Post the message new_message = MailMessage.create(values) - self._message_post_after_hook(new_message, values, notif_layout) + self._message_post_after_hook(new_message, values, notif_layout, notif_values) return new_message - def _message_post_after_hook(self, message, values, notif_layout): + def _message_post_after_hook(self, message, values, notif_layout, notif_values): """ Hook to add custom behavior after having posted the message. Both message and computed value are given, to try to lessen query count by using already-computed values instead of having to rebrowse things. """ @@ -1924,7 +1920,7 @@ class MailThread(models.AbstractModel): message._notify( layout=notif_layout, force_send=self.env.context.get('mail_notify_force_send', True), - user_signature=self.env.context.get('mail_notify_user_signature', True) + values=notif_values, ) # Post-process: subscribe author diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index 4cee80af16a..19ef96a100c 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -37,13 +37,16 @@ class Partner(models.Model): return dict((res_id, {'partner_ids': [res_id], 'email_to': False, 'email_cc': False}) for res_id in self.ids) @api.model - def _notify_prepare_template_context(self, message): + def _notify_prepare_template_context(self, message, notif_values): # compute signature - signature = "" - if message.author_id and message.author_id.user_ids and message.author_id.user_ids[0].signature: + if not notif_values.pop('add_sign', True): + signature = False + elif message.author_id and message.author_id.user_ids and message.author_id.user_ids[0].signature: signature = message.author_id.user_ids[0].signature elif message.author_id: signature = "

--
%s

" % message.author_id.name + else: + signature = "" # compute Sent by if message.author_id and message.author_id.user_ids: @@ -91,7 +94,7 @@ class Partner(models.Model): } @api.model - def _notify_prepare_email_values(self, message): + def _notify_prepare_email_values(self, message, notif_values): # compute email references references = message.parent_id.message_id if message.parent_id else False @@ -103,7 +106,7 @@ class Partner(models.Model): mail_values = { 'mail_message_id': message.id, 'mail_server_id': message.mail_server_id.id, - 'auto_delete': self._context.get('mail_auto_delete', True), + 'auto_delete': notif_values.pop('mail_auto_delete', True), 'references': references, } mail_values.update(custom_values) @@ -146,16 +149,23 @@ class Partner(models.Model): }) @api.multi - def _notify(self, message, layout=False, force_send=False, send_after_commit=True, user_signature=True): + def _notify(self, message, layout=False, force_send=False, send_after_commit=True, values=None): """ Method to send email linked to notified messages. The recipients are the recordset on which this method is called. :param boolean force_send: send notification emails now instead of letting the scheduler handle the email queue :param boolean send_after_commit: send notification emails after the transaction end instead of durign the transaction; this option is used only if force_send is True - :param user_signature: add current user signature to notification emails """ + :param dict values: values used to compute the notification process, containing + + * add_sign: add user signature to notification email, default is True + * mail_auto_delete: auto delete send emails, default is True + * other values are given to the context used to render the notification template, allowing customization + + """ if not self.ids: return True + values = values if values is not None else {} template_xmlid = layout if layout else 'mail.message_notification_email' try: @@ -164,10 +174,8 @@ class Partner(models.Model): _logger.warning('QWeb template %s not found when sending notification emails. Skipping.' % (template_xmlid)) return False - base_template_ctx = self._notify_prepare_template_context(message) - if not user_signature: - base_template_ctx['signature'] = False - base_mail_values = self._notify_prepare_email_values(message) + base_template_ctx = self._notify_prepare_template_context(message, values) + base_mail_values = self._notify_prepare_email_values(message, values) # classify recipients: actions / no action if message.model and message.res_id and hasattr(self.env[message.model], '_notify_classify_recipients'): @@ -180,7 +188,7 @@ class Partner(models.Model): for email_type, recipient_template_values in recipients.items(): if recipient_template_values['recipients']: # generate notification email content - template_ctx = dict(base_template_ctx, **recipient_template_values) # fixme: set button_unfollow to none + template_ctx = {**base_template_ctx, **recipient_template_values, **values} # fixme: set button_unfollow to none fol_values = { 'subject': message.subject or (message.record_name and 'Re: %s' % message.record_name), 'body': base_template.render(template_ctx, engine='ir.qweb'), diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index acb89641f75..579c304b191 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -59,6 +59,6 @@ class Invite(models.TransientModel): 'res_id': wizard.res_id, 'no_auto_thread': True, }) - new_partners.with_context(auto_delete=True)._notify(message, force_send=True, send_after_commit=False, user_signature=True) + new_partners.with_context(auto_delete=True)._notify(message, force_send=True, send_after_commit=False, values={'add_sign': True}) message.unlink() return {'type': 'ir.actions.act_window_close'} diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 1aa444cd854..573e54898b4 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -147,7 +147,7 @@ class MailComposer(models.TransientModel): return super(MailComposer, self).check_access_rule(operation) @api.multi - def _notify(self, force_send=False, user_signature=True): + def _notify(self, layout=False, force_send=False, send_after_commit=True, values=None): """ Override specific notify method of mail.message, because we do not want that feature in the wizard. """ return @@ -217,11 +217,6 @@ class MailComposer(models.TransientModel): Mail = self.env['mail.mail'] ActiveModel = self.env[wizard.model if wizard.model else 'mail.thread'] - if wizard.template_id: - # template user_signature is added when generating body_html - # mass mailing: use template auto_delete value -> note, for emails mass mailing only - Mail = Mail.with_context(mail_notify_user_signature=False) - ActiveModel = ActiveModel.with_context(mail_notify_user_signature=False, mail_auto_delete=wizard.template_id.auto_delete) if not hasattr(ActiveModel, 'message_post'): ActiveModel = self.env['mail.thread'].with_context(thread_model=wizard.model) if wizard.composition_mode == 'mass_post': @@ -257,6 +252,10 @@ class MailComposer(models.TransientModel): message_type=wizard.message_type, subtype_id=subtype_id, notif_layout=notif_layout, + notif_values={ + 'add_sign': not bool(wizard.template_id), + 'mail_auto_delete': wizard.template_id.auto_delete if wizard.template_id else False, + }, **mail_values) if wizard.composition_mode == 'mass_mail': diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 319f50724b6..ff9181938bc 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -998,7 +998,7 @@ class Task(models.Model): res['headers'] = repr(headers) return res - def _message_post_after_hook(self, message, values, notif_layout): + def _message_post_after_hook(self, message, values, notif_layout, notif_values): if self.email_from and not self.partner_id: # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using @@ -1009,7 +1009,7 @@ class Task(models.Model): ('partner_id', '=', False), ('email_from', '=', new_partner.email), ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id}) - return super(Task, self)._message_post_after_hook(message, values, notif_layout) + return super(Task, self)._message_post_after_hook(message, values, notif_layout, notif_values) def action_assign_to_me(self): self.write({'user_id': self.env.user.id}) diff --git a/addons/test_mail/tests/test_message_compose.py b/addons/test_mail/tests/test_message_compose.py index e2668cdac45..6fe72d4da6f 100644 --- a/addons/test_mail/tests/test_message_compose.py +++ b/addons/test_mail/tests/test_message_compose.py @@ -63,10 +63,11 @@ class TestMessagePost(BaseFunctionalTest, MockEmails, TestRecipients): def test_post_notifications_keep_emails(self): self.test_record.message_subscribe_users(user_ids=[self.user_admin.id]) - msg = self.test_record.sudo(self.user_employee).with_context(mail_auto_delete=False).message_post( + msg = self.test_record.sudo(self.user_employee).message_post( body='Test', subject='Test', message_type='comment', subtype='mt_comment', - partner_ids=[self.partner_1.id, self.partner_2.id] + partner_ids=[self.partner_1.id, self.partner_2.id], + notif_values={'mail_auto_delete': False} ) # notifications emails should not have been deleted: one for customers, one for user diff --git a/addons/website_event_track/models/event_track.py b/addons/website_event_track/models/event_track.py index 9306cd6a80b..62d1afff823 100644 --- a/addons/website_event_track/models/event_track.py +++ b/addons/website_event_track/models/event_track.py @@ -159,7 +159,7 @@ class Track(models.Model): track._message_add_suggested_recipient(recipients, email=track.partner_email, reason=_('Speaker Email')) return recipients - def _message_post_after_hook(self, message, values, notif_layout): + def _message_post_after_hook(self, message, values, notif_layout, notif_values): if self.partner_email and not self.partner_id: # we consider that posting a message with a specified recipient (not a follower, a specific one) # on a document without customer means that it was created through the chatter using @@ -171,7 +171,7 @@ class Track(models.Model): ('partner_email', '=', new_partner.email), ('stage_id.is_cancel', '=', False), ]).write({'partner_id': new_partner.id}) - return super(Track, self)._message_post_after_hook(message, values, notif_layout) + return super(Track, self)._message_post_after_hook(message, values, notif_layout, notif_values) @api.multi def open_track_speakers_list(self):