Skip to content

Commit 1f7d487

Browse files
committed
Refactor role handling in group editing slightly and add support for
editing reviewer roles in review teams. Also fix a couple of review related bugs. - Legacy-Id: 11921
1 parent 119f725 commit 1f7d487

8 files changed

Lines changed: 93 additions & 31 deletions

File tree

ietf/group/features.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class GroupFeatures(object):
1111
about_page = "group_about"
1212
default_tab = about_page
1313
material_types = ["slides"]
14+
admin_roles = ["chair"]
1415

1516
def __init__(self, group):
1617
if group.type_id in ("wg", "rg"):
@@ -31,3 +32,6 @@ def __init__(self, group):
3132
self.has_reviews = True
3233
import ietf.group.views
3334
self.default_tab = ietf.group.views.review_requests
35+
36+
if group.type_id == "dir":
37+
self.admin_roles = ["chair", "secr"]

ietf/group/milestones.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
9494

9595
needs_review = False
9696
if not can_manage_group(request.user, group):
97-
if group.has_role(request.user, "chair"):
97+
if group.has_role(request.user, group.features.admin_roles):
9898
if milestone_set == "current":
9999
needs_review = True
100100
else:

ietf/group/tests_info.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ietf.utils.test_utils import TestCase, unicontent
2727
from ietf.utils.mail import outbox, empty_outbox
2828
from ietf.utils.test_data import make_test_data, create_person, make_review_data
29-
from ietf.utils.test_utils import login_testing_unauthorized
29+
from ietf.utils.test_utils import login_testing_unauthorized, reload_db_objects
3030
from ietf.group.factories import GroupFactory, RoleFactory, GroupEventFactory
3131
from ietf.meeting.factories import SessionFactory
3232
import ietf.group.views
@@ -569,10 +569,10 @@ def test_edit_info(self):
569569
parent=area.pk,
570570
ad=ad.pk,
571571
state=state.pk,
572-
chairs="aread@ietf.org, ad1@ietf.org",
573-
secretaries="aread@ietf.org, ad1@ietf.org, ad2@ietf.org",
574-
techadv="aread@ietf.org",
575-
delegates="ad2@ietf.org",
572+
chair_roles="aread@ietf.org, ad1@ietf.org",
573+
secr_roles="aread@ietf.org, ad1@ietf.org, ad2@ietf.org",
574+
techadv_roles="aread@ietf.org",
575+
delegate_roles="ad2@ietf.org",
576576
list_email="mars@mail",
577577
list_subscribe="subscribe.mars",
578578
list_archive="archive.mars",
@@ -598,6 +598,40 @@ def test_edit_info(self):
598598
for prefix in ['ad1','ad2','aread','marschairman','marsdelegate']:
599599
self.assertTrue(prefix+'@' in outbox[0]['To'])
600600

601+
def test_edit_reviewers(self):
602+
doc = make_test_data()
603+
review_req = make_review_data(doc)
604+
group = review_req.team
605+
606+
url = urlreverse('group_edit', kwargs=dict(group_type=group.type_id, acronym=group.acronym))
607+
login_testing_unauthorized(self, "secretary", url)
608+
609+
# normal get
610+
r = self.client.get(url)
611+
self.assertEqual(r.status_code, 200)
612+
q = PyQuery(r.content)
613+
self.assertEqual(len(q('form input[name=reviewer_roles]')), 1)
614+
615+
# set reviewers
616+
empty_outbox()
617+
r = self.client.post(url,
618+
dict(name=group.name,
619+
acronym=group.acronym,
620+
parent=group.parent_id,
621+
ad=Person.objects.get(name="Areað Irector").pk,
622+
state=group.state_id,
623+
reviewer_roles="ad2@ietf.org",
624+
list_email=group.list_email,
625+
list_subscribe=group.list_subscribe,
626+
list_archive=group.list_archive,
627+
urls=""
628+
))
629+
self.assertEqual(r.status_code, 302)
630+
631+
group = reload_db_objects(group)
632+
self.assertEqual(list(group.role_set.filter(name="reviewer").values_list("email", flat=True)), ["ad2@ietf.org"])
633+
self.assertTrue('Personnel change' in outbox[0]['Subject'])
634+
601635
def test_initial_charter(self):
602636
make_test_data()
603637
group = Group.objects.get(acronym="mars")

ietf/group/views.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
from tempfile import mkstemp
3939
import datetime
4040
from collections import OrderedDict
41-
import math
4241

4342
import debug # pyflakes:ignore
4443

@@ -364,11 +363,11 @@ def construct_group_menu_context(request, group, selected, group_type, others):
364363
# actions
365364
actions = []
366365

