[MOV] mail: move code for email notification

Move the notification management from mail.notification to res.partner model.
Those methods belong more to the partner model, as they work on partners.
Moreover with the upcoming new model for NewChatter notification will be
heavily refactored.
This commit is contained in:
Thibault Delavallée
2015-08-21 12:11:55 +02:00
parent 1617db3337
commit 098fcb7e4e
3 changed files with 151 additions and 137 deletions
+15 -135
View File
@@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import threading
from openerp import _, api, fields, models
from openerp import tools
@@ -73,132 +71,6 @@ class Notification(models.Model):
if not cr.fetchone():
cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, is_read, starred, message_id)')
def get_partners_to_email(self, message):
""" Return the list of partners to notify, based on their preferences.
:param browse_record message: mail.message to notify
:param list partners_to_notify: optional list of partner ids restricting
the notifications to process
"""
notify_pids = []
for notification in self:
if notification.is_read:
continue
partner = notification.partner_id
# Do not send to partners without email address defined
if not partner.email:
continue
# Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database)
if message.author_id and message.author_id.email == partner.email:
continue
# Partner does not want to receive any emails or is opt-out
if partner.notify_email == 'none':
continue
notify_pids.append(partner.id)
return notify_pids
@api.model
def get_signature_footer(self, user_id, res_model=None, res_id=None, user_signature=True):
""" Format a standard footer for notification emails (such as pushed messages
notification or invite emails).
Format:
<p>--<br />
Administrator
</p>
<div>
<small>Sent from <a ...>Your Company</a> using <a ...>Odoo</a>.</small>
</div>
"""
footer = ""
if not user_id:
return footer
# add user signature
user = self.env.user
if user_signature:
if self.env.user.signature:
signature = user.signature
else:
signature = "--<br />%s" % user.name
footer = tools.append_content_to_html(footer, signature, plaintext=False)
# add company signature
if user.company_id.website:
website_url = ('http://%s' % user.company_id.website) if not user.company_id.website.lower().startswith(('http:', 'https:')) \
else user.company_id.website
company = "<a style='color:inherit' href='%s'>%s</a>" % (website_url, user.company_id.name)
else:
company = user.company_id.name
sent_by = _('Sent by %(company)s using %(odoo)s')
signature_company = '<br /><small>%s</small>' % (sent_by % {
'company': company,
'odoo': "<a style='color:inherit' href='https://www.odoo.com/'>Odoo</a>"
})
footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')
return footer
def update_message_notification(self, message, partners):
# update existing notifications
self.write({'is_read': False})
# create new notifications
new_notif_ids = self.env['mail.notification']
for new_pid in partners - self.mapped('partner_id'):
new_notif_ids |= self.create({'message_id': message.id, 'partner_id': new_pid.id, 'is_read': False})
return new_notif_ids
@api.multi
def _notify_email(self, message, force_send=False, user_signature=True):
# compute partners
email_pids = self.get_partners_to_email(message)
if not email_pids:
return True
# compute email body (signature, company data)
body_html = message.body
# add user signature except for mail.channels, where users are usually adding their own signatures already
user_id = message.author_id and message.author_id.user_ids and message.author_id.user_ids[0] and message.author_id.user_ids[0].id or None
signature_company = self.get_signature_footer(user_id, res_model=message.model, res_id=message.res_id, user_signature=(user_signature and message.model != 'mail.channel'))
if signature_company:
body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
# compute email references
references = message.parent_id.message_id if message.parent_id else False
# custom values
custom_values = dict()
if message.model and message.res_id and self.pool.get(message.model) and hasattr(self.pool[message.model], 'message_get_email_values'):
custom_values = self.env[message.model].browse(message.res_id).message_get_email_values(message)
# create email values
max_recipients = 50
chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
emails = self.env['mail.mail']
for chunk in chunks:
mail_values = {
'mail_message_id': message.id,
'auto_delete': self._context.get('mail_auto_delete', True),
'mail_server_id': self._context.get('mail_server_id', False),
'body_html': body_html,
'recipient_ids': [(4, id) for id in chunk],
'references': references,
}
mail_values.update(custom_values)
emails |= self.env['mail.mail'].create(mail_values)
# NOTE:
# 1. for more than 50 followers, use the queue system
# 2. do not send emails immediately if the registry is not loaded,
# to prevent sending email during a simple update of the database
# using the command-line.
if force_send and len(chunks) < 2 and \
(not self.pool._init or
getattr(threading.currentThread(), 'testing', False)):
emails.send()
return True
@api.model
def _notify(self, message, recipients=None, force_send=False, user_signature=True):
""" Send by email the notification depending on the user preferences
@@ -211,14 +83,22 @@ class Notification(models.Model):
:param bool user_signature: if True, the generated mail.mail body is
the body of the related mail.message with the author's signature
"""
notif_ids = self.sudo().search([('message_id', '=', message.id), ('partner_id', 'in', recipients.ids)])
# update or create notifications
new_notif_ids = notif_ids.update_message_notification(message, recipients) # tde check: sudo
# mail_notify_noemail (do not send email) or no partner_ids: do not send, return
if self.env.context.get('mail_notify_noemail'):
return True
if not recipients:
recipients = message.partner_ids
# browse as SUPERUSER_ID because of access to res_partner not necessarily allowed
new_notif_ids._notify_email(message, force_send, user_signature) # tde check this one too
notifications = self.env['mail.notification'].sudo().search(
[('message_id', '=', message.id), ('partner_id', 'in', recipients.ids)])
notifications.write({'is_read': False})
new_notifications = self.env['mail.notification'].sudo()
for new_partner in recipients - notifications.mapped('partner_id'):
new_notifications |= self.env['mail.notification'].sudo().create({
'message_id': message.id,
'partner_id': new_partner.id,
'is_read': False})
new_recipients = new_notifications.mapped('partner_id')
# notify
new_recipients._notify(message, force_send=force_send, user_signature=user_signature)
+135 -1
View File
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from openerp import _, api, fields, models
import threading
from openerp import _, api, fields, models, tools
class Partner(models.Model):
@@ -33,3 +35,135 @@ class Partner(models.Model):
@api.multi
def message_get_default_recipients(self):
return dict((res_id, {'partner_ids': [res_id], 'email_to': False, 'email_cc': False}) for res_id in self.ids)
def get_partners_to_email(self, message, recipients):
""" Return the list of partners to notify, based on their preferences.
:param browse_record message: mail.message to notify
:param record set recipients: recipients
"""
email_recipients = self.env['res.partner']
for partner in recipients:
# Do not send to partners without email address defined
if not partner.email:
continue
# Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database)
if message.author_id and message.author_id.email == partner.email:
continue
# Partner does not want to receive any emails or is opt-out
if partner.notify_email == 'none':
continue
email_recipients |= partner
return email_recipients
@api.model
def get_signature_footer(self, user_id, res_model=None, res_id=None, user_signature=True):
""" Format a standard footer for notification emails (such as pushed messages
notification or invite emails).
Format:
<p>--<br />
Administrator
</p>
<div>
<small>Sent from <a ...>Your Company</a> using <a ...>Odoo</a>.</small>
</div>
"""
footer = ""
if not user_id:
return footer
# add user signature
user = self.env.user
if user_signature:
if self.env.user.signature:
signature = user.signature
else:
signature = "--<br />%s" % user.name
footer = tools.append_content_to_html(footer, signature, plaintext=False)
# add company signature
if user.company_id.website:
website_url = ('http://%s' % user.company_id.website) if not user.company_id.website.lower().startswith(('http:', 'https:')) \
else user.company_id.website
company = "<a style='color:inherit' href='%s'>%s</a>" % (website_url, user.company_id.name)
else:
company = user.company_id.name
sent_by = _('Sent by %(company)s using %(odoo)s')
signature_company = '<br /><small>%s</small>' % (sent_by % {
'company': company,
'odoo': "<a style='color:inherit' href='https://www.odoo.com/'>Odoo</a>"
})
footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')
return footer
@api.multi
def _notify_email(self, message, force_send=False, user_signature=True):
# compute partners
if not self.ids:
return True
email_pids = self.ids
# rebrowse as sudo to avoid access rigths on author, user, ... -> portal / public goes through this method
message_sudo = message.sudo()
# compute email body (signature, company data)
body_html = message_sudo.body
# add user signature except for mail.channels, where users are usually adding their own signatures already
user_id = message_sudo.author_id and message_sudo.author_id.user_ids and message_sudo.author_id.user_ids[0] and message_sudo.author_id.user_ids[0].id or None
signature_company = self.get_signature_footer(user_id, res_model=message_sudo.model, res_id=message_sudo.res_id, user_signature=(user_signature and message_sudo.model != 'mail.channel'))
if signature_company:
body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
# compute email references
references = message_sudo.parent_id.message_id if message_sudo.parent_id else False
# custom values
custom_values = dict()
if message_sudo.model and message_sudo.res_id and self.pool.get(message_sudo.model) and hasattr(self.pool[message_sudo.model], 'message_get_email_values'):
custom_values = self.env[message_sudo.model].browse(message_sudo.res_id).message_get_email_values(message_sudo)
# create email values
max_recipients = 50
chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
emails = self.env['mail.mail']
for chunk in chunks:
mail_values = {
'mail_message_id': message_sudo.id,
'auto_delete': self._context.get('mail_auto_delete', True),
'mail_server_id': self._context.get('mail_server_id', False),
'body_html': body_html,
'recipient_ids': [(4, id) for id in chunk],
'references': references,
}
mail_values.update(custom_values)
emails |= self.env['mail.mail'].create(mail_values)
# NOTE:
# 1. for more than 50 followers, use the queue system
# 2. do not send emails immediately if the registry is not loaded,
# to prevent sending email during a simple update of the database
# using the command-line.
if force_send and len(chunks) < 2 and \
(not self.pool._init or
getattr(threading.currentThread(), 'testing', False)):
emails.send()
return True
@api.multi
def _notify(self, message, force_send=False, user_signature=True):
""" Send by email the notification depending on the user preferences
:param list partners_to_notify: optional list of partner ids restricting
the notifications to process
:param bool force_send: if True, the generated mail.mail is
immediately sent after being created, as if the scheduler
was executed for this message only.
:param bool user_signature: if True, the generated mail.mail body is
the body of the related mail.message with the author's signature
"""
if self.ids:
recipients = self
else:
recipients = message.notified_partner_ids
email_recipients = self.get_partners_to_email(message, recipients)
email_recipients._notify_email(message, force_send=force_send, user_signature=user_signature)
+1 -1
View File
@@ -48,7 +48,7 @@ class Invite(models.TransientModel):
if wizard.send_mail and wizard.message and not wizard.message == '<br>': # when deleting the message, cleditor keeps a <br>
# add signature
# FIXME 8.0: use notification_email_send, send a wall message and let mail handle email notification + message box
signature_company = self.env['mail.notification'].get_signature_footer(user_id=self._uid, res_model=wizard.res_model, res_id=wizard.res_id)
signature_company = self.env['res.partner'].get_signature_footer(user_id=self._uid, res_model=wizard.res_model, res_id=wizard.res_id)
wizard.message = tools.append_content_to_html(wizard.message, signature_company, plaintext=False, container_tag='div')
# send mail to new followers