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 4b5ac9e09e

Note: SVN reference [10771] has been migrated to Git commit 46589ee421

Note: SVN reference [10772] has been migrated to Git commit c73659e95c

Note: SVN reference [10773] has been migrated to Git commit 2e6633c016
This commit is contained in:
Henrik Levkowetz 2016-02-05 22:18:14 +00:00
commit 174cfeafa3
16 changed files with 254 additions and 11 deletions

View file

@ -92,6 +92,17 @@ def generate_ballot_writeup(request, doc):
return e
def generate_ballot_rfceditornote(request, doc):
e = WriteupDocEvent()
e.type = "changed_ballot_rfceditornote_text"
e.by = request.user.person
e.doc = doc
e.desc = u"RFC Editor Note for ballot was generated"
e.text = unicode(render_to_string("doc/mail/ballot_rfceditornote.txt"))
e.save()
return e
def generate_last_call_announcement(request, doc):
expiration_date = datetime.date.today() + datetime.timedelta(days=14)
if doc.group.type_id in ("individ", "area"):

View file

@ -232,6 +232,10 @@ class DocumentInfo(models.Model):
else:
return None
def has_rfc_editor_note(self):
e = self.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
return bool(e and (e.text != ""))
def meeting_related(self):
answer = False
if self.type_id in ("agenda","minutes","bluesheets","slides","recording"):
@ -675,6 +679,7 @@ EVENT_TYPES = [
("changed_ballot_approval_text", "Changed ballot approval text"),
("changed_ballot_writeup_text", "Changed ballot writeup text"),
("changed_rfc_editor_note_text", "Changed RFC Editor Note text"),
("changed_last_call_text", "Changed last call text"),
("requested_last_call", "Requested last call"),

View file

@ -710,11 +710,19 @@ class DocTestCase(TestCase):
text="This is ballot writeup notes.",
by=Person.objects.get(name="(System)"))
rfced_note = WriteupDocEvent.objects.create(
doc=doc,
desc="Changed text",
type="changed_rfc_editor_note_text",
text="This is a note for the RFC Editor.",
by=Person.objects.get(name="(System)"))
url = urlreverse('doc_writeup', kwargs=dict(name=doc.name))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(appr.text in unicontent(r))
self.assertTrue(notes.text in unicontent(r))
self.assertTrue(rfced_note.text in r.content)
def test_history(self):
doc = make_test_data()

View file

