Skip to content

Commit 2739083

Browse files
committed
Initial version of forms and views for mailing list submissions.
The nonwg_lists/submit wizard is nearly done; it needs templates and to figure out why submit from step 2 goes back to step 1 instead of calling done. The req_list wizard is just started. It needs to get the incoming value of domain_name as an alternat version of dname, and be able to get dname from GET args to handle the javascript redirect. It also needs all the workflow ("I'm allowed to create this list", who-is-approver, etc.) and additional forms. - Legacy-Id: 122
1 parent 9520961 commit 2739083

5 files changed

Lines changed: 225 additions & 14 deletions

File tree

ietf/mailinglists/forms.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from django import newforms as forms
2+
from models import NonWgMailingList, ImportedMailingList
3+
from ietf.idtracker.models import PersonOrOrgInfo, GroupIETF
4+
5+
class NonWgStep1(forms.Form):
6+
add_edit = forms.ChoiceField(choices=(
7+
('add', 'Add a new entry'),
8+
('edit', 'Modify an existing entry'),
9+
('delete', 'Delete an existing entry'),
10+
), widget=forms.RadioSelect)
11+
list_id = forms.ChoiceField(required=False)
12+
list_id_delete = forms.ChoiceField(required=False)
13+
def add_edit_fields(self):
14+
field = self['add_edit']
15+
return field.as_widget(field.field.widget)
16+
def __init__(self, *args, **kwargs):
17+
super(NonWgStep1, self).__init__(*args, **kwargs)
18+
choices=[('', '-- Select an item from the list below')] + NonWgMailingList.choices()
19+
self.fields['list_id'].choices = choices
20+
self.fields['list_id_delete'].choices = choices
21+
def clean_list_id(self):
22+
if self.clean_data.get('add_edit', None) == 'edit':
23+
if not self.clean_data.get('list_id'):
24+
raise forms.ValidationError, 'Please pick a mailing list to modify'
25+
return self.clean_data['list_id']
26+
def clean_list_id_delete(self):
27+
if self.clean_data.get('add_edit', None) == 'delete':
28+
if not self.clean_data.get('list_id_delete'):
29+
raise forms.ValidationError, 'Please pick a mailing list to delete'
30+
return self.clean_data['list_id_delete']
31+
32+
class ListReqStep1(forms.Form):
33+
DOMAIN_CHOICES = (
34+
('ietf.org', 'ietf.org'),
35+
('iab.org', 'iab.org'),
36+
('irtf.org', 'irtf.org'),
37+
)
38+
mail_type = forms.ChoiceField(choices=(
39+
('newwg', 'Create new WG email list at ietf.org'),
40+
('movewg', 'Move existing WG email list to ietf.org'),
41+
('closewg', 'Close existing WG email list at ietf.org'),
42+
('newnon', 'Create new non-WG email list at selected domain above'),
43+
('movenon', 'Move existing non-WG email list to selected domain above'),
44+
('closenon', 'Close existing non-WG email list at selected domain above'),
45+
), widget=forms.RadioSelect)
46+
group = forms.ChoiceField(required=False)
47+
domain_name = forms.ChoiceField(choices=DOMAIN_CHOICES, required=False)
48+
list_to_close = forms.ChoiceField(required=False)
49+
def mail_type_fields(self):
50+
field = self['mail_type']
51+
return field.as_widget(field.field.widget)
52+
def __init__(self, *args, **kwargs):
53+
dname = kwargs.get('dname', 'ietf.org')
54+
super(ListReqStep1, self).__init__(*args, **kwargs)
55+
self.fields['group'].choices = [('', '-- Select Working Group')] + GroupIETF.choices()
56+
self.fields['list_to_close'].choices = [('', '-- Select List To Close')] + ImportedMailingList.choices(dname)
57+
self.fields['domain_name'].initial = dname
58+
def clean_group(self):
59+
if self.clean_data.get('mail_type', '').endswith('wg'):
60+
if not self.clean_data.get('group'):
61+
raise forms.ValidationError, 'Please pick a working group'
62+
return self.clean_data['group']
63+
def clean_list_to_close(self):
64+
if self.clean_data.get('mail_type', '') == 'closenon':
65+
if not self.clean_data.get('list_to_close'):
66+
raise forms.ValidationError, 'Please pick a list to close'
67+
return self.clean_data['list_to_close']
68+
69+
# multiwidget for separate scheme and rest for urls
70+
# todo: can the clean return the "smart" value?
71+
class UrlMultiWidget(forms.MultiWidget):
72+
def decompress(self, value):
73+
if value:
74+
if '//' in value:
75+
(scheme, rest) = value.split('//', 1)
76+
scheme += '//'
77+
else:
78+
scheme = 'http://'
79+
rest = value
80+
return [scheme, rest]
81+
else:
82+
return ['', '']
83+
84+
def __init__(self, choices=(('http://', 'http://'), ('https://', 'https://')), attrs=None):
85+
widgets = (forms.RadioSelect(choices=choices, attrs=attrs), forms.TextInput(attrs=attrs))
86+
super(UrlMultiWidget, self).__init__(widgets, attrs)
87+
88+
def format_output(self, rendered_widgets):
89+
return u'%s\n%s\n<br/>' % ( u'<br/>\n'.join(["%s" % w for w in rendered_widgets[0]]), rendered_widgets[1] )
90+
91+
92+
class PickApprover(forms.Form):
93+
"""
94+
When instantiating, supply a list of person tags in approvers=
95+
"""
96+
approver = forms.ChoiceField(choices=(
97+
('', '-- Pick an approver from the list below'),
98+
))
99+
def __init__(self, approvers, *args, **kwargs):
100+
super(PickApprover, self).__init__(*args, **kwargs)
101+
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)]
102+
103+
class DeletionPickApprover(PickApprover):
104+
ds_name = forms.CharField(label = 'Enter your name', widget = forms.TextInput(attrs = {'size': 45}))
105+
ds_email = forms.EmailField(label = 'Enter your email', widget = forms.TextInput(attrs = {'size': 45}))
106+
msg_to_ad = forms.CharField(label = 'Message to the Area Director', widget = forms.Textarea(attrs = {'rows': 5, 'cols': 50}))

