Skip to content

Commit 3cd72bc

Browse files
committed
Great leaps towards completing the mailing list request tool.
- Legacy-Id: 259
1 parent a99d598 commit 3cd72bc

9 files changed

Lines changed: 225 additions & 59 deletions

File tree

ietf/mailinglists/forms.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,41 @@ class ListReqStep1(forms.Form):
4343
('movenon', 'Move existing non-WG email list to selected domain above'),
4444
('closenon', 'Close existing non-WG email list at selected domain above'),
4545
), widget=forms.RadioSelect)
46-
group = forms.ChoiceField(required=False)
46+
#group = forms.ChoiceField(required=False)
47+
group = forms.ModelChoiceField(queryset=IETFWG.objects.all().select_related().order_by('acronym.acronym'), required=False, empty_label="-- Select Working Group")
4748
domain_name = forms.ChoiceField(choices=DOMAIN_CHOICES, required=False)
48-
list_to_close = forms.ChoiceField(required=False)
49+
list_to_close = forms.ModelChoiceField(queryset=ImportedMailingList.objects.all(), required=False, empty_label="-- Select List To Close")
4950
def mail_type_fields(self):
5051
field = self['mail_type']
5152
return field.as_widget(field.field.widget)
5253
def __init__(self, *args, **kwargs):
53-
dname = kwargs.get('dname', 'ietf.org')
54+
dname = 'ietf.org'
55+
if args:
56+
dn = 'domain_name'
57+
if kwargs.has_key('prefix'):
58+
dn = kwargs['prefix'] + '-' + dn
59+
dname = args[0][dn]
60+
dname = kwargs.get('dname', dname)
5461
super(ListReqStep1, self).__init__(*args, **kwargs)
55-
self.fields['group'].choices = [('', '-- Select Working Group')] + IETFWG.choices()
56-
self.fields['list_to_close'].choices = [('', '-- Select List To Close')] + ImportedMailingList.choices(dname)
62+
#self.fields['group'].choices = [('', '-- Select Working Group')] + IETFWG.choices()
63+
#self.fields['list_to_close'].choices = [('', '-- Select List To Close')] + ImportedMailingList.choices(dname)
64+
#XXX This doesn't work yet. Maybe switch back to choices.
65+
self.fields['list_to_close'].queryset = ImportedMailingList.choices(dname)
66+
print "dname %s list_to_close values: %s" % (dname, self.fields['list_to_close'].queryset)
5767
self.fields['domain_name'].initial = dname
5868
def clean_group(self):
5969
group = self.clean_data['group']
6070
action = self.clean_data.get('mail_type', '')
6171
if action.endswith('wg'):
6272
if not self.clean_data.get('group'):
6373
raise forms.ValidationError, 'Please pick a working group'
64-
group_name = Acronym.objects.get(pk=group).acronym
65-
#group_list_exists = ImportedMailingList.objects.filter(acronym=group_name).count()
6674
group_list_exists = ImportedMailingList.objects.filter(group_acronym=group).count()
6775
if action.startswith('close'):
6876
if group_list_exists == 0:
69-
raise forms.ValidationError, 'The %s mailing list does not exist.' % group_name
77+
raise forms.ValidationError, 'The %s mailing list does not exist.' % group
7078
else:
7179
if group_list_exists:
72-
raise forms.ValidationError, 'The %s mailing list already exists.' % group_name
80+
raise forms.ValidationError, 'The %s mailing list already exists.' % group
7381
return self.clean_data['group']
7482
def clean_list_to_close(self):
7583
if self.clean_data.get('mail_type', '') == 'closenon':
@@ -126,6 +134,21 @@ def __init__(self, approvers, *args, **kwargs):
126134
super(PickApprover, self).__init__(*args, **kwargs)
127135
self.fields['approver'].choices = [('', '-- Pick an approver from the list below')] + [(person.person_or_org_tag, str(person)) for person in PersonOrOrgInfo.objects.filter(pk__in=approvers)]
128136