@ -274,6 +274,45 @@ class BallotWriteupsTests(TestCase):
draft = Document.objects.get(name=draft.name)
self.assertTrue("This is a simple test" in draft.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text").text)
def test_edit_ballot_rfceditornote(self):
draft = make_test_data()
url = urlreverse('doc_ballot_rfceditornote', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
# add a note to the RFC Editor
WriteupDocEvent.objects.create(
doc=draft,
desc="Changed text",
type="changed_rfc_editor_note_text",
text="This is a note for the RFC Editor.",
by=Person.objects.get(name="(System)"))
# normal get
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('textarea[name=rfc_editor_note]')), 1)
self.assertTrue(q('[type=submit]:contains("Save")'))
self.assertTrue("<label class=\"control-label\">RFC Editor Note</label>" in r.content)
self.assertTrue("This is a note for the RFC Editor" in r.content)
# save with a note
r = self.client.post(url, dict(
rfc_editor_note="This is a simple test.",
save_ballot_rfceditornote="1"))
self.assertEqual(r.status_code, 200)
draft = Document.objects.get(name=draft.name)
self.assertTrue(draft.has_rfc_editor_note())
self.assertTrue("This is a simple test" in draft.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text").text)
# clear the existing note
r = self.client.post(url, dict(
rfc_editor_note=" ",
clear_ballot_rfceditornote="1"))
self.assertEqual(r.status_code, 200)
draft = Document.objects.get(name=draft.name)
self.assertFalse(draft.has_rfc_editor_note())
def test_issue_ballot(self):
draft = make_test_data()
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
@ -341,6 +380,55 @@ class BallotWriteupsTests(TestCase):
draft = Document.objects.get(name=draft.name)
self.assertTrue("Subject: Results of IETF-conflict review" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text)
def test_edit_verify_permissions(self):
def verify_fail(username, url):
if username:
self.client.login(username=username, password=username+"+password")
r = self.client.get(url)
self.assertEqual(r.status_code,403)
def verify_can_see(username, url):
self.client.login(username=username, password=username+"+password")
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
self.assertEqual(len(q("<textarea class=\"form-control\"")),1)
draft = make_test_data()
e = WriteupDocEvent()
e.type = "changed_ballot_approval_text"
e.by = Person.objects.get(name="(System)")
e.doc = draft
e.desc = u"Ballot approval text was generated"
e.text = u"Test approval text."
e.save()
e = WriteupDocEvent()
e.type = "changed_ballot_writeup_text"
e.by = Person.objects.get(name="(System)")
e.doc = draft
e.desc = u"Ballot writeup was generated"
e.text = u"Test ballot writeup text."
e.save()
e = WriteupDocEvent()
e.type = "changed_ballot_rfceditornote_text"
e.by = Person.objects.get(name="(System)")
e.doc = draft
e.desc = u"RFC Editor Note for ballot was generated"
e.text = u"Test note to the RFC Editor text."
e.save()
for p in ['doc_ballot_approvaltext','doc_ballot_writeupnotes','doc_ballot_rfceditornote']:
url = urlreverse(p, kwargs=dict(name=draft.name))
for username in ['plain','marschairman','iana','iab chair']:
verify_fail(username, url)
for username in ['secretary','ad']:
verify_can_see(username, url)
class ApproveBallotTests(TestCase):
def test_approve_ballot(self):

View file

@ -100,6 +100,7 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/undeferballot/$', views_ballot.undefer_ballot, name='doc_undefer_ballot'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/lastcalltext/$', views_ballot.lastcalltext, name='doc_ballot_lastcall'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ballotwriteupnotes/$', views_ballot.ballot_writeupnotes, name='doc_ballot_writeupnotes'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ballotrfceditornote/$', views_ballot.ballot_rfceditornote, name='doc_ballot_rfceditornote'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approvaltext/$', views_ballot.ballot_approvaltext, name='doc_ballot_approvaltext'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),

View file

@ -19,7 +19,8 @@ from ietf.doc.utils import ( add_state_change_event, close_ballot, close_open_ba
create_ballot_if_not_open, update_telechat )
from ietf.doc.mails import ( email_ballot_deferred, email_ballot_undeferred,
extra_automation_headers, generate_last_call_announcement,
generate_issue_ballot_mail, generate_ballot_writeup, generate_approval_mail )
generate_issue_ballot_mail, generate_ballot_writeup, generate_ballot_rfceditornote,
generate_approval_mail )
from ietf.doc.lastcall import request_last_call
from ietf.iesg.models import TelechatDate
from ietf.ietfauth.utils import has_role, role_required
@ -585,6 +586,57 @@ def ballot_writeupnotes(request, name):
),
context_instance=RequestContext(request))
class BallotRfcEditorNoteForm(forms.Form):
rfc_editor_note = forms.CharField(widget=forms.Textarea, label="RFC Editor Note", required=True)
def clean_rfc_editor_note(self):
return self.cleaned_data["rfc_editor_note"].replace("\r", "")
@role_required('Area Director','Secretariat')
def ballot_rfceditornote(request, name):
"""Editing of RFC Editor Note in the ballot"""
doc = get_object_or_404(Document, docalias__name=name)
login = request.user.person
existing = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
if not existing or (existing.text == ""):
existing = generate_ballot_rfceditornote(request, doc)
form = BallotRfcEditorNoteForm(auto_id=False, initial=dict(rfc_editor_note=existing.text))
if request.method == 'POST' and "save_ballot_rfceditornote" in request.POST:
form = BallotRfcEditorNoteForm(request.POST)
if form.is_valid():
t = form.cleaned_data["rfc_editor_note"]
if t != existing.text:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_rfc_editor_note_text"
e.desc = "RFC Editor Note was changed"
e.text = t.rstrip()
e.save()
if request.method == 'POST' and "clear_ballot_rfceditornote" in request.POST:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_rfc_editor_note_text"
e.desc = "RFC Editor Note was cleared"
e.text = ""
e.save()
# make sure form shows a blank RFC Editor Note
form = BallotRfcEditorNoteForm(initial=dict(rfc_editor_note=" "))
return render_to_response('doc/ballot/rfceditornote.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
ballot_rfceditornote_form=form,
),
context_instance=RequestContext(request))
class ApprovalTextForm(forms.Form):
approval_text = forms.CharField(widget=forms.Textarea, required=True)
@ -657,7 +709,19 @@ def approve_ballot(request, name):
if not e:
e = generate_ballot_writeup(request, doc)
ballot_writeup = e.text
error_duplicate_rfc_editor_note = False
e = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
if e and (e.text != ""):
if "RFC Editor Note" in ballot_writeup:
error_duplicate_rfc_editor_note = True
ballot_writeup += "\n\n" + e.text
if error_duplicate_rfc_editor_note:
return render_to_response('doc/draft/rfceditor_note_duplicate_error.html',
dict(doc=doc),
context_instance=RequestContext(request))
if "NOT be published" in approval_text:
action = "do_not_publish"
elif "To: RFC Editor" in approval_text:

View file

