Merged in [13996] from rcross@amsl.com:

Move Announcement app From addresses out of code and into database table.  Fixes #1681.
 - Legacy-Id: 14006
Note: SVN reference [13996] has been migrated to Git commit d49787a0f6060e7db64f5a0e684bf1d980f85290
This commit is contained in:
Henrik Levkowetz 2017-07-29 18:12:45 +00:00
parent 5c66ec25f2
commit 868427a645
7 changed files with 217 additions and 97 deletions

View file

@ -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)

View file

@ -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')),
],
),
]

View file

@ -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 <chair@ietf.org>')
AnnouncementFrom.objects.create(name=chair,group=ietf,address='The IESG <iesg@ietf.org>')
# IAB Chair
iab = Group.objects.get(acronym='iab')
AnnouncementFrom.objects.create(name=chair,group=iab,address='IAB Chair <iab-chair@ietf.org>')
# IAB Execdir
AnnouncementFrom.objects.create(name=execdir,group=iab,address='IAB Executive Administrative Manager <execd@iab.org>')
AnnouncementFrom.objects.create(name=execdir,group=iab,address='IAB Chair <iab-chair@ietf.org>')
# IAD
AnnouncementFrom.objects.create(name=admdir,group=ietf,address='IETF Administrative Director <iad@ietf.org>')
AnnouncementFrom.objects.create(name=admdir,group=ietf,address='The IETF Trust <ietf-trust@ietf.org>')
AnnouncementFrom.objects.create(name=admdir,group=ietf,address='ISOC CEO <ceo@isoc.org>')
AnnouncementFrom.objects.create(name=admdir,group=ietf,address='IAOC Chair <iaoc-chair@ietf.org>')
# RSOC Chair
rsoc = Group.objects.get(acronym='rsoc')
AnnouncementFrom.objects.create(name=chair,group=rsoc,address='RSOC Chair <rsoc-chair@iab.org>')
# IAOC Chair
iaoc = Group.objects.get(acronym='iaoc')
AnnouncementFrom.objects.create(name=chair,group=iaoc,address='IAOC Chair <iaoc-chair@ietf.org>')
# RSE Chair
rse = Group.objects.get(acronym='rse')
AnnouncementFrom.objects.create(name=chair,group=rse,address='RFC Series Editor <rse@rfc-editor.org>')
# Mentor Chair
mentor = Group.objects.get(acronym='mentor')
AnnouncementFrom.objects.create(name=chair,group=mentor,address='IETF Mentoring Program <mentoring@ietf.org>')
# ISOC CEO
isoc = Group.objects.get(acronym='isoc')
AnnouncementFrom.objects.create(name=ceo,group=isoc,address='ISOC CEO <ceo@isoc.org>')
# ISOC BOARD OF TRUSTEES
isocbot = Group.objects.get(acronym='isocbot')
AnnouncementFrom.objects.create(name=chair,group=isocbot,address='ISOC Board of Trustees <bob.hinden@gmail.com>')
# IETF TRUST
ietftrust = Group.objects.get(acronym='ietf-trust')
AnnouncementFrom.objects.create(name=chair,group=ietftrust,address='The IETF Trust <ietf-trust@ietf.org>')
# Misc
secretariat = Group.objects.get(acronym='secretariat')
AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Secretariat <ietf-secretariat@ietf.org>')
AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IESG Secretary <iesg-secretary@ietf.org>')
AnnouncementFrom.objects.create(name=secr,group=secretariat,address='Internet-Drafts Administrator <internet-drafts@ietf.org>')
AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Agenda <agenda@ietf.org>')
AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Registrar <ietf-registrar@ietf.org>')
AnnouncementFrom.objects.create(name=secr,group=secretariat,address='IETF Executive Director <exec-director@ietf.org>')
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),
]

View file

@ -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 "<not yet>")
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'

View file

@ -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())

View file

@ -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 <ietf-secretariat@ietf.org>',
'IESG Secretary <iesg-secretary@ietf.org>',
'The IESG <iesg@ietf.org>',
'Internet-Drafts Administrator <internet-drafts@ietf.org>',
'IETF Agenda <agenda@ietf.org>',
'IETF Chair <chair@ietf.org>',
'IAB Chair <iab-chair@ietf.org> ',
'NomCom Chair <nomcom-chair@ietf.org>',
'IETF Registrar <ietf-registrar@ietf.org>',
'IETF Administrative Director <iad@ietf.org>',
'IETF Executive Director <exec-director@ietf.org>',
'IAOC Chair <iaoc-chair@ietf.org>',
'The IETF Trust <ietf-trust@ietf.org>',
'RSOC Chair <rsoc-chair@iab.org>',
'ISOC Board of Trustees <bob.hinden@gmail.com>',
'RFC Series Editor <rse@rfc-editor.org>',
'IAB Executive Administrative Manager <execd@iab.org>',
'IETF Mentoring Program <mentoring@ietf.org>',
'ISOC CEO <ceo@isoc.org>')
TO_LIST = ('IETF Announcement List <ietf-announce@ietf.org>',
'I-D Announcement List <i-d-announce@ietf.org>',
'The IESG <iesg@ietf.org>',
@ -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 <nomcom-chair-%s@ietf.org>' % (year,year)
f = (alias,)
person=user.person)
addresses = []
for nomcom in nomcoms:
year = nomcom.group.acronym[-4:]
addresses.append('NomCom Chair %s <nomcom-chair-%s@ietf.org>' % (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))

View file

@ -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 <ietf-secretariat@ietf.org>')
AnnouncementFrom.objects.create(name=chair,group=ietf,address='IETF Chair <chair@ietf.org>')
AnnouncementFrom.objects.create(name=chair,group=iab,address='IAB Chair <iab-chair@ietf.org>')
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('<iab-chair@ietf.org>' 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('<chair@ietf.org>' in q('#id_frm option').val())
class UnauthorizedAnnouncementCase(TestCase):
def test_unauthorized(self):