Skip to content

Commit 295b484

Browse files
committed
Merged [7210] from rjsparks@nostrum.com: Allow the IRTF Chair and the ISE to request a conflict review directly through the tracker.
Notify the Secretariat when someone other than the secretariat initiates a conflict review. Notify IANA when anyone initiates a conflict review. Fixes tickets ietf-tools#1287 and ietf-tools#1289 - Legacy-Id: 7225 Note: SVN reference [7210] has been migrated to Git commit a0fd974
1 parent c83ef0c commit 295b484

6 files changed

Lines changed: 186 additions & 47 deletions

File tree

ietf/doc/tests_conflict_review.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
class ConflictReviewTests(TestCase):
26-
def test_start_review(self):
26+
def test_start_review_as_secretary(self):
2727

2828
doc = Document.objects.get(name='draft-imaginary-independent-submission')
2929
url = urlreverse('conflict_review_start',kwargs=dict(name=doc.name))
@@ -71,11 +71,59 @@ def test_start_review(self):
7171

7272
self.assertTrue(review_doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review requested"))
7373
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review initiated"))
74+
self.assertTrue('Conflict Review requested' in outbox[-1]['Subject'])
75+
self.assertTrue(settings.IANA_EVAL_EMAIL in outbox[-1]['To'])
7476

7577
# verify you can't start a review when a review is already in progress
7678
r = self.client.post(url,dict(ad="Aread Irector",create_in_state="Needs Shepherd",notify='ipu@ietf.org'))
7779
self.assertEqual(r.status_code, 404)
7880

81+
82+
def test_start_review_as_stream_owner(self):
83+
84+
doc = Document.objects.get(name='draft-imaginary-independent-submission')
85+
url = urlreverse('conflict_review_start',kwargs=dict(name=doc.name))
86+
87+
login_testing_unauthorized(self, "ise-chair", url)
88+
89+
# can't start conflict reviews on documents not in a stream
90+
r = self.client.get(url)
91+
self.assertEquals(r.status_code, 404)
92+
93+
94+
# can't start conflict reviews on documents in some other stream
95+
doc.stream=StreamName.objects.get(slug='irtf')
96+
doc.save()
97+
r = self.client.get(url)
98+
self.assertEquals(r.status_code, 404)
99+
100+
# successful get
101+
doc.stream=StreamName.objects.get(slug='ise')
102+
doc.save()
103+
r = self.client.get(url)
104+
self.assertEquals(r.status_code, 200)
105+
q = PyQuery(r.content)
106+
self.assertEquals(len(q('form input[name=notify]')),1)
107+
self.assertEquals(len(q('form select[name=ad]')),0)
108+
109+
# successfully starts a review, and notifies the secretariat
110+
messages_before = len(outbox)
111+
r = self.client.post(url,dict(notify='ipu@ietf.org'))
112+
self.assertEquals(r.status_code, 302)
113+
review_doc = Document.objects.get(name='conflict-review-imaginary-independent-submission')
114+
self.assertEquals(review_doc.get_state('conflrev').slug,'needshep')
115+
self.assertEquals(review_doc.rev,u'00')
116+
self.assertEquals(review_doc.telechat_date(),None)
117+
self.assertEquals(review_doc.ad.name,u'Ietf Chair')
118+
self.assertEquals(review_doc.notify,u'ipu@ietf.org')
119+
doc = Document.objects.get(name='draft-imaginary-independent-submission')
120+
self.assertTrue(doc in [x.target.document for x in review_doc.relateddocument_set.filter(relationship__slug='conflrev')])
121+
self.assertEqual(len(outbox), messages_before + 2)
122+
self.assertTrue('Conflict Review requested' in outbox[-1]['Subject'])
123+
self.assertTrue(any('iesg-secretary@ietf.org' in x['To'] for x in outbox[-2:]))
124+
self.assertTrue(any(settings.IANA_EVAL_EMAIL in x['To'] for x in outbox[-2:]))
125+
126+
79127
def test_change_state(self):
80128

81129
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')

ietf/doc/views_conflict_review.py

Lines changed: 120 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from ietf.doc.utils import add_state_change_event, update_telechat
1212
from ietf.doc.models import save_document_in_history
1313
from ietf.doc.utils import create_ballot_if_not_open, close_open_ballots, get_document_content
14-
from ietf.ietfauth.utils import has_role, role_required
14+
from ietf.ietfauth.utils import has_role, role_required, is_authorized_in_doc_stream
1515
from ietf.utils.textupload import get_cleaned_text_file_content
1616
from ietf.utils.mail import send_mail_preformatted
1717
from ietf.doc.mails import email_iana
@@ -88,6 +88,22 @@ def change_state(request, name, option=None):
8888
),
8989
context_instance=RequestContext(request))
9090