@ -735,6 +735,10 @@ def document_writeup(request, name):
text_from_writeup("changed_ballot_writeup_text"),
urlreverse("doc_ballot_writeupnotes", kwargs=dict(name=doc.name))))
writeups.append(("RFC Editor Note",
text_from_writeup("changed_rfc_editor_note_text"),
urlreverse("doc_ballot_rfceditornote", kwargs=dict(name=doc.name))))
elif doc.type_id == "charter":
sections.append(("WG Review Announcement",
"",

View file

@ -160,6 +160,9 @@ def agenda_json(request, date=None):
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
if e:
docinfo['consensus'] = e.consensus
docinfo['rfc-ed-note'] = doc.has_rfc_editor_note()
elif doc.type_id == 'conflrev':
docinfo['rev'] = doc.rev
td = doc.relateddocument_set.get(relationship__slug='conflrev').target.document

View file

@ -57,7 +57,14 @@ def get_doc_writeup(doc):
want to display the contents of the document
'''
writeup = 'This document has no writeup'
if doc.type_id in ('draft','charter'):
if doc.type_id == 'draft':
latest = doc.latest_event(WriteupDocEvent, type='changed_ballot_writeup_text')
if latest and doc.has_rfc_editor_note:
rfced_note = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
writeup = latest.text + "\n\n" + rfced_note.text
else:
writeup = latest.text
if doc.type_id == 'charter':
latest = doc.latest_event(WriteupDocEvent, type='changed_ballot_writeup_text')
if latest:
writeup = latest.text

View file

@ -0,0 +1,30 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2016, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
{% block title %}RFC Editor Note for ballot for {{ doc }}{% endblock %}
{% block content %}
{% origin %}
<h1>RFC Editor Note for ballot<br><small><a href="{% url "doc_view" name=doc.canonical_name %}">{{ doc }}</a></small></h1>
{% bootstrap_messages %}
<form method="post">
{% csrf_token %}
{% bootstrap_form ballot_rfceditornote_form %}
<div class="help-block">
RFC Editor Note. This text will be appended to all announcements and messages to the RFC Editor.
</div>
{% buttons %}
<button type="submit" class="btn btn-primary" name="save_ballot_rfceditornote" value="Save Ballot RFC Editor Note">Save</button>
<button type="submit" class="btn btn-warning" name="clear_ballot_rfceditornote" value="Clear Ballot RFC Editor Note">Clear</button>
{% endbuttons %}
</form>
{% endblock%}

View file

@ -1,5 +1,5 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2016, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
@ -18,7 +18,7 @@
{% bootstrap_form ballot_writeup_form %}
<div class="help-block">
Technical summary, Working Group summary, document quality, personnel, RFC Editor note, IRTF note, IESG note, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
Technical summary, Working Group summary, document quality, personnel, IRTF note, IESG note, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
</div>
{% buttons %}

View file

@ -0,0 +1,18 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2016, All Rights Reserved #}
{% load origin %}
{% block title %}Duplicate RFC Editor Note Error{% endblock %}
{% block content %}
{% origin %}
<h1>There appear to be two RFC Editor Notes for this approved I-D</h1>
<p>Sorry, there appears to be an RFC Editor Note in the ballot writeup and
another one in the RFC Editor Note. Please put all of the information for
the RFC Editor in the RFC Editor Note.</p>
<p>The document writeup can be edited
<a href="{% url "doc_view" name=doc.canonical_name %}writeup/"> <em>Here</em></a>.</p>
{% endblock %}

View file

@ -0,0 +1,5 @@
{% autoescape off %}
RFC Editor Note
(Insert RFC Editor Note here or remove section)
{% endautoescape%}

View file

@ -32,10 +32,6 @@ Personnel
experts(s), insert 'The IANA Expert(s) for the registries
in this document are <TO BE ADDED BY THE AD>.'
RFC Editor Note
(Insert RFC Editor Note here or remove section)
IRTF Note
(Insert IRTF Note here or remove section)

View file

@ -1,4 +1,4 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
{# Copyright The IETF Trust 2016, All Rights Reserved #}{% load origin %}{% origin %}
{% load ietf_filters ballot_icon %}
<div class="pull-right">{% ballot_icon doc %}</div>
@ -20,6 +20,9 @@
<span class="fa fa-file"></span></a>
{% endwith %}
<a href="{% url "doc_view" name=doc.canonical_name %}">{{ doc.canonical_name }}</a>
{% if doc.has_rfc_editor_note %}
<a href="{% url "doc_view" name=doc.canonical_name %}writeup/"> <em>(Has RFC Editor Note)</em></a>
{% endif %}
</dd>
<dt>{{ doc.intended_std_level }}</dt><dd><b>{{ doc.title }}</b></dd>

View file

@ -1,5 +1,5 @@
{% load ietf_filters %}{% with doc.rfc_number as rfc_number %}
o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %}
o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{%if doc.has_rfc_editor_note %} (Has RFC Editor Note){% endif %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %}
{% filter wordwrap:"68"|indent|indent %}{{ doc.title }} ({{ doc.intended_std_level }}){% endfilter %}
{% if doc.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.note|striptags }}{% endfilter %}
{% endif %} Token: {{ doc.ad }}{% if doc.iana_review_state %}