Skip to content

Commit 5581dc7

Browse files
feat: doc mgrs can edit/remind action holders (ietf-tools#7971)
* feat: doc mgrs can edit/remind action holders * test: test docman_roles, not "chair"
1 parent 06b9df1 commit 5581dc7

4 files changed

Lines changed: 67 additions & 10 deletions

File tree

ietf/doc/tests.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
from ietf.meeting.factories import ( MeetingFactory, SessionFactory, SessionPresentationFactory,
6060
ProceedingsMaterialFactory )
6161

62-
from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName
62+
from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName, RoleName
6363
from ietf.person.models import Person
6464
from ietf.person.factories import PersonFactory, EmailFactory
6565
from ietf.utils.mail import outbox, empty_outbox
@@ -1450,6 +1450,14 @@ def test_document_draft_action_holders_buttons(self, mock_method):
14501450
"""Buttons for action holders should be shown when AD or secretary"""
14511451
draft = WgDraftFactory()
14521452
draft.action_holders.set([PersonFactory()])
1453+
other_group = GroupFactory(type_id=draft.group.type_id)
1454+
1455+
# create a test RoleName and put it in the docman_roles for the document group
1456+
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
1457+
draft.group.features.docman_roles.append("wrangler")
1458+
draft.group.features.save()
1459+
wrangler = RoleFactory(group=draft.group, name_id="wrangler").person
1460+
wrangler_of_other_group = RoleFactory(group=other_group, name_id="wrangler").person
14531461

14541462
url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=draft.name))
14551463
edit_ah_url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=draft.name))
@@ -1482,6 +1490,8 @@ def _run_test(username=None, expect_buttons=False):
14821490

14831491
_run_test(None, False)
14841492
_run_test('plain', False)
1493+
_run_test(wrangler_of_other_group.user.username, False)
1494+
_run_test(wrangler.user.username, True)
14851495
_run_test('ad', True)
14861496
_run_test('secretary', True)
14871497

ietf/doc/tests_draft.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
WriteupDocEvent, DocRelationshipName, IanaExpertDocEvent )
2727
from ietf.doc.utils import get_tags_for_stream_id, create_ballot_if_not_open
2828
from ietf.doc.views_draft import AdoptDraftForm
29-
from ietf.name.models import StreamName, DocTagName
29+
from ietf.name.models import StreamName, DocTagName, RoleName
3030
from ietf.group.factories import GroupFactory, RoleFactory
3131
from ietf.group.models import Group, Role
3232
from ietf.person.factories import PersonFactory, EmailFactory
@@ -935,6 +935,7 @@ def setUp(self):
935935
super().setUp()
936936
doc = WgDraftFactory(group__acronym='mars',shepherd=PersonFactory(user__username='plain',name='Plain Man').email_set.first())
937937
self.docname = doc.name
938+
self.doc_group = doc.group
938939

939940
def test_doc_change_stream(self):
940941
url = urlreverse('ietf.doc.views_draft.change_stream', kwargs=dict(name=self.docname))
@@ -1319,8 +1320,10 @@ def do_doc_change_action_holders_test(self, username):
13191320
RoleFactory(name_id='techadv', person=PersonFactory(), group=doc.group)
13201321
RoleFactory(name_id='editor', person=PersonFactory(), group=doc.group)
13211322
RoleFactory(name_id='secr', person=PersonFactory(), group=doc.group)
1322-
1323+
some_other_chair = RoleFactory(name_id="chair").person
1324+
13231325
url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=doc.name))
1326+
login_testing_unauthorized(self, some_other_chair.user.username, url) # other chair can't edit action holders
13241327
login_testing_unauthorized(self, username, url)
13251328

13261329
r = self.client.get(url)
@@ -1363,6 +1366,14 @@ def _test_changing_ah(action_holders, reason):
13631366
_test_changing_ah(doc.authors(), 'authors can do it, too')
13641367
_test_changing_ah([], 'clear it back out')
13651368

1369+
def test_doc_change_action_holders_as_doc_manager(self):
1370+
# create a test RoleName and put it in the docman_roles for the document group
1371+
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
1372+
self.doc_group.features.docman_roles.append("wrangler")
1373+
self.doc_group.features.save()
1374+
wrangler = RoleFactory(group=self.doc_group, name_id="wrangler").person
1375+
self.do_doc_change_action_holders_test(wrangler.user.username)
1376+
13661377
def test_doc_change_action_holders_as_secretary(self):
13671378
self.do_doc_change_action_holders_test('secretary')
13681379

@@ -1372,9 +1383,11 @@ def test_doc_change_action_holders_as_ad(self):
13721383
def do_doc_remind_action_holders_test(self, username):
13731384
doc = Document.objects.get(name=self.docname)
13741385
doc.action_holders.set(PersonFactory.create_batch(3))
1375-
1386+
some_other_chair = RoleFactory(name_id="chair").person
1387+
13761388
url = urlreverse('ietf.doc.views_doc.remind_action_holders', kwargs=dict(name=doc.name))
13771389

1390+
login_testing_unauthorized(self, some_other_chair.user.username, url) # other chair can't send reminder
13781391
login_testing_unauthorized(self, username, url)
13791392
r = self.client.get(url)
13801393
self.assertEqual(r.status_code, 200)
@@ -1401,6 +1414,14 @@ def do_doc_remind_action_holders_test(self, username):
14011414
self.client.post(url)
14021415
self.assertEqual(len(outbox), 1) # still 1
14031416