91+
def send_conflict_review_started_email(request, review):
92+
msg = render_to_string("doc/conflict_review/review_started.txt",
93+
dict(frm = settings.DEFAULT_FROM_EMAIL,
94+
by = request.user.person,
95+
review = review,
96+
reviewed_doc = review.relateddocument_set.get(relationship__slug='conflrev').target.document,
97+
review_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
98+
)
99+
)
100+
if not has_role(request.user,"Secretariat"):
101+
send_mail_preformatted(request,msg)
102+
email_iana(request,
103+
review.relateddocument_set.get(relationship__slug='conflrev').target.document,
104+
settings.IANA_EVAL_EMAIL,
105+
msg)
106+
91107
def send_conflict_eval_email(request,review):
92108
msg = render_to_string("doc/eval_email.txt",
93109
dict(doc=review,
@@ -345,6 +361,9 @@ def approve(request, name):
345361
),
346362
context_instance=RequestContext(request))
347363

364+
class SimpleStartReviewForm(forms.Form):
365+
notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False)
366+
348367
class StartReviewForm(forms.Form):
349368
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'),
350369
label="Shepherding AD", empty_label="(None)", required=True)
@@ -363,73 +382,96 @@ def __init__(self, *args, **kwargs):
363382

364383
self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, d.strftime("%Y-%m-%d")) for d in dates]
365384

366-
@role_required("Secretariat")
385+
@role_required("Secretariat","IRTF Chair","ISE")
367386
def start_review(request, name):
368-
"""Start the conflict review process, setting the initial shepherding AD, and possibly putting the review on a telechat."""
387+
if has_role(request.user,"Secretariat"):
388+
return start_review_as_secretariat(request,name)
389+
else:
390+
return start_review_as_stream_owner(request,name)
369391

392+
def start_review_sanity_check(request, name):
370393
doc_to_review = get_object_or_404(Document, type="draft", name=name)
371394

372-
if not doc_to_review.stream_id in ('ise','irtf'):
395+
if ( not doc_to_review.stream_id in ('ise','irtf') ) or ( not is_authorized_in_doc_stream(request.user,doc_to_review)):
373396
raise Http404
374397

375398
# sanity check that there's not already a conflict review document for this document
376399
if [ rel.source for alias in doc_to_review.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='conflrev') ]:
377400
raise Http404
378401

379-
login = request.user.person
402+
return doc_to_review
380403

