diff --git a/ietf/person/migrations/0009_auto_20190118_0725.py b/ietf/person/migrations/0009_auto_20190118_0725.py new file mode 100644 index 000000000..e3a1e4233 --- /dev/null +++ b/ietf/person/migrations/0009_auto_20190118_0725.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.18 on 2019-01-18 07:25 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('person', '0008_auto_20181014_1448'), + ] + + operations = [ + migrations.AlterField( + model_name='email', + name='origin', + field=models.CharField(help_text=b"The origin of the address: the user's email address, or 'author: DRAFTNAME' if a draft, or 'role: GROUP/ROLE' if a role.", max_length=150), + ), + migrations.AlterField( + model_name='historicalemail', + name='origin', + field=models.CharField(help_text=b"The origin of the address: the user's email address, or 'author: DRAFTNAME' if a draft, or 'role: GROUP/ROLE' if a role.", max_length=150), + ), + ] diff --git a/ietf/person/models.py b/ietf/person/models.py index 68edc683a..d0159c69c 100644 --- a/ietf/person/models.py +++ b/ietf/person/models.py @@ -258,7 +258,7 @@ class Email(models.Model): person = ForeignKey(Person, null=True) time = models.DateTimeField(auto_now_add=True) primary = models.BooleanField(default=False) - origin = models.CharField(max_length=150, default='', editable=False) # User.username or Document.name + origin = models.CharField(max_length=150, blank=False, help_text="The origin of the address: the user's email address, or 'author: DRAFTNAME' if a draft, or 'role: GROUP/ROLE' if a role.") # User.username or Document.name active = models.BooleanField(default=True) # Old email addresses are *not* purged, as history # information points to persons through these diff --git a/ietf/secr/rolodex/forms.py b/ietf/secr/rolodex/forms.py index b6f17e0dd..eafcd5147 100644 --- a/ietf/secr/rolodex/forms.py +++ b/ietf/secr/rolodex/forms.py @@ -1,12 +1,22 @@ +# Copyright The IETF Trust 2016, All Rights Reserved +# -*- coding: utf-8 -*- +from __future__ import unicode_literals, print_function + +import re + from django import forms from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.core.validators import validate_email +from django.core.validators import validate_email, EmailValidator +import debug # pyflakes:ignore + +from ietf.doc.models import Document +from ietf.group.models import Group +from ietf.name.models import RoleName from ietf.person.models import Email, Person -import re class SearchForm(forms.Form): name = forms.CharField(max_length=50,required=False) @@ -27,7 +37,37 @@ class EmailForm(forms.ModelForm): class Meta: model = Email fields = '__all__' - widgets = {'address': forms.TextInput(attrs={'readonly':True})} + widgets = { + 'address': forms.TextInput(attrs={'readonly':True}), + 'origin': forms.TextInput(attrs={'blank':False}), + } + + def clean_origin(self): + validate_email = EmailValidator("Please provide the origin of the new email: A valid user email if provided by email, or 'author: doc' or 'role: role spec'.") + if 'origin' in self.changed_data and self.instance.origin: + raise forms.ValidationError("You may not change existing origin fields, only set the value when empty") + origin = self.cleaned_data['origin'] + if ':' in origin: + valid_tags = ['author', 'role', 'registration', ] + tag, value = [ v.strip() for v in origin.split(':', 1) ] + if not tag in valid_tags: + raise forms.ValidationError("Invalid tag. Valid tags are: %s" % ','.join(valid_tags)) + if tag == 'author': + if not Document.objects.filter(name=value).exists(): + raise forms.ValidationError("Invalid document: %s. A valid document is required with 'author:'" % value) + elif tag == 'role': + if not ' ' in value: + raise forms.ValidationError("Invalid role spec: %s. Please indicate 'group role'." % value) + acronym, slug = value.split(None, 1) + if not Group.objects.filter(acronym=acronym).exists(): + raise forms.ValidationError("Invalid group: %s. A valid 'group role' string is required with 'role:'" % acronym) + if not RoleName.objects.filter(slug=slug).exists(): + roles = RoleName.objects.values_list('slug', flat=True) + raise forms.ValidationError("Invalid role: %s. A valid 'group role' string is required with 'role:'.\n Valid roles are: %s" % (slug, ', '.join(roles))) + else: + validate_email(origin) + return origin + class EditPersonForm(forms.ModelForm): class Meta: diff --git a/ietf/secr/rolodex/tests.py b/ietf/secr/rolodex/tests.py index 3cd897127..93e0e9e2f 100644 --- a/ietf/secr/rolodex/tests.py +++ b/ietf/secr/rolodex/tests.py @@ -3,6 +3,7 @@ from django.urls import reverse import debug # pyflakes:ignore from ietf.utils.test_utils import TestCase +from ietf.group.factories import GroupFactory, RoleFactory from ietf.person.factories import PersonFactory, UserFactory from ietf.person.models import Person, User @@ -47,11 +48,15 @@ class RolodexTestCase(TestCase): def test_edit_replace_user(self): person = PersonFactory() + email = person.email() user = UserFactory() + group = GroupFactory(type_id='wg') + role = RoleFactory(group=group,name_id='chair',person=person) url = reverse('ietf.secr.rolodex.views.edit', kwargs={'id':person.id}) redirect_url = reverse('ietf.secr.rolodex.views.view', kwargs={'id':person.id}) self.client.login(username="secretary", password="secretary+password") response = self.client.get(url) + #debug.show('unicontent(response)') self.assertEqual(response.status_code, 200) post_data = { 'name': person.name, @@ -59,8 +64,12 @@ class RolodexTestCase(TestCase): 'ascii_short': person.ascii_short, 'user': user.username, 'email-0-person':person.pk, - 'email-0-address': person.email_address(), - 'email-TOTAL_FORMS':1, + 'email-0-address': email.address, + 'email-0-origin': email.origin, + 'email-1-person':person.pk, + 'email-1-address': 'name@example.com', + 'email-1-origin': 'role: %s %s' % (group.acronym, role.name.slug), + 'email-TOTAL_FORMS':2, 'email-INITIAL_FORMS':1, 'email-MIN_NUM_FORMS':0, 'email-MAX_NUM_FORMS':1000, diff --git a/ietf/secr/static/secr/css/custom.css b/ietf/secr/static/secr/css/custom.css index 69353c0dd..d94427252 100644 --- a/ietf/secr/static/secr/css/custom.css +++ b/ietf/secr/static/secr/css/custom.css @@ -612,11 +612,11 @@ td.document-name { ========================================================================== */ form[id^="rolodex-"] input[type=text] { - width: 30em; + width: 25em; } form[id^="rolodex-"] #id_address { - width: 30em; + width: 25em; height: 7em; }