367-
is_chair = group.has_role(request.user, "chair")
366+
is_admin = group.has_role(request.user, group.features.admin_roles)
368367
can_manage = can_manage_group(request.user, group)
369368

370369
if group.features.has_milestones:
371-
if group.state_id != "proposed" and (is_chair or can_manage):
370+
if group.state_id != "proposed" and (is_admin or can_manage):
372371
actions.append((u"Edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs)))
373372

374373
if group.features.has_documents:
@@ -384,10 +383,10 @@ def construct_group_menu_context(request, group, selected, group_type, others):
384383
import ietf.group.views_review
385384
actions.append((u"Manage review requests", urlreverse(ietf.group.views_review.manage_review_requests, kwargs=kwargs)))
386385

387-
if group.state_id != "conclude" and (is_chair or can_manage):
386+
if group.state_id != "conclude" and (is_admin or can_manage):
388387
actions.append((u"Edit group", urlreverse("group_edit", kwargs=kwargs)))
389388

390-
if group.features.customize_workflow and (is_chair or can_manage):
389+
if group.features.customize_workflow and (is_admin or can_manage):
391390
actions.append((u"Customize workflow", urlreverse("ietf.group.views_edit.customize_workflow", kwargs=kwargs)))
392391

393392
if group.state_id in ("active", "dormant") and not group.type_id in ["sdo", "rfcedtyp", "isoc", ] and can_manage:

ietf/group/views_edit.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,31 @@
2323
from ietf.person.models import Person, Email
2424
from ietf.group.mails import ( email_admin_re_charter, email_personnel_change)
2525
from ietf.utils.ordereddict import insert_after_in_ordered_dict
26+
from ietf.utils.text import skip_suffix
27+
2628

2729
MAX_GROUP_DELEGATES = 3
2830

31+
def roles_for_group_type(group_type):
32+
roles = ["chair", "secr", "techadv", "delegate"]
33+
if group_type == "dir":
34+
roles.append("reviewer")
35+
return roles
36+
2937
class GroupForm(forms.Form):
3038
name = forms.CharField(max_length=255, label="Name", required=True)
3139
acronym = forms.CharField(max_length=10, label="Acronym", required=True)
3240
state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True)
33-
chairs = SearchableEmailsField(label="Chairs", required=False, only_users=True)
34-
secretaries = SearchableEmailsField(label="Secretaries", required=False, only_users=True)
35-
techadv = SearchableEmailsField(label="Technical Advisors", required=False, only_users=True)
36-
delegates = SearchableEmailsField(label="Delegates", required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES,
41+
42+
# roles
43+
chair_roles = SearchableEmailsField(label="Chairs", required=False, only_users=True)
44+
secr_roles = SearchableEmailsField(label="Secretaries", required=False, only_users=True)
45+
techadv_roles = SearchableEmailsField(label="Technical Advisors", required=False, only_users=True)
46+
delegate_roles = SearchableEmailsField(label="Delegates", required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES,
3747
help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - at most %s persons at a given time." % MAX_GROUP_DELEGATES))
48+
reviewer_roles = SearchableEmailsField(label="Reviewers", required=False, only_users=True)
3849
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by('name'), label="Shepherding AD", empty_label="(None)", required=False)
50+
3951
parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", required=False)
4052
list_email = forms.CharField(max_length=64, required=False)
4153
list_subscribe = forms.CharField(max_length=255, required=False)
@@ -69,6 +81,11 @@ def __init__(self, *args, **kwargs):
6981
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(type="area")
7082
self.fields['parent'].label = "IETF Area"
7183

84+
role_fields_to_remove = (set(roles_for_group_type(self.group_type))
85+
- set(skip_suffix(attr, "_roles") for attr in self.fields if attr.endswith("_roles")))
86+
for r in role_fields_to_remove:
87+
del self.fields[r + "_roles"]
88+
7289
def clean_acronym(self):
7390
# Changing the acronym of an already existing group will cause 404s all
7491
# over the place, loose history, and generally muck up a lot of
@@ -211,7 +228,8 @@ def edit(request, group_type=None, acronym=None, action="edit"):
211228
group = get_group_or_404(acronym, group_type)
212229
if not group_type and group:
213230
group_type = group.type_id
214-
if not (can_manage_group(request.user, group) or group.has_role(request.user, "chair")):
231+
if not (can_manage_group(request.user, group)
232+
or group.has_role(request.user, group.features.admin_roles)):
215233
return HttpResponseForbidden("You don't have permission to access this view")
216234

217235
if request.method == 'POST':
@@ -274,10 +292,18 @@ def diff(attr, name):
274292
personnel_change_text=""
275293
changed_personnel = set()
276294
# update roles
277-
for attr, slug, title in [('ad','ad','Shepherding AD'), ('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors"), ('delegates', 'delegate', "Delegates")]:
295+
for attr, f in form.fields.iteritems():
296+
if not (attr.endswith("_roles") or attr == "ad"):
297+
continue
298+
299+
slug = attr
300+
slug = skip_suffix(slug, "_roles")
301+
302+
title = f.label
303+
278304
new = clean[attr]
279305
if attr == 'ad':
280-
new = [ new.role_email('ad'),] if new else []
306+
new = [ new.role_email('ad') ] if new else []
281307
old = Email.objects.filter(role__group=group, role__name=slug).select_related("person")
282308
if set(new) != set(old):
283309
changes.append((attr, new, desc(title,
@@ -336,17 +362,16 @@ def diff(attr, name):
336362
init = dict(name=group.name,
337363
acronym=group.acronym,
338364
state=group.state,
339-
chairs=Email.objects.filter(role__group=group, role__name="chair"),
340-
secretaries=Email.objects.filter(role__group=group, role__name="secr"),
341-
techadv=Email.objects.filter(role__group=group, role__name="techadv"),
342-
delegates=Email.objects.filter(role__group=group, role__name="delegate"),
343365
ad=ad_role and ad_role.person and ad_role.person.id,
344366
parent=group.parent.id if group.parent else None,
345367
list_email=group.list_email if group.list_email else None,
346368
list_subscribe=group.list_subscribe if group.list_subscribe else None,
347369
list_archive=group.list_archive if group.list_archive else None,
348370
urls=format_urls(group.groupurl_set.all()),
349371
)
372+
373+
for slug in roles_for_group_type(group_type):
374+
init[slug + "_roles"] = Email.objects.filter(role__group=group, role__name=slug)
350375
else:
351376
init = dict(ad=request.user.person.id if group_type == "wg" and has_role(request.user, "Area Director") else None,
352377
)
@@ -400,8 +425,8 @@ def customize_workflow(request, group_type=None, acronym=None):
400425
if not group.features.customize_workflow:
401426
raise Http404
402427

403-
if (not has_role(request.user, "Secretariat") and
404-
not group.role_set.filter(name="chair", person__user=request.user)):
428+
if not (can_manage_group(request.user, group)
429+
or group.has_role(request.user, group.features.admin_roles)):
405430
return HttpResponseForbidden("You don't have permission to access this view")
406431

407432
if group_type == "rg":

ietf/name/fixtures/names.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,7 @@
18271827
"fields": {
18281828
"order": 7,
18291829
"used": true,
1830-
"name": "No Review of Version",
1830+
"name": "Team Will not Review Version",
18311831
"desc": ""
18321832
},
18331833
"model": "name.reviewrequeststatename",
@@ -1837,7 +1837,7 @@
18371837
"fields": {
18381838
"order": 8,
18391839
"used": true,
1840-
"name": "No Review of Document",
1840+
"name": "Team Will not Review Document",
18411841
"desc": ""
18421842
},
18431843
"model": "name.reviewrequeststatename",

ietf/utils/test_data.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ def other_doc_factory(type_id,name):
360360
return draft
361361

362362
def make_review_data(doc):
363-
team = Group.objects.create(state_id="active", acronym="reviewteam", name="Review Team", type_id="team", list_email="reviewteam@ietf.org")
363+
team = Group.objects.create(state_id="active", acronym="reviewteam", name="Review Team", type_id="dir", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
364364
for r in ReviewResultName.objects.filter(slug__in=["issues", "ready-issues", "ready", "not-ready"]):
365365
ReviewTeamResult.objects.create(team=team, result=r)
366366

@@ -383,7 +383,7 @@ def make_review_data(doc):
383383
Role.objects.create(name_id="reviewer", person=p, email=p.email_set.first(), group=team)
384384

385385
p = Person.objects.get(user__username="secretary")
386-
Role.objects.create(name_id="secretary", person=p, email=p.email_set.first(), group=team)
386+
Role.objects.create(name_id="secr", person=p, email=p.email_set.first(), group=team)
387387

388388
return review_req
389389

ietf/utils/text.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ def skip_prefix(text, prefix):
44
else:
55
return text
66

7-
def skip_suffix(text, prefix):
8-
if text.endswith(prefix):
9-
return text[:-len(prefix)]
7+
def skip_suffix(text, suffix):
8+
if text.endswith(suffix):
9+
return text[:-len(suffix)]
1010
else:
1111
return text

0 commit comments

Comments
 (0)