feat: doc mgrs can edit/remind action holders (#7971)
* feat: doc mgrs can edit/remind action holders * test: test docman_roles, not "chair"
This commit is contained in:
parent
06b9df10ee
commit
5581dc79e1
|
@ -59,7 +59,7 @@ from ietf.meeting.models import Meeting, SessionPresentation, SchedulingEvent
|
||||||
from ietf.meeting.factories import ( MeetingFactory, SessionFactory, SessionPresentationFactory,
|
from ietf.meeting.factories import ( MeetingFactory, SessionFactory, SessionPresentationFactory,
|
||||||
ProceedingsMaterialFactory )
|
ProceedingsMaterialFactory )
|
||||||
|
|
||||||
from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName
|
from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName, RoleName
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.person.factories import PersonFactory, EmailFactory
|
from ietf.person.factories import PersonFactory, EmailFactory
|
||||||
from ietf.utils.mail import outbox, empty_outbox
|
from ietf.utils.mail import outbox, empty_outbox
|
||||||
|
@ -1450,6 +1450,14 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
"""Buttons for action holders should be shown when AD or secretary"""
|
"""Buttons for action holders should be shown when AD or secretary"""
|
||||||
draft = WgDraftFactory()
|
draft = WgDraftFactory()
|
||||||
draft.action_holders.set([PersonFactory()])
|
draft.action_holders.set([PersonFactory()])
|
||||||
|
other_group = GroupFactory(type_id=draft.group.type_id)
|
||||||
|
|
||||||
|
# create a test RoleName and put it in the docman_roles for the document group
|
||||||
|
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
|
||||||
|
draft.group.features.docman_roles.append("wrangler")
|
||||||
|
draft.group.features.save()
|
||||||
|
wrangler = RoleFactory(group=draft.group, name_id="wrangler").person
|
||||||
|
wrangler_of_other_group = RoleFactory(group=other_group, name_id="wrangler").person
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=draft.name))
|
url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=draft.name))
|
||||||
edit_ah_url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=draft.name))
|
edit_ah_url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=draft.name))
|
||||||
|
@ -1482,6 +1490,8 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
|
|
||||||
_run_test(None, False)
|
_run_test(None, False)
|
||||||
_run_test('plain', False)
|
_run_test('plain', False)
|
||||||
|
_run_test(wrangler_of_other_group.user.username, False)
|
||||||
|
_run_test(wrangler.user.username, True)
|
||||||
_run_test('ad', True)
|
_run_test('ad', True)
|
||||||
_run_test('secretary', True)
|
_run_test('secretary', True)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ from ietf.doc.models import ( Document, DocReminder, DocEvent,
|
||||||
WriteupDocEvent, DocRelationshipName, IanaExpertDocEvent )
|
WriteupDocEvent, DocRelationshipName, IanaExpertDocEvent )
|
||||||
from ietf.doc.utils import get_tags_for_stream_id, create_ballot_if_not_open
|
from ietf.doc.utils import get_tags_for_stream_id, create_ballot_if_not_open
|
||||||
from ietf.doc.views_draft import AdoptDraftForm
|
from ietf.doc.views_draft import AdoptDraftForm
|
||||||
from ietf.name.models import StreamName, DocTagName
|
from ietf.name.models import StreamName, DocTagName, RoleName
|
||||||
from ietf.group.factories import GroupFactory, RoleFactory
|
from ietf.group.factories import GroupFactory, RoleFactory
|
||||||
from ietf.group.models import Group, Role
|
from ietf.group.models import Group, Role
|
||||||
from ietf.person.factories import PersonFactory, EmailFactory
|
from ietf.person.factories import PersonFactory, EmailFactory
|
||||||
|
@ -935,6 +935,7 @@ class IndividualInfoFormsTests(TestCase):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
doc = WgDraftFactory(group__acronym='mars',shepherd=PersonFactory(user__username='plain',name='Plain Man').email_set.first())
|
doc = WgDraftFactory(group__acronym='mars',shepherd=PersonFactory(user__username='plain',name='Plain Man').email_set.first())
|
||||||
self.docname = doc.name
|
self.docname = doc.name
|
||||||
|
self.doc_group = doc.group
|
||||||
|
|
||||||
def test_doc_change_stream(self):
|
def test_doc_change_stream(self):
|
||||||
url = urlreverse('ietf.doc.views_draft.change_stream', kwargs=dict(name=self.docname))
|
url = urlreverse('ietf.doc.views_draft.change_stream', kwargs=dict(name=self.docname))
|
||||||
|
@ -1319,8 +1320,10 @@ class IndividualInfoFormsTests(TestCase):
|
||||||
RoleFactory(name_id='techadv', person=PersonFactory(), group=doc.group)
|
RoleFactory(name_id='techadv', person=PersonFactory(), group=doc.group)
|
||||||
RoleFactory(name_id='editor', person=PersonFactory(), group=doc.group)
|
RoleFactory(name_id='editor', person=PersonFactory(), group=doc.group)
|
||||||
RoleFactory(name_id='secr', person=PersonFactory(), group=doc.group)
|
RoleFactory(name_id='secr', person=PersonFactory(), group=doc.group)
|
||||||
|
some_other_chair = RoleFactory(name_id="chair").person
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=doc.name))
|
url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=doc.name))
|
||||||
|
login_testing_unauthorized(self, some_other_chair.user.username, url) # other chair can't edit action holders
|
||||||
login_testing_unauthorized(self, username, url)
|
login_testing_unauthorized(self, username, url)
|
||||||
|
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -1363,6 +1366,14 @@ class IndividualInfoFormsTests(TestCase):
|
||||||
_test_changing_ah(doc.authors(), 'authors can do it, too')
|
_test_changing_ah(doc.authors(), 'authors can do it, too')
|
||||||
_test_changing_ah([], 'clear it back out')
|
_test_changing_ah([], 'clear it back out')
|
||||||
|
|
||||||
|
def test_doc_change_action_holders_as_doc_manager(self):
|
||||||
|
# create a test RoleName and put it in the docman_roles for the document group
|
||||||
|
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
|
||||||
|
self.doc_group.features.docman_roles.append("wrangler")
|
||||||
|
self.doc_group.features.save()
|
||||||
|
wrangler = RoleFactory(group=self.doc_group, name_id="wrangler").person
|
||||||
|
self.do_doc_change_action_holders_test(wrangler.user.username)
|
||||||
|
|
||||||
def test_doc_change_action_holders_as_secretary(self):
|
def test_doc_change_action_holders_as_secretary(self):
|
||||||
self.do_doc_change_action_holders_test('secretary')
|
self.do_doc_change_action_holders_test('secretary')
|
||||||
|
|
||||||
|
@ -1372,9 +1383,11 @@ class IndividualInfoFormsTests(TestCase):
|
||||||
def do_doc_remind_action_holders_test(self, username):
|
def do_doc_remind_action_holders_test(self, username):
|
||||||
doc = Document.objects.get(name=self.docname)
|
doc = Document.objects.get(name=self.docname)
|
||||||
doc.action_holders.set(PersonFactory.create_batch(3))
|
doc.action_holders.set(PersonFactory.create_batch(3))
|
||||||
|
some_other_chair = RoleFactory(name_id="chair").person
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_doc.remind_action_holders', kwargs=dict(name=doc.name))
|
url = urlreverse('ietf.doc.views_doc.remind_action_holders', kwargs=dict(name=doc.name))
|
||||||
|
|
||||||
|
login_testing_unauthorized(self, some_other_chair.user.username, url) # other chair can't send reminder
|
||||||
login_testing_unauthorized(self, username, url)
|
login_testing_unauthorized(self, username, url)
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -1401,6 +1414,14 @@ class IndividualInfoFormsTests(TestCase):
|
||||||
self.client.post(url)
|
self.client.post(url)
|
||||||
self.assertEqual(len(outbox), 1) # still 1
|
self.assertEqual(len(outbox), 1) # still 1
|
||||||
|
|
||||||
|
def test_doc_remind_action_holders_as_doc_manager(self):
|
||||||
|
# create a test RoleName and put it in the docman_roles for the document group
|
||||||
|
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
|
||||||
|
self.doc_group.features.docman_roles.append("wrangler")
|
||||||
|
self.doc_group.features.save()
|
||||||
|
wrangler = RoleFactory(group=self.doc_group, name_id="wrangler").person
|
||||||
|
self.do_doc_remind_action_holders_test(wrangler.user.username)
|
||||||
|
|
||||||
def test_doc_remind_action_holders_as_ad(self):
|
def test_doc_remind_action_holders_as_ad(self):
|
||||||
self.do_doc_remind_action_holders_test('ad')
|
self.do_doc_remind_action_holders_test('ad')
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core.cache import caches
|
from django.core.cache import caches
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.http import HttpResponse, Http404, HttpResponseBadRequest
|
from django.http import HttpResponse, Http404, HttpResponseBadRequest
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
|
@ -403,6 +404,10 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
|
|
||||||
can_edit_replaces = has_role(request.user, ("Area Director", "Secretariat", "IRTF Chair", "WG Chair", "RG Chair", "WG Secretary", "RG Secretary"))
|
can_edit_replaces = has_role(request.user, ("Area Director", "Secretariat", "IRTF Chair", "WG Chair", "RG Chair", "WG Secretary", "RG Secretary"))
|
||||||
|
|
||||||
|
can_edit_action_holders = can_edit or (
|
||||||
|
request.user.is_authenticated and group.has_role(request.user, group.features.docman_roles)
|
||||||
|
)
|
||||||
|
|
||||||
is_author = request.user.is_authenticated and doc.documentauthor_set.filter(person__user=request.user).exists()
|
is_author = request.user.is_authenticated and doc.documentauthor_set.filter(person__user=request.user).exists()
|
||||||
can_view_possibly_replaces = can_edit_replaces or is_author
|
can_view_possibly_replaces = can_edit_replaces or is_author
|
||||||
|
|
||||||
|
@ -660,6 +665,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
can_edit_iana_state=can_edit_iana_state,
|
can_edit_iana_state=can_edit_iana_state,
|
||||||
can_edit_consensus=can_edit_consensus,
|
can_edit_consensus=can_edit_consensus,
|
||||||
can_edit_replaces=can_edit_replaces,
|
can_edit_replaces=can_edit_replaces,
|
||||||
|
can_edit_action_holders=can_edit_action_holders,
|
||||||
can_view_possibly_replaces=can_view_possibly_replaces,
|
can_view_possibly_replaces=can_view_possibly_replaces,
|
||||||
can_request_review=can_request_review,
|
can_request_review=can_request_review,
|
||||||
can_submit_unsolicited_review_for_teams=can_submit_unsolicited_review_for_teams,
|
can_submit_unsolicited_review_for_teams=can_submit_unsolicited_review_for_teams,
|
||||||
|
@ -1871,11 +1877,21 @@ def edit_authors(request, name):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@role_required('Area Director', 'Secretariat')
|
@login_required
|
||||||
def edit_action_holders(request, name):
|
def edit_action_holders(request, name):
|
||||||
"""Change the set of action holders for a doc"""
|
"""Change the set of action holders for a doc"""
|
||||||
doc = get_object_or_404(Document, name=name)
|
doc = get_object_or_404(Document, name=name)
|
||||||
|
|
||||||
|
can_edit = has_role(request.user, ("Area Director", "Secretariat")) or (
|
||||||
|
doc.group and doc.group.has_role(request.user, doc.group.features.docman_roles)
|
||||||
|
)
|
||||||
|
if not can_edit:
|
||||||
|
# Keep the list of roles in this message up-to-date with the can_edit logic
|
||||||
|
message = "Restricted to roles: Area Director, Secretariat"
|
||||||
|
if doc.group and doc.group.acronym != "none":
|
||||||
|
message += f", and document managers for the {doc.group.acronym} group"
|
||||||
|
raise PermissionDenied(message)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ActionHoldersForm(request.POST)
|
form = ActionHoldersForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
@ -1985,10 +2001,20 @@ class ReminderEmailForm(forms.Form):
|
||||||
strip=True,
|
strip=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@role_required('Area Director', 'Secretariat')
|
@login_required
|
||||||
def remind_action_holders(request, name):
|
def remind_action_holders(request, name):
|
||||||
doc = get_object_or_404(Document, name=name)
|
doc = get_object_or_404(Document, name=name)
|
||||||
|
|
||||||
|
can_edit = has_role(request.user, ("Area Director", "Secretariat")) or (
|
||||||
|
doc.group and doc.group.has_role(request.user, doc.group.features.docman_roles)
|
||||||
|
)
|
||||||
|
if not can_edit:
|
||||||
|
# Keep the list of roles in this message up-to-date with the can_edit logic
|
||||||
|
message = "Restricted to roles: Area Director, Secretariat"
|
||||||
|
if doc.group and doc.group.acronym != "none":
|
||||||
|
message += f", and document managers for the {doc.group.acronym} group"
|
||||||
|
raise PermissionDenied(message)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ReminderEmailForm(request.POST)
|
form = ReminderEmailForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
|
@ -304,7 +304,7 @@
|
||||||
Action Holder{{ doc.documentactionholder_set.all|pluralize }}
|
Action Holder{{ doc.documentactionholder_set.all|pluralize }}
|
||||||
</th>
|
</th>
|
||||||
<td class="edit">
|
<td class="edit">
|
||||||
{% if can_edit %}
|
{% if can_edit_action_holders %}
|
||||||
<a class="btn btn-primary btn-sm"
|
<a class="btn btn-primary btn-sm"
|
||||||
href="{% url 'ietf.doc.views_doc.edit_action_holders' name=doc.name %}">
|
href="{% url 'ietf.doc.views_doc.edit_action_holders' name=doc.name %}">
|
||||||
Edit
|
Edit
|
||||||
|
@ -319,7 +319,7 @@
|
||||||
{% person_link action_holder.person title=action_holder.role_for_doc %} {{ action_holder|action_holder_badge }}
|
{% person_link action_holder.person title=action_holder.role_for_doc %} {{ action_holder|action_holder_badge }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if can_edit %}
|
{% if can_edit_action_holders %}
|
||||||
<a class="btn btn-primary btn-sm mt-3"
|
<a class="btn btn-primary btn-sm mt-3"
|
||||||
href="{% url "ietf.doc.views_doc.remind_action_holders" name=doc.name %}">
|
href="{% url "ietf.doc.views_doc.remind_action_holders" name=doc.name %}">
|
||||||
<i class="bi bi-envelope">
|
<i class="bi bi-envelope">
|
||||||
|
|
Loading…
Reference in a new issue