[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
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <NaMe@DoMaIn.CoM>'
|
||||
- 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 <NaMe@DoMaIn.CoM>'
|
||||
- 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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user