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 #2080.
- Legacy-Id: 12872
This commit is contained in:
parent
a78c419845
commit
cf4a4b02a7
|
@ -1,15 +1,18 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
import datetime
|
||||
import email.utils
|
||||
from urlparse import urljoin
|
||||
|
||||
from django.db import models
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.group.colors import fg_group_colors, bg_group_colors
|
||||
from ietf.name.models import GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName
|
||||
from ietf.person.models import Email, Person
|
||||
from ietf.utils.mail import formataddr
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
class GroupInfo(models.Model):
|
||||
time = models.DateTimeField(default=datetime.datetime.now)
|
||||
|
@ -254,8 +257,11 @@ class Role(models.Model):
|
|||
def __unicode__(self):
|
||||
return u"%s is %s in %s" % (self.person.plain_name(), self.name.name, self.group.acronym or self.group.name)
|
||||
|
||||
def formatted_ascii_email(self):
|
||||
return email.utils.formataddr((self.person.plain_ascii(), self.email.address))
|
||||
|
||||
def formatted_email(self):
|
||||
return u'"%s" <%s>' % (self.person.plain_name(), self.email.address)
|
||||
return formataddr((self.person.plain_name(), self.email.address))
|
||||
|
||||
class RoleHistory(models.Model):
|
||||
# RoleHistory doesn't have a time field as it's not supposed to be
|
||||
|
|
|
@ -127,9 +127,9 @@ def all_id2_txt():
|
|||
else:
|
||||
l.append(a.author.person.plain_name())
|
||||
|
||||
shepherds = dict((e.pk, e.formatted_email().replace('"', ''))
|
||||
shepherds = dict((e.pk, e.formatted_ascii_email().replace('"', ''))
|
||||
for e in Email.objects.filter(shepherd_document_set__type="draft").select_related("person").distinct())
|
||||
ads = dict((p.pk, p.formatted_email().replace('"', ''))
|
||||
ads = dict((p.pk, p.formatted_ascii_email().replace('"', ''))
|
||||
for p in Person.objects.filter(ad_document_set__type="draft").distinct())
|
||||
|
||||
res = []
|
||||
|
|
|
@ -4,6 +4,10 @@ from django.db import models
|
|||
from django.template import Template, Context
|
||||
|
||||
from email.utils import parseaddr
|
||||
from ietf.utils.mail import formataddr
|
||||
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.group.models import Role
|
||||
|
||||
|
@ -14,7 +18,7 @@ def clean_duplicates(addrlist):
|
|||
if (name,addr)==('',''):
|
||||
retval.add(a)
|
||||
elif name:
|
||||
retval.add('"%s" <%s>'%(name,addr))
|
||||
retval.add(formataddr((name,addr)))
|
||||
else:
|
||||
retval.add(addr)
|
||||
return list(retval)
|
||||
|
@ -200,7 +204,7 @@ class Recipient(models.Model):
|
|||
doc=submission.existing_document()
|
||||
if doc:
|
||||
old_authors = [i.author.formatted_email() for i in doc.documentauthor_set.all() if not i.author.invalid_address()]
|
||||
new_authors = [u'"%s" <%s>' % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]]
|
||||
new_authors = [ formataddr((author["name"], author["email"])) for author in submission.authors_parsed() if author["email"]]
|
||||
addrs.extend(old_authors)
|
||||
if doc.group and set(old_authors)!=set(new_authors):
|
||||
if doc.group.type_id in ['wg','rg','ag']:
|
||||
|
|
|
@ -5,15 +5,15 @@ from django import template
|
|||
from django.conf import settings
|
||||
from django.template.defaultfilters import linebreaksbr, force_escape
|
||||
|
||||
from ietf.utils.pipe import pipe
|
||||
from ietf.utils.log import log
|
||||
from ietf.doc.templatetags.ietf_filters import wrap_text
|
||||
|
||||
from ietf.person.models import Person
|
||||
from ietf.nomcom.utils import get_nomcom_by_year, retrieve_nomcom_private_key
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.doc.templatetags.ietf_filters import wrap_text
|
||||
from ietf.nomcom.utils import get_nomcom_by_year, retrieve_nomcom_private_key
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.log import log
|
||||
from ietf.utils.mail import formataddr
|
||||
from ietf.utils.pipe import pipe
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -41,7 +41,7 @@ def formatted_email(address):
|
|||
persons = Person.objects.filter(email__address__in=[address])
|
||||
person = persons and persons[0] or None
|
||||
if person and person.name:
|
||||
return u'"%s" <%s>' % (person.plain_name(), address)
|
||||
return formataddr((person.plain_name(), address))
|
||||
else:
|
||||
return address
|
||||
|
||||
|
|
|
@ -1705,7 +1705,7 @@ Junk body for testing
|
|||
'duplicate_persons':[nominee2.person.pk]})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(len(outbox),1)
|
||||
self.assertTrue(all([str(x.person.pk) in unicode(outbox[0]) for x in [nominee1,nominee2]]))
|
||||
self.assertTrue(all([str(x.person.pk) in outbox[0].get_payload(decode=True) for x in [nominee1,nominee2]]))
|
||||
|
||||
|
||||
class NomComIndexTests(TestCase):
|
||||
|
|
|
@ -2,11 +2,15 @@ import os
|
|||
import factory
|
||||
import faker
|
||||
import shutil
|
||||
import random
|
||||
import faker.config
|
||||
from unidecode import unidecode
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.person.models import Person, Alias, Email
|
||||
|
||||
fake = faker.Factory.create()
|
||||
|
@ -15,10 +19,12 @@ class UserFactory(factory.DjangoModelFactory):
|
|||
class Meta:
|
||||
model = User
|
||||
django_get_or_create = ('username',)
|
||||
exclude = ['locale', ]
|
||||
|
||||
first_name = factory.Faker('first_name')
|
||||
last_name = factory.Faker('last_name')
|
||||
email = factory.LazyAttributeSequence(lambda u, n: '%s.%s_%d@%s'%(u.first_name,u.last_name,n,fake.domain_name()))
|
||||
locale = random.sample(faker.config.AVAILABLE_LOCALES, 1)[0]
|
||||
first_name = factory.Faker('first_name', locale)
|
||||
last_name = factory.Faker('last_name', locale)
|
||||
email = factory.LazyAttributeSequence(lambda u, n: '%s.%s_%d@%s'%(unidecode(u.first_name),unidecode(u.last_name),n, fake.domain_name()))
|
||||
username = factory.LazyAttribute(lambda u: u.email)
|
||||
|
||||
@factory.post_generation
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import six
|
||||
|
||||
from collections import Counter
|
||||
from urllib import urlencode
|
||||
|
@ -108,7 +109,7 @@ class SearchablePersonsField(forms.CharField):
|
|||
#if self.only_users:
|
||||
# objs = objs.exclude(person__user=None)
|
||||
|
||||
found_pks = [str(o.pk) for o in objs]
|
||||
found_pks = [ six.text_type(o.pk) for o in objs]
|
||||
failed_pks = [x for x in pks if x not in found_pks]
|
||||
if failed_pks:
|
||||
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()))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
import datetime
|
||||
import email.utils
|
||||
import email.header
|
||||
from hashids import Hashids
|
||||
from unidecode import unidecode
|
||||
from urlparse import urljoin
|
||||
|
@ -17,6 +19,8 @@ import debug # pyflakes:ignore
|
|||
from ietf.person.name import name_parts, initials
|
||||
from ietf.utils.mail import send_mail_preformatted
|
||||
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
|
||||
from ietf.utils.mail import formataddr
|
||||
|
||||
|
||||
class PersonInfo(models.Model):
|
||||
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
|
||||
|
@ -106,6 +110,14 @@ class PersonInfo(models.Model):
|
|||
return e.address
|
||||
else:
|
||||
return ""
|
||||
def formatted_ascii_email(self):
|
||||
e = self.email_set.filter(primary=True).first()
|
||||
if not e:
|
||||
e = self.email_set.order_by("-active", "-time").first()
|
||||
if e:
|
||||
return e.formatted_ascii_email()
|
||||
else:
|
||||
return ""
|
||||
def formatted_email(self):
|
||||
e = self.email_set.filter(primary=True).first()
|
||||
if not e:
|
||||
|
@ -225,9 +237,15 @@ class Email(models.Model):
|
|||
def get_name(self):
|
||||
return self.person.plain_name() if self.person else self.address
|
||||
|
||||
def formatted_ascii_email(self):
|
||||
if self.person:
|
||||
return email.utils.formataddr((self.person.plain_ascii(), self.address))
|
||||
else:
|
||||
return self.address
|
||||
|
||||
def formatted_email(self):
|
||||
if self.person:
|
||||
return u'"%s" <%s>' % (self.person.plain_ascii(), self.address)
|
||||
return formataddr((self.person.plain_name(), self.address))
|
||||
else:
|
||||
return self.address
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
from pyquery import PyQuery
|
||||
|
@ -41,7 +42,7 @@ class PersonTests(TestCase):
|
|||
url = urlreverse("ietf.person.views.profile", kwargs={ "email_or_name": person.plain_name()})
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn(person.photo_name(), r.content)
|
||||
self.assertIn(person.photo_name(), r.content.decode(r.charset))
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn("Photo of %s"%person, q("div.bio-text img.bio-photo").attr("alt"))
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import unicode_literals
|
||||
import pprint
|
||||
|
||||
from django.contrib import admin
|
||||
|
@ -18,7 +19,7 @@ def merge_persons(source,target,stream):
|
|||
if alias.name in target_aliases:
|
||||
alias.delete()
|
||||
else:
|
||||
print >>stream,"Merging alias: {}".format(alias.name)
|
||||
print >>stream, "Merging alias: {}".format(alias.name)
|
||||
alias.person = target
|
||||
alias.save()
|
||||
|
||||
|
|
|
@ -358,8 +358,8 @@ class SubmitTests(TestCase):
|
|||
if stream_type=='ise':
|
||||
self.assertTrue("rfc-ise@" in confirm_email["To"].lower())
|
||||
else:
|
||||
self.assertTrue("chairs have been copied" not in unicode(confirm_email))
|
||||
self.assertTrue("mars-chairs@" not in confirm_email["To"].lower())
|
||||
self.assertNotIn("chairs have been copied", unicode(confirm_email))
|
||||
self.assertNotIn("mars-chairs@", confirm_email["To"].lower())
|
||||
|
||||
confirm_url = self.extract_confirm_url(confirm_email)
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from email.utils import make_msgid, formatdate, formataddr, parseaddr, getaddresses
|
||||
import copy
|
||||
import datetime
|
||||
import smtplib
|
||||
import sys
|
||||
import textwrap
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from email.utils import make_msgid, formatdate, formataddr as simple_formataddr, parseaddr, getaddresses
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.message import MIMEMessage
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
@ -8,20 +16,17 @@ from email.header import Header
|
|||
from email import message_from_string
|
||||
from email import charset as Charset
|
||||
|
||||
import smtplib
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template.loader import render_to_string
|
||||
from django.template import Context,RequestContext
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
import ietf
|
||||
from ietf.utils.log import log
|
||||
import sys
|
||||
import time
|
||||
import copy
|
||||
import textwrap
|
||||
import traceback
|
||||
import datetime
|
||||
from ietf.utils.text import isascii
|
||||
|
||||
# Testing mode:
|
||||
# import ietf.utils.mail
|
||||
|
@ -189,9 +194,22 @@ def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=F
|
|||
msg = encode_message(txt)
|
||||
return send_mail_mime(request, to, frm, subject, msg, cc, extra, toUser, bcc)
|
||||
|
||||
def formataddr(addrtuple):
|
||||
"""
|
||||
Takes a name and email address, and inspects the name to see if it needs
|
||||
to be encoded in an email.header.Header before being used in an email.message
|
||||
address field. Does what's needed, and returns a string value suitable for
|
||||
use in a To: or Cc: email header field.
|
||||
"""
|
||||
name, addr = addrtuple
|
||||
if name and not isascii(name):
|
||||
name = str(Header(name, 'utf-8'))
|
||||
return simple_formataddr((name, addr))
|
||||
|
||||
def condition_message(to, frm, subject, msg, cc, extra):
|
||||
|
||||
if isinstance(frm, tuple):
|
||||
frm = formataddr(frm)
|
||||
frm = formataddr(frm)
|
||||
if isinstance(to, list) or isinstance(to, tuple):
|
||||
to = ", ".join([isinstance(addr, tuple) and formataddr(addr) or addr for addr in to if addr])
|
||||
if isinstance(cc, list) or isinstance(cc, tuple):
|
||||
|
|
|
@ -49,3 +49,10 @@ def fill(text, width):
|
|||
wrapped.append(para)
|
||||
return "\n\n".join(wrapped)
|
||||
|
||||
def isascii(text):
|
||||
try:
|
||||
text.encode('ascii')
|
||||
return True
|
||||
except UnicodeEncodeError:
|
||||
return False
|
||||
|
||||
|
|
Loading…
Reference in a new issue