[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.
This commit is contained in:
Thibault Delavallée
2018-02-20 14:32:09 +01:00
parent 2709ffe0e2
commit 8a026e26c6
11 changed files with 45 additions and 41 deletions
+2 -2
View File
@@ -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):
+2 -2
View File
@@ -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):
@@ -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):
+2 -2
View File
@@ -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)
+4 -8
View File
@@ -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
+20 -12
View File
@@ -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 = "<p>-- <br/>%s</p>" % 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'),
+1 -1
View File
@@ -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'}
+5 -6
View File
@@ -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':
+2 -2
View File
@@ -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})
@@ -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
@@ -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):