Skip to content

Commit a9c33ed

Browse files
committed
Add view to edit nomcom
Merge public key edit view whith nomcom edit view Add view to delete nomcom Add hash url to accept o decline nominations Change new nominee template to include hash urls See ietf-tools#976 ietf-tools#977 - Legacy-Id: 5578
1 parent d3731ce commit a9c33ed

9 files changed

Lines changed: 174 additions & 32 deletions

File tree

ietf/nomcom/forms.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import datetime
2+
13
from django.conf import settings
24
from django import forms
35
from django.contrib.formtools.preview import FormPreview, AUTO_ID
@@ -8,6 +10,7 @@
810
from django.utils.decorators import method_decorator
911
from django.shortcuts import render_to_response
1012
from django.template.context import RequestContext
13+
from django.contrib.sites.models import Site
1114

1215
from ietf.dbtemplate.forms import DBTemplateForm
1316
from ietf.utils import unaccent
@@ -22,7 +25,7 @@
2225
from ietf.nomcom.utils import QUESTIONNAIRE_TEMPLATE, NOMINATION_EMAIL_TEMPLATE, \
2326
INEXISTENT_PERSON_TEMPLATE, NOMINEE_EMAIL_TEMPLATE, \
2427
NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE, \
25-
get_user_email
28+
get_user_email, get_hash_nominee_position, get_year_by_nomcom
2629
from ietf.nomcom.decorators import member_required
2730

2831
ROLODEX_URL = getattr(settings, 'ROLODEX_URL', None)
@@ -190,17 +193,13 @@ def done(self, request, cleaned_data):
190193
return HttpResponseRedirect(reverse('nomcom_edit_chair', kwargs={'year': self.year}))
191194

192195

193-
class EditPublicKeyForm(BaseNomcomForm, forms.ModelForm):
196+
class EditNomcomForm(BaseNomcomForm, forms.ModelForm):
194197

195-
fieldsets = [('Public Key', ('public_key',))]
198+
fieldsets = [('Edit nomcom', ('public_key', 'send_questionnaire'))]
196199

197200
class Meta:
198201
model = NomCom
199-
fields = ('public_key',)
200-
201-
def __init__(self, *args, **kwargs):
202-
super(EditPublicKeyForm, self).__init__(*args, **kwargs)
203-
self.fields['public_key'].required = True
202+
fields = ('public_key', 'send_questionnaire')
204203

205204

206205
class MergeForm(BaseNomcomForm, forms.Form):
@@ -393,8 +392,30 @@ def save(self, commit=True):
393392
subject = 'IETF Nomination Information'
394393
from_email = settings.NOMCOM_FROM_EMAIL
395394
to_email = email.address
395+
domain = Site.objects.get_current().domain
396+
today = datetime.date.today().strftime('%Y%m%d')
397+
hash = get_hash_nominee_position(today, nominee_position.id)
398+
accept_url = reverse('nomcom_process_nomination_status',
399+
None,
400+
args=(get_year_by_nomcom(self.nomcom),
401+
nominee_position.id,
402+
'accepted',
403+
today,
404+
hash))
405+
decline_url = reverse('nomcom_process_nomination_status',
406+
None,
407+
args=(get_year_by_nomcom(self.nomcom),
408+
nominee_position.id,
409+
'declined',
410+
today,
411+
hash))
412+
396413
context = {'nominee': email.person.name,
397-
'position': position.name}
414+
'position': position.name,
415+
'domain': domain,
416+
'accept_url': accept_url,
417+
'decline_url': decline_url}
418+
398419
path = nomcom_template_path + NOMINEE_EMAIL_TEMPLATE
399420
send_mail(None, to_email, from_email, subject, path, context)
400421

ietf/nomcom/urls.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.conf.urls.defaults import patterns, url
2+
from django.views.generic.simple import direct_to_template
23
from ietf.nomcom.forms import EditChairForm, EditChairFormPreview, \
34
EditMembersForm, EditMembersFormPreview
45

