From dbd4aab6c3172688d98cef700265795527d63cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 18 Apr 2019 13:25:09 +0000 Subject: [PATCH] [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 --- addons/crm/models/crm_lead.py | 5 +-- addons/mail/models/ir_model.py | 6 ++-- addons/mail/models/mail_address_mixin.py | 32 +++++++++---------- addons/mail/models/mail_blacklist.py | 27 +++++++++++----- addons/mail/models/mail_thread.py | 17 +++++----- addons/mail/models/res_partner.py | 3 +- addons/mail/wizard/mail_compose_message.py | 2 +- addons/mass_mailing/models/mass_mailing.py | 3 +- addons/test_mail/models/test_mail_models.py | 2 +- .../models/mass_mail_test.py | 2 +- 10 files changed, 53 insertions(+), 46 deletions(-) diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index ff91389cbce..96a4048d891 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -50,7 +50,7 @@ class Lead(models.Model): _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority desc,activity_date_deadline,id desc" - _inherit = ['mail.thread.cc', 'mail.activity.mixin', 'utm.mixin', 'format.address.mixin', 'mail.blacklist.mixin'] + _inherit = ['mail.thread.cc', 'mail.thread.blacklist', 'mail.activity.mixin', 'utm.mixin', 'format.address.mixin'] _primary_email = 'email_from' def _default_probability(self): @@ -96,9 +96,6 @@ class Lead(models.Model): date_last_stage_update = fields.Datetime(string='Last Stage Update', index=True, default=fields.Datetime.now) date_conversion = fields.Datetime('Conversion Date', readonly=True) - # Messaging and marketing - message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0) - # Only used for type opportunity probability = fields.Float('Probability', group_operator="avg", default=lambda self: self._default_probability()) planned_revenue = fields.Monetary('Expected Revenue', currency_field='company_currency', tracking=True) diff --git a/addons/mail/models/ir_model.py b/addons/mail/models/ir_model.py index 4ac1e8d7fc4..b4950d73ed4 100644 --- a/addons/mail/models/ir_model.py +++ b/addons/mail/models/ir_model.py @@ -78,7 +78,7 @@ class IrModel(models.Model): vals = super(IrModel, self)._reflect_model_params(model) vals['is_mail_thread'] = issubclass(type(model), self.pool['mail.thread']) vals['is_mail_activity'] = issubclass(type(model), self.pool['mail.activity.mixin']) - vals['is_mail_blacklist'] = issubclass(type(model), self.pool['mail.blacklist.mixin']) + vals['is_mail_blacklist'] = issubclass(type(model), self.pool['mail.thread.blacklist']) return vals @api.model @@ -92,8 +92,8 @@ class IrModel(models.Model): parents = model_class._inherit or [] parents = [parents] if isinstance(parents, str) else parents model_class._inherit = parents + ['mail.activity.mixin'] - if model_data.get('is_mail_blacklist') and model_class._name != 'mail.blacklist.mixin': + if model_data.get('is_mail_blacklist') and model_class._name != 'mail.thread.blacklist': parents = model_class._inherit or [] parents = [parents] if isinstance(parents, str) else parents - model_class._inherit = parents + ['mail.blacklist.mixin'] + model_class._inherit = parents + ['mail.thread.blacklist'] return model_class diff --git a/addons/mail/models/mail_address_mixin.py b/addons/mail/models/mail_address_mixin.py index 49dff2f86c5..c2e5fdb9111 100644 --- a/addons/mail/models/mail_address_mixin.py +++ b/addons/mail/models/mail_address_mixin.py @@ -6,25 +6,25 @@ from odoo.exceptions import UserError class MailAddressMixin(models.AbstractModel): - """ Purpose of this mixing 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). - """ + """ 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' + _description = 'Email Address Mixin' _primary_email = 'email' - email_normalized = fields.Char(string='Normalized email address', compute="_compute_email_normalized", invisible=True, - compute_sudo=True, store=True, help="""This field is used to search on email address, - as the primary email field can contain more than strictly an email address.""") + 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): diff --git a/addons/mail/models/mail_blacklist.py b/addons/mail/models/mail_blacklist.py index 4636a19dcbe..4311aa54b6e 100644 --- a/addons/mail/models/mail_blacklist.py +++ b/addons/mail/models/mail_blacklist.py @@ -94,19 +94,22 @@ class MailBlackList(models.Model): class MailBlackListMixin(models.AbstractModel): - """ Mixin that is inherited by all model with opt out. - This mixin is inheriting of another mixin, mail.address.mixin which defines the _primary_email variable - and the email_normalized field that are mandatory to use the blacklist mixin. - """ - _name = 'mail.blacklist.mixin' + """ 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.address.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", + 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): @@ -147,3 +150,11 @@ class MailBlackListMixin(models.AbstractModel): ('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 diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index df59d76692c..3d645bca794 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -1472,17 +1472,18 @@ class MailThread(models.AbstractModel): def _message_receive_bounce(self, email, partner, mail_id=None): """Called by ``message_process`` when a bounce email (such as Undelivered Mail Returned to Sender) is received for an existing thread. The default - behavior is to check is an integer ``message_bounce`` column exists. + behavior is to do nothing. This method is meant to be overridden in various + modules to add some specific behavior like blacklist management or mass + mailing statistics update. check is an integer ``message_bounce`` column exists. If it is the case, its content is incremented. + :param record partner: partner matching the bounced email address, if any; + :param string email: email that caused the bounce; :param mail_id: ID of the sent email that bounced. It may not exist anymore - but it could be usefull if the information was kept. This is - used notably in mass mailing. - :param RecordSet partner: partner matching the bounced email address, if any - :param string email: email that caused the bounce """ - if 'message_bounce' in self._fields: - for record in self: - record.message_bounce = record.message_bounce + 1 + but it could be useful if the information was kept. This is + used notably in mass mailing; + """ + pass def _message_extract_payload_postprocess(self, message, body, attachments): """ Perform some cleaning / postprocess in the body and attachments diff --git a/addons/mail/models/res_partner.py b/addons/mail/models/res_partner.py index e5877a2f201..966d0bb2dbd 100644 --- a/addons/mail/models/res_partner.py +++ b/addons/mail/models/res_partner.py @@ -18,10 +18,9 @@ class Partner(models.Model): """ Update partner to add a field about notification preferences. Add a generic opt-out field that can be used to restrict usage of automatic email templates. """ _name = "res.partner" - _inherit = ['res.partner', 'mail.thread', 'mail.activity.mixin', 'mail.blacklist.mixin'] + _inherit = ['res.partner', 'mail.activity.mixin', 'mail.thread.blacklist'] _mail_flat_thread = False - message_bounce = fields.Integer('Bounce', help="Counter of the number of bounced emails for this contact", default=0) channel_ids = fields.Many2many('mail.channel', 'mail_channel_partner', 'partner_id', 'channel_id', string='Channels', copy=False) # override the field to track the visibility of user user_id = fields.Many2one(tracking=True) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 0fdc996a600..72076826085 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -305,7 +305,7 @@ class MailComposer(models.TransientModel): reply_to_value = self.env['mail.thread']._notify_get_reply_to_on_records(default=self.email_from, records=records) blacklisted_rec_ids = [] - if mass_mail_mode and issubclass(type(self.env[self.model]), self.pool['mail.blacklist.mixin']): + if mass_mail_mode and issubclass(type(self.env[self.model]), self.pool['mail.thread.blacklist']): BL_sudo = self.env['mail.blacklist'].sudo() blacklist = set(BL_sudo.search([]).mapped('email')) if blacklist: diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index b43b6e8a428..6d47d33e356 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -211,7 +211,7 @@ class MassMailingContact(models.Model): be able to deal with large contact list to email without bloating the partner base.""" _name = 'mail.mass_mailing.contact' - _inherit = ['mail.thread', 'mail.blacklist.mixin'] + _inherit = ['mail.thread.blacklist'] _description = 'Mass Mailing Contact' _order = 'email' _rec_name = 'email' @@ -226,7 +226,6 @@ class MassMailingContact(models.Model): 'contact_id', 'list_id', string='Mailing Lists') subscription_list_ids = fields.One2many('mail.mass_mailing.list_contact_rel', 'contact_id', string='Subscription Information') - message_bounce = fields.Integer(string='Bounced', help='Counter of the number of bounced emails for this contact.', default=0) country_id = fields.Many2one('res.country', string='Country') tag_ids = fields.Many2many('res.partner.category', string='Tags') opt_out = fields.Boolean('Opt Out', compute='_compute_opt_out', search='_search_opt_out', diff --git a/addons/test_mail/models/test_mail_models.py b/addons/test_mail/models/test_mail_models.py index bdf9f164da8..b9ff40ebc7a 100644 --- a/addons/test_mail/models/test_mail_models.py +++ b/addons/test_mail/models/test_mail_models.py @@ -20,7 +20,7 @@ class MailTestGateway(models.Model): mailing features and base performances. """ _description = 'Simple Chatter Model for Mail Gateway' _name = 'mail.test.gateway' - _inherit = ['mail.blacklist.mixin', 'mail.thread'] + _inherit = ['mail.thread.blacklist'] _primary_email = 'email_from' name = fields.Char() diff --git a/addons/test_mass_mailing/models/mass_mail_test.py b/addons/test_mass_mailing/models/mass_mail_test.py index 1abe766c412..3da40c424ce 100644 --- a/addons/test_mass_mailing/models/mass_mail_test.py +++ b/addons/test_mass_mailing/models/mass_mail_test.py @@ -20,7 +20,7 @@ class MassMailTestBlacklist(models.Model): """ Model using blacklist mechanism for mass mailing. """ _description = 'Mass Mailing Model w Blacklist' _name = 'mass.mail.test.bl' - _inherit = ['mail.thread', 'mail.blacklist.mixin'] + _inherit = ['mail.thread.blacklist'] _primary_email = 'email_from' # blacklist field to check