Skip to content

Commit 174cfea

Browse files
committed
Merged in [10770], [10771], [10772], and [10773] from housley@vigilsec.com:
The IESG Telechat Agenda now shows "(Has RFC Editor Note)" after the I-D filename if there is an RFC Editor Note associated with the document. This was added to the html and txt versions of the agenda. It was not added to the Secretariat view or the Scribe view of the agenda. For transition, when an AD edits the RFC Editor Note, they need to move the text from the current writeup into the new field. Returning documents on the telechat agenda seems to be the biggest opportunity for something to fall between the cracks. If an event of type "changed_rfc_editor_note' exists, and the string "RFC Editor Note" appears in the text of the most recent 'changed_ballot_writeup_text' event, then a message is shown that tells the AD to remove the RFC Editor Note from the writeup. - Legacy-Id: 10783 Note: SVN reference [10770] has been migrated to Git commit 4b5ac9e Note: SVN reference [10771] has been migrated to Git commit 46589ee Note: SVN reference [10772] has been migrated to Git commit c73659e Note: SVN reference [10773] has been migrated to Git commit 2e6633c
2 parents a66ad95 + 2e6633c commit 174cfea

16 files changed

Lines changed: 254 additions & 11 deletions

ietf/doc/mails.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ def generate_ballot_writeup(request, doc):
9292

9393
return e
9494

95+
def generate_ballot_rfceditornote(request, doc):
96+
e = WriteupDocEvent()
97+
e.type = "changed_ballot_rfceditornote_text"
98+
e.by = request.user.person
99+
e.doc = doc
100+
e.desc = u"RFC Editor Note for ballot was generated"
101+
e.text = unicode(render_to_string("doc/mail/ballot_rfceditornote.txt"))
102+
e.save()
103+
104+
return e
105+
95106
def generate_last_call_announcement(request, doc):
96107
expiration_date = datetime.date.today() + datetime.timedelta(days=14)
97108
if doc.group.type_id in ("individ", "area"):

ietf/doc/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ def active_ballot(self):
232232
else:
233233
return None
234234

235+
def has_rfc_editor_note(self):
236+
e = self.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
237+
return bool(e and (e.text != ""))
238+
235239
def meeting_related(self):
236240
answer = False
237241
if self.type_id in ("agenda","minutes","bluesheets","slides","recording"):
@@ -675,6 +679,7 @@ class DocReminder(models.Model):
675679

676680
("changed_ballot_approval_text", "Changed ballot approval text"),
677681
("changed_ballot_writeup_text", "Changed ballot writeup text"),
682+
("changed_rfc_editor_note_text", "Changed RFC Editor Note text"),
678683

679684
("changed_last_call_text", "Changed last call text"),
680685
("requested_last_call", "Requested last call"),

ietf/doc/tests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,11 +710,19 @@ def test_writeup(self):
710710
text="This is ballot writeup notes.",
711711
by=Person.objects.get(name="(System)"))
712712

713+
rfced_note = WriteupDocEvent.objects.create(
714+
doc=doc,
715+
desc="Changed text",
716+
type="changed_rfc_editor_note_text",
717+
text="This is a note for the RFC Editor.",
718+
by=Person.objects.get(name="(System)"))
719+
713720
url = urlreverse('doc_writeup', kwargs=dict(name=doc.name))
714721
r = self.client.get(url)
715722
self.assertEqual(r.status_code, 200)
716723
self.assertTrue(appr.text in unicontent(r))
717724
self.assertTrue(notes.text in unicontent(r))
725+
self.assertTrue(rfced_note.text in r.content)
718726

719727
def test_history(self):
720728
doc = make_test_data()