ietf/mailinglists/models.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22
from ietf.idtracker.models import Acronym, Areas, PersonOrOrgInfo
33

44
class ImportedMailingList(models.Model):
5-
group_acronym = models.ForeignKey(Acronym)
6-
list_acronym = models.CharField(blank=True, maxlength=255)
7-
list_name = models.CharField(blank=True, maxlength=255)
8-
list_domain = models.CharField(blank=True, maxlength=25)
5+
group_acronym = models.ForeignKey(Acronym, null=True)
6+
acronym = models.CharField(maxlength=255, db_column='list_acronym')
7+
name = models.CharField(blank=True, maxlength=255, db_column='list_name')
8+
domain = models.CharField(blank=True, maxlength=25, db_column='list_domain')
99
def __str__(self):
10-
return self.list_name or self.group_acronym
10+
return self.name or self.group_acronym
11+
def choices(dname):
12+
objects = ImportedMailingList.objects.all().filter(domain__icontains=dname).exclude(acronym__iendswith='announce')
13+
if dname == "ietf.org":
14+
objects = objects.exclude(acronym__istartswith='ietf').exclude(acronym__icontains='iesg')
15+
return [(list.acronym, list.acronym) for list in objects]
16+
choices = staticmethod(choices)
1117
class Meta:
1218
db_table = 'imported_mailing_list'
1319
class Admin:
@@ -72,22 +78,25 @@ class Admin:
7278
class NonWgMailingList(models.Model):
7379
id = models.CharField(primary_key=True, maxlength=35)
7480
purpose = models.TextField(blank=True)
75-
area_acronym = models.ForeignKey(Areas)
76-
admin = models.TextField(blank=True)
77-
list_url = models.CharField(maxlength=255)
78-
s_name = models.CharField(blank=True, maxlength=255)
79-
s_email = models.CharField(blank=True, maxlength=255)
81+
area = models.ForeignKey(Areas, db_column='area_acronym_id')
82+
admin = models.TextField("Administrator(s)' Email Address(es)", blank=True)
83+
list_url = models.CharField("List URL", maxlength=255)
84+
s_name = models.CharField("Submitter's Name", blank=True, maxlength=255)
85+
s_email = models.CharField("Submitter's Email Address", blank=True, maxlength=255)
8086
# Can be 0, 1, -1, or what looks like a person_or_org_tag, positive or neg.
8187
# The values less than 1 don't get displayed on the list of lists.
8288
status = models.IntegerField()
83-
list_name = models.CharField(blank=True, maxlength=255)
84-
subscribe_url = models.CharField(blank=True, maxlength=255)
85-
subscribe_other = models.TextField(blank=True)
89+
list_name = models.CharField("Mailing List Name", unique=True, maxlength=255)
90+
subscribe_url = models.CharField("Subscribe URL", blank=True, maxlength=255)
91+
subscribe_other = models.TextField("Subscribe Other", blank=True)
8692
ds_name = models.CharField(blank=True, maxlength=255)
8793
ds_email = models.CharField(blank=True, maxlength=255)
8894
msg_to_ad = models.TextField(blank=True)
8995
def __str__(self):
9096
return self.list_name
97+
def choices():
98+
return [(list.id, list.list_name) for list in NonWgMailingList.objects.all().filter(status__gt=0)]
99+
choices = staticmethod(choices)
91100
class Meta:
92101
db_table = 'none_wg_mailing_list'
93102
ordering = ['list_name']

ietf/mailinglists/urls.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
from django.conf.urls.defaults import *
22
from ietf.idtracker.models import Areas
3+
from ietf.mailinglists import views
34
from ietf.mailinglists.models import NonWgMailingList
5+
from ietf.mailinglists.forms import NonWgStep1
46

