From e69b89b8fa8439fea27094ef86d97ffd8972124c Mon Sep 17 00:00:00 2001 From: David Beguin Date: Tue, 30 Oct 2018 10:47:28 +0000 Subject: [PATCH] [IMP] tools, mail : adds email normalization method in mail tools In order to normalize email independently from the mail.address.mixin model. --- addons/mail/models/mail_address_mixin.py | 11 +---------- addons/mail/models/mail_blacklist.py | 19 +++++-------------- addons/mass_mailing/controllers/main.py | 8 ++++---- addons/mass_mailing/models/mass_mailing.py | 4 ++-- odoo/tools/mail.py | 15 +++++++++++++++ 5 files changed, 27 insertions(+), 30 deletions(-) diff --git a/addons/mail/models/mail_address_mixin.py b/addons/mail/models/mail_address_mixin.py index 740191dcd4c..490c7b7d617 100644 --- a/addons/mail/models/mail_address_mixin.py +++ b/addons/mail/models/mail_address_mixin.py @@ -31,16 +31,7 @@ class MailAddressMixin(models.AbstractModel): self._assert_primary_email() [email_field] = self._primary_email for record in self: - record.email_normalized = self._normalize_email(record[email_field]) - - def _normalize_email(self, email): - """ Sanitize and standardize email address entries: all emails should be - only real email extracted from strings (A -> a@a) and should be - lower case. """ - emails = tools.email_split(email) - if not emails or len(emails) != 1: - return False - return emails[0].lower() + record.email_normalized = tools.email_normalize(record[email_field]) def _assert_primary_email(self): if not hasattr(self, "_primary_email") or \ diff --git a/addons/mail/models/mail_blacklist.py b/addons/mail/models/mail_blacklist.py index d9f7b95a0a0..919d3c20823 100644 --- a/addons/mail/models/mail_blacklist.py +++ b/addons/mail/models/mail_blacklist.py @@ -30,7 +30,7 @@ class MailBlackList(models.Model): new_values = [] all_emails = [] for value in values: - email = self._normalize_email(value.get('email')) + email = tools.email_normalize(value.get('email')) if not email: raise UserError(_('Invalid email address %r') % value['email']) if email in all_emails: @@ -53,7 +53,7 @@ class MailBlackList(models.Model): @api.multi def write(self, values): if 'email' in values: - values['email'] = self._normalize_email(values['email']) + 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): @@ -63,7 +63,7 @@ class MailBlackList(models.Model): new_args = [] for arg in args: if isinstance(arg, (list, tuple)) and arg[0] == 'email' and isinstance(arg[2], tools.pycompat.text_type): - normalized = self._normalize_email(arg[2]) + normalized = tools.email_normalize(arg[2]) if normalized: new_args.append([arg[0], arg[1], normalized]) else: @@ -75,7 +75,7 @@ class MailBlackList(models.Model): 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 = self._normalize_email(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}) @@ -84,7 +84,7 @@ class MailBlackList(models.Model): return record def _remove(self, email): - normalized = self._normalize_email(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}) @@ -92,15 +92,6 @@ class MailBlackList(models.Model): record = record.create({'email': email, 'active': False}) return record - def _normalize_email(self, email): - """ Sanitize and standardize email address entries: all emails should be - only real email extracted from strings (A -> a@a) and should be - lower case. """ - emails = tools.email_split(email) - if not emails or len(emails) != 1: - return False - return emails[0].lower() - class MailBlackListMixin(models.AbstractModel): """ Mixin that is inherited by all model with opt out. diff --git a/addons/mass_mailing/controllers/main.py b/addons/mass_mailing/controllers/main.py index 9f703881c46..11b05685a06 100644 --- a/addons/mass_mailing/controllers/main.py +++ b/addons/mass_mailing/controllers/main.py @@ -4,7 +4,7 @@ import base64 import werkzeug -from odoo import _, exceptions, http +from odoo import _, exceptions, http, tools from odoo.http import request from odoo.tools import consteq @@ -39,7 +39,7 @@ class MassMailController(http.Controller): # Unsubscribe directly + Let the user choose his subscriptions mailing.update_opt_out(email, mailing.contact_list_ids.ids, True) - contacts = request.env['mail.mass_mailing.contact'].sudo().search([('email_normalized', '=', email)]) + contacts = request.env['mail.mass_mailing.contact'].sudo().search([('email_normalized', '=', tools.email_normalize(email))]) subscription_list_ids = contacts.mapped('subscription_list_ids') # In many user are found : if user is opt_out on the list with contact_id 1 but not with contact_id 2, # assume that the user is not opt_out on both @@ -115,7 +115,7 @@ class MassMailController(http.Controller): if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): return 'unauthorized' if email: - record = request.env['mail.blacklist'].sudo().with_context(active_test=False).search([('email', '=', email)]) + record = request.env['mail.blacklist'].sudo().with_context(active_test=False).search([('email', '=', tools.email_normalize(email))]) if record['active']: return True return False @@ -152,7 +152,7 @@ class MassMailController(http.Controller): if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): return 'unauthorized' model = request.env[mailing.mailing_model_real] - records = model.sudo().search([('email_normalized', '=', email)]) + records = model.sudo().search([('email_normalized', '=', tools.email_normalize(email))]) for record in records: record.sudo().message_post(body=_("Feedback from %s: %s" % (email, feedback))) return bool(records) diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index 3397acdfba5..ca6f39c07de 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -230,7 +230,7 @@ class MassMailingContact(models.Model): @api.depends('email') def _compute_is_email_valid(self): for record in self: - normalized = self._normalize_email(record.email) + normalized = tools.email_normalize(record.email) record.is_email_valid = normalized if not normalized else True @api.model @@ -646,7 +646,7 @@ class MassMailing(models.Model): def update_opt_out(self, email, list_ids, value): if len(list_ids) > 0: model = self.env['mail.mass_mailing.contact'].with_context(active_test=False) - records = model.search([('email_normalized', '=', email)]) + records = model.search([('email_normalized', '=', tools.email_normalize(email))]) opt_out_records = self.env['mail.mass_mailing.list_contact_rel'].search([ ('contact_id', 'in', records.ids), ('list_id', 'in', list_ids), diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py index 668d0678f88..3bb080094ab 100644 --- a/odoo/tools/mail.py +++ b/odoo/tools/mail.py @@ -502,6 +502,21 @@ def email_split_and_format(text): if addr[1] if '@' in addr[1]] +def email_normalize(text): + """ Sanitize and standardize email address entries. + 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: + - Possible Input Email : 'Name ' + - Normalized Output Email : 'name@domain.com' + """ + emails = email_split(text) + if not emails or len(emails) != 1: + return False + return emails[0].lower() + def email_escape_char(email_address): """ Escape problematic characters in the given email address string""" return email_address.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')