Skip to content

Commit 34cbc46

Browse files
committed
Manage delegates views. See ietf-tools#557
- Legacy-Id: 2690
1 parent e322555 commit 34cbc46

10 files changed

Lines changed: 329 additions & 2 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{% extends "wginfo/wg_base.html" %}
2+
3+
{% block morecss %}
4+
{{ block.super }}
5+
.wg-chair-management ul {
6+
list-style: none;
7+
}
8+
9+
.wg-chair-management input {
10+
border: 1px solid green;
11+
}
12+
{% endblock %}
13+
14+
{% block wg_titledetail %}Delegates{% endblock %}
15+
16+
{% block wg_content %}
17+
<div class="wg-chair-management">
18+
<h2>Add new delegate</h2>
19+
{% if add_form.message %}
20+
<div class="message message-{{ add_form.message.type }}">
21+
{{ add_form.message.value }}
22+
</div>
23+
{% endif %}
24+
{% if can_add %}
25+
<form action="" method="POST">
26+
{{ add_form.as_p }}
27+
<p>
28+
<input type="submit" value="{% if add_form.submit_msg %}{{ add_form.submit_msg }}{% else %}Add delegate{% endif %}" name="add" />
29+
{% if add_form.can_cancel %}<a href="">No! I don't want to continue</a>{% endif %}
30+
</p>
31+
</form>
32+
{% else %}
33+
<p>
34+
You can only assign three delegates. Please remove delegates to add a new one.
35+
</p>
36+
{% endif %}
37+
38+
<h2>Delegates</h2>
39+
{% if delegates %}
40+
<form action="" method="POST">
41+
<table>
42+
<tr><th>Remove</th><th>Delegate name</th></tr>
43+
{% for delegate in delegates %}
44+
<tr><td><input type="checkbox" name="delete" value="{{ delegate.pk }}" /></td><td>{{ delegate.person }}</td></tr>
45+
{% endfor %}
46+
</table>
47+
<input type="submit" value="Remove delegate(s)" name="remove" />
48+
</form>
49+
{% else %}
50+
<p>
51+
No delegates
52+
</p>
53+
{% endif %}
54+
</div>
55+
{% endblock %}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<p>
2+
The delegate you are trying to designate does not have a personal user-id and password to log-on to the Datatracker.
3+
</p>
4+
<p>
5+
An email will be sent to the following address to inform that the person designated sould contact with the Secretariat
6+
to obtain their own user-id and password to the Datatracker.
7+
</p>
8+
<ul>
9+
{% for email in email_list %}
10+
<li>{{ email }}</li>
11+
{% endfor %}
12+
</ul>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% if can_manage_workflow %}
2+
{% ifequal selected "manage_workflow" %}
3+
<span class="selected">Manage workflow</span>
4+
{% else %}
5+
<a href="{% url manage_workflow wg.group_acronym.acronym %}">Manage workflow</a>
6+
{% endifequal %} |
7+
{% endif %}
8+
9+
{% if can_manage_delegates %}
10+
{% ifequal selected "manage_delegates" %}
11+
<span class="selected">Manage delegates</span>
12+
{% else %}
13+
<a href="{% url manage_delegates wg.group_acronym.acronym %}">Manage delegates</a>
14+
{% endifequal %} |
15+
{% endif %}

ietf/templates/wginfo/wg_base.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3333
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3434
{% endcomment %}
35-
{% load ietf_filters %}
35+
{% load ietf_filters wgchairs_tags %}
3636
{% block title %}{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}) - {% block wg_titledetail %}{% endblock %}{% endblock %}
3737

3838
{% block morecss %}
@@ -58,6 +58,7 @@ <h1>{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}){% if concluded %}<b
5858
<div class="ietf-navset">
5959
{% ifequal selected "documents" %}<span class="selected">Documents</span>{% else %}<a href="/wg/{{wg}}/">Documents</a>{% endifequal %} |
6060
{% ifequal selected "charter" %}<span class="selected">Charter</span>{% else %}<a href="/wg/{{wg}}/charter/">Charter</a>{% endifequal %} |
61+
{% wgchairs_admin_options wg %}
6162
{% if wg.clean_email_archive|startswith:"http:" or wg.clean_email_archive|startswith:"ftp:" %}
6263
<a href="{{ wg.clean_email_archive }}">List Archive &#187;</a> |
6364
{% endif %}

ietf/wgchairs/accounts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def can_do_wg_workflow_in_document(user, document):
2222
person = get_person_for_user(user)
2323
if not person or not document.group:
2424
return False
25-
return can_do_wg_wgorkflow_in_group(document.group)
25+
return can_do_wg_workflow_in_group(document.group)
2626

2727

2828
def can_manage_workflow_in_group(user, group):