@@ -14,7 +15,9 @@
1415
url(r'^(?P<year>\d{4})/private/send-reminder-mail/$', 'send_reminder_mail', name='nomcom_send_reminder_mail'),
1516
url(r'^(?P<year>\d{4})/private/edit-members/$', EditMembersFormPreview(EditMembersForm), name='nomcom_edit_members'),
1617
url(r'^(?P<year>\d{4})/private/edit-chair/$', EditChairFormPreview(EditChairForm), name='nomcom_edit_chair'),
17-
url(r'^(?P<year>\d{4})/private/edit-publickey/$', 'edit_publickey', name='nomcom_edit_publickey'),
18+
url(r'^(?P<year>\d{4})/private/edit-nomcom/$', 'edit_nomcom', name='nomcom_edit_nomcom'),
19+
url(r'^(?P<year>\d{4})/private/delete-nomcom/$', 'delete_nomcom', name='nomcom_delete_nomcom'),
20+
url(r'^deleted/$', direct_to_template, {'template': 'nomcom/deleted.html'}, name='nomcom_deleted'),
1821
url(r'^(?P<year>\d{4})/private/chair/templates/$', 'list_templates', name='nomcom_list_templates'),
1922
url(r'^(?P<year>\d{4})/private/chair/templates/(?P<template_id>\d+)/$', 'edit_template', name='nomcom_edit_template'),
2023
url(r'^(?P<year>\d{4})/private/chair/position/$', 'list_positions', name='nomcom_list_positions'),
@@ -27,6 +30,7 @@
2730
url(r'^(?P<year>\d{4})/questionnaires/$', 'questionnaires', name='nomcom_questionnaires'),
2831
url(r'^(?P<year>\d{4})/feedback/$', 'public_feedback', name='nomcom_public_feedback'),
2932
url(r'^(?P<year>\d{4})/nominate/$', 'public_nominate', name='nomcom_public_nominate'),
33+
url(r'^(?P<year>\d{4})/process-nomination-status/(?P<nominee_position_id>\d+)/(?P<state>[\w]+)/(?P<date>[\d]+)/(?P<hash>[a-f0-9]+)/$', 'process_nomination_status', name='nomcom_process_nomination_status'),
3034
url(r'^ajax/position-text/(?P<position_id>\d+)/$', 'ajax_position_text', name='nomcom_ajax_position_text'),
3135

3236
)

ietf/nomcom/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import hashlib
2+
import re
3+
14
from django.conf import settings
25
from django.core.exceptions import PermissionDenied
36
from django.shortcuts import get_object_or_404
@@ -34,6 +37,12 @@ def get_nomcom_by_year(year):
3437
group__state__slug='active')
3538

3639

40+
def get_year_by_nomcom(nomcom):
41+
acronym = nomcom.group.acronym
42+
m = re.search('(?P<year>\d\d\d\d)', acronym)
43+
return m.group(0)
44+
45+
3746
def get_user_email(user):
3847
emails = Email.objects.filter(person__user=user)
3948
email = emails and emails[0] or None
@@ -52,6 +61,10 @@ def is_nomcom_chair(user, nomcom):
5261
raise PermissionDenied("Must be nomcom chair")
5362

5463

64+
def get_hash_nominee_position(date, nominee_position_id):
65+
return hashlib.md5('%s%s%s' % (settings.SECRET_KEY, date, nominee_position_id)).hexdigest()
66+
67+
5568
def initialize_templates_for_group(group):
5669
for template_name in DEFAULT_NOMCOM_TEMPLATES:
5770
template_path = MAIN_NOMCOM_TEMPLATE_PATH + template_name

ietf/nomcom/views.py

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# -*- coding: utf-8 -*-
2+
import datetime
3+
4+
from django.views.generic.create_update import delete_object
25
from django.conf import settings
36
from django.contrib.auth.decorators import login_required
47
from django.core.urlresolvers import reverse
5-
from django.http import HttpResponse, Http404, HttpResponseRedirect
8+
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseForbidden
69
from django.shortcuts import render_to_response, get_object_or_404
710
from django.template import RequestContext
811
from django.template.loader import render_to_string
@@ -17,12 +20,14 @@
1720
from ietf.name.models import NomineePositionState, FeedbackType
1821

1922
from ietf.nomcom.decorators import member_required, private_key_required
20-
from ietf.nomcom.forms import (EditPublicKeyForm, NominateForm, FeedbackForm, MergeForm,
21-
NomComTemplateForm, PositionForm, PrivateKeyForm)
22-
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback
23+
from ietf.nomcom.forms import (NominateForm, FeedbackForm, MergeForm,
24+
NomComTemplateForm, PositionForm, PrivateKeyForm,
25+
EditNomcomForm)
26+
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom
2327
from ietf.nomcom.utils import (get_nomcom_by_year, HOME_TEMPLATE,
2428
retrieve_nomcom_private_key,
25-
store_nomcom_private_key, NOMINEE_REMINDER_TEMPLATE)
29+
store_nomcom_private_key, get_hash_nominee_position,
30+
NOMINEE_REMINDER_TEMPLATE)
2631