1417+
def test_doc_remind_action_holders_as_doc_manager(self):
1418+
# create a test RoleName and put it in the docman_roles for the document group
1419+
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
1420+
self.doc_group.features.docman_roles.append("wrangler")
1421+
self.doc_group.features.save()
1422+
wrangler = RoleFactory(group=self.doc_group, name_id="wrangler").person
1423+
self.do_doc_remind_action_holders_test(wrangler.user.username)
1424+
14041425
def test_doc_remind_action_holders_as_ad(self):
14051426
self.do_doc_remind_action_holders_test('ad')
14061427

ietf/doc/views_doc.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from pathlib import Path
4343

4444
from django.core.cache import caches
45+
from django.core.exceptions import PermissionDenied
4546
from django.db.models import Max
4647
from django.http import HttpResponse, Http404, HttpResponseBadRequest
4748
from django.shortcuts import render, get_object_or_404, redirect
@@ -403,6 +404,10 @@ def document_main(request, name, rev=None, document_html=False):
403404

404405
can_edit_replaces = has_role(request.user, ("Area Director", "Secretariat", "IRTF Chair", "WG Chair", "RG Chair", "WG Secretary", "RG Secretary"))
405406

407+
can_edit_action_holders = can_edit or (
408+
request.user.is_authenticated and group.has_role(request.user, group.features.docman_roles)
409+
)
410+
406411
is_author = request.user.is_authenticated and doc.documentauthor_set.filter(person__user=request.user).exists()
407412
can_view_possibly_replaces = can_edit_replaces or is_author
408413

@@ -660,6 +665,7 @@ def document_main(request, name, rev=None, document_html=False):
660665
can_edit_iana_state=can_edit_iana_state,
661666
can_edit_consensus=can_edit_consensus,
662667
can_edit_replaces=can_edit_replaces,
668+
can_edit_action_holders=can_edit_action_holders,
663669
can_view_possibly_replaces=can_view_possibly_replaces,
664670
can_request_review=can_request_review,
665671
can_submit_unsolicited_review_for_teams=can_submit_unsolicited_review_for_teams,
@@ -1871,11 +1877,21 @@ def add_fields(self, form, index):
18711877
})
18721878

18731879

1874-
@role_required('Area Director', 'Secretariat')
1880+
@login_required
18751881
def edit_action_holders(request, name):
18761882
"""Change the set of action holders for a doc"""
18771883
doc = get_object_or_404(Document, name=name)
1878-
1884+
1885+
can_edit = has_role(request.user, ("Area Director", "Secretariat")) or (
1886+
doc.group and doc.group.has_role(request.user, doc.group.features.docman_roles)
1887+
)
1888+
if not can_edit:
1889+
# Keep the list of roles in this message up-to-date with the can_edit logic
1890+
message = "Restricted to roles: Area Director, Secretariat"
1891+
if doc.group and doc.group.acronym != "none":
1892+
message += f", and document managers for the {doc.group.acronym} group"
1893+
raise PermissionDenied(message)
1894+
18791895
if request.method == 'POST':
18801896
form = ActionHoldersForm(request.POST)
18811897
if form.is_valid():
@@ -1985,10 +2001,20 @@ class ReminderEmailForm(forms.Form):
19852001
strip=True,
19862002
)
19872003

1988-
@role_required('Area Director', 'Secretariat')
2004+
@login_required
19892005
def remind_action_holders(request, name):
19902006
doc = get_object_or_404(Document, name=name)
1991-
2007+
2008+
can_edit = has_role(request.user, ("Area Director", "Secretariat")) or (
2009+
doc.group and doc.group.has_role(request.user, doc.group.features.docman_roles)
2010+
)
2011+
if not can_edit:
2012+
# Keep the list of roles in this message up-to-date with the can_edit logic
2013+
message = "Restricted to roles: Area Director, Secretariat"
2014+
if doc.group and doc.group.acronym != "none":
2015+
message += f", and document managers for the {doc.group.acronym} group"
2016+
raise PermissionDenied(message)
2017+
19922018
if request.method == 'POST':
19932019
form = ReminderEmailForm(request.POST)
19942020
if form.is_valid():

ietf/templates/doc/document_draft.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@
304304
Action Holder{{ doc.documentactionholder_set.all|pluralize }}
305305
</th>
306306
<td class="edit">
307-
{% if can_edit %}
307+
{% if can_edit_action_holders %}
308308
<a class="btn btn-primary btn-sm"
309309
href="{% url 'ietf.doc.views_doc.edit_action_holders' name=doc.name %}">
310310
Edit
@@ -319,7 +319,7 @@
319319
{% person_link action_holder.person title=action_holder.role_for_doc %} {{ action_holder|action_holder_badge }}
320320
</div>
321321
{% endfor %}
322-
{% if can_edit %}
322+
{% if can_edit_action_holders %}
323323
<a class="btn btn-primary btn-sm mt-3"
324324
href="{% url "ietf.doc.views_doc.remind_action_holders" name=doc.name %}">
325325
<i class="bi bi-envelope">

0 commit comments

Comments
 (0)