diff --git a/ietf/doc/tests_review.py b/ietf/doc/tests_review.py index a03b3c060..a19717633 100644 --- a/ietf/doc/tests_review.py +++ b/ietf/doc/tests_review.py @@ -14,7 +14,7 @@ import debug # pyflakes:ignore from ietf.review.models import ReviewRequest, ReviewTeamResult import ietf.review.mailarch -from ietf.person.models import Email +from ietf.person.models import Email, Person from ietf.name.models import ReviewResultName, ReviewRequestStateName from ietf.utils.test_utils import TestCase from ietf.utils.test_data import make_test_data, make_review_data @@ -57,7 +57,8 @@ class ReviewTests(TestCase): "type": "early", "team": review_team.pk, "deadline_date": deadline_date.isoformat(), - "requested_rev": "01" + "requested_rev": "01", + "requested_by": Person.objects.get(user__username="plain").pk, }) self.assertEqual(r.status_code, 302) @@ -82,13 +83,13 @@ class ReviewTests(TestCase): self.assertEqual(r.status_code, 200) self.assertTrue(review_req.team.acronym.upper() in unicontent(r)) - def test_withdraw_request(self): + def test_close_request(self): doc = make_test_data() review_req = make_review_data(doc) review_req.state = ReviewRequestStateName.objects.get(slug="accepted") review_req.save() - withdraw_url = urlreverse('ietf.doc.views_review.withdraw_request', kwargs={ "name": doc.name, "request_id": review_req.pk }) + close_url = urlreverse('ietf.doc.views_review.close_request', kwargs={ "name": doc.name, "request_id": review_req.pk }) # follow link @@ -96,26 +97,26 @@ class ReviewTests(TestCase): self.client.login(username="secretary", password="secretary+password") r = self.client.get(req_url) self.assertEqual(r.status_code, 200) - self.assertTrue(withdraw_url in unicontent(r)) + self.assertTrue(close_url in unicontent(r)) self.client.logout() - # get withdraw page - login_testing_unauthorized(self, "secretary", withdraw_url) - r = self.client.get(withdraw_url) + # get close page + login_testing_unauthorized(self, "secretary", close_url) + r = self.client.get(close_url) self.assertEqual(r.status_code, 200) - # withdraw + # close empty_outbox() - r = self.client.post(withdraw_url, { "action": "withdraw" }) + r = self.client.post(close_url, { "close_reason": "withdrawn" }) self.assertEqual(r.status_code, 302) review_req = reload_db_objects(review_req) self.assertEqual(review_req.state_id, "withdrawn") e = doc.latest_event() self.assertEqual(e.type, "changed_review_request") - self.assertTrue("Withdrew" in e.desc) + self.assertTrue("closed" in e.desc.lower()) self.assertEqual(len(outbox), 1) - self.assertTrue("withdrawn" in unicode(outbox[0])) + self.assertTrue("closed" in unicode(outbox[0]).lower()) def test_assign_reviewer(self): doc = make_test_data() diff --git a/ietf/doc/urls_review.py b/ietf/doc/urls_review.py index 89be3f732..0085d6add 100644 --- a/ietf/doc/urls_review.py +++ b/ietf/doc/urls_review.py @@ -4,7 +4,7 @@ from ietf.doc import views_review urlpatterns = patterns('', url(r'^$', views_review.request_review), url(r'^(?P[0-9]+)/$', views_review.review_request), - url(r'^(?P[0-9]+)/withdraw/$', views_review.withdraw_request), + url(r'^(?P[0-9]+)/close/$', views_review.close_request), url(r'^(?P[0-9]+)/assignreviewer/$', views_review.assign_reviewer), url(r'^(?P[0-9]+)/rejectreviewerassignment/$', views_review.reject_reviewer_assignment), url(r'^(?P[0-9]+)/complete/$', views_review.complete_review), diff --git a/ietf/doc/views_review.py b/ietf/doc/views_review.py index 6d5fb1d0d..e6400516f 100644 --- a/ietf/doc/views_review.py +++ b/ietf/doc/views_review.py @@ -10,13 +10,14 @@ from django.core.exceptions import ValidationError from django.template.loader import render_to_string from ietf.doc.models import Document, NewRevisionDocEvent, DocEvent, State, DocAlias -from ietf.ietfauth.utils import is_authorized_in_doc_stream, user_is_person +from ietf.ietfauth.utils import is_authorized_in_doc_stream, user_is_person, has_role from ietf.name.models import ReviewRequestStateName, ReviewResultName, DocTypeName from ietf.review.models import ReviewRequest -from ietf.person.fields import PersonEmailChoiceField +from ietf.person.fields import PersonEmailChoiceField, SearchablePersonField from ietf.review.utils import (active_review_teams, assign_review_request_to_reviewer, can_request_review_of_doc, can_manage_review_requests_for_team, - email_about_review_request, make_new_review_request_from_existing) + email_review_request_change, make_new_review_request_from_existing, + close_review_request_states, close_review_request) from ietf.review import mailarch from ietf.utils.fields import DatepickerDateField from ietf.utils.text import skip_prefix @@ -38,7 +39,7 @@ class RequestReviewForm(forms.ModelForm): class Meta: model = ReviewRequest - fields = ('type', 'team', 'deadline', 'requested_rev') + fields = ('requested_by', 'type', 'team', 'deadline', 'requested_rev') def __init__(self, user, doc, *args, **kwargs): super(RequestReviewForm, self).__init__(*args, **kwargs) @@ -57,6 +58,12 @@ class RequestReviewForm(forms.ModelForm): self.fields["deadline"].required = False self.fields["requested_rev"].label = "Document revision" + if has_role(user, "Secretariat"): + self.fields["requested_by"] = SearchablePersonField() + else: + self.fields["requested_by"].widget = forms.HiddenInput() + self.fields["requested_by"].initial = user.person.pk + def clean_deadline_date(self): v = self.cleaned_data.get('deadline_date') if v < datetime.date.today(): @@ -119,9 +126,9 @@ def review_request(request, name, request_id): is_reviewer = review_req.reviewer and user_is_person(request.user, review_req.reviewer.person) can_manage_request = can_manage_review_requests_for_team(request.user, review_req.team) - can_withdraw_request = (review_req.state_id in ["requested", "accepted"] - and (is_authorized_in_doc_stream(request.user, doc) - or can_manage_request)) + can_close_request = (review_req.state_id in ["requested", "accepted"] + and (is_authorized_in_doc_stream(request.user, doc) + or can_manage_request)) can_assign_reviewer = (review_req.state_id in ["requested", "accepted"] and can_manage_request) @@ -147,47 +154,55 @@ def review_request(request, name, request_id): return render(request, 'doc/review/review_request.html', { 'doc': doc, 'review_req': review_req, - 'can_withdraw_request': can_withdraw_request, + 'can_close_request': can_close_request, 'can_reject_reviewer_assignment': can_reject_reviewer_assignment, 'can_assign_reviewer': can_assign_reviewer, 'can_accept_reviewer_assignment': can_accept_reviewer_assignment, 'can_complete_review': can_complete_review, }) + +class CloseReviewRequestForm(forms.Form): + close_reason = forms.ModelChoiceField(queryset=close_review_request_states(), widget=forms.RadioSelect, empty_label=None) + + def __init__(self, can_manage_request, *args, **kwargs): + super(CloseReviewRequestForm, self).__init__(*args, **kwargs) + + if not can_manage_request: + self.fields["close_reason"].queryset = self.fields["close_reason"].queryset.filter(slug__in=["withdrawn"]) + + if len(self.fields["close_reason"].queryset) == 1: + self.fields["close_reason"].initial = self.fields["close_reason"].queryset.first().pk + self.fields["close_reason"].widget = forms.HiddenInput() + + @login_required -def withdraw_request(request, name, request_id): +def close_request(request, name, request_id): doc = get_object_or_404(Document, name=name) review_req = get_object_or_404(ReviewRequest, pk=request_id, state__in=["requested", "accepted"]) - if not is_authorized_in_doc_stream(request.user, doc): + 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): return HttpResponseForbidden("You do not have permission to perform this action") - if request.method == "POST" and request.POST.get("action") == "withdraw": - prev_state = review_req.state - review_req.state = ReviewRequestStateName.objects.get(slug="withdrawn") - review_req.save() - - DocEvent.objects.create( - type="changed_review_request", - doc=doc, - by=request.user.person, - desc="Withdrew request for {} review by {}".format(review_req.type.name, review_req.team.acronym.upper()), - ) - - if prev_state.slug != "requested": - email_about_review_request( - request, review_req, - "Withdrew review request for %s" % review_req.doc.name, - "Review request has been withdrawn by %s." % request.user.person, - by=request.user.person, notify_secretary=False, notify_reviewer=True) + if request.method == "POST": + form = CloseReviewRequestForm(can_manage_request, request.POST) + if form.is_valid(): + close_review_request(request, review_req, form.cleaned_data["close_reason"]) return redirect(review_request, name=review_req.doc.name, request_id=review_req.pk) + else: + form = CloseReviewRequestForm(can_manage_request) - return render(request, 'doc/review/withdraw_request.html', { + return render(request, 'doc/review/close_request.html', { 'doc': doc, 'review_req': review_req, + 'form': form, }) + class AssignReviewerForm(forms.Form): reviewer = PersonEmailChoiceField(widget=forms.RadioSelect, empty_label="(None)", required=False) @@ -266,7 +281,7 @@ def reject_reviewer_assignment(request, name, request_id): "message_to_secretary": form.cleaned_data.get("message_to_secretary") }) - email_about_review_request(request, review_req, "Reviewer assignment rejected", msg, by=request.user.person, notify_secretary=True, notify_reviewer=True) + email_review_request_change(request, review_req, "Reviewer assignment rejected", msg, by=request.user.person, notify_secretary=True, notify_reviewer=True, notify_requested_by=False) return redirect(review_request, name=new_review_req.doc.name, request_id=new_review_req.pk) else: @@ -438,7 +453,7 @@ def complete_review(request, name, request_id): "new_review_req": new_review_req, }) - email_about_review_request(request, review_req, subject, msg, request.user.person, notify_secretary=True, notify_reviewer=False) + email_review_request_change(request, review_req, subject, msg, request.user.person, notify_secretary=True, notify_reviewer=False, notify_requested_by=False) if review_submission != "link" and review_req.team.list_email: # email the review diff --git a/ietf/group/tests_review.py b/ietf/group/tests_review.py index 6dc5d5147..8420894b7 100644 --- a/ietf/group/tests_review.py +++ b/ietf/group/tests_review.py @@ -7,7 +7,7 @@ from django.core.urlresolvers import reverse as urlreverse from ietf.utils.test_data import make_test_data, make_review_data from ietf.utils.test_utils import login_testing_unauthorized, TestCase, unicontent, reload_db_objects from ietf.review.models import ReviewRequest -from ietf.person.models import Email +from ietf.person.models import Email, Person import ietf.group.views_review class ReviewTests(TestCase): @@ -28,6 +28,7 @@ class ReviewTests(TestCase): deadline=datetime.datetime.combine(datetime.date.today() + datetime.timedelta(days=30), datetime.time(23, 59, 59)), state_id="accepted", reviewer=review_req1.reviewer, + requested_by=Person.objects.get(user__username="plain"), ) review_req3 = ReviewRequest.objects.create( @@ -36,6 +37,7 @@ class ReviewTests(TestCase): type_id="early", deadline=datetime.datetime.combine(datetime.date.today() + datetime.timedelta(days=30), datetime.time(23, 59, 59)), state_id="requested", + requested_by=Person.objects.get(user__username="plain"), ) # get diff --git a/ietf/group/views_review.py b/ietf/group/views_review.py index e46c45ae2..c634ddf22 100644 --- a/ietf/group/views_review.py +++ b/ietf/group/views_review.py @@ -4,10 +4,11 @@ from django.contrib.auth.decorators import login_required from django import forms from ietf.review.models import ReviewRequest, ReviewRequestStateName -from ietf.review.utils import (can_manage_review_requests_for_team, +from ietf.review.utils import (can_manage_review_requests_for_team, close_review_request_states, extract_revision_ordered_review_requests_for_documents, assign_review_request_to_reviewer, -# email_about_review_request, make_new_review_request_from_existing, + close_review_request, +# email_review_request_change, make_new_review_request_from_existing, suggested_review_requests_for_team) from ietf.group.utils import get_group_or_404 from ietf.person.fields import PersonEmailChoiceField @@ -21,14 +22,7 @@ class ManageReviewRequestForm(forms.Form): action = forms.ChoiceField(choices=ACTIONS, widget=forms.HiddenInput, required=False) - CLOSE_OPTIONS = [ - ("noreviewversion", "No review of this version"), - ("noreviewdocument", "No review of document"), - ("withdraw", "Withdraw request"), - ("no-response", "No response"), - ("overtaken", "Overtaken by events"), - ] - close = forms.ChoiceField(choices=CLOSE_OPTIONS, required=False) + close = forms.ModelChoiceField(queryset=close_review_request_states(), required=False) reviewer = PersonEmailChoiceField(empty_label="(None)", required=False, label_with="person") @@ -44,9 +38,9 @@ class ManageReviewRequestForm(forms.Form): close_initial = None if review_req.pk is None: if review_req.latest_reqs: - close_initial = "noreviewversion" + close_initial = "no-review-version" else: - close_initial = "noreviewdocument" + close_initial = "no-review-document" elif review_req.reviewer: close_initial = "no-response" else: @@ -55,6 +49,9 @@ class ManageReviewRequestForm(forms.Form): if close_initial: self.fields["close"].initial = close_initial + if review_req.pk is None: + self.fields["close"].queryset = self.fields["close"].queryset.filter(slug__in=["noreviewversion", "noreviewdocument"]) + self.fields["close"].widget.attrs["class"] = "form-control input-sm" self.fields["reviewer"].queryset = self.fields["reviewer"].queryset.filter( @@ -115,18 +112,13 @@ def manage_review_requests(request, acronym, group_type=None): form_results.append(req.form.is_valid()) if all(form_results): - for req in review_requests: - action = req.form.cleaned_data.get("action") + for review_req in review_requests: + action = review_req.form.cleaned_data.get("action") if action == "assign": - assign_review_request_to_reviewer(request, req, req.form.cleaned_data["reviewer"]) + assign_review_request_to_reviewer(request, review_req, review_req.form.cleaned_data["reviewer"]) elif action == "close": - close_reason = req.form.cleaned_data["close"] - if close_reason in ("withdraw", "no-response", "overtaken"): - req.state = ReviewRequestStateName.objects.get(slug=close_reason, used=True) - req.save() - # FIXME: notify? - else: - FIXME + close_review_request(request, review_req, review_req.form.cleaned_data["close"]) + kwargs = { "acronym": group.acronym } if group_type: diff --git a/ietf/name/migrations/0012_insert_review_name_data.py b/ietf/name/migrations/0012_insert_review_name_data.py index 92da06b94..13cc20786 100644 --- a/ietf/name/migrations/0012_insert_review_name_data.py +++ b/ietf/name/migrations/0012_insert_review_name_data.py @@ -12,8 +12,10 @@ def insert_initial_review_data(apps, schema_editor): ReviewRequestStateName.objects.get_or_create(slug="withdrawn", name="Withdrawn", order=4) ReviewRequestStateName.objects.get_or_create(slug="overtaken", name="Overtaken By Events", order=5) ReviewRequestStateName.objects.get_or_create(slug="no-response", name="No Response", order=6) - ReviewRequestStateName.objects.get_or_create(slug="part-completed", name="Partially Completed", order=7) - ReviewRequestStateName.objects.get_or_create(slug="completed", name="Completed", order=8) + ReviewRequestStateName.objects.get_or_create(slug="no-review-version", name="No Review of Version", order=7) + ReviewRequestStateName.objects.get_or_create(slug="no-review-document", name="No Review of Document", order=8) + ReviewRequestStateName.objects.get_or_create(slug="part-completed", name="Partially Completed", order=9) + ReviewRequestStateName.objects.get_or_create(slug="completed", name="Completed", order=10) ReviewTypeName = apps.get_model("name", "ReviewTypeName") ReviewTypeName.objects.get_or_create(slug="early", name="Early", order=1) diff --git a/ietf/name/models.py b/ietf/name/models.py index 58b861326..4fc9dc720 100644 --- a/ietf/name/models.py +++ b/ietf/name/models.py @@ -89,7 +89,7 @@ class LiaisonStatementTagName(NameModel): "Action Required, Action Taken" class ReviewRequestStateName(NameModel): """Requested, Accepted, Rejected, Withdrawn, Overtaken By Events, - No Response, Partially Completed, Completed""" + No Response, No Review of Version, No Review of Document, Partially Completed, Completed""" class ReviewTypeName(NameModel): """Early Review, Last Call, Telechat""" class ReviewResultName(NameModel): diff --git a/ietf/review/import_from_review_tool.py b/ietf/review/import_from_review_tool.py index 325f6e06b..ec6a4e422 100755 --- a/ietf/review/import_from_review_tool.py +++ b/ietf/review/import_from_review_tool.py @@ -161,6 +161,8 @@ with db_con.cursor() as c: doc_metadata[(row.docname, row.version)] = doc_metadata[row.docname] = (parse_timestamp(row.deadline), parse_timestamp(row.telechat), parse_timestamp(row.lcend), row.status) +system_person = Person.objects.get(name="(System)") + with db_con.cursor() as c: c.execute("select * from reviews order by reviewid;") @@ -207,6 +209,7 @@ with db_con.cursor() as c: "state": states["requested"], "type": type_name, "deadline": deadline, + "requested_by": system_person, } ) diff --git a/ietf/review/migrations/0001_initial.py b/ietf/review/migrations/0001_initial.py index cb85178c2..a78220cc0 100644 --- a/ietf/review/migrations/0001_initial.py +++ b/ietf/review/migrations/0001_initial.py @@ -42,6 +42,7 @@ class Migration(migrations.Migration): ('result', models.ForeignKey(blank=True, to='name.ReviewResultName', null=True)), ('review', models.OneToOneField(null=True, blank=True, to='doc.Document')), ('reviewer', models.ForeignKey(blank=True, to='person.Email', null=True)), + ('requested_by', models.ForeignKey(to='person.Person')), ('state', models.ForeignKey(to='name.ReviewRequestStateName')), ('team', models.ForeignKey(to='group.Group')), ('type', models.ForeignKey(to='name.ReviewTypeName')), diff --git a/ietf/review/models.py b/ietf/review/models.py index 589899b91..7f39f96c3 100644 --- a/ietf/review/models.py +++ b/ietf/review/models.py @@ -42,6 +42,7 @@ class ReviewRequest(models.Model): doc = models.ForeignKey(Document, related_name='review_request_set') team = models.ForeignKey(Group, limit_choices_to=~models.Q(reviewteamresult=None)) deadline = models.DateTimeField() + requested_by = models.ForeignKey(Person) requested_rev = models.CharField(verbose_name="requested revision", max_length=16, blank=True, help_text="Fill in if a specific revision is to be reviewed, e.g. 02") # Fields filled in as reviewer is assigned and as the review is diff --git a/ietf/review/utils.py b/ietf/review/utils.py index 911b6390b..43611cea4 100644 --- a/ietf/review/utils.py +++ b/ietf/review/utils.py @@ -6,6 +6,7 @@ from django.contrib.sites.models import Site from ietf.group.models import Group, Role from ietf.doc.models import Document, DocEvent, State, LastCallDocEvent from ietf.iesg.models import TelechatDate +from ietf.person.models import Person from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream from ietf.review.models import ReviewRequest, ReviewRequestStateName, ReviewTypeName from ietf.utils.mail import send_mail @@ -15,6 +16,9 @@ def active_review_teams(): # if there's a ReviewTeamResult defined, it's a review team return Group.objects.filter(state="active").exclude(reviewteamresult=None) +def close_review_request_states(): + return ReviewRequestStateName.objects.filter(used=True).exclude(slug__in=["requested", "accepted", "rejected", "part-completed", "completed"]) + def can_request_review_of_doc(user, doc): if not user.is_authenticated(): return False @@ -35,30 +39,44 @@ def make_new_review_request_from_existing(review_req): obj.team = review_req.team obj.deadline = review_req.deadline obj.requested_rev = review_req.requested_rev + obj.requested_by = review_req.requested_by obj.state = ReviewRequestStateName.objects.get(slug="requested") return obj -def email_about_review_request(request, review_req, subject, msg, by, notify_secretary, notify_reviewer): +def email_review_request_change(request, review_req, subject, msg, by, notify_secretary, notify_reviewer, notify_requested_by): """Notify possibly both secretary and reviewer about change, skipping a party if the change was done by that party.""" - def extract_email_addresses(objs): - if any(o.person == by for o in objs if o): - return [] - else: - return [o.formatted_email() for o in objs if o] + system_email = Person.objects.get(name="(System)").formatted_email() to = [] - if notify_secretary: - to += extract_email_addresses(Role.objects.filter(name__in=["secretary", "delegate"], group=review_req.team).distinct()) - if notify_reviewer: - to += extract_email_addresses([review_req.reviewer]) + def extract_email_addresses(objs): + if any(o.person == by for o in objs if o): + l = [] + else: + l = [] + for o in objs: + if o: + e = o.formatted_email() + if e != system_email: + l.append(e) + for e in l: + if e not in to: + to.append(e) + + if notify_secretary: + extract_email_addresses(Role.objects.filter(name__in=["secretary", "delegate"], group=review_req.team).distinct()) + if notify_reviewer: + extract_email_addresses([review_req.reviewer]) + if notify_requested_by: + extract_email_addresses([review_req.requested_by.email()]) + if not to: return - send_mail(request, list(set(to)), None, subject, "doc/mail/review_request_changed.txt", { + send_mail(request, to, None, subject, "doc/mail/review_request_changed.txt", { "domain": Site.objects.get_current().domain, "review_req": review_req, "msg": msg, @@ -71,11 +89,11 @@ def assign_review_request_to_reviewer(request, review_req, reviewer): return if review_req.reviewer: - email_about_review_request( + email_review_request_change( request, review_req, "Unassigned from review of %s" % review_req.doc.name, "%s has cancelled your assignment to the review." % request.user.person, - by=request.user.person, notify_secretary=False, notify_reviewer=True) + by=request.user.person, notify_secretary=False, notify_reviewer=True, notify_requested_by=False) review_req.state = ReviewRequestStateName.objects.get(slug="requested") review_req.reviewer = reviewer @@ -92,11 +110,36 @@ def assign_review_request_to_reviewer(request, review_req, reviewer): ), ) - email_about_review_request( + email_review_request_change( request, review_req, "Assigned to review of %s" % review_req.doc.name, "%s has assigned you to review the document." % request.user.person, - by=request.user.person, notify_secretary=False, notify_reviewer=True) + by=request.user.person, notify_secretary=False, notify_reviewer=True, notify_requested_by=False) + +def close_review_request(request, review_req, close_state): + suggested_req = review_req.pk is None + + prev_state = review_req.state + review_req.state = close_state + if close_state.slug == "no-review-version": + review_req.reviewed_rev = review_req.doc.rev # save rev for later reference + review_req.save() + + if not suggested_req: + DocEvent.objects.create( + type="changed_review_request", + doc=review_req.doc, + by=request.user.person, + desc="Closed request for {} review by {} with state '{}'".format( + review_req.type.name, review_req.team.acronym.upper(), close_state.name), + ) + + if prev_state.slug != "requested": + email_review_request_change( + request, review_req, + "Closed review request for {}: {}".format(review_req.doc.name, close_state.name), + "Review request has been closed by {}.".format(request.user.person), + by=request.user.person, notify_secretary=False, notify_reviewer=True, notify_requested_by=True) def suggested_review_requests_for_team(team): def fixup_deadline(d): @@ -104,11 +147,13 @@ def suggested_review_requests_for_team(team): d = d - datetime.timedelta(seconds=1) # 23:59:59 is treated specially in the view code return d + system_person = Person.objects.get(name="(System)") + seen_deadlines = {} requests = {} - if True: + if True: # FIXME # in Last Call last_call_type = ReviewTypeName.objects.get(slug="lc") last_call_docs = Document.objects.filter(states=State.objects.get(type="draft-iesg", slug="lc", used=True)) @@ -125,12 +170,13 @@ def suggested_review_requests_for_team(team): doc=doc, team=team, deadline=deadline, + requested_by=system_person, ) seen_deadlines[doc.pk] = deadline - if True: + if True: # FIXME # on Telechat Agenda telechat_dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4]) @@ -153,6 +199,7 @@ def suggested_review_requests_for_team(team): doc=doc, team=team, deadline=deadline, + requested_by=system_person, ) seen_deadlines[doc.pk] = deadline diff --git a/ietf/templates/doc/review/withdraw_request.html b/ietf/templates/doc/review/close_request.html similarity index 56% rename from ietf/templates/doc/review/withdraw_request.html rename to ietf/templates/doc/review/close_request.html index 191f236dc..2413cf22f 100644 --- a/ietf/templates/doc/review/withdraw_request.html +++ b/ietf/templates/doc/review/close_request.html @@ -2,20 +2,22 @@ {# Copyright The IETF Trust 2016, All Rights Reserved #} {% load origin bootstrap3 static %} -{% block title %}Withdraw review request for {{ review_req.doc.name }}{% endblock %} +{% block title %}Close review request for {{ review_req.doc.name }}{% endblock %} {% block content %} {% origin %} -