2732

2833
def index(request, year):
@@ -71,13 +76,13 @@ def private_index(request, year):
7176
nominations = all_nominee_positions.filter(id__in=nominations_to_modify)
7277
if action == "set_as_accepted":
7378
nominations.update(state='accepted')
74-
message = ('success', 'The selected nominations has been set as accepted')
79+
message = ('success', 'The selected nominations have been set as accepted')
7580
elif action == "set_as_declined":
7681
nominations.update(state='declined')
77-
message = ('success', 'The selected nominations has been set as declined')
82+
message = ('success', 'The selected nominations have been set as declined')
7883
elif action == "set_as_pending":
7984
nominations.update(state='pending')
80-
message = ('success', 'The selected nominations has been set as pending')
85+
message = ('success', 'The selected nominations have been set as pending')
8186
else:
8287
message = ('warning', "Please, select some nominations to work with")
8388

@@ -164,7 +169,7 @@ def private_merge(request, year):
164169
form = MergeForm(request.POST, nomcom=nomcom)
165170
if form.is_valid():
166171
form.save()
167-
message = ('success', 'The emails has been unified')
172+
message = ('success', 'The emails have been unified')
168173
else:
169174
form = MergeForm(nomcom=nomcom)
170175

@@ -251,6 +256,42 @@ def private_feedback(request, year):
251256
return feedback(request, year, False)
252257

253258

259+
def process_nomination_status(request, year, nominee_position_id, state, date, hash):
260+
valid = get_hash_nominee_position(date, nominee_position_id) == hash
261+
if not valid:
262+
return HttpResponseForbidden("Bad hash!")
263+
expiration_days = getattr(settings, 'DAYS_TO_EXPIRE_NOMINATION_LINK', None)
264+
if expiration_days:
265+
request_date = datetime.date(int(date[:4]), int(date[4:6]), int(date[6:]))
266+
if datetime.date.today() > (request_date + datetime.timedelta(days=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK)):
267+
return HttpResponseForbidden("Link expired")
268+
269+
need_confirmation = True
270+
nomcom = get_nomcom_by_year(year)
271+
nominee_position = get_object_or_404(NomineePosition, id=nominee_position_id)
272+
if nominee_position.state.slug != "pending":
273+
return HttpResponseForbidden("The nomination already was %s" % nominee_position.state)
274+
275+
state = get_object_or_404(NomineePositionState, slug=state)
276+
message = ('warning', "Are you sure to change the nomination on %s as %s?" % (nominee_position.position.name,
277+
state.name))
278+
if request.method == 'POST':
279+
nominee_position.state = state
280+
nominee_position.save()
281+
need_confirmation = False
282+
message = message = ('success', 'Your nomination on %s has been set as %s' % (nominee_position.position.name,
283+
state.name))
284+
285+
return render_to_response('nomcom/process_nomination_status.html',
286+
{'message': message,
287+
'nomcom': nomcom,
288+
'year': year,
289+
'nominee_position': nominee_position,
290+
'state': state,
291+
'need_confirmation': need_confirmation,
292+
'selected': 'feedback'}, RequestContext(request))
293+
294+
254295
def feedback(request, year, public):
255296
nomcom = get_nomcom_by_year(year)
256297
has_publickey = nomcom.public_key and True or False
@@ -358,27 +399,42 @@ def view_feedback_nominee(request, year, nominee_id):
358399

359400

360401
@member_required(role='chair')
361-
def edit_publickey(request, year):
402+
def edit_nomcom(request, year):
362403
nomcom = get_nomcom_by_year(year)
363404

364405
message = ('warning', 'Previous data will remain encrypted with the old key')
365406
if request.method == 'POST':
366-
form = EditPublicKeyForm(request.POST,
367-
request.FILES,
368-
instance=nomcom,
369-
initial={'public_key': None})
407+
form = EditNomcomForm(request.POST,
408+
request.FILES,
409+
instance=nomcom)
370410
if form.is_valid():
371411
form.save()
372-
message = ('success', 'The public key has been changed')
412+
message = ('success', 'The nomcom has been changed')
373413
else:
374-
form = EditPublicKeyForm()
414+
form = EditNomcomForm(instance=nomcom)
375415

