summaryrefslogtreecommitdiff
path: root/addons/mail/models/mail_thread_blacklist.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/models/mail_thread_blacklist.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/models/mail_thread_blacklist.py')
-rw-r--r--addons/mail/models/mail_thread_blacklist.py126
1 files changed, 126 insertions, 0 deletions
diff --git a/addons/mail/models/mail_thread_blacklist.py b/addons/mail/models/mail_thread_blacklist.py
new file mode 100644
index 00000000..f17302f3
--- /dev/null
+++ b/addons/mail/models/mail_thread_blacklist.py
@@ -0,0 +1,126 @@
+# -*- 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 AccessError, UserError
+
+
+class MailBlackListMixin(models.AbstractModel):
+ """ 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 <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).
+
+ 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(
+ 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.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
+ self.flush(['email_normalized'])
+ self.env['mail.blacklist'].flush(['email', 'active'])
+ 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
+
+ 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. """
+ super(MailBlackListMixin, self)._message_receive_bounce(email, partner)
+ for record in self:
+ record.message_bounce = record.message_bounce + 1
+
+ def _message_reset_bounce(self, email):
+ """ Override of mail.thread generic method. Purpose is to reset the
+ bounce counter of the record. """
+ super(MailBlackListMixin, self)._message_reset_bounce(email)
+ self.write({'message_bounce': 0})
+
+ def mail_action_blacklist_remove(self):
+ # wizard access rights currently not working as expected and allows users without access to
+ # open this wizard, therefore we check to make sure they have access before the wizard opens.
+ can_access = self.env['mail.blacklist'].check_access_rights('write', raise_exception=False)
+ if can_access:
+ return {
+ 'name': 'Are you sure you want to unblacklist this Email Address?',
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'mail.blacklist.remove',
+ 'target': 'new',
+ }
+ else:
+ raise AccessError("You do not have the access right to unblacklist emails. Please contact your administrator.")