404+
def build_notify_addresses(doc_to_review):
405+
# Take care to do the right thing during ietf chair and stream owner transitions
406+
notify_addresses = []
407+
notify_addresses.extend([x.person.formatted_email() for x in Role.objects.filter(group__acronym=doc_to_review.stream.slug,name='chair')])
408+
notify_addresses.append("%s@%s" % (doc_to_review.name, settings.TOOLS_SERVER))
409+
return notify_addresses
410+
411+
def build_conflict_review_document(login, doc_to_review, ad, notify, create_in_state):
412+
if doc_to_review.name.startswith('draft-'):
413+
review_name = 'conflict-review-'+doc_to_review.name[6:]
414+
else:
415+
# This is a failsafe - and might be treated better as an error
416+
review_name = 'conflict-review-'+doc_to_review.name
417+
418+
iesg_group = Group.objects.get(acronym='iesg')
419+
420+
conflict_review=Document( type_id = "conflrev",
421+
title = "IETF conflict review for %s" % doc_to_review.name,
422+
name = review_name,
423+
rev = "00",
424+
ad = ad,
425+
notify = notify,
426+
stream_id = 'ietf',
427+
group = iesg_group,
428+
)
429+
conflict_review.save()
430+
conflict_review.set_state(create_in_state)
431+
432+
DocAlias.objects.create( name=review_name , document=conflict_review )
433+
434+
conflict_review.relateddocument_set.create(target=DocAlias.objects.get(name=doc_to_review.name),relationship_id='conflrev')
435+
436+
c = DocEvent(type="added_comment", doc=conflict_review, by=login)
437+
c.desc = "IETF conflict review requested"
438+
c.save()
439+
440+
c = DocEvent(type="added_comment", doc=doc_to_review, by=login)
441+
# Is it really OK to put html tags into comment text?
442+
c.desc = 'IETF conflict review initiated - see <a href="%s">%s</a>' % (reverse('doc_view', kwargs={'name':conflict_review.name}),conflict_review.name)
443+
c.save()
444+
445+
return conflict_review
446+
447+
def start_review_as_secretariat(request, name):
448+
"""Start the conflict review process, setting the initial shepherding AD, and possibly putting the review on a telechat."""
449+
450+
doc_to_review = start_review_sanity_check(request, name)
451+
452+
login = request.user.person
381453

382454
if request.method == 'POST':
383455
form = StartReviewForm(request.POST)
384456
if form.is_valid():
457+
conflict_review = build_conflict_review_document(login = login,
458+
doc_to_review = doc_to_review,
459+
ad = form.cleaned_data['ad'],
460+
notify = form.cleaned_data['notify'],
461+
create_in_state = form.cleaned_data['create_in_state']
462+
)
385463

386-
if doc_to_review.name.startswith('draft-'):
387-
review_name = 'conflict-review-'+doc_to_review.name[6:]
388-
else:
389-
# This is a failsafe - and might be treated better as an error
390-
review_name = 'conflict-review-'+doc_to_review.name
391-
392-
iesg_group = Group.objects.get(acronym='iesg')
393-
394-
conflict_review=Document( type_id = "conflrev",
395-
title = "IETF conflict review for %s" % doc_to_review.name,
396-
name = review_name,
397-
rev = "00",
398-
ad = form.cleaned_data['ad'],
399-
notify = form.cleaned_data['notify'],
400-
stream_id = 'ietf',
401-
group = iesg_group,
402-
)
403-
conflict_review.save()
404-
conflict_review.set_state(form.cleaned_data['create_in_state'])
405-
406-
DocAlias.objects.create( name=review_name , document=conflict_review )
407-
408-
conflict_review.relateddocument_set.create(target=DocAlias.objects.get(name=doc_to_review.name),relationship_id='conflrev')
409-
410-
c = DocEvent(type="added_comment", doc=conflict_review, by=login)
411-
c.desc = "IETF conflict review requested"
412-
c.save()
413-
414-
c = DocEvent(type="added_comment", doc=doc_to_review, by=login)
415-
# Is it really OK to put html tags into comment text?
416-
c.desc = 'IETF conflict review initiated - see <a href="%s">%s</a>' % (reverse('doc_view', kwargs={'name':conflict_review.name}),conflict_review.name)
417-
c.save()
418-
419464
tc_date = form.cleaned_data['telechat_date']
420465
if tc_date:
421466
update_telechat(request, conflict_review, login, tc_date)
422467

468+
send_conflict_review_started_email(request, conflict_review)
469+
423470
return HttpResponseRedirect(conflict_review.get_absolute_url())
424471
else:
425-
# Take care to do the right thing during ietf chair and stream owner transitions
426-
ietf_chair_id = Role.objects.filter(group__acronym='ietf',name='chair')[0].person.id
427-
notify_addresses = []
428-
notify_addresses.extend([x.person.formatted_email() for x in Role.objects.filter(group__acronym=doc_to_review.stream.slug,name='chair')])
429-
notify_addresses.append("%s@%s" % (name, settings.TOOLS_SERVER))
430-
472+
notify_addresses = build_notify_addresses(doc_to_review)
431473
init = {
432-
"ad" : ietf_chair_id,
474+
"ad" : Role.objects.filter(group__acronym='ietf',name='chair')[0].person.id,
433475
"notify" : u', '.join(notify_addresses),
434476
}
435477
form = StartReviewForm(initial=init)
@@ -440,6 +482,39 @@ def start_review(request, name):
440482
},
441483
context_instance = RequestContext(request))
442484

