Skip to content

Commit cf4a4b0

Browse files
committed
Reworked the email address handling in order to be able to support non-ascii names as part of email address fields. Reworked the generation of user names in the test suite to generate names from multiple non-ascii locales. Fixes issue ietf-tools#2080.
- Legacy-Id: 12872
1 parent a78c419 commit cf4a4b0

13 files changed

Lines changed: 94 additions & 32 deletions

File tree

ietf/group/models.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
# Copyright The IETF Trust 2007, All Rights Reserved
22

33
import datetime
4+
import email.utils
45
from urlparse import urljoin
56

67
from django.db import models
78

9+
import debug # pyflakes:ignore
10+
811
from ietf.group.colors import fg_group_colors, bg_group_colors
912
from ietf.name.models import GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName
1013
from ietf.person.models import Email, Person
14+
from ietf.utils.mail import formataddr
1115

12-
import debug # pyflakes:ignore
1316

1417
class GroupInfo(models.Model):
1518
time = models.DateTimeField(default=datetime.datetime.now)
@@ -254,8 +257,11 @@ class Role(models.Model):
254257
def __unicode__(self):
255258
return u"%s is %s in %s" % (self.person.plain_name(), self.name.name, self.group.acronym or self.group.name)
256259

260+
def formatted_ascii_email(self):
261+
return email.utils.formataddr((self.person.plain_ascii(), self.email.address))
262+
257263
def formatted_email(self):
258-
return u'"%s" <%s>' % (self.person.plain_name(), self.email.address)
264+
return formataddr((self.person.plain_name(), self.email.address))
259265

260266
class RoleHistory(models.Model):
261267
# RoleHistory doesn't have a time field as it's not supposed to be

ietf/idindex/index.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ def all_id2_txt():
127127
else:
128128
l.append(a.author.person.plain_name())
129129

130-
shepherds = dict((e.pk, e.formatted_email().replace('"', ''))
130+
shepherds = dict((e.pk, e.formatted_ascii_email().replace('"', ''))
131131
for e in Email.objects.filter(shepherd_document_set__type="draft").select_related("person").distinct())
132-
ads = dict((p.pk, p.formatted_email().replace('"', ''))
132+
ads = dict((p.pk, p.formatted_ascii_email().replace('"', ''))
133133
for p in Person.objects.filter(ad_document_set__type="draft").distinct())
134134

135135
res = []

ietf/mailtrigger/models.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
from django.template import Template, Context
55

66
from email.utils import parseaddr
7+
from ietf.utils.mail import formataddr
8+
9+
10+
import debug # pyflakes:ignore
711

812
from ietf.group.models import Role
913

@@ -14,7 +18,7 @@ def clean_duplicates(addrlist):
1418
if (name,addr)==('',''):
1519
retval.add(a)
1620
elif name:
17-
retval.add('"%s" <%s>'%(name,addr))
21+
retval.add(formataddr((name,addr)))
1822
else:
1923
retval.add(addr)
2024
return list(retval)
@@ -200,7 +204,7 @@ def gather_submission_confirmers(self, **kwargs):
200204
doc=submission.existing_document()
201205
if doc:
202206
old_authors = [i.author.formatted_email() for i in doc.documentauthor_set.all() if not i.author.invalid_address()]
203-
new_authors = [u'"%s" <%s>' % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]]
207+
new_authors = [ formataddr((author["name"], author["email"])) for author in submission.authors_parsed() if author["email"]]
204208
addrs.extend(old_authors)
205209
if doc.group and set(old_authors)!=set(new_authors):
206210
if doc.group.type_id in ['wg','rg','ag']:

ietf/nomcom/templatetags/nomcom_tags.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
from django.conf import settings
66
from django.template.defaultfilters import linebreaksbr, force_escape
77

8-
from ietf.utils.pipe import pipe
9-
from ietf.utils.log import log
10-
from ietf.doc.templatetags.ietf_filters import wrap_text
8+
import debug # pyflakes:ignore
119

12-
from ietf.person.models import Person
10+
from ietf.doc.templatetags.ietf_filters import wrap_text
1311
from ietf.nomcom.utils import get_nomcom_by_year, retrieve_nomcom_private_key
14-
15-
import debug # pyflakes:ignore
12+
from ietf.person.models import Person
13+
from ietf.utils.log import log
14+
from ietf.utils.mail import formataddr
15+
from ietf.utils.pipe import pipe
1616

1717

1818
register = template.Library()
@@ -41,7 +41,7 @@ def formatted_email(address):
4141
persons = Person.objects.filter(email__address__in=[address])
4242
person = persons and persons[0] or None
4343
if person and person.name:
44-
return u'"%s" <%s>' % (person.plain_name(), address)
44+
return formataddr((person.plain_name(), address))
4545
else:
4646
return address
4747

ietf/nomcom/tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1705,7 +1705,7 @@ def test_request_merge(self):
17051705
'duplicate_persons':[nominee2.person.pk]})
17061706
self.assertEqual(response.status_code, 302)
17071707
self.assertEqual(len(outbox),1)
1708-
self.assertTrue(all([str(x.person.pk) in unicode(outbox[0]) for x in [nominee1,nominee2]]))
1708+
self.assertTrue(all([str(x.person.pk) in outbox[0].get_payload(decode=True) for x in [nominee1,nominee2]]))
17091709

