diff --git a/ietf/message/admin.py b/ietf/message/admin.py index b95a1a180..14f4ba47f 100644 --- a/ietf/message/admin.py +++ b/ietf/message/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from ietf.message.models import Message, SendQueue +from ietf.message.models import Message, SendQueue, AnnouncementFrom class MessageAdmin(admin.ModelAdmin): list_display = ["subject", "by", "time", "groups"] @@ -19,5 +19,8 @@ class SendQueueAdmin(admin.ModelAdmin): search_fields = ["message__body"] raw_id_fields = ["by", "message"] ordering = ["-time"] - admin.site.register(SendQueue, SendQueueAdmin) + +class AnnouncementFromAdmin(admin.ModelAdmin): + list_display = ['name', 'group', 'address', ] +admin.site.register(AnnouncementFrom, AnnouncementFromAdmin) diff --git a/ietf/message/migrations/0005_announcementfrom.py b/ietf/message/migrations/0005_announcementfrom.py new file mode 100644 index 000000000..0480384ba --- /dev/null +++ b/ietf/message/migrations/0005_announcementfrom.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-07-28 11:36 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0011_auto_20170301_0332'), + ('name', '0024_merge_20170606_1320'), + ('message', '0004_change_msgid_default'), + ] + + operations = [ + migrations.CreateModel( + name='AnnouncementFrom', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('address', models.EmailField(max_length=254)), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group.Group')), + ('name', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='name.RoleName')), + ], + ), + ] diff --git a/ietf/message/migrations/0006_add_announcement_from.py b/ietf/message/migrations/0006_add_announcement_from.py new file mode 100644 index 000000000..0b63f7ae8 --- /dev/null +++ b/ietf/message/migrations/0006_add_announcement_from.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-07-05 16:10 +from __future__ import unicode_literals + +from django.db import migrations + + +def forwards(apps, schema_editor): + AnnouncementFrom = apps.get_model('message', 'AnnouncementFrom') + Group = apps.get_model('group', 'Group') + RoleName = apps.get_model('name', 'RoleName') + + chair = RoleName.objects.get(slug='chair') + admdir = RoleName.objects.get(slug='admdir') + execdir = RoleName.objects.get(slug='execdir') + ceo = RoleName.objects.get(slug='ceo') + secr = RoleName.objects.get(slug='secr') + + # IETF Chair + ietf = Group.objects.get(acronym='ietf') + AnnouncementFrom.objects.create(name=chair,group=ietf,address='IETF Chair ') + AnnouncementFrom.objects.create(name=chair,group=ietf,address='The IESG ') + + # IAB Chair + iab = Group.objects.get(acronym='iab') + AnnouncementFrom.objects.create(name=chair,group=iab,address='IAB Chair ') + + # IAB Execdir + AnnouncementFrom.objects.create(name=execdir,group=iab,address='IAB Executive Administrative Manager ') + AnnouncementFrom.objects.create(name=execdir,group=iab,address='IAB Chair ') + + # IAD + AnnouncementFrom.objects.create(name=admdir,group=ietf,address='IETF Administrative Director ') + AnnouncementFrom.objects.create(name=admdir,group=ietf,address='The IETF Trust ') + AnnouncementFrom.objects.create(name=admdir,group=ietf,address='ISOC CEO ') + AnnouncementFrom.objects.create(name=admdir,group=ietf,address='IAOC Chair ') + + # RSOC Chair + rsoc = Group.objects.get(acronym='rsoc') + AnnouncementFrom.objects.create(name=chair,group=rsoc,address='RSOC Chair ') + + # IAOC Chair + iaoc = Group.objects.get(acronym='iaoc') + AnnouncementFrom.objects.create(name=chair,group=iaoc,address='IAOC Chair ') + + # RSE Chair + rse = Group.objects.get(acronym='rse') + AnnouncementFrom.objects.create(name=chair,group=rse,address='RFC Series Editor ') + + # Mentor Chair + mentor = Group.objects.get(acronym='mentor') + AnnouncementFrom.objects.create(name=chair,group=mentor,address='IETF Mentoring Program ') + + # ISOC CEO + isoc = Group.objects.get(acronym='isoc') + AnnouncementFrom.objects.create(name=ceo,group=isoc,address='ISOC CEO ') + + # ISOC BOARD OF TRUSTEES + isocbot = Group.objects.get(acronym='isocbot') + AnnouncementFrom.objects.create(name=chair,group=isocbot,address='ISOC Board of Trustees ') + + # IETF TRUST + ietftrust = Group.objects.get(acronym='ietf-trust') + AnnouncementFrom.objects.create(name=chair,group=ietftrust,address='The IETF Trust ') + + # Misc + secretariat = Group.objects.get(acronym='secretariat') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Secretariat ') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IESG Secretary ') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='Internet-Drafts Administrator ') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Agenda ') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Registrar ') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Executive Director ') + +def backwards(apps, schema_editor): + AnnouncementFrom = apps.get_model('announcement', "AnnouncementFrom") + AnnouncementFrom.objects.all().delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('message', '0005_announcementfrom'), + ] + + operations = [ + migrations.RunPython(forwards, backwards), + ] diff --git a/ietf/message/models.py b/ietf/message/models.py index 2670eb139..51b04cf83 100644 --- a/ietf/message/models.py +++ b/ietf/message/models.py @@ -8,6 +8,7 @@ import debug # pyflakes:ignore from ietf.person.models import Person from ietf.group.models import Group from ietf.doc.models import Document +from ietf.name.models import RoleName class Message(models.Model): time = models.DateTimeField(default=datetime.datetime.now) @@ -61,3 +62,16 @@ class SendQueue(models.Model): def __unicode__(self): return u"'%s' %s -> %s (sent at %s)" % (self.message.subject, self.message.frm, self.message.to, self.sent_at or "") + + +class AnnouncementFrom(models.Model): + name = models.ForeignKey(RoleName) + group = models.ForeignKey(Group) + address = models.EmailField() + + def __unicode__(self): + return self.address + + class Meta: + verbose_name_plural='Announcement From addresses' + diff --git a/ietf/message/resources.py b/ietf/message/resources.py index a128597a1..e156f044e 100644 --- a/ietf/message/resources.py +++ b/ietf/message/resources.py @@ -7,7 +7,7 @@ from tastypie.cache import SimpleCache from ietf import api -from ietf.message.models import Message, SendQueue, MessageAttachment +from ietf.message.models import Message, SendQueue, MessageAttachment, AnnouncementFrom from ietf.person.resources import PersonResource from ietf.group.resources import GroupResource from ietf.doc.resources import DocumentResource @@ -76,3 +76,22 @@ class MessageAttachmentResource(ModelResource): } api.message.register(MessageAttachmentResource()) + + +from ietf.group.resources import GroupResource +from ietf.name.resources import RoleNameResource +class AnnouncementFromResource(ModelResource): + name = ToOneField(RoleNameResource, 'name') + group = ToOneField(GroupResource, 'group') + class Meta: + queryset = AnnouncementFrom.objects.all() + serializer = api.Serializer() + cache = SimpleCache() + #resource_name = 'announcementfrom' + filtering = { + "id": ALL, + "address": ALL, + "name": ALL_WITH_RELATIONS, + "group": ALL_WITH_RELATIONS, + } +api.message.register(AnnouncementFromResource()) diff --git a/ietf/secr/announcement/forms.py b/ietf/secr/announcement/forms.py index 575c535f5..6aaca732d 100644 --- a/ietf/secr/announcement/forms.py +++ b/ietf/secr/announcement/forms.py @@ -2,40 +2,13 @@ from django import forms from ietf.group.models import Group, Role from ietf.ietfauth.utils import has_role -from ietf.message.models import Message -from ietf.secr.utils.group import current_nomcom +from ietf.message.models import Message, AnnouncementFrom from ietf.utils.fields import MultiEmailField # --------------------------------------------- # Globals # --------------------------------------------- -ANNOUNCE_FROM_GROUPS = ['ietf','rsoc','iab'] -if current_nomcom(): - ANNOUNCE_FROM_GROUPS += [ current_nomcom().acronym ] -ANNOUNCE_TO_GROUPS= ['ietf'] - -# this list isn't currently available as a Role query so it's hardcoded -FROM_LIST = ('IETF Secretariat ', - 'IESG Secretary ', - 'The IESG ', - 'Internet-Drafts Administrator ', - 'IETF Agenda ', - 'IETF Chair ', - 'IAB Chair ', - 'NomCom Chair ', - 'IETF Registrar ', - 'IETF Administrative Director ', - 'IETF Executive Director ', - 'IAOC Chair ', - 'The IETF Trust ', - 'RSOC Chair ', - 'ISOC Board of Trustees ', - 'RFC Series Editor ', - 'IAB Executive Administrative Manager ', - 'IETF Mentoring Program ', - 'ISOC CEO ') - TO_LIST = ('IETF Announcement List ', 'I-D Announcement List ', 'The IESG ', @@ -53,85 +26,48 @@ def get_from_choices(user): all the Announced From choices. Including leadership chairs and other entities. ''' - person = user.person - f = [] + addresses = [] if has_role(user,'Secretariat'): - f = FROM_LIST - elif has_role(user,'IETF Chair'): - f = (FROM_LIST[2],FROM_LIST[5]) - elif has_role(user,'IAB Chair'): - f = (FROM_LIST[6],) - elif has_role(user,'IAD'): - f = (FROM_LIST[9],FROM_LIST[12],FROM_LIST[18],FROM_LIST[11],) - #RSOC Chair, IAOC Chair aren't supported by has_role() - elif Role.objects.filter(person=person, - group__acronym='rsoc', - name="chair"): - f = (FROM_LIST[13],) - elif Role.objects.filter(person=person, - group__acronym='iaoc', - name="chair"): - f = (FROM_LIST[11],) - elif Role.objects.filter(person=person, - group__acronym='rse', - name="chair"): - f = (FROM_LIST[15],) - elif Role.objects.filter(person=person, - group__acronym='iab', - name='execdir'): - f = (FROM_LIST[6],FROM_LIST[16]) - elif Role.objects.filter(person=person, - group__acronym='mentor', - name="chair"): - f = (FROM_LIST[17],) - elif Role.objects.filter(person=person, - group__acronym='isoc', - name="ceo"): - f = (FROM_LIST[18],) - elif Role.objects.filter(person=person, - group__acronym='isocbot', - name="chair"): - f = (FROM_LIST[14],) - elif Role.objects.filter(person=person, - group__acronym='ietf-trust', - name="chair"): - f = (FROM_LIST[12],) + addresses = AnnouncementFrom.objects.values_list('address', flat=True).order_by('address').distinct() + else: + for role in user.person.role_set.all(): + addresses.extend(AnnouncementFrom.objects.filter(name=role.name, group=role.group).values_list('address', flat=True).order_by('address')) - # NomCom + nomcom_choices = get_nomcom_choices(user) + if nomcom_choices: + addresses = list(addresses) + nomcom_choices + + return zip(addresses, addresses) + + +def get_nomcom_choices(user): + ''' + Returns the list of nomcom email addresses for given user + ''' nomcoms = Role.objects.filter(name="chair", group__acronym__startswith="nomcom", group__state="active", group__type="nomcom", - person=person) - if nomcoms: - year = nomcoms[0].group.acronym[-4:] - alias = 'NomCom Chair %s ' % (year,year) - f = (alias,) + person=user.person) + addresses = [] + for nomcom in nomcoms: + year = nomcom.group.acronym[-4:] + addresses.append('NomCom Chair %s ' % (year,year)) - return zip(f,f) + return addresses + def get_to_choices(): - #groups = Group.objects.filter(acronym__in=ANNOUNCE_TO_GROUPS) - #roles = Role.objects.filter(group__in=(groups),name="Announce") - #choices = [ (r.email, r.person.name) for r in roles ] - #choices.append(('Other...','Other...'),) return zip(TO_LIST,TO_LIST) -# --------------------------------------------- -# Select Choices -# --------------------------------------------- -TO_CHOICES = get_to_choices() -#FROM_CHOICES = get_from_choices() # --------------------------------------------- # Forms # --------------------------------------------- class AnnounceForm(forms.ModelForm): - #nomcom = forms.BooleanField(required=False) nomcom = forms.ModelChoiceField(queryset=Group.objects.filter(acronym__startswith='nomcom',type='nomcom',state='active'),required=False) to_custom = MultiEmailField(required=False,label='') - #cc = MultiEmailField(required=False) class Meta: model = Message @@ -145,7 +81,7 @@ class AnnounceForm(forms.ModelForm): user = kwargs.pop('user') person = user.person super(AnnounceForm, self).__init__(*args, **kwargs) - self.fields['to'].widget = forms.Select(choices=TO_CHOICES) + self.fields['to'].widget = forms.Select(choices=get_to_choices()) self.fields['to'].help_text = 'Select name OR select Other... and enter email below' self.fields['cc'].help_text = 'Use comma separated lists for emails (Cc, Bcc, Reply To)' self.fields['frm'].widget = forms.Select(choices=get_from_choices(user)) diff --git a/ietf/secr/announcement/tests.py b/ietf/secr/announcement/tests.py index ac7ea0f3e..464b0e07a 100644 --- a/ietf/secr/announcement/tests.py +++ b/ietf/secr/announcement/tests.py @@ -1,4 +1,3 @@ -from django.db import connection from django.urls import reverse from pyquery import PyQuery @@ -6,8 +5,10 @@ from pyquery import PyQuery from ietf.utils.test_utils import TestCase from ietf.group.models import Group from ietf.message.models import Message +from ietf.name.models import RoleName from ietf.nomcom.test_data import nomcom_test_data from ietf.person.models import Person +from ietf.message.models import AnnouncementFrom from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox, empty_outbox @@ -16,17 +17,49 @@ WG_USER='' AD_USER='' class SecrAnnouncementTestCase(TestCase): - # ------- Test View -------- # + def setUp(self): + make_test_data() + chair = RoleName.objects.get(slug='chair') + secr = RoleName.objects.get(slug='secr') + ietf = Group.objects.get(acronym='ietf') + iab = Group.objects.get(acronym='iab') + secretariat = Group.objects.get(acronym='secretariat') + AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Secretariat ') + AnnouncementFrom.objects.create(name=chair,group=ietf,address='IETF Chair ') + AnnouncementFrom.objects.create(name=chair,group=iab,address='IAB Chair ') + def test_main(self): "Main Test" - make_test_data() url = reverse('ietf.secr.announcement.views.main') self.client.login(username="secretary", password="secretary+password") r = self.client.get(url) self.assertEqual(r.status_code, 200) + + def test_main_announce_from(self): + url = reverse('ietf.secr.announcement.views.main') -class DummyCase(TestCase): - name = connection.settings_dict['NAME'] + # Secretariat + self.client.login(username="secretary", password="secretary+password") + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#id_frm option')),3) + + # IAB Chair + self.client.login(username="iab-chair", password="iab-chair+password") + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#id_frm option')),1) + self.assertTrue('' in q('#id_frm option').val()) + + # IETF Chair + self.client.login(username="ietf-chair", password="ietf-chair+password") + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('#id_frm option')),1) + self.assertTrue('' in q('#id_frm option').val()) class UnauthorizedAnnouncementCase(TestCase): def test_unauthorized(self):