Repository: ietf-tools/datatracker

Issue:
secretaries (review team and the secretariat) should be able to add comments to a review requests's history
See pages like https://datatracker.ietf.org/doc/draft-ietf-dnssd-srp/reviewrequest/17822/

Secretaries should be able to add history comments.


Discussion:
cc: @cindymorgan

Resolving patch:
diff --git a/ietf/doc/urls_review.py b/ietf/doc/urls_review.py
--- a/ietf/doc/urls_review.py
+++ b/ietf/doc/urls_review.py
@@ -9,6 +9,7 @@
     url(r'^(?P<request_id>[0-9]+)/$', views_review.review_request),
     url(r'^(?P<request_id>[0-9]+)/login/$', views_review.review_request_forced_login),
     url(r'^(?P<request_id>[0-9]+)/close/$', views_review.close_request),
+    url(r'^(?P<request_id>[0-9]+)/addrequestcomment/$', views_review.add_request_comment),
     url(r'^(?P<request_id>[0-9]+)/assignreviewer/$', views_review.assign_reviewer),
     url(r'^(?P<assignment_id>[0-9]+)/rejectreviewerassignment/$', views_review.reject_reviewer_assignment),
     url(r'^(?P<assignment_id>[0-9]+)/complete/$', views_review.complete_review),
diff --git a/ietf/doc/views_review.py b/ietf/doc/views_review.py
--- a/ietf/doc/views_review.py
+++ b/ietf/doc/views_review.py
@@ -219,6 +219,8 @@ def review_request(request, name, request_id):
     
     can_edit_deadline = can_edit_comment
 
+    can_add_comment = can_manage_request
+
     assignments = review_req.reviewassignment_set.all()
     for assignment in assignments:
         assignment.is_reviewer = user_is_person(request.user, assignment.reviewer.person)
@@ -260,6 +262,7 @@ def review_request(request, name, request_id):
         'can_assign_reviewer': can_assign_reviewer,
         'can_edit_comment': can_edit_comment,
         'can_edit_deadline': can_edit_deadline,
+        'can_add_comment': can_add_comment,
         'assignments': assignments,
         'wg_chairs': wg_chairs,
         'iesg_state_summary': iesg_state_summary,
@@ -310,6 +313,31 @@ def close_request(request, name, request_id):
         'form': form,
     })
 
+class AddCommentForm(forms.Form):
+    comment = forms.CharField(required=True, widget=forms.Textarea, strip=False)
+
+@login_required
+def add_request_comment(request, name, request_id):
+    doc = get_object_or_404(Document, name=name)
+    review_req = get_object_or_404(ReviewRequest, pk=request_id)
+
+    can_request = is_authorized_in_doc_stream(request.user, doc)
+    can_manage_request = can_manage_review_requests_for_team(request.user, review_req.team)
+    if not (can_request or can_manage_request):
+        permission_denied(request, "You do not have permission to perform this action")
+
+    if request.method == "POST":
+        form = AddCommentForm(request.POST)
+        if form.is_valid():
+            c = form.cleaned_data['comment']
+
+            review_req.add_history(c)
+            return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk)
+    else:
+        form = AddCommentForm()
+
+    return render(request, 'doc/add_comment.html',
+                  dict(doc=doc, form=form, review_req=review_req))
 
 class AssignReviewerForm(forms.Form):
     reviewer = PersonEmailChoiceField(label="Assign Additional Reviewer", empty_label="(None)")
diff --git a/ietf/review/models.py b/ietf/review/models.py
--- a/ietf/review/models.py
+++ b/ietf/review/models.py
@@ -143,6 +143,10 @@ def all_completed_assignments_for_doc(self):
     def request_closed_time(self):
         return self.doc.request_closed_time(self) or self.time
 
+    def add_history(self, description):
+        self._change_reason = description
+        self.save()
+
 class ReviewAssignment(models.Model):
     """ One of possibly many reviews assigned in response to a ReviewRequest """
     history        = HistoricalRecords(history_change_reason_field=models.TextField(null=True))


Test patch:
diff --git a/ietf/review/tests.py b/ietf/review/tests.py
--- a/ietf/review/tests.py
+++ b/ietf/review/tests.py
@@ -1,10 +1,14 @@
 # Copyright The IETF Trust 2019-2020, All Rights Reserved
 # -*- coding: utf-8 -*-
 import datetime
+import debug # pyflakes:ignore
 
+from pyquery import PyQuery
 from ietf.group.factories import RoleFactory
+from ietf.doc.factories import WgDraftFactory
 from ietf.utils.mail import empty_outbox, get_payload_text, outbox
 from ietf.utils.test_utils import TestCase, reload_db_objects
+from ietf.utils.test_utils import login_testing_unauthorized, unicontent
 from ietf.utils.timezone import date_today, datetime_from_date
 from .factories import ReviewAssignmentFactory, ReviewRequestFactory, ReviewerSettingsFactory
 from .mailarch import hash_list_message_id
@@ -14,6 +18,7 @@
                     send_reminder_unconfirmed_assignments, send_review_reminder_overdue_assignment,
                     send_reminder_all_open_reviews, send_unavailability_period_ending_reminder,
                     ORIGIN_DATE_PERIODIC_REMINDERS)
+from django.urls import reverse as urlreverse
 
 class HashTest(TestCase):
 
@@ -508,3 +513,40 @@ def test_send_reminder_all_open_reviews(self):
         self.assertTrue(self.reviewer.email_address() in log[0])
         self.assertTrue('1 open review' in log[0])
 
+class AddReviewCommentTestCase(TestCase):
+    def test_review_add_comment(self):
+        draft = WgDraftFactory(name='draft-ietf-mars-test',group__acronym='mars')
+        review_req = ReviewRequestFactory(doc=draft, state_id='assigned')
+        ReviewAssignmentFactory(review_request=review_req, state_id='assigned')
+
+        url_post = urlreverse('ietf.doc.views_review.add_request_comment', kwargs=dict(name=draft.name, request_id=review_req.pk))
+        url_page = urlreverse('ietf.doc.views_review.review_request', kwargs=dict(name=draft.name, request_id=review_req.pk))
+
+        login_testing_unauthorized(self, "secretary", url_post)
+
+        # Check that we do not have entry on the page
+        r = self.client.get(url_page)
+        self.assertEqual(r.status_code, 200)
+        # Needs to have history
+        self.assertContains(r, 'History')
+        # But can't have the comment we are goint to add.
+        self.assertNotContains(r, 'This is a test.')
+
+        # Get the form
+        r = self.client.get(url_post)
+        self.assertEqual(r.status_code, 200)
+        q = PyQuery(unicontent(r))
+        self.assertEqual(len(q('form textarea[name=comment]')), 1)
+
+        # Post the comment
+        r = self.client.post(url_post, dict(comment="This is a test."))
+        self.assertEqual(r.status_code, 302)
+
+        # Get the main page again
+        r = self.client.get(url_page)
+        self.assertEqual(r.status_code, 200)
+        # Needs to have history
+        self.assertContains(r, 'History')
+        # But can't have the comment we are goint to add.
+        self.assertContains(r, 'This is a test.')
+