376-
return render_to_response('nomcom/edit_publickey.html',
416+
return render_to_response('nomcom/edit_nomcom.html',
377417
{'form': form,
378-
'group': nomcom.group,
418+
'nomcom': nomcom,
379419
'message': message,
380420
'year': year,
381-
'selected': 'edit_publickey'}, RequestContext(request))
421+
'selected': 'edit_nomcom'}, RequestContext(request))
422+
423+
424+
@member_required(role='chair')
425+
def delete_nomcom(request, year):
426+
nomcom = get_nomcom_by_year(year)
427+
post_delete_redirect = reverse('nomcom_deleted')
428+
extra_context = {'year': year,
429+
'selected': 'edit_nomcom',
430+
'nomcom': nomcom}
431+
432+
return delete_object(request,
433+
model=NomCom,
434+
object_id=nomcom.id,
435+
post_delete_redirect=post_delete_redirect,
436+
template_name='nomcom/delete_nomcom.html',
437+
extra_context=extra_context)
382438

383439

384440
@member_required(role='chair')
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% extends "nomcom/nomcom_private_base.html" %}
2+
3+
{% block subtitle %}- Delete Nomcom{% endblock %}
4+
5+
{% block nomcom_content %}
6+
7+
<p>Are you sure you want to delete all data about {{ nomcom.group.name }}?</p>
8+
<form action="" method="post">{% csrf_token %}
9+
<div>
10+
<input type="hidden" name="post" value="yes" />
11+
<input type="submit" value="Yes, I'm sure" />
12+
</div>
13+
</form>
14+
15+
{% endblock %}

ietf/templates/nomcom/deleted.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% extends "nomcom/nomcom_base.html" %}
2+
3+
{% block content %}
4+
5+
<div class="info-message-success">All data about the nomcom has been removed</div>
6+
7+
{% endblock %}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{% extends "nomcom/nomcom_private_base.html" %}
22

3-
{% block subtitle %}- Edit public key{% endblock %}
3+
{% block subtitle %}- Edit Nomcom{% endblock %}
44

55
{% block nomcom_content %}
6-
<h2>Edit public key</h2>
6+
<h2>Edit Nomcom</h2>
77

88
{% if message %}
99
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
@@ -18,4 +18,8 @@ <h2>Edit public key</h2>
1818
<p><input type="submit" value="Save" /></p>
1919
</form>
2020

21+
<h2>Delete Nomcom</h2>
22+
23+
<p>To delete all data about {{ nomcom.group.name }}, <a href="{% url nomcom_delete_nomcom year %}" class="deletelink">click here</a></p>
24+
2125
{% endblock %}

ietf/templates/nomcom/nomcom_private_base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ <h1>Nomcom {{ year }} Private Area</h1>
1616
{% if selected == "merge" %}<span class="selected">Merge nominee email addr</span>{% else %}<a href="{% url nomcom_private_merge year %}">Merge nominee email addr</a>{% endif %} |
1717
{% if selected == "send_reminder_mail" %}<span class="selected">Send Reminder Mail</span>{% else %}<a href="{% url nomcom_send_reminder_mail year %}">Send reminder mail</a>{% endif %} |
1818
{% if selected == "edit_members" %}<span class="selected">Nomcom members</span>{% else %}<a href="{% url nomcom_edit_members year %}">Nomcom members</a>{% endif %} |
19-
{% if selected == "edit_publickey" %}<span class="selected">Public key</span>{% else %}<a href="{% url nomcom_edit_publickey year %}">Public key</a>{% endif %} |
19+
{% if selected == "edit_nomcom" %}<span class="selected">Edit Nomcom</span>{% else %}<a href="{% url nomcom_edit_nomcom year %}">Edit Nomcom</a>{% endif %} |
2020
{% if selected == "edit_templates" %}<span class="selected">Templates</span>{% else %}<a href="{% url nomcom_list_templates year %}">Templates</a>{% endif %} |
2121
{% if selected == "edit_positions" %}<span class="selected">Positions</span>{% else %}<a href="{% url nomcom_list_positions year %}">Positions</a>{% endif %}
2222
{% endif %}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{% extends "nomcom/nomcom_public_base.html" %}
2+
3+
{% block subtitle %} - Change Nomination {% endblock %}
4+
5+
{% block nomcom_content %}
6+
7+
{% if message %}
8+
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
9+
{% endif %}
10+
11+
{% if need_confirmation %}
12+
<form action="" method="post">{% csrf_token %}
13+
{{ form }}
14+
15+
<div class="submitrow">
16+
<input type="submit" value="Save" name="save"/>
17+
</div>
18+
19+
</form>
20+
{% endif %}
21+
22+
{% endblock %}

0 commit comments

Comments
 (0)