ietf/doc/tests_ballot.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,45 @@ def test_edit_ballot_writeup(self):
274274
draft = Document.objects.get(name=draft.name)
275275
self.assertTrue("This is a simple test" in draft.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text").text)
276276

277+
def test_edit_ballot_rfceditornote(self):
278+
draft = make_test_data()
279+
url = urlreverse('doc_ballot_rfceditornote', kwargs=dict(name=draft.name))
280+
login_testing_unauthorized(self, "secretary", url)
281+
282+
# add a note to the RFC Editor
283+
WriteupDocEvent.objects.create(
284+
doc=draft,
285+
desc="Changed text",
286+
type="changed_rfc_editor_note_text",
287+
text="This is a note for the RFC Editor.",
288+
by=Person.objects.get(name="(System)"))
289+
290+
# normal get
291+
r = self.client.get(url)
292+
self.assertEqual(r.status_code, 200)
293+
q = PyQuery(r.content)
294+
self.assertEqual(len(q('textarea[name=rfc_editor_note]')), 1)
295+
self.assertTrue(q('[type=submit]:contains("Save")'))
296+
self.assertTrue("<label class=\"control-label\">RFC Editor Note</label>" in r.content)
297+
self.assertTrue("This is a note for the RFC Editor" in r.content)
298+
299+
# save with a note
300+
r = self.client.post(url, dict(
301+
rfc_editor_note="This is a simple test.",
302+
save_ballot_rfceditornote="1"))
303+
self.assertEqual(r.status_code, 200)
304+
draft = Document.objects.get(name=draft.name)
305+
self.assertTrue(draft.has_rfc_editor_note())
306+
self.assertTrue("This is a simple test" in draft.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text").text)
307+
308+
# clear the existing note
309+
r = self.client.post(url, dict(
310+
rfc_editor_note=" ",
311+
clear_ballot_rfceditornote="1"))
312+
self.assertEqual(r.status_code, 200)
313+
draft = Document.objects.get(name=draft.name)
314+
self.assertFalse(draft.has_rfc_editor_note())
315+
277316
def test_issue_ballot(self):
278317
draft = make_test_data()
279318
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
@@ -341,6 +380,55 @@ def test_edit_approval_text(self):
341380
draft = Document.objects.get(name=draft.name)
342381
self.assertTrue("Subject: Results of IETF-conflict review" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text)
343382

383+
def test_edit_verify_permissions(self):
384+
385+
def verify_fail(username, url):
386+
if username:
387+
self.client.login(username=username, password=username+"+password")
388+
r = self.client.get(url)
389+
self.assertEqual(r.status_code,403)
390+
391+
def verify_can_see(username, url):
392+
self.client.login(username=username, password=username+"+password")
393+
r = self.client.get(url)
394+
self.assertEqual(r.status_code,200)
395+
q = PyQuery(r.content)
396+
self.assertEqual(len(q("<textarea class=\"form-control\"")),1)
397+
398+
draft = make_test_data()
399+
400+
e = WriteupDocEvent()
401+
e.type = "changed_ballot_approval_text"
402+
e.by = Person.objects.get(name="(System)")
403+
e.doc = draft
404+
e.desc = u"Ballot approval text was generated"
405+
e.text = u"Test approval text."
406+
e.save()
407+
408+
e = WriteupDocEvent()
409+
e.type = "changed_ballot_writeup_text"
410+
e.by = Person.objects.get(name="(System)")
411+
e.doc = draft
412+
e.desc = u"Ballot writeup was generated"
413+
e.text = u"Test ballot writeup text."
414+
e.save()
415+
416+
e = WriteupDocEvent()
417+
e.type = "changed_ballot_rfceditornote_text"
418+
e.by = Person.objects.get(name="(System)")
419+
e.doc = draft
420+
e.desc = u"RFC Editor Note for ballot was generated"
421+
e.text = u"Test note to the RFC Editor text."
422+
e.save()
423+
424+
for p in ['doc_ballot_approvaltext','doc_ballot_writeupnotes','doc_ballot_rfceditornote']:
425+
url = urlreverse(p, kwargs=dict(name=draft.name))
426+
427+
for username in ['plain','marschairman','iana','iab chair']:
428+
verify_fail(username, url)
429+
430+
for username in ['secretary','ad']:
431+
verify_can_see(username, url)
344432

345433
class ApproveBallotTests(TestCase):
346434
def test_approve_ballot(self):

ietf/doc/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/undeferballot/$', views_ballot.undefer_ballot, name='doc_undefer_ballot'),
101101
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/lastcalltext/$', views_ballot.lastcalltext, name='doc_ballot_lastcall'),
102102
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ballotwriteupnotes/$', views_ballot.ballot_writeupnotes, name='doc_ballot_writeupnotes'),
103+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ballotrfceditornote/$', views_ballot.ballot_rfceditornote, name='doc_ballot_rfceditornote'),
103104
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approvaltext/$', views_ballot.ballot_approvaltext, name='doc_ballot_approvaltext'),
104105
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'),
105106
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),

ietf/doc/views_ballot.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
create_ballot_if_not_open, update_telechat )
2020
from ietf.doc.mails import ( email_ballot_deferred, email_ballot_undeferred,
2121
extra_automation_headers, generate_last_call_announcement,
22-
generate_issue_ballot_mail, generate_ballot_writeup, generate_approval_mail )
22+
generate_issue_ballot_mail, generate_ballot_writeup, generate_ballot_rfceditornote,
23+
generate_approval_mail )
2324
from ietf.doc.lastcall import request_last_call
2425
from ietf.iesg.models import TelechatDate
2526
from ietf.ietfauth.utils import has_role, role_required
@@ -585,6 +586,57 @@ def ballot_writeupnotes(request, name):
585586
),
586587
context_instance=RequestContext(request))
587588