ietf/wgchairs/forms.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
from django import forms
2+
from django.conf import settings
3+
from django.core.mail import EmailMessage
4+
from django.template.loader import render_to_string
5+
6+
from ietf.wgchairs.models import WGDelegate
7+
from ietf.wgchairs.accounts import get_person_for_user
8+
from ietf.idtracker.models import PersonOrOrgInfo
9+
10+
11+
class RelatedWGForm(forms.Form):
12+
13+
can_cancel = False
14+
15+
def __init__(self, *args, **kwargs):
16+
self.wg = kwargs.pop('wg', None)
17+
self.user = kwargs.pop('user', None)
18+
self.message = {}
19+
super(RelatedWGForm, self).__init__(*args, **kwargs)
20+
21+
def get_message(self):
22+
return self.message
23+
24+
def set_message(self, msg_type, msg_value):
25+
self.message = {'type': msg_type,
26+
'value': msg_value,
27+
}
28+
29+
30+
class RemoveDelegateForm(RelatedWGForm):
31+
32+
delete = forms.MultipleChoiceField()
33+
34+
def __init__(self, *args, **kwargs):
35+
super(RemoveDelegateForm, self).__init__(*args, **kwargs)
36+
self.fields['delete'].choices = [(i.pk, i.pk) for i in self.wg.wgdelegate_set.all()]
37+
38+
def save(self):
39+
delegates = self.cleaned_data.get('delete')
40+
WGDelegate.objects.filter(pk__in=delegates).delete()
41+
self.set_message('success', 'Delegates removed')
42+
43+
44+
class AddDelegateForm(RelatedWGForm):
45+
46+
email = forms.EmailField()
47+
form_type = forms.CharField(widget=forms.HiddenInput, initial='single')
48+
49+
def __init__(self, *args, **kwargs):
50+
super(AddDelegateForm, self).__init__(*args, **kwargs)
51+
self.next_form = self
52+
53+
def get_next_form(self):
54+
return self.next_form
55+
56+
def get_person(self, email):
57+
persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email, iesglogin__isnull=False).distinct()
58+
if not persons:
59+
raise PersonOrOrgInfo.DoesNotExist
60+
if len(persons) > 1:
61+
raise PersonOrOrgInfo.MultipleObjectsReturned
62+
return persons[0]
63+
64+
def save(self):
65+
email = self.cleaned_data.get('email')
66+
try:
67+
person = self.get_person(email)
68+
except PersonOrOrgInfo.DoesNotExist:
69+
self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email)
70+
self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system')
71+
return
72+
except PersonOrOrgInfo.MultipleObjectsReturned:
73+
self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email)
74+
self.next_form.set_message('multiple', 'There are multiple users with this email in the system')
75+
return
76+
self.create_delegate(person)
77+
78+
def create_delegate(self, person):
79+
(delegate, created) = WGDelegate.objects.get_or_create(wg=self.wg,
80+
person=person)
81+
if not created:
82+
self.set_message('error', 'The email belongs to a person who is already a delegate')
83+
else:
84+
self.next_form = AddDelegateForm(wg=self.wg, user=self.user)
85+
self.next_form.set_message('success', 'A new delegate has been added')
86+
87+
88+
class MultipleDelegateForm(AddDelegateForm):
89+
90+
email = forms.EmailField(widget=forms.HiddenInput)
91+
form_type = forms.CharField(widget=forms.HiddenInput, initial='multiple')
92+
persons = forms.ChoiceField(widget=forms.RadioSelect, help_text='Please select one person from the list')
93+
submit_msg = 'Designate as delegate'
94+
95+
def __init__(self, *args, **kwargs):
96+
self.email = kwargs.pop('email', None)
97+
super(MultipleDelegateForm, self).__init__(*args, **kwargs)
98+
if not self.email:
99+
self.email = self.data.get('email', None)
100+
self.fields['email'].initial = self.email
101+
self.fields['persons'].choices = [(i.pk, unicode(i)) for i in PersonOrOrgInfo.objects.filter(emailaddress__address=self.email, iesglogin__isnull=False).distinct().order_by('first_name')]
102+
103+
def save(self):
104+
person_id = self.cleaned_data.get('persons')
105+
person = PersonOrOrgInfo.objects.get(pk=person_id)
106+
self.create_delegate(person)
107+
108+
109+
class NotExistDelegateForm(MultipleDelegateForm):
110+
111+
email = forms.EmailField(widget=forms.HiddenInput)
112+
form_type = forms.CharField(widget=forms.HiddenInput, initial='notexist')
113+
can_cancel = True
114+
submit_msg = 'Send email to these addresses'
115+
116+
def __init__(self, *args, **kwargs):
117+
super(NotExistDelegateForm, self).__init__(*args, **kwargs)
118+
self.email_list = []
119+
120+
def get_email_list(self):
121+
if self.email_list:
122+
return self.email_list
123+
email_list = [self.email]
124+
email_list.append('IETF Secretariat <iesg-secretary@ietf.org>')
125+
email_list += ['%s <%s>' % i.person.email() for i in self.wg.wgchair_set.all() if i.person.email()]
126+
self.email_list = email_list
127+
return email_list
128+
129+
def as_p(self):
130+
email_list = self.get_email_list()
131+
return render_to_string('wgchairs/notexistdelegate.html', {'email_list': email_list})
132+
133+
def send_email(self, email, template):
134+
subject = 'WG Delegate needs system credentials'
135+
persons = PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).distinct()
136+
body = render_to_string(template,
137+
{'chair': get_person_for_user(self.user),
138+
'delegate_email': self.email,
139+
'delegate_persons': persons,
140+
})
141+
mail = EmailMessage(subject=subject,
142+
body=body,
143+
to=email,
144+
from_email=settings.DEFAULT_FROM_EMAIL)
145+
return mail
146+
147+
def send_email_to_delegate(self, email):
148+
self.send_email(email, 'wgchairs/notexistsdelegate_delegate_email.txt')
149+
150+
def send_email_to_secretariat(self, email):
151+
self.send_email(email, 'wgchairs/notexistsdelegate_secretariat_email.txt')
152+
153+
def send_email_to_wgchairs(self, email):
154+
self.send_email(email, 'wgchairs/notexistsdelegate_wgchairs_email.txt')
155+
156+
def save(self):
157+
self.next_form = AddDelegateForm(wg=self.wg, user=self.user)
158+
if settings.DEBUG:
159+
self.next_form.set_message('warning', 'Email was not sent cause tool is in DEBUG mode')
160+
else:
161+
email_list = self.get_email_list()
162+
self.send_email_to_delegate([email_list[0]])
163+
self.send_email_to_secretariat([email_list[1]])
164+
self.send_email_to_wgchairs(email_list[2:])
165+
self.next_form.set_message('success', 'Email sent successfully')
166+
167+
168+
def add_form_factory(request, wg, user):
169+
if request.method != 'POST':
170+
return AddDelegateForm(wg=wg, user=user)
171+
172+
if request.POST.get('form_type', None) == 'multiple':
173+
return MultipleDelegateForm(wg=wg, user=user, data=request.POST.copy())
174+
elif request.POST.get('form_type', None) == 'notexist':
175+
return NotExistDelegateForm(wg=wg, user=user, data=request.POST.copy())
176+
elif request.POST.get('form_type', None) == 'single':
177+
return AddDelegateForm(wg=wg, user=user, data=request.POST.copy())
178+
179+
return AddDelegateForm(wg=wg, user=user)

