From 59fb8f7ce91f8c5820da8e3fc9b894c6a3e442b4 Mon Sep 17 00:00:00 2001 From: Vincent Vinet Date: Mon, 2 Feb 2015 12:47:28 -0500 Subject: [PATCH] [ADD] partner_email_validation --- partner_email_validation/__init__.py | 26 ++++ partner_email_validation/__openerp__.py | 62 ++++++++++ partner_email_validation/partner.py | 117 ++++++++++++++++++ partner_email_validation/tests/__init__.py | 29 +++++ .../tests/test_email_validate.py | 56 +++++++++ 5 files changed, 290 insertions(+) create mode 100644 partner_email_validation/__init__.py create mode 100644 partner_email_validation/__openerp__.py create mode 100644 partner_email_validation/partner.py create mode 100644 partner_email_validation/tests/__init__.py create mode 100644 partner_email_validation/tests/test_email_validate.py diff --git a/partner_email_validation/__init__.py b/partner_email_validation/__init__.py new file mode 100644 index 000000000..849266497 --- /dev/null +++ b/partner_email_validation/__init__.py @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import ( + partner, + tests, +) diff --git a/partner_email_validation/__openerp__.py b/partner_email_validation/__openerp__.py new file mode 100644 index 000000000..74f9204e7 --- /dev/null +++ b/partner_email_validation/__openerp__.py @@ -0,0 +1,62 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Partner Email Validation', + 'version': '1.0', + 'author': 'Savoir-faire Linux', + 'maintainer': 'Savoir-faire Linux', + 'website': 'http://www.savoirfairelinux.com', + 'license': 'AGPL-3', + 'category': 'Customer Relationship Management', + 'summary': 'Validate Partner Email', + 'description': """ + This module performs simple validation on email addresses. Emails are + checked for: + - Having a mailbox and domain part: mailbox@domain + - Having a seemingly valid domain + - (sub.)*domain.tld where tld has at least two letters + - @test and @localhost + - Containing letters or numbers, with a few special characters: + - ".-_+" for mailbox part + - "-." for domain part + - Quoted mailboxes "Foo Bar"@spam.eggs are not supported as of now. + + No email is sent for validation. + + Contributors + ------------ + * Vincent Vinet (vincent.vinet@savoirfairelinux.com) + """, + 'depends': [ + 'base', + ], + 'external_dependencies': { + 'python': [], + }, + 'data': [ + ], + 'demo': [], + 'test': [], + 'installable': True, + 'auto_install': False, +} diff --git a/partner_email_validation/partner.py b/partner_email_validation/partner.py new file mode 100644 index 000000000..1cd6799a0 --- /dev/null +++ b/partner_email_validation/partner.py @@ -0,0 +1,117 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import logging +import unicodedata + +from openerp.osv import orm +from openerp.tools.translate import _ + +_logger = logging.getLogger(__name__) + + +class Partner(orm.Model): + _name = _inherit = 'res.partner' + + def _check_email_domain(self, domain): + """ Checks if a domain is valid for an email """ + # Allow localhost and test, those are valid domains + if domain in (u'localhost', u'test'): + return True + + domain_parts = domain.split(u".") + + # Disallow @foo @foo. @foo..com + if len(domain_parts) <= 1: + _logger.debug("Email has single domain part") + return False + + # Disallow @foo. @foo..com, @.b + for part in domain_parts: + if not part: + _logger.debug("Empty domain part") + return False + + # TLD must have 2 chars at least + if len(domain_parts[-1]) < 2: + _logger.debug("TLD too short") + return False + + # Check that we have letters, numbers, '.' or '-' + for letter in domain: + if letter in ".-": + continue + + cat = unicodedata.category(letter) + if cat[0] not in u'LN': # Letter or Number + _logger.debug("Character not accepted in domain: %r", + letter) + return False + + return True + + def _check_email_mailbox(self, mailbox): + """ Checks if a mailbox is valid for an email """ + for letter in mailbox: + if letter in ".-_+": # Few accepted symbols + continue + + cat = unicodedata.category(letter) + if cat[0] not in u'LN': # Letter or Number + _logger.debug("Character not accepted in mailbox: %r", + letter) + return False + + return True + + def _check_valid_email(self, email): + """ Checks if an email address is valid """ + mail, sep, domain = email.rpartition(u'@') + # We need a full "mail@domain" + if not mail or not sep or not domain: + _logger.debug("Mail is not mail@domain") + return False + + if not self._check_email_domain(domain): + return False + + if not self._check_email_mailbox(mail): + return False + + return True + + def _check_customer_email(self, cr, uid, ids, context=None): + for partner in self.browse(cr, uid, ids, context=None): + if not partner.email: + # The view already forces the email to be filled, do not + # double check or repeat conditions + continue + + if not self._check_valid_email(partner.email): + return False + + return True + + _constraints = [ + (_check_customer_email, + _('You must provide a valid email address'), ['email']), + ] diff --git a/partner_email_validation/tests/__init__.py b/partner_email_validation/tests/__init__.py new file mode 100644 index 000000000..b267a5ae4 --- /dev/null +++ b/partner_email_validation/tests/__init__.py @@ -0,0 +1,29 @@ +# -*- encoding: utf-8 -*- +############################################################################### +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from . import ( + test_email_validate, +) + +checks = [ + test_email_validate, +] diff --git a/partner_email_validation/tests/test_email_validate.py b/partner_email_validation/tests/test_email_validate.py new file mode 100644 index 000000000..d2cf0c7b5 --- /dev/null +++ b/partner_email_validation/tests/test_email_validate.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014 Savoir-faire Linux (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from __future__ import unicode_literals + +from openerp.tests.common import TransactionCase + + +class TestEmailValidate(TransactionCase): + """ + Tests specific reseller behaviors for invoices + """ + + def test_valid_emails(self): + check = self.registry("res.partner")._check_valid_email + for email in [ + "mailbox+tag@foo.bar", + "domain@has-dash.com", + "hey.I.just.met.y0u@call.me", + "allô@mail.me", + "aлrr@test.рф", + "foo@123.com" + ]: + self.assertTrue(check(email)) + + def test_invalid_emails(self): + check = self.registry("res.partner")._check_valid_email + for email in [ + "", + "noarobas" + "nodomain@" + "empty@domain..part", + "@nomailbox.com" + "not@fqdn", + "not@twoletter.c", + "symbol@in.domain!", + "s!ymbol@in.mailbox", + ]: + self.assertFalse(check(email))