17101710

17111711
class NomComIndexTests(TestCase):

ietf/person/factories.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
import factory
33
import faker
44
import shutil
5+
import random
6+
import faker.config
57
from unidecode import unidecode
68

79
from django.conf import settings
810
from django.contrib.auth.models import User
911

12+
import debug # pyflakes:ignore
13+
1014
from ietf.person.models import Person, Alias, Email
1115

1216
fake = faker.Factory.create()
@@ -15,10 +19,12 @@ class UserFactory(factory.DjangoModelFactory):
1519
class Meta:
1620
model = User
1721
django_get_or_create = ('username',)
22+
exclude = ['locale', ]
1823

19-
first_name = factory.Faker('first_name')
20-
last_name = factory.Faker('last_name')
21-
email = factory.LazyAttributeSequence(lambda u, n: '%s.%s_%d@%s'%(u.first_name,u.last_name,n,fake.domain_name()))
24+
locale = random.sample(faker.config.AVAILABLE_LOCALES, 1)[0]
25+
first_name = factory.Faker('first_name', locale)
26+
last_name = factory.Faker('last_name', locale)
27+
email = factory.LazyAttributeSequence(lambda u, n: '%s.%s_%d@%s'%(unidecode(u.first_name),unidecode(u.last_name),n, fake.domain_name()))
2228
username = factory.LazyAttribute(lambda u: u.email)
2329

2430
@factory.post_generation

ietf/person/fields.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import six
23

34
from collections import Counter
45
from urllib import urlencode
@@ -108,7 +109,7 @@ def clean(self, value):
108109
#if self.only_users:
109110
# objs = objs.exclude(person__user=None)
110111

111-
found_pks = [str(o.pk) for o in objs]
112+
found_pks = [ six.text_type(o.pk) for o in objs]
112113
failed_pks = [x for x in pks if x not in found_pks]
113114
if failed_pks:
114115
raise forms.ValidationError(u"Could not recognize the following {model_name}s: {pks}. You can only input {model_name}s already registered in the Datatracker.".format(pks=", ".join(failed_pks), model_name=self.model.__name__.lower()))

ietf/person/models.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright The IETF Trust 2007, All Rights Reserved
22

33
import datetime
4+
import email.utils
5+
import email.header
46
from hashids import Hashids
57
from unidecode import unidecode
68
from urlparse import urljoin
@@ -17,6 +19,8 @@
1719
from ietf.person.name import name_parts, initials
1820
from ietf.utils.mail import send_mail_preformatted
1921
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
22+
from ietf.utils.mail import formataddr
23+
2024

2125
class PersonInfo(models.Model):
2226
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
@@ -106,6 +110,14 @@ def email_address(self):
106110
return e.address
107111
else:
108112
return ""
113+
def formatted_ascii_email(self):
114+
e = self.email_set.filter(primary=True).first()
115+
if not e:
116+
e = self.email_set.order_by("-active", "-time").first()
117+
if e:
118+
return e.formatted_ascii_email()
119+
else:
120+
return ""
109121
def formatted_email(self):
110122
e = self.email_set.filter(primary=True).first()
111123
if not e:
@@ -225,9 +237,15 @@ def __unicode__(self):
225237
def get_name(self):
226238
return self.person.plain_name() if self.person else self.address
227239

240+
def formatted_ascii_email(self):
241+
if self.person:
242+
return email.utils.formataddr((self.person.plain_ascii(), self.address))
243+
else:
244+
return self.address
245+
228246
def formatted_email(self):
229247
if self.person:
230-
return u'"%s" <%s>' % (self.person.plain_ascii(), self.address)
248+
return formataddr((self.person.plain_name(), self.address))
231249
else:
232250
return self.address
233251

ietf/person/tests.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
23

34
import json
45
from pyquery import PyQuery
@@ -41,7 +42,7 @@ def test_profile(self):
4142
url = urlreverse("ietf.person.views.profile", kwargs={ "email_or_name": person.plain_name()})
4243
r = self.client.get(url)
4344
self.assertEqual(r.status_code, 200)
44-
self.assertIn(person.photo_name(), r.content)
45+
self.assertIn(person.photo_name(), r.content.decode(r.charset))
4546
q = PyQuery(r.content)
4647
self.assertIn("Photo of %s"%person, q("div.bio-text img.bio-photo").attr("alt"))
4748

ietf/person/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from __future__ import unicode_literals
12
import pprint
23

34
from django.contrib import admin
@@ -18,7 +19,7 @@ def merge_persons(source,target,stream):
1819
if alias.name in target_aliases:
1920
alias.delete()
2021
else:
21-
print >>stream,"Merging alias: {}".format(alias.name)
22+
print >>stream, "Merging alias: {}".format(alias.name)
2223
alias.person = target
2324
alias.save()
2425

0 commit comments

Comments
 (0)