589+
class BallotRfcEditorNoteForm(forms.Form):
590+
rfc_editor_note = forms.CharField(widget=forms.Textarea, label="RFC Editor Note", required=True)
591+
592+
def clean_rfc_editor_note(self):
593+
return self.cleaned_data["rfc_editor_note"].replace("\r", "")
594+
595+
@role_required('Area Director','Secretariat')
596+
def ballot_rfceditornote(request, name):
597+
"""Editing of RFC Editor Note in the ballot"""
598+
doc = get_object_or_404(Document, docalias__name=name)
599+
600+
login = request.user.person
601+
602+
603+
604+
existing = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
605+
if not existing or (existing.text == ""):
606+
existing = generate_ballot_rfceditornote(request, doc)
607+
608+
form = BallotRfcEditorNoteForm(auto_id=False, initial=dict(rfc_editor_note=existing.text))
609+
610+
if request.method == 'POST' and "save_ballot_rfceditornote" in request.POST:
611+
form = BallotRfcEditorNoteForm(request.POST)
612+
if form.is_valid():
613+
t = form.cleaned_data["rfc_editor_note"]
614+
if t != existing.text:
615+
e = WriteupDocEvent(doc=doc, by=login)
616+
e.by = login
617+
e.type = "changed_rfc_editor_note_text"
618+
e.desc = "RFC Editor Note was changed"
619+
e.text = t.rstrip()
620+
e.save()
621+
622+
if request.method == 'POST' and "clear_ballot_rfceditornote" in request.POST:
623+
e = WriteupDocEvent(doc=doc, by=login)
624+
e.by = login
625+
e.type = "changed_rfc_editor_note_text"
626+
e.desc = "RFC Editor Note was cleared"
627+
e.text = ""
628+
e.save()
629+
630+
# make sure form shows a blank RFC Editor Note
631+
form = BallotRfcEditorNoteForm(initial=dict(rfc_editor_note=" "))
632+
633+
return render_to_response('doc/ballot/rfceditornote.html',
634+
dict(doc=doc,
635+
back_url=doc.get_absolute_url(),
636+
ballot_rfceditornote_form=form,
637+
),
638+
context_instance=RequestContext(request))
639+
588640
class ApprovalTextForm(forms.Form):
589641
approval_text = forms.CharField(widget=forms.Textarea, required=True)
590642