485+
def start_review_as_stream_owner(request, name):
486+
"""Start the conflict review process using defaults for everything but notify and let the secretariat know"""
487+
488+
doc_to_review = start_review_sanity_check(request, name)
489+
490+
login = request.user.person
491+
492+
if request.method == 'POST':
493+
form = SimpleStartReviewForm(request.POST)
494+
if form.is_valid():
495+
conflict_review = build_conflict_review_document(login = login,
496+
doc_to_review = doc_to_review,
497+
ad = Role.objects.filter(group__acronym='ietf',name='chair')[0].person,
498+
notify = form.cleaned_data['notify'],
499+
create_in_state = State.objects.get(used=True,type='conflrev',slug='needshep')
500+
)
501+
502+
send_conflict_review_started_email(request, conflict_review)
503+
504+
return HttpResponseRedirect(conflict_review.get_absolute_url())
505+
else:
506+
notify_addresses = build_notify_addresses(doc_to_review)
507+
508+
init = {
509+
"notify" : u', '.join(notify_addresses),
510+
}
511+
form = SimpleStartReviewForm(initial=init)
512+
513+
return render_to_response('doc/conflict_review/start.html',
514+
{'form': form,
515+
'doc_to_review': doc_to_review,
516+
},
517+
context_instance = RequestContext(request))
443518

444519
@role_required("Area Director", "Secretariat")
445520
def telechat_date(request, name):

ietf/doc/views_doc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def document_main(request, name, rev=None):
294294
actions.append(("Resurrect", urlreverse('doc_resurrect', kwargs=dict(name=doc.name))))
295295

296296
if (doc.get_state_slug() != "expired" and doc.stream_id in ("ise", "irtf")
297-
and has_role(request.user, ("Secretariat",)) and not conflict_reviews):
297+
and can_edit_stream_info and not conflict_reviews):
298298
label = "Begin IETF Conflict Review"
299299
if not doc.intended_std_level:
300300
label += " (note that intended status is not set)"

ietf/ietfauth/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def has_role(user, role_names, *args, **kwargs):
4646
"Secretariat": Q(person=person, name="secr", group__acronym="secretariat"),
4747
"IANA": Q(person=person, name="auth", group__acronym="iana"),
4848
"RFC Editor": Q(person=person, name="auth", group__acronym="rfceditor"),
49+
"ISE" : Q(person=person, name="chair", group__acronym="ise"),
4950
"IAD": Q(person=person, name="admdir", group__acronym="ietf"),
5051
"IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
5152
"IRTF Chair": Q(person=person, name="chair", group__acronym="irtf"),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{% load mail_filters %}{% autoescape off %}To: IESG Secretary <iesg-secretary@ietf.org>
2+
From: {{ frm }}
3+
Subject: Conflict Review requested for {{reviewed_doc.name}}
4+
5+
{{ by.name }} has requested a conflict review for:
6+
{{ reviewed_doc.name }}
7+
{{ reviewed_doc.title }}
8+
9+
The conflict review is being tracked at <{{ review_url }}>
10+
11+
{% endautoescape%}

ietf/templates/doc/conflict_review/start.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{% extends "base.html" %}
22

3+
{% load ietf_filters %}
4+
35
{% block title %}Begin IETF conflict review : {{doc_to_review.canonical_name}}-{{doc_to_review.rev}}{% endblock %}
46

57
{% block morecss %}
@@ -18,7 +20,9 @@
1820
{% block content %}
1921
<h1>Begin IETF conflict review for {{doc_to_review.canonical_name}}-{{doc_to_review.rev}}</h1>
2022

23+
{% if user|has_role:"Secretariat" %}
2124
<p class="helptext">For help on the initial state choice, see the <a href="{% url "state_help" type="conflict-review" %}">state table</a>.</p>
25+
{% endif %}
2226

2327
<form class="start-conflict-review" action="" method="post">{% csrf_token %}
2428
<table>

0 commit comments

Comments
 (0)