diff --git a/addons/mail/models/__init__.py b/addons/mail/models/__init__.py index e123a3920be..7f45f11aece 100644 --- a/addons/mail/models/__init__.py +++ b/addons/mail/models/__init__.py @@ -12,7 +12,6 @@ from . import mail_activity from . import mail_mail from . import mail_thread from . import mail_cc_mixin -from . import mail_address_mixin from . import mail_blacklist from . import mail_channel from . import mail_template diff --git a/addons/mail/models/mail_address_mixin.py b/addons/mail/models/mail_address_mixin.py deleted file mode 100644 index c2e5fdb9111..00000000000 --- a/addons/mail/models/mail_address_mixin.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import api, fields, models, tools, _ -from odoo.exceptions import UserError - - -class MailAddressMixin(models.AbstractModel): - """ Purpose of this mixin is to store a normalized email based on the primary email field. - A normalized email is considered as : - - having a left part + @ + a right part (the domain can be without '.something') - - being lower case - - having no name before the address. Typically, having no 'Name <>' - Ex: - - Formatted Email : 'Name ' - - Normalized Email : 'name@domain.com' - The primary email field can be specified on the parent model, if it differs from the default one ('email') - The email_normalized field can than be used on that model to search quickly on emails (by simple comparison - and not using time consuming regex anymore). """ - _name = 'mail.address.mixin' - _description = 'Email Address Mixin' - _primary_email = 'email' - - email_normalized = fields.Char( - string='Normalized Email', compute="_compute_email_normalized", compute_sudo=True, - store=True, invisible=True, - help="This field is used to search on email address as the primary email field can contain more than strictly an email address.") - - @api.depends(lambda self: [self._primary_email]) - def _compute_email_normalized(self): - self._assert_primary_email() - for record in self: - record.email_normalized = tools.email_normalize(record[self._primary_email]) - - def _assert_primary_email(self): - if not hasattr(self, "_primary_email") or not isinstance(self._primary_email, str): - raise UserError(_('Invalid primary email field on model %s') % self._name) - if self._primary_email not in self._fields or self._fields[self._primary_email].type != 'char': - raise UserError(_('Invalid primary email field on model %s') % self._name) diff --git a/addons/mail/models/mail_blacklist.py b/addons/mail/models/mail_blacklist.py index 24c7f2c7338..abae3f35fbd 100644 --- a/addons/mail/models/mail_blacklist.py +++ b/addons/mail/models/mail_blacklist.py @@ -1,13 +1,9 @@ # -*- 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.""" @@ -93,14 +89,34 @@ class MailBlackList(models.Model): 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'] + """ Mixin that is inherited by all model with opt out. This mixin stores a normalized + email based on primary_email field. + A normalized email is considered as : + - having a left part + @ + a right part (the domain can be without '.something') + - being lower case + - having no name before the address. Typically, having no 'Name <>' + Ex: + - Formatted Email : 'Name ' + - Normalized Email : 'name@domain.com' + + The primary email field can be specified on the parent model, if it differs from the default one ('email') + The email_normalized field can than be used on that model to search quickly on emails (by simple comparison + and not using time consuming regex anymore). + + Using this email_normalized field, blacklist status is computed. + + Mail Thread capabilities are required for this mixin. """ + + _name = 'mail.thread.blacklist' + _inherit = ['mail.thread'] + _description = 'Mail Blacklist mixin' + _primary_email = 'email' + + email_normalized = fields.Char( + string='Normalized Email', compute="_compute_email_normalized", compute_sudo=True, + store=True, invisible=True, + help="This field is used to search on email address as the primary email field can contain more than strictly an email address.") # 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( @@ -110,6 +126,12 @@ class MailBlackListMixin(models.AbstractModel): # messaging message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0) + @api.depends(lambda self: [self._primary_email]) + def _compute_email_normalized(self): + self._assert_primary_email() + for record in self: + record.email_normalized = tools.email_normalize(record[self._primary_email]) + @api.model def _search_is_blacklisted(self, operator, value): # Assumes operator is '=' or '!=' and value is True or False @@ -152,6 +174,12 @@ class MailBlackListMixin(models.AbstractModel): for record in self: record.is_blacklisted = record.email_normalized in blacklist + def _assert_primary_email(self): + if not hasattr(self, "_primary_email") or not isinstance(self._primary_email, str): + raise UserError(_('Invalid primary email field on model %s') % self._name) + if self._primary_email not in self._fields or self._fields[self._primary_email].type != 'char': + raise UserError(_('Invalid primary email field on model %s') % self._name) + def _message_receive_bounce(self, email, partner): """ Override of mail.thread generic method. Purpose is to increment the bounce counter of the record. """ diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 0cb176d3174..88c5a9c4574 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -443,7 +443,7 @@ class MassMailing(models.Model): JOIN res_partner p ON (t.partner_id = p.id) WHERE substring(p.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)') IS NOT NULL """ - elif issubclass(type(target), self.pool['mail.address.mixin']): + elif issubclass(type(target), self.pool['mail.thread.blacklist']): mail_field = 'email_normalized' elif 'email_from' in target._fields: mail_field = 'email_from' diff --git a/addons/test_mass_mailing/models/mass_mail_test.py b/addons/test_mass_mailing/models/mass_mail_test.py index 3da40c424ce..d4772433748 100644 --- a/addons/test_mass_mailing/models/mass_mail_test.py +++ b/addons/test_mass_mailing/models/mass_mail_test.py @@ -9,7 +9,7 @@ class MassMailTest(models.Model): mailing features and base performances. """ _description = 'Simple Mass Mailing Model' _name = 'mass.mail.test' - _inherit = ['mail.thread', 'mail.address.mixin'] + _inherit = ['mail.thread.blacklist'] _primary_email = 'email_from' name = fields.Char() diff --git a/addons/test_mass_mailing/tests/test_performance.py b/addons/test_mass_mailing/tests/test_performance.py index d1791ecb1bb..276b2701731 100644 --- a/addons/test_mass_mailing/tests/test_performance.py +++ b/addons/test_mass_mailing/tests/test_performance.py @@ -51,7 +51,7 @@ class TestMassMailPerformance(TestMassMailPerformanceBase): }) # runbot needs +50 compared to local - with self.assertQueryCount(__system__=1912, marketing=1913): + with self.assertQueryCount(__system__=1915, marketing=1916): mailing.action_send_mail() self.assertEqual(mailing.sent, 50)