137+
class ListApprover(forms.Form):
138+
"""
139+
When instantiating, supply a list of AreaDirector, WGChair and/or Role
140+
objects (or other objects with a person_id and appropriate str value).
141+
"""
142+
approver = forms.ChoiceField(choices=(
143+
('', '-- Pick an approver from the list below'),
144+
))
145+
def __init__(self, approvers, requestor=None, *args, **kwargs):
146+
super(ListApprover, self).__init__(*args, **kwargs)
147+
self.fields['approver'].choices = [('', '-- Pick an approver from the list below')] + [(item.person_id, str(item)) for item in approvers]
148+
if requestor:
149+
self.fields['approver'].initial = requestor.person_id
150+
self.fields['approver'].widget = forms.widgets.HiddenInput()
151+
129152
class DeletionPickApprover(PickApprover):
130153
ds_name = forms.CharField(label = 'Enter your name', widget = forms.TextInput(attrs = {'size': 45}))
131154
ds_email = forms.EmailField(label = 'Enter your email', widget = forms.TextInput(attrs = {'size': 45}))
@@ -143,9 +166,11 @@ def clean_authorized(self):
143166
raise forms.ValidationError, 'You must assert that you are authorized to perform this action.'
144167
return self.clean_data['authorized']
145168

146-
# subclass pickapprover here too?
147169
class ListReqClose(forms.Form):
148-
pass
170+
requestor = forms.CharField(label = "Requestor's full name", widget = forms.TextInput(attrs = {'size': 55}))
171+
requestor_email = forms.EmailField(label = "Requestor's email address", widget = forms.TextInput(attrs = {'size': 55}))
172+
#mlist_name: with a widget that just displays the value as text
173+
reason_to_delete = forms.CharField(label = 'Reason for closing list', widget = forms.Textarea(attrs = {'rows': 4, 'cols': 60}))
149174

150175
class AdminRequestor(forms.MultiWidget):
151176
def decompress(self, value):
@@ -169,7 +194,8 @@ def value_from_datadict(self, data, name):
169194
# this is used.
170195
key = name.replace('admins', 'requestor_email')
171196
try:
172-
return data[key] + "\n" + rest
197+
ret = data[key] + "\r\n" + rest
198+
return ret.strip()
173199
except KeyError:
174200
return rest
175201
else:

ietf/mailinglists/models.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ class ImportedMailingList(models.Model):
1010
name = models.CharField(blank=True, maxlength=255, db_column='list_name')
1111
domain = models.CharField(blank=True, maxlength=25, db_column='list_domain')
1212
def __str__(self):
13-
return self.name or self.group_acronym
13+
return self.acronym or self.group_acronym.acronym
1414
def choices(dname):
1515
objects = ImportedMailingList.objects.all().filter(domain__icontains=dname).exclude(acronym__iendswith='announce')
1616
if dname == "ietf.org":
1717
objects = objects.exclude(acronym__istartswith='ietf').exclude(acronym__icontains='iesg')
18-
return [(list.acronym, list.acronym) for list in objects]
18+
return objects
19+
#return [(list.acronym, list.acronym) for list in objects]
1920
choices = staticmethod(choices)
2021
class Meta:
2122
db_table = 'imported_mailing_list'
@@ -24,7 +25,9 @@ class Admin:
2425

2526
class Domain(models.Model):
2627
domain = models.CharField("Mailing List Domain Name", maxlength=100)
27-
approvers = models.ManyToManyField(Role)
28+
approvers = models.ManyToManyField(Role, filter_interface=models.HORIZONTAL)
29+
def __str__(self):
30+
return self.domain
2831
class Admin:
2932
pass
3033