@@ -657,7 +709,19 @@ def approve_ballot(request, name):
657709
if not e:
658710
e = generate_ballot_writeup(request, doc)
659711
ballot_writeup = e.text
660-
712+
713+
error_duplicate_rfc_editor_note = False
714+
e = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
715+
if e and (e.text != ""):
716+
if "RFC Editor Note" in ballot_writeup:
717+
error_duplicate_rfc_editor_note = True
718+
ballot_writeup += "\n\n" + e.text
719+
720+
if error_duplicate_rfc_editor_note:
721+
return render_to_response('doc/draft/rfceditor_note_duplicate_error.html',
722+
dict(doc=doc),
723+
context_instance=RequestContext(request))
724+
661725
if "NOT be published" in approval_text:
662726
action = "do_not_publish"
663727
elif "To: RFC Editor" in approval_text:

ietf/doc/views_doc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,10 @@ def text_from_writeup(event_type):
735735
text_from_writeup("changed_ballot_writeup_text"),
736736
urlreverse("doc_ballot_writeupnotes", kwargs=dict(name=doc.name))))
737737

738+
writeups.append(("RFC Editor Note",
739+
text_from_writeup("changed_rfc_editor_note_text"),
740+
urlreverse("doc_ballot_rfceditornote", kwargs=dict(name=doc.name))))
741+
738742
elif doc.type_id == "charter":
739743
sections.append(("WG Review Announcement",
740744
"",

ietf/iesg/views.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ def agenda_json(request, date=None):
160160
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
161161
if e:
162162
docinfo['consensus'] = e.consensus
163+
164+
docinfo['rfc-ed-note'] = doc.has_rfc_editor_note()
165+
163166
elif doc.type_id == 'conflrev':
164167
docinfo['rev'] = doc.rev
165168
td = doc.relateddocument_set.get(relationship__slug='conflrev').target.document

ietf/secr/telechat/views.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,14 @@ def get_doc_writeup(doc):
5757
want to display the contents of the document
5858
'''
5959
writeup = 'This document has no writeup'
60-
if doc.type_id in ('draft','charter'):
60+
if doc.type_id == 'draft':
61+
latest = doc.latest_event(WriteupDocEvent, type='changed_ballot_writeup_text')
62+
if latest and doc.has_rfc_editor_note:
63+
rfced_note = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
64+
writeup = latest.text + "\n\n" + rfced_note.text
65+
else:
66+
writeup = latest.text
67+
if doc.type_id == 'charter':
6168
latest = doc.latest_event(WriteupDocEvent, type='changed_ballot_writeup_text')
6269
if latest:
6370
writeup = latest.text
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{% extends "base.html" %}
2+
{# Copyright The IETF Trust 2016, All Rights Reserved #}
3+
{% load origin %}
4+
5+
{% load bootstrap3 %}
6+
7+
{% block title %}RFC Editor Note for ballot for {{ doc }}{% endblock %}
8+
9+
{% block content %}
10+
{% origin %}
11+
12+
<h1>RFC Editor Note for ballot<br><small><a href="{% url "doc_view" name=doc.canonical_name %}">{{ doc }}</a></small></h1>
13+
14+
{% bootstrap_messages %}
15+
16+
<form method="post">
17+
{% csrf_token %}
18+
{% bootstrap_form ballot_rfceditornote_form %}
19+
20+
<div class="help-block">
21+
RFC Editor Note. This text will be appended to all announcements and messages to the RFC Editor.
22+
</div>
23+
24+
{% buttons %}
25+
<button type="submit" class="btn btn-primary" name="save_ballot_rfceditornote" value="Save Ballot RFC Editor Note">Save</button>
26+
<button type="submit" class="btn btn-warning" name="clear_ballot_rfceditornote" value="Clear Ballot RFC Editor Note">Clear</button>
27+
{% endbuttons %}
28+
</form>
29+
30+
{% endblock%}

0 commit comments

Comments
 (0)