57
urlpatterns = patterns('django.views.generic.list_detail',
68
(r'^area_lists/$', 'object_list', { 'queryset': Areas.objects.filter(status=1).select_related().order_by('acronym.acronym'), 'template_name': 'mailinglists/areas_list.html' }),
79
(r'^nonwg_lists/$', 'object_list', { 'queryset': NonWgMailingList.objects.filter(status__gt=0) }),
810
)
11+
urlpatterns += patterns('',
12+
(r'^nonwg_lists/submit/$', views.non_wg_wizard),
13+
(r'^request/$', views.list_req_wizard),
14+
(r'^nonwg_lists/s2/$', views.non_wg_submit),
15+
)

ietf/mailinglists/views.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,72 @@
1-
# Create your views here.
1+
from forms import NonWgStep1, ListReqStep1, PickApprover, DeletionPickApprover, UrlMultiWidget
2+
from models import NonWgMailingList
3+
from ietf.idtracker.models import Areas
4+
from django import newforms as forms
5+
from django.shortcuts import render_to_response
6+
from ietf.contrib import wizard, form_decorator
7+
8+
nonwg_fields = {
9+
'id': None,
10+
'status': None,
11+
'ds_name': None,
12+
'ds_email': None,
13+
'msg_to_ad': None,
14+
}
15+
16+
nonwg_widgets = {
17+
'list_url': UrlMultiWidget(choices=(('http://', 'http://'), ('https://', 'https://'), ('mailto:', 'mailto:'))),
18+
'subscribe_url': UrlMultiWidget(choices=(('n/a', 'Not Applicable'), ('http://', 'http://'), ('https://', 'https://'))),
19+
}
20+
21+
nonwg_callback = form_decorator(fields=nonwg_fields, widgets=nonwg_widgets)
22+
23+
def gen_approval(approvers, parent):
24+
class BoundApproval(parent):
25+
_approvers = approvers
26+
def __init__(self, *args, **kwargs):
27+
super(BoundApproval, self).__init__(self._approvers, *args, **kwargs)
28+
return BoundApproval
29+
30+
class NonWgWizard(wizard.Wizard):
31+
def get_template(self):
32+
return "mailinglists/nwg_wizard.html"
33+
def hash_failed(self, step):
34+
raise NotImplementedError("step %d hash failed" % step)
35+
def process_step(self, request, form, step):
36+
form.full_clean()
37+
if step == 0:
38+
if form.clean_data['add_edit'] == 'add':
39+
self.form_list.append(forms.form_for_model(NonWgMailingList, formfield_callback=nonwg_callback))
40+
elif form.clean_data['add_edit'] == 'edit':
41+
self.form_list.append(forms.form_for_instance(NonWgMailingList.objects.get(pk=form.clean_data['list_id']), formfield_callback=nonwg_callback))
42+
elif form.clean_data['add_edit'] == 'delete':
43+
list = NonWgMailingList.objects.get(pk=form.clean_data['list_id_delete'])
44+
self.form_list.append(gen_approval([ad.person_id for ad in list.area.areadirectors_set.all()], DeletionPickApprover))
45+
if step == 1:
46+
form0 = self.get_form(0, request.POST)
47+
form0.full_clean()
48+
add_edit = form0.clean_data['add_edit']
49+
if add_edit == 'add' or add_edit == 'edit':
50+
self.form_list.append(gen_approval([ad.person_id for ad in Areas.objects.get(area_acronym=form.clean_data['area']).areadirectors_set.all()], PickApprover))
51+
super(NonWgWizard, self).process_step(request, form, step)
52+
53+
def non_wg_wizard(request):
54+
wiz = NonWgWizard([ NonWgStep1 ])
55+
return wiz(request)
56+
57+
class ListReqWizard(wizard.Wizard):
58+
def get_template(self):
59+
return "mailinglists/nwg_wizard.html"
60+
def hash_failed(self, step):
61+
raise NotImplementedError("step %d hash failed" % step)
62+
def process_step(self, request, form, step):
63+
form.full_clean()
64+
super(ListReqWizard, self).process_step(request, form, step)
65+
66+
def list_req_wizard(request):
67+
wiz = ListReqWizard([ ListReqStep1 ])
68+
return wiz(request)
69+
70+
def non_wg_submit(request):
71+
form = NonWgStep1()
72+
return render_to_response('mailinglists/step1.html', { 'form': form })
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{% extends "base.html" %}
2+
3+
{% block css %}
4+
ul.errorlist { color: red; border: 1px solid red; }
5+
{% endblock %}
6+
7+
{% block content %}
8+
<form action="." method="POST">
9+
FORM( {{ step }} ):<table> {{ form }} </table>
10+
11+
step_info : <input type="hidden" name="{{ step_field }}" value="{{ step }}" />
12+
13+
previous_fields: {{ previous_fields }}
14+
15+
<input type="submit">
16+
</form>
17+
18+
{% endblock %}

0 commit comments

Comments
 (0)