@@ -34,6 +37,14 @@ class MailingList(models.Model):
3437
(2, 'Approval'),
3538
(3, 'Confirm+Approval'),
3639
)
40+
MAILTYPE_MAP = {
41+
'newwg': 1,
42+
'movewg': 2,
43+
'closewg': 5,
44+
'newnon': 4,
45+
'movenon': 3,
46+
'closenon': 6,
47+
}
3748
MAILTYPE_CHOICES = (
3849
(1, 'Create new WG email list at ietf.org'),
3950
(2, 'Move existing WG email list to ietf.org'),
@@ -87,7 +98,7 @@ def save(self, *args, **kwargs):
8798
if self.mailing_list_id is None:
8899
generate = True
89100
while generate:
90-
self.mailing_list_id = ''.join([random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for i in range(35)])
101+
self.mailing_list_id = ''.join([random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for i in range(25)])
91102
try:
92103
MailingList.objects.get(pk=self.mailing_list_id)
93104
except MailingList.DoesNotExist:
@@ -110,7 +121,7 @@ class NonWgMailingList(models.Model):
110121
list_url = models.CharField("List URL", maxlength=255)
111122
admin = models.TextField("Administrator(s)' Email Address(es)", blank=True)
112123
purpose = models.TextField(blank=True)
113-
area = models.ForeignKey(Area, db_column='area_acronym_id', null=True)
124+
area = models.ForeignKey(Area, db_column='area_acronym_id')
114125
subscribe_url = models.CharField("Subscribe URL", blank=True, maxlength=255)
115126
subscribe_other = models.TextField("Subscribe Other", blank=True)
116127
# Can be 0, 1, -1, or what looks like a person_or_org_tag, positive or neg.

ietf/mailinglists/views.py

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from forms import NonWgStep1, ListReqStep1, PickApprover, DeletionPickApprover, UrlMultiWidget, Preview, ListReqAuthorized, ListReqClose, MultiEmailField, AdminRequestor, ApprovalComment
2-
from models import NonWgMailingList, MailingList
3-
from ietf.idtracker.models import Area, PersonOrOrgInfo
1+
from forms import NonWgStep1, ListReqStep1, PickApprover, DeletionPickApprover, UrlMultiWidget, Preview, ListReqAuthorized, ListReqClose, MultiEmailField, AdminRequestor, ApprovalComment, ListApprover
2+
from models import NonWgMailingList, MailingList, Domain
3+
from ietf.idtracker.models import Area, PersonOrOrgInfo, AreaDirector, WGChair
44
from django import newforms as forms
55
from django.shortcuts import get_object_or_404, render_to_response
66
from django.template import RequestContext
@@ -145,7 +145,6 @@ def non_wg_wizard(request):
145145
'add_comment': None,
146146
'mail_type': None,
147147
'mail_cat': None,
148-
'domain_name': None,
149148
'admins': MultiEmailField(label='List Administrator(s)', widget=AdminRequestor(attrs={'cols': 41, 'rows': 4})),
150149
'initial': MultiEmailField(label='Initial list member(s)', widget=forms.Textarea(attrs={'cols': 41, 'rows': 4}), required=False),
151150
}
@@ -160,6 +159,7 @@ def non_wg_wizard(request):
160159
'post_who': forms.Select(choices=(('1', 'List members only'), ('0', 'Open'))),
161160
'post_admin': forms.Select(choices=(('0', 'No'), ('1', 'Yes'))),
162161
'archive_private': forms.Select(choices=(('0', 'No'), ('1', 'Yes'))),
162+
'domain_name': forms.HiddenInput(),
163163
}
164164

