Files
odoo_source/addons/mail/models/mail_blacklist.py
T
Thibault Delavallée dbd4aab6c3 [REF] mail: make blacklist a mixin built upon mail.thread and add message_bounce
* mail.blacklist.mixin now clearly make inheritance on mail.thread. Indeed
    we consider models using the blacklist mechanism as being used in mailing
    features, meaning they will anyway inherit form mail.thread. In standard
    Odoo it is the case for the 3 models using it (Lead/Opportunity, Contact
    and Mailing Contact);
  * define message_bounce directly in mail.blacklist.mixin. Bounce counter
    is indeed linked to the blacklist and mailing mechanism;
  * rename mail.blacklist.mixin to mail.thread.blacklist to ensure coherency
    with mail.thread and mail.thread.cc (another mixin build on mailL.thread);

Inherit declarations in various addons are updated accordingly.

Related to task ID 1911679
Linked to PR #29483
2019-05-13 15:18:40 +00:00

161 lines
6.7 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class MailBlackList(models.Model):
""" Model of blacklisted email addresses to stop sending emails."""
_name = 'mail.blacklist'
_inherit = ['mail.thread']
_description = 'Mail Blacklist'
_rec_name = 'email'
email = fields.Char(string='Email Address', required=True, index=True, help='This field is case insensitive.',
tracking=True)
active = fields.Boolean(default=True, tracking=True)
_sql_constraints = [
('unique_email', 'unique (email)', 'Email address already exists!')
]
@api.model_create_multi
def create(self, values):
# First of all, extract values to ensure emails are really unique (and don't modify values in place)
new_values = []
all_emails = []
for value in values:
email = tools.email_normalize(value.get('email'))
if not email:
raise UserError(_('Invalid email address %r') % value['email'])
if email in all_emails:
continue
all_emails.append(email)
new_value = dict(value, email=email)
new_values.append(new_value)
""" To avoid crash during import due to unique email, return the existing records if any """
sql = '''SELECT email, id FROM mail_blacklist WHERE email = ANY(%s)'''
emails = [v['email'] for v in new_values]
self._cr.execute(sql, (emails,))
bl_entries = dict(self._cr.fetchall())
to_create = [v for v in new_values if v['email'] not in bl_entries]
# TODO DBE Fixme : reorder ids according to incoming ids.
results = super(MailBlackList, self).create(to_create)
return self.env['mail.blacklist'].browse(bl_entries.values()) | results
@api.multi
def write(self, values):
if 'email' in values:
values['email'] = tools.email_normalize(values['email'])
return super(MailBlackList, self).write(values)
def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
""" Override _search in order to grep search on email field and make it
lower-case and sanitized """
if args:
new_args = []
for arg in args:
if isinstance(arg, (list, tuple)) and arg[0] == 'email' and isinstance(arg[2], str):
normalized = tools.email_normalize(arg[2])
if normalized:
new_args.append([arg[0], arg[1], normalized])
else:
new_args.append(arg)
else:
new_args.append(arg)
else:
new_args = args
return super(MailBlackList, self)._search(new_args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid)
def _add(self, email):
normalized = tools.email_normalize(email)
record = self.env["mail.blacklist"].with_context(active_test=False).search([('email', '=', normalized)])
if len(record) > 0:
record.write({'active': True})
else:
record = self.create({'email': email})
return record
def _remove(self, email):
normalized = tools.email_normalize(email)
record = self.env["mail.blacklist"].with_context(active_test=False).search([('email', '=', normalized)])
if len(record) > 0:
record.write({'active': False})
else:
record = record.create({'email': email, 'active': False})
return record
class MailBlackListMixin(models.AbstractModel):
""" Mixin that is inherited by all model with opt out. This mixin inherits from
mail.address.mixin which defines the _primary_email variable and the email_normalized
field that are mandatory to use the blacklist mixin. Mail Thread capabilities
are required for this mixin. """
_name = 'mail.thread.blacklist'
_description = 'Mail Blacklist mixin'
_inherit = ['mail.thread', 'mail.address.mixin']
# Note : is_blacklisted sould only be used for display. As the compute is not depending on the blacklist,
# once read, it won't be re-computed again if the blacklist is modified in the same request.
is_blacklisted = fields.Boolean(
string='Blacklist', compute="_compute_is_blacklisted", compute_sudo=True, store=False,
search="_search_is_blacklisted", groups="base.group_user",
help="If the email address is on the blacklist, the contact won't receive mass mailing anymore, from any list")
# messaging
message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0)
@api.model
def _search_is_blacklisted(self, operator, value):
# Assumes operator is '=' or '!=' and value is True or False
self._assert_primary_email()
if operator != '=':
if operator == '!=' and isinstance(value, bool):
value = not value
else:
raise NotImplementedError()
if value:
query = """
SELECT m.id
FROM mail_blacklist bl
JOIN %s m
ON m.email_normalized = bl.email AND bl.active
"""
else:
query = """
SELECT m.id
FROM %s m
LEFT JOIN mail_blacklist bl
ON m.email_normalized = bl.email AND bl.active
WHERE bl.id IS NULL
"""
self._cr.execute(query % self._table)
res = self._cr.fetchall()
if not res:
return [(0, '=', 1)]
return [('id', 'in', [r[0] for r in res])]
@api.depends('email_normalized')
def _compute_is_blacklisted(self):
# TODO : Should remove the sudo as compute_sudo defined on methods.
# But if user doesn't have access to mail.blacklist, doen't work without sudo().
blacklist = set(self.env['mail.blacklist'].sudo().search([
('email', 'in', self.mapped('email_normalized'))]).mapped('email'))
for record in self:
record.is_blacklisted = record.email_normalized in blacklist
@api.multi
def _message_receive_bounce(self, email, partner, mail_id=None):
""" Override of mail.thread generic method. Purpose is to increment the
bounce counter of the record. """
super(MailBlackListMixin, self)._message_receive_bounce(email, partner, mail_id=mail_id)
for record in self:
record.message_bounce = record.message_bounce + 1