Withdraw review request
{{ review_req.doc.name }}

+

Close review request
{{ review_req.doc.name }}

-

Do you want to withdraw the review request?

+

Do you want to close the review request?

{% csrf_token %} + {% bootstrap_form form %} + {% buttons %} Cancel - + {% endbuttons %}
diff --git a/ietf/templates/doc/review/request_review.html b/ietf/templates/doc/review/request_review.html index aa1700aab..0a9d06b1d 100644 --- a/ietf/templates/doc/review/request_review.html +++ b/ietf/templates/doc/review/request_review.html @@ -3,6 +3,8 @@ {% load origin bootstrap3 static %} {% block pagehead %} + + {% endblock %} @@ -16,6 +18,7 @@
{% csrf_token %} + {% bootstrap_field form.requested_by layout="horizontal" %} {% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.team layout="horizontal" %} {% bootstrap_field form.deadline_date layout="horizontal" %} @@ -31,4 +34,6 @@ {% block js %} + + {% endblock %} diff --git a/ietf/templates/doc/review/review_request.html b/ietf/templates/doc/review/review_request.html index 3c9826b37..77ce5b1f7 100644 --- a/ietf/templates/doc/review/review_request.html +++ b/ietf/templates/doc/review/review_request.html @@ -51,6 +51,12 @@ Requested {{ review_req.time|date:"Y-m-d" }} + + + + Requested by + {{ review_req.requested_by }} + @@ -143,8 +149,8 @@
- {% if can_withdraw_request %} - Withdraw request + {% if can_close_request %} + Close request {% endif %}
diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index d19be0f93..d2ed4e20a 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -375,6 +375,7 @@ def make_review_data(doc): type_id="early", deadline=datetime.datetime.now() + datetime.timedelta(days=20), state_id="accepted", + requested_by=p, reviewer=email, )