ietf/wgchairs/templatetags/__init__.py

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from django import template
2+
3+
from ietf.wgchairs.accounts import (can_manage_workflow_in_group,
4+
can_manage_delegates_in_group)
5+
6+
7+
register = template.Library()
8+
9+
10+
@register.inclusion_tag('wgchairs/wgchairs_admin_options.html', takes_context=True)
11+
def wgchairs_admin_options(context, wg):
12+
request = context.get('request', None)
13+
user = request and request.user
14+
return {'user': user,
15+
'can_manage_delegates': can_manage_delegates_in_group(user, wg),
16+
'can_manage_workflow': can_manage_workflow_in_group(user, wg),
17+
'wg': wg,
18+
'selected': context.get('selected', None),
19+
}

ietf/wgchairs/urls.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright The IETF Trust 2008, All Rights Reserved
2+
3+
from django.conf.urls.defaults import patterns, url
4+
5+
urlpatterns = patterns('ietf.wgchairs.views',
6+
url(r'^workflows/$', 'manage_workflow', name='manage_workflow'),
7+
url(r'^delegates/$', 'manage_delegates', name='manage_delegates'),
8+
)

ietf/wgchairs/views.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from ietf.idtracker.models import IETFWG
2+
from django.shortcuts import get_object_or_404, render_to_response
3+
from django.template import RequestContext
4+
from django.http import HttpResponseForbidden
5+
6+
from ietf.wgchairs.forms import RemoveDelegateForm, add_form_factory
7+
from ietf.wgchairs.accounts import can_manage_delegates_in_group
8+
9+
10+
def manage_delegates(request, acronym):
11+
wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
12+
user = request.user
13+
if not can_manage_delegates_in_group(user, wg):
14+
return HttpResponseForbidden('You have no permission to access this view')
15+
delegates = wg.wgdelegate_set.all()
16+
add_form = add_form_factory(request, wg, user)
17+
if request.method == 'POST':
18+
if request.POST.get('remove', None):
19+
form = RemoveDelegateForm(wg=wg, data=request.POST.copy())
20+
if form.is_valid():
21+
form.save()
22+
elif add_form.is_valid():
23+
add_form.save()
24+
add_form = add_form.get_next_form()
25+
return render_to_response('wgchairs/manage_delegates.html',
26+
{'wg': wg,
27+
'delegates': delegates,
28+
'selected': 'manage_delegates',
29+
'can_add': delegates.count() < 3,
30+
'add_form': add_form,
31+
},
32+
RequestContext(request))
33+
34+
35+
def manage_workflow(request, acronym):
36+
wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
37+
concluded = (wg.status_id != 1)
38+
return render_to_response('wginfo/wg_charter.html', {'wg': wg, 'concluded': concluded, 'selected': 'manage_workflow'}, RequestContext(request))

0 commit comments

Comments
 (0)