Add custom validator for multiple emails in a single text field. Change the widget of the email fields in the mailer app to avoid browser side email validation. Closes GitLab issue #530. Thanks to Mark Maglana @relaxdiego for the report.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2018-10-17 00:01:48 -04:00
parent cc79e75d35
commit 321b7ad5ae
7 changed files with 217 additions and 39 deletions
+4
View File
@@ -8,6 +8,10 @@
* Add improvements to the metadata URL encoding and decoding to support * Add improvements to the metadata URL encoding and decoding to support
ampersand characters as part of the metadata value. GitLab issue ampersand characters as part of the metadata value. GitLab issue
#529. Thanks to Mark Maglana @relaxdiego for the report. #529. Thanks to Mark Maglana @relaxdiego for the report.
* Add custom validator for multiple emails in a single text field.
Change the widget of the email fields in the mailer app to avoid
browser side email validation. Closes GitLab issue #530.
Thanks to Mark Maglana @relaxdiego for the report.
3.1.7 (2018-10-14) 3.1.7 (2018-10-14)
================== ==================
+9 -3
View File
@@ -18,6 +18,7 @@ from .settings import (
setting_document_body_template, setting_document_subject_template, setting_document_body_template, setting_document_subject_template,
setting_link_body_template, setting_link_subject_template setting_link_body_template, setting_link_subject_template
) )
from .validators import validate_email_multiple
class DocumentMailForm(forms.Form): class DocumentMailForm(forms.Form):
@@ -56,11 +57,11 @@ class DocumentMailForm(forms.Form):
except UserMailer.DoesNotExist: except UserMailer.DoesNotExist:
pass pass
email = forms.EmailField( email = forms.CharField(
help_text=_( help_text=_(
'Email address of the recipient. Can be multiple addresses ' 'Email address of the recipient. Can be multiple addresses '
'separated by comma or semicolon.' 'separated by comma or semicolon.'
), label=_('Email address') ), label=_('Email address'), validators=[validate_email_multiple]
) )
subject = forms.CharField(label=_('Subject'), required=False) subject = forms.CharField(label=_('Subject'), required=False)
body = forms.CharField( body = forms.CharField(
@@ -117,4 +118,9 @@ class UserMailerDynamicForm(DynamicModelForm):
class UserMailerTestForm(forms.Form): class UserMailerTestForm(forms.Form):
email = forms.EmailField(label=_('Email address')) email = forms.CharField(
help_text=_(
'Email address of the recipient. Can be multiple addresses '
'separated by comma or semicolon.'
), label=_('Email address'), validators=[validate_email_multiple]
)
+9 -3
View File
@@ -3,10 +3,16 @@ from __future__ import unicode_literals
TEST_BODY_HTML = '<strong>test body</strong>' TEST_BODY_HTML = '<strong>test body</strong>'
TEST_EMAIL_ADDRESS = 'test@example.com' TEST_EMAIL_ADDRESS = 'test@example.com'
TEST_RECIPIENTS_MULTIPLE_COMMA = 'test@example.com,test2@example.com' TEST_RECIPIENTS_MULTIPLE_COMMA = 'test@example.com,test2@example.com'
TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT = [
'test@example.com', 'test2@example.com'
]
TEST_RECIPIENTS_MULTIPLE_SEMICOLON = 'test@example.com;test2@example.com' TEST_RECIPIENTS_MULTIPLE_SEMICOLON = 'test@example.com;test2@example.com'
TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT = [
'test@example.com', 'test2@example.com'
]
TEST_RECIPIENTS_MULTIPLE_MIXED = 'test@example.com,test2@example.com;test2@example.com' TEST_RECIPIENTS_MULTIPLE_MIXED = 'test@example.com,test2@example.com;test2@example.com'
TEST_RECIPIENTS_MULTIPLE_MIXED_LIST = ( TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT = [
'test@example.com', 'test2@example.com', 'test2@example.com', 'test@example.com', 'test2@example.com', 'test2@example.com'
) ]
TEST_USER_MAILER_BACKEND_PATH = 'mailer.tests.mailers.TestBackend' TEST_USER_MAILER_BACKEND_PATH = 'mailer.tests.mailers.TestBackend'
TEST_USER_MAILER_LABEL = 'test user mailer label' TEST_USER_MAILER_LABEL = 'test user mailer label'
+16
View File
@@ -0,0 +1,16 @@
from __future__ import unicode_literals
from ..models import UserMailer
from .literals import TEST_USER_MAILER_BACKEND_PATH, TEST_USER_MAILER_LABEL
class MailerTestMixin(object):
def _create_user_mailer(self):
self.user_mailer = UserMailer.objects.create(
default=True,
enabled=True,
label=TEST_USER_MAILER_LABEL,
backend_path=TEST_USER_MAILER_BACKEND_PATH,
backend_data='{}'
)
+8 -18
View File
@@ -4,26 +4,16 @@ from django.core import mail
from documents.tests.test_models import GenericDocumentTestCase from documents.tests.test_models import GenericDocumentTestCase
from ..models import UserMailer
from .literals import ( from .literals import (
TEST_BODY_HTML, TEST_EMAIL_ADDRESS, TEST_RECIPIENTS_MULTIPLE_COMMA, TEST_BODY_HTML, TEST_EMAIL_ADDRESS, TEST_RECIPIENTS_MULTIPLE_COMMA,
TEST_RECIPIENTS_MULTIPLE_SEMICOLON, TEST_RECIPIENTS_MULTIPLE_MIXED, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT, TEST_RECIPIENTS_MULTIPLE_SEMICOLON,
TEST_RECIPIENTS_MULTIPLE_MIXED_LIST, TEST_USER_MAILER_LABEL, TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT, TEST_RECIPIENTS_MULTIPLE_MIXED,
TEST_USER_MAILER_BACKEND_PATH TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT,
) )
from .mixins import MailerTestMixin
class ModelTestCase(GenericDocumentTestCase): class ModelTestCase(MailerTestMixin, GenericDocumentTestCase):
def _create_user_mailer(self):
self.user_mailer = UserMailer.objects.create(
default=True,
enabled=True,
label=TEST_USER_MAILER_LABEL,
backend_path=TEST_USER_MAILER_BACKEND_PATH,
backend_data='{}'
)
def test_send_simple(self): def test_send_simple(self):
self._create_user_mailer() self._create_user_mailer()
self.user_mailer.send(to=TEST_EMAIL_ADDRESS) self.user_mailer.send(to=TEST_EMAIL_ADDRESS)
@@ -61,7 +51,7 @@ class ModelTestCase(GenericDocumentTestCase):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual( self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_COMMA.split(',') mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT
) )
def test_send_multiple_recipients_semicolon(self): def test_send_multiple_recipients_semicolon(self):
@@ -70,7 +60,7 @@ class ModelTestCase(GenericDocumentTestCase):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual( self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_SEMICOLON.split(';') mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT
) )
def test_send_multiple_recipient_mixed(self): def test_send_multiple_recipient_mixed(self):
@@ -79,5 +69,5 @@ class ModelTestCase(GenericDocumentTestCase):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual( self.assertEqual(
list(mail.outbox[0].to), list(TEST_RECIPIENTS_MULTIPLE_MIXED_LIST) mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT
) )
+153 -15
View File
@@ -12,20 +12,14 @@ from ..permissions import (
) )
from .literals import ( from .literals import (
TEST_EMAIL_ADDRESS, TEST_USER_MAILER_BACKEND_PATH, TEST_USER_MAILER_LABEL TEST_EMAIL_ADDRESS, TEST_USER_MAILER_BACKEND_PATH, TEST_USER_MAILER_LABEL,
TEST_RECIPIENTS_MULTIPLE_COMMA, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT,
TEST_RECIPIENTS_MULTIPLE_MIXED, TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT,
TEST_RECIPIENTS_MULTIPLE_SEMICOLON,
TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT
) )
from .mailers import TestBackend from .mailers import TestBackend
from .mixins import MailerTestMixin
class MailerTestMixin(object):
def _create_user_mailer(self):
self.user_mailer = UserMailer.objects.create(
default=True,
enabled=True,
label=TEST_USER_MAILER_LABEL,
backend_path=TEST_USER_MAILER_BACKEND_PATH,
backend_data='{}'
)
class MailerViewsTestCase(MailerTestMixin, GenericDocumentViewTestCase): class MailerViewsTestCase(MailerTestMixin, GenericDocumentViewTestCase):
@@ -33,7 +27,9 @@ class MailerViewsTestCase(MailerTestMixin, GenericDocumentViewTestCase):
return self.post( return self.post(
'mailer:send_document_link', args=(self.document.pk,), 'mailer:send_document_link', args=(self.document.pk,),
data={ data={
'email': TEST_EMAIL_ADDRESS, 'email': getattr(
self, 'test_email_address', TEST_EMAIL_ADDRESS
),
'user_mailer': self.user_mailer.pk 'user_mailer': self.user_mailer.pk
}, },
) )
@@ -42,7 +38,9 @@ class MailerViewsTestCase(MailerTestMixin, GenericDocumentViewTestCase):
return self.post( return self.post(
'mailer:send_document', args=(self.document.pk,), 'mailer:send_document', args=(self.document.pk,),
data={ data={
'email': TEST_EMAIL_ADDRESS, 'email': getattr(
self, 'test_email_address', TEST_EMAIL_ADDRESS
),
'user_mailer': self.user_mailer.pk 'user_mailer': self.user_mailer.pk
}, },
) )
@@ -66,7 +64,9 @@ class MailerViewsTestCase(MailerTestMixin, GenericDocumentViewTestCase):
def _request_user_mailer_test(self): def _request_user_mailer_test(self):
return self.post( return self.post(
'mailer:user_mailer_test', args=(self.user_mailer.pk,), data={ 'mailer:user_mailer_test', args=(self.user_mailer.pk,), data={
'email': TEST_EMAIL_ADDRESS 'email': getattr(
self, 'test_email_address', TEST_EMAIL_ADDRESS
)
}, follow=True }, follow=True
) )
@@ -207,3 +207,141 @@ class MailerViewsTestCase(MailerTestMixin, GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS])
def test_send_multiple_recipients_comma(self):
self._create_user_mailer()
self.login_user()
self.grant_access(
obj=self.user_mailer, permission=permission_user_mailer_use
)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_COMMA
response = self._request_user_mailer_test()
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT
)
def test_send_multiple_recipients_mixed(self):
self._create_user_mailer()
self.login_user()
self.grant_access(
obj=self.user_mailer, permission=permission_user_mailer_use
)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_MIXED
response = self._request_user_mailer_test()
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT
)
def test_send_multiple_recipients_semicolon(self):
self._create_user_mailer()
self.login_user()
self.grant_access(
obj=self.user_mailer, permission=permission_user_mailer_use
)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_SEMICOLON
response = self._request_user_mailer_test()
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT
)
def test_mail_link_view_recipients_comma(self):
self._create_user_mailer()
self.login_user()
self.grant_permission(permission=permission_mailing_link)
self.grant_permission(permission=permission_user_mailer_use)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_COMMA
self._request_document_link_send()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT
)
def test_mail_link_view_recipients_mixed(self):
self._create_user_mailer()
self.login_user()
self.grant_permission(permission=permission_mailing_link)
self.grant_permission(permission=permission_user_mailer_use)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_MIXED
self._request_document_link_send()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT
)
def test_mail_link_view_recipients_semicolon(self):
self._create_user_mailer()
self.login_user()
self.grant_permission(permission=permission_mailing_link)
self.grant_permission(permission=permission_user_mailer_use)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_SEMICOLON
self._request_document_link_send()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT
)
def test_mail_document_view_recipients_comma(self):
self._create_user_mailer()
self.login_user()
self.grant_permission(permission=permission_mailing_send_document)
self.grant_permission(permission=permission_user_mailer_use)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_COMMA
self._request_document_send()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT
)
def test_mail_document_view_recipients_mixed(self):
self._create_user_mailer()
self.login_user()
self.grant_permission(permission=permission_mailing_send_document)
self.grant_permission(permission=permission_user_mailer_use)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_MIXED
self._request_document_send()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_MIXED_RESULT
)
def test_mail_document_view_recipients_semicolon(self):
self._create_user_mailer()
self.login_user()
self.grant_permission(permission=permission_mailing_send_document)
self.grant_permission(permission=permission_user_mailer_use)
self.test_email_address = TEST_RECIPIENTS_MULTIPLE_SEMICOLON
self._request_document_send()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].to, TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT
)
+18
View File
@@ -0,0 +1,18 @@
from __future__ import unicode_literals
from django.core import validators
from django.utils.translation import ugettext_lazy as _
from .utils import split_recipient_list
def validate_email_multiple(value):
recipient_list = split_recipient_list(recipients=[value])
for recipient in recipient_list:
validate_email = validators.EmailValidator(
message=_('%(email)s is not a valid email address.') % {
'email': recipient
}
)
validate_email(value=recipient)