165165
list_attrs = {
@@ -177,7 +177,18 @@ def non_wg_wizard(request):
177177

178178
list_callback = form_decorator(fields=list_fields, widgets=list_widgets, attrs=list_attrs)
179179

180+
def gen_list_approval(approvers, requestor, parent):
181+
class ListApproval(parent):
182+
_approvers = approvers
183+
_requestor = requestor
184+
def __init__(self, *args, **kwargs):
185+
super(ListApproval, self).__init__(self._approvers, self._requestor, *args, **kwargs)
186+
return ListApproval
187+
180188
class ListReqWizard(wizard.Wizard):
189+
clean_forms = []
190+
main_step = 1
191+
requestor_is_approver = False
181192
def get_template(self):
182193
templates = []
183194
#if self.step > 0:
@@ -190,22 +201,73 @@ def get_template(self):
190201
templates.append("mailinglists/list_wizard.html")
191202
print templates
192203
return templates
204+
def render_template(self, *args, **kwargs):
205+
#self.extra_context['clean_forms'] = self.clean_forms
206+
if self.step > self.main_step:
207+
self.extra_context['main_form'] = self.clean_forms[self.main_step]
208+
self.extra_context['requestor_is_approver'] = self.requestor_is_approver
209+
return super(ListReqWizard, self).render_template(*args, **kwargs)
193210
# want to implement parse_params to get domain for list
194211
def process_step(self, request, form, step):
195212
form.full_clean()
196213
if step == 0:
197214
self.clean_forms = [ form ]
198215
else:
199216
self.clean_forms.append(form)
217+
form0 = self.clean_forms[0]
218+
needs_auth = form0.clean_data['mail_type'].endswith('non') and form0.clean_data['domain_name'] != 'ietf.org'
200219
if step == 0:
201-
if form.clean_data['mail_type'].endswith('non') and form.clean_data['domain_name'] != 'ietf.org':
220+
self.main_step = 1
221+
if needs_auth:
202222
self.form_list.append(ListReqAuthorized)
223+
self.main_step = 2
203224
if form.clean_data['mail_type'].startswith('close'):
204225
self.form_list.append(ListReqClose)
226+
if form.clean_data['mail_type'] == 'closewg':
227+
self.initial[self.main_step] = {'mlist_name': form.clean_data['group']}
228+
else:
229+
self.initial[self.main_step] = {'mlist_name': form.clean_data['list_to_close']}
205230
else:
206231
self.form_list.append(forms.form_for_model(MailingList, formfield_callback=list_callback))
207-
#XXX not quite
232+
if form.clean_data['mail_type'].endswith('wg'):
233+
self.initial[self.main_step] = {'mlist_name': form.clean_data['group']}
234+
else:
235+
self.initial[self.main_step] = {}
236+
if form.clean_data['mail_type'].endswith('wg'):
237+
self.initial[self.main_step].update({'domain_name': 'ietf.org'})
238+
else:
239+
self.initial[self.main_step].update({'domain_name': form.clean_data['domain_name']})
240+
if step == self.main_step:
241+
approvers = mlist_approvers(form0.clean_data['mail_type'], form0.clean_data['domain_name'], form0.clean_data['group'])
242+
main_form = self.clean_forms[self.main_step]
243+
requestor_email = main_form.clean_data['requestor_email']
244+
requestor_person = None
245+
for a in approvers:
246+
if requestor_email == a.person.email()[1]:
247+
requestor_person = a
248+
self.requestor_is_approver = True
249+
self.form_list.append(gen_list_approval(approvers, requestor_person, ListApprover))
208250
super(ListReqWizard, self).process_step(request, form, step)
251+
def done(self, request, form_list):
252+
list = MailingList(**self.clean_forms[self.main_step].clean_data)
253+
list.mailing_list_id = None # make sure that we create a new row
254+
list.auth_person_id = int(self.clean_forms[self.main_step + 1].clean_data['approver'])
255+
list.mail_type = MailingList.MAILTYPE_MAP[self.clean_forms[0].clean_data['mail_type']]
256+
list.approved = 0
257+
list.save()
258+
approver_email = list.auth_person.email()
259+
send_mail_subj(request, [ approver_email ], None, 'mailinglists/list_wizard_subject.txt', 'mailinglists/list_wizard_done_email.txt', {'list': list, 'forms': self.clean_forms, 'requestor_is_approver': self.requestor_is_approver})
260+
return render_to_response('mailinglists/list_wizard_done.html', {'list': list, 'forms': self.clean_forms, 'requestor_is_approver': self.requestor_is_approver}, context_instance=RequestContext(request) )
261+
262+
def mlist_approvers(mail_type, domain_name, group):
263+
approvers = []
264+
if domain_name == 'ietf.org':
265+
approvers += AreaDirector.objects.filter(area__status=Area.ACTIVE)
266+
if mail_type.endswith('wg'):
267+
approvers += WGChair.objects.filter(group_acronym=group)
268+
domain = Domain.objects.get(domain=domain_name)
269+
approvers += domain.approvers.all()
270+
return approvers
209271

210272
def list_req_wizard(request):
211273
wiz = ListReqWizard([ ListReqStep1 ])
Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,5 @@
11
{% extends "mailinglists/list_wizard_base.html" %}
22

3-
{% block mlcontent %}
4-
<table bgcolor="#88AED2" cellspacing="1" border="0" width="594">
5-
<tr><td>
6-
<table bgcolor="#f3f8fd" cellpadding="3" cellspacing="0" border="0" width="100%">
7-
8-
<tr>
9-
<td colspan="2"><img src="/images/ietf_topleft.gif" border="0"><img src="/images/blue_title.gif" border="0"></td>
10-
</tr>
11-
<tr><td colspan="2">
12-
<img src="/images/mail_title.gif" border="0">
13-
</td></tr>
14-
<tr><td colspan="2" valign="top">
15-
<img src="/images/t_un.gif" border="0">
16-
</td></tr>
17-
</table>
18-
<form action="." method="POST">
19-
20-
<h2>Step {{ step|add:"1" }}:</h2>
21-
<table bgcolor="f3f8fd" cellpadding="3" cellspacing="0" border="0" width="100%">
3+
{% block mlform %}
224
{{ form }}
23-
</table>
24-
25-
26-
<input type="hidden" name="{{ step_field }}" value="{{ step }}" />
27-
28-
{{ previous_fields }}
29-
30-
<input type="submit" value="Next">
31-
</form>
32-
33-
<!--
34-
clean_forms: {{ clean_forms|escape }}
35-
-->
36-
</td></tr>
37-
</table>
38-
395
{% endblock %}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{% extends "mailinglists/list_wizard_base.html" %}
2+
3+
{% block mlform %}
4+
<tr>
5+
<th colspan=2>Preview</th>
6+
</tr>
7+
<tr>
8+
<th>{{ main_form.requestor.label }}</th>
9+
<td>{{ main_form.requestor.field.data|escape }}</td>
10+
</tr>
11+
<tr>
12+
<th>{{ main_form.requestor_email.label }}</th>
13+
<td>{{ main_form.requestor_email.field.data|escape }}</td>
14+
</tr>
15+
<tr>
16+
<th>{{ main_form.mlist_name.label }}</th>
17+
<td>{{ main_form.mlist_name.field.data|escape }}@{{ main_form.domain_name }}</td>
18+
</tr>
19+
<tr>
20+
<th>{{ main_form.requestor_email.label }}</th>
21+
<td>{{ main_form.requestor_email.field.data|escape }}</td>
22+
</tr>
23+
<!-- make sure we have all the fields -->
24+
{% for field in main_form %}
25+
<tr>
26+
<th>{{ field.label }}</th>
27+
<td>{{ field.data|escape|linebreaksbr }}</td>
28+
</tr>
29+
{% endfor %}
30+
<!-- must figure out whether this is an approver or a hidden field -->
31+
{{ form }}
32+
{% endblock %}

ietf/templates/mailinglists/list_wizard_base.html

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,43 @@
1717
<img src="/images/nwg/t_un1.gif" border="0">
1818
<!-- form step {{ step }} -->
1919
<br>
20-
{% block mlcontent %}
20+
<table bgcolor="#88AED2" cellspacing="1" border="0" width="594">
21+
<tr><td>
22+
<table bgcolor="#f3f8fd" cellpadding="3" cellspacing="0" border="0" width="100%">
23+
24+
<tr>
25+
<td colspan="2"><img src="/images/ietf_topleft.gif" border="0"><img src="/images/blue_title.gif" border="0"></td>
26+
</tr>
27+
<tr><td colspan="2">
28+
<img src="/images/mail_title.gif" border="0">
29+
</td></tr>
30+
<tr><td colspan="2" valign="top">
31+
<img src="/images/t_un.gif" border="0">
32+
</td></tr>
33+
</table>
34+
<form action="." method="POST">
35+
36+
<h2>Step {{ step|add:"1" }}:</h2>
37+
<table bgcolor="f3f8fd" cellpadding="3" cellspacing="0" border="0" width="100%">
38+
{% block mlform %}
2139
form goes here
2240
{% endblock %}
41+
</table>
42+
43+
44+
<input type="hidden" name="{{ step_field }}" value="{{ step }}" />
45+
46+
{{ previous_fields }}
47+
48+
<input type="submit" value="Next">
49+
</form>
50+
51+
<!--
52+
clean_forms: {{ clean_forms|escape }}
53+
-->
54+
</td></tr>
55+
</table>
56+
2357
</blockquote>
2458

2559
{% endblock %}

0 commit comments

Comments
 (0)