diff --git a/ietf/doc/tests_conflict_review.py b/ietf/doc/tests_conflict_review.py index 6fa1a4d9b6..a442eaffd5 100644 --- a/ietf/doc/tests_conflict_review.py +++ b/ietf/doc/tests_conflict_review.py @@ -1,4 +1,4 @@ -# Copyright The IETF Trust 2012-2020, All Rights Reserved +# Copyright The IETF Trust 2012-2023, All Rights Reserved # -*- coding: utf-8 -*- @@ -168,6 +168,21 @@ def test_change_state(self): self.assertTrue(review_doc.active_ballot()) self.assertEqual(review_doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position").pos_id,'yes') + # try to change to an AD-forbidden state + appr_noprob_sent_pk = str(State.objects.get(used=True, slug='appr-noprob-sent',type__slug='conflrev').pk) + r = self.client.post(url,dict(review_state=appr_noprob_sent_pk,comment='xyzzy')) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(q('form .invalid-feedback')) + + # try again as secretariat + self.client.logout() + login_testing_unauthorized(self, 'secretary', url) + r = self.client.post(url,dict(review_state=appr_noprob_sent_pk,comment='xyzzy')) + self.assertEqual(r.status_code, 302) + review_doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission') + self.assertEqual(review_doc.get_state('conflrev').slug, 'appr-noprob-sent') + def test_edit_notices(self): doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission') diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 85efbf1a6c..b12e8133b3 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -98,7 +98,7 @@ def test_change_state(self): draft.action_holders.add(ad) url = urlreverse('ietf.doc.views_draft.change_state', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "secretary", url) + login_testing_unauthorized(self, "ad", url) first_state = draft.get_state("draft-iesg") next_states = first_state.next_states.all() @@ -154,6 +154,20 @@ def test_change_state(self): q = PyQuery(r.content) self.assertEqual(len(q('form [type=submit]:contains("%s")' % first_state.name)), 1) + # try to change to an AD-forbidden state + r = self.client.post(url, dict(state=State.objects.get(used=True, type='draft-iesg', slug='ann').pk, comment='Test comment')) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(q('form .invalid-feedback')) + + # try again as secretariat + self.client.logout() + login_testing_unauthorized(self, 'secretary', url) + r = self.client.post(url, dict(state=State.objects.get(used=True, type='draft-iesg', slug='ann').pk, comment='Test comment')) + self.assertEqual(r.status_code, 302) + draft = Document.objects.get(name=draft.name) + self.assertEqual(draft.get_state_slug('draft-iesg'), 'ann') + def test_pull_from_rfc_queue(self): ad = Person.objects.get(user__username="ad") draft = WgDraftFactory( diff --git a/ietf/doc/tests_status_change.py b/ietf/doc/tests_status_change.py index 4064b52523..7afd5e9d29 100644 --- a/ietf/doc/tests_status_change.py +++ b/ietf/doc/tests_status_change.py @@ -147,6 +147,21 @@ def test_change_state(self): self.assertTrue(doc.active_ballot()) self.assertEqual(doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position").pos_id,'yes') + # try to change to an AD-forbidden state + appr_sent_pk = str(State.objects.get(used=True, slug='appr-sent',type__slug='statchg').pk) + r = self.client.post(url, dict(new_state=appr_sent_pk, comment='xyzzy')) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(q('form .invalid-feedback')) + + # try again as secretariat + self.client.logout() + login_testing_unauthorized(self, 'secretary', url) + r = self.client.post(url, dict(new_state=appr_sent_pk, comment='xyzzy')) + self.assertEqual(r.status_code, 302) + doc = Document.objects.get(name='status-change-imaginary-mid-review') + self.assertEqual(doc.get_state('statchg').slug, 'appr-sent') + def test_edit_notices(self): doc = Document.objects.get(name='status-change-imaginary-mid-review') url = urlreverse('ietf.doc.views_doc.edit_notify;status-change',kwargs=dict(name=doc.name)) diff --git a/ietf/doc/views_conflict_review.py b/ietf/doc/views_conflict_review.py index 5efd62dbe9..2c22c82a46 100644 --- a/ietf/doc/views_conflict_review.py +++ b/ietf/doc/views_conflict_review.py @@ -1,4 +1,4 @@ -# Copyright The IETF Trust 2012-2020, All Rights Reserved +# Copyright The IETF Trust 2012-2023, All Rights Reserved # -*- coding: utf-8 -*- @@ -35,6 +35,12 @@ class ChangeStateForm(forms.Form): review_state = forms.ModelChoiceField(State.objects.filter(used=True, type="conflrev"), label="Conflict review state", empty_label=None, required=True) comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the review history.", required=False, strip=False) + def __init__(self, *args, **kwargs): + user = kwargs.pop("user") + super(ChangeStateForm, self).__init__(*args, **kwargs) + if not has_role(user, "Secretariat"): + self.fields["review_state"].queryset = self.fields["review_state"].queryset.exclude(slug__in=("appr-reqnopub-sent","appr-noprob-sent")) + @role_required("Area Director", "Secretariat") def change_state(request, name, option=None): """Change state of an IESG review for IETF conflicts in other stream's documents, notifying parties as necessary @@ -44,7 +50,7 @@ def change_state(request, name, option=None): login = request.user.person if request.method == 'POST': - form = ChangeStateForm(request.POST) + form = ChangeStateForm(request.POST, user=request.user) if form.is_valid(): clean = form.cleaned_data new_state = clean['review_state'] @@ -95,7 +101,7 @@ def change_state(request, name, option=None): else: s = review.get_state() init = dict(review_state=s.pk if s else None) - form = ChangeStateForm(initial=init) + form = ChangeStateForm(initial=init, user=request.user) return render(request, 'doc/change_state.html', dict(form=form, diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index a71a586edd..485b7b9ad3 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -1,4 +1,4 @@ -# Copyright The IETF Trust 2010-2020, All Rights Reserved +# Copyright The IETF Trust 2010-2023, All Rights Reserved # -*- coding: utf-8 -*- @@ -61,6 +61,12 @@ class ChangeStateForm(forms.Form): substate = forms.ModelChoiceField(DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS), required=False) comment = forms.CharField(widget=forms.Textarea, required=False, strip=False) + def __init__(self, *args, **kwargs): + user = kwargs.pop("user") + super(ChangeStateForm, self).__init__(*args, **kwargs) + if not has_role(user, "Secretariat"): + self.fields["state"].queryset = self.fields["state"].queryset.exclude(slug="ann") + def clean(self): retclean = self.cleaned_data state = self.cleaned_data.get('state', '(None)') @@ -94,7 +100,7 @@ def change_state(request, name): login = request.user.person if request.method == 'POST': - form = ChangeStateForm(request.POST) + form = ChangeStateForm(request.POST, user=request.user) form.docname=name if form.is_valid(): @@ -175,7 +181,8 @@ def change_state(request, name): state = doc.get_state("draft-iesg") t = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS) form = ChangeStateForm(initial=dict(state=state.pk if state else None, - substate=t[0].pk if t else None)) + substate=t[0].pk if t else None), + user=request.user) form.docname=name state = doc.get_state("draft-iesg") diff --git a/ietf/doc/views_status_change.py b/ietf/doc/views_status_change.py index 5f9b6090fc..6db4338f42 100644 --- a/ietf/doc/views_status_change.py +++ b/ietf/doc/views_status_change.py @@ -42,6 +42,12 @@ class ChangeStateForm(forms.Form): new_state = forms.ModelChoiceField(State.objects.filter(type="statchg", used=True), label="Status Change Evaluation State", empty_label=None, required=True) comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the review history.", required=False, strip=False) + def __init__(self, *args, **kwargs): + user = kwargs.pop("user") + super(ChangeStateForm, self).__init__(*args, **kwargs) + if not has_role(user, "Secretariat"): + self.fields["new_state"].queryset = self.fields["new_state"].queryset.exclude(slug="appr-sent") + @role_required("Area Director", "Secretariat") def change_state(request, name, option=None): @@ -52,7 +58,7 @@ def change_state(request, name, option=None): login = request.user.person if request.method == 'POST': - form = ChangeStateForm(request.POST) + form = ChangeStateForm(request.POST, user=request.user) if form.is_valid(): clean = form.cleaned_data new_state = clean['new_state'] @@ -116,7 +122,7 @@ def change_state(request, name, option=None): type='statchg', label='Status Change Evaluation State', ) - form = ChangeStateForm(initial=init) + form = ChangeStateForm(initial=init, user=request.user) return render(request, 'doc/change_state.html', dict(form=form,