Email IANA and RFC Editor when a draft is pulled from the queue at the

Datatracker, add page for editing IANA states, add more info to the
/doc/draft-XXXXX/doc.json dump for the RFC Editor, add page for
editing consensus, add page for requesting publication at the RFC
Editor for alternate streams (this will email the RFC Editor and set
the draft in the appropriate state), make it possible for alternate
streams to change the intended RFC status of a draft in the stream,
refactor how IANA copies are handled slightly so it's less code, put
drafts automatically in IANA Review "Need Review" state upon last
call, fix a bug in ballot issuing, remove a bit of dead code
 - Legacy-Id: 4857
This commit is contained in:
Ole Laursen 2012-09-17 16:15:45 +00:00
parent 5c89b8a51d
commit 6062e16c2d
22 changed files with 740 additions and 514 deletions

View file

@ -62,8 +62,26 @@ def email_stream_changed(request, doc, old_stream, new_stream, text=""):
"idrfc/stream_changed_email.txt",
dict(text=text,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
def email_pulled_from_rfc_queue(request, doc, comment, prev_state, next_state):
send_mail(request, ['IANA <iana@iana.org>', 'rfc-editor@rfc-editor.org'], None,
"%s changed state from %s to %s" % (doc.name, prev_state.name, next_state.name),
"idrfc/pulled_from_rfc_queue_email.txt",
dict(doc=doc,
prev_state=prev_state,
next_state=next_state,
comment=comment,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()),
extra=extra_automation_headers(doc))
def email_authors(request, doc, subject, text):
to = [x.strip() for x in doc.author_list().split(',')]
if not to:
return
send_mail_text(request, to, None, subject, text)
def html_to_text(html):
return strip_tags(html.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&").replace("<br>", "\n"))
@ -98,12 +116,15 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
def generate_ballot_writeup(request, doc):
e = doc.latest_event(type="iana_review")
iana = e.desc if e else ""
e = WriteupDocEvent()
e.type = "changed_ballot_writeup_text"
e.by = request.user.get_profile()
e.doc = doc
e.desc = u"Ballot writeup was generated"
e.text = unicode(render_to_string("idrfc/ballot_writeup.txt"))
e.text = unicode(render_to_string("idrfc/ballot_writeup.txt", {'iana': iana}))
e.save()
return e
@ -263,6 +284,20 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_approval_mail = generate_approval_mailREDESIGN
generate_approval_mail_rfc_editor = generate_approval_mail_rfc_editorREDESIGN
def generate_publication_request(request, doc):
group_description = ""
if doc.group and doc.group.acronym != "none":
group_description = doc.group.name
if doc.group.type_id in ("wg", "rg", "area"):
group_description += " %s (%s)" % (doc.group.type, doc.group.acronym)
return render_to_string("idrfc/publication_request.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
group_description=group_description,
)
)
def send_last_call_request(request, doc, ballot):
to = "iesg-secretary@ietf.org"
@ -425,39 +460,13 @@ def generate_issue_ballot_mailREDESIGN(request, doc, ballot):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_issue_ballot_mail = generate_issue_ballot_mailREDESIGN
def email_iana(request, doc, to, msg):
# fix up message and send message to IANA for each in ballot set
import email
parsed_msg = email.message_from_string(msg.encode("utf-8"))
for i in doc.idinternal.ballot_set():
extra = {}
extra["Reply-To"] = "noreply@ietf.org"
extra["X-IETF-Draft-string"] = i.document().filename
extra["X-IETF-Draft-revision"] = i.document().revision_display()
send_mail_text(request, "To: IANA <%s>" % to,
parsed_msg["From"], parsed_msg["Subject"],
parsed_msg.get_payload(),
extra=extra)
def email_ianaREDESIGN(request, doc, to, msg):
# fix up message and send it with extra info on doc in headers
import email
parsed_msg = email.message_from_string(msg.encode("utf-8"))
def extra_automation_headers(doc):
extra = {}
extra["Reply-To"] = "noreply@ietf.org"
extra["X-IETF-Draft-string"] = doc.name
extra["X-IETF-Draft-revision"] = doc.rev
send_mail_text(request, "IANA <%s>" % to,
parsed_msg["From"], parsed_msg["Subject"],
parsed_msg.get_payload(),
extra=extra)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_iana = email_ianaREDESIGN
return extra
def email_last_call_expired(doc):
text = "IETF Last Call has ended, and the state has been changed to\n%s." % doc.idinternal.cur_state.state

View file

@ -119,7 +119,61 @@ class ChangeStateTestCase(django.test.TestCase):
q = PyQuery(r.content)
self.assertEquals(len(q('.prev-state form input[name="state"]')), 1)
def test_pull_from_rfc_queue(self):
draft = make_test_data()
draft.set_state(State.objects.get(type="draft-iesg", slug="rfcqueue"))
url = urlreverse('doc_change_state', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
# change state
mailbox_before = len(outbox)
r = self.client.post(url,
dict(state=State.objects.get(type="draft-iesg", slug="review-e").pk,
substate="",
comment="Test comment"))
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.get_state_slug("draft-iesg"), "review-e")
self.assertEquals(len(outbox), mailbox_before + 2 + 1)
self.assertTrue(draft.name in outbox[-1]['Subject'])
self.assertTrue("changed state" in outbox[-1]['Subject'])
self.assertTrue("is no longer" in str(outbox[-1]))
self.assertTrue("Test comment" in str(outbox[-1]))
def test_change_iana_state(self):
draft = make_test_data()
first_state = State.objects.get(type="draft-iana-review", slug="need-rev")
next_state = State.objects.get(type="draft-iana-review", slug="ok-noact")
draft.set_state(first_state)
url = urlreverse('doc_change_iana_state', kwargs=dict(name=draft.name, state_type="iana-review"))
login_testing_unauthorized(self, "iana", url)
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form select[name=state]')), 1)
# faulty post
r = self.client.post(url, dict(state="foobarbaz"))
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(len(q('form ul.errorlist')) > 0)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.get_state("draft-iana-review"), first_state)
# change state
r = self.client.post(url, dict(state=next_state.pk))
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.get_state("draft-iana-review"), next_state)
def test_request_last_call(self):
draft = make_test_data()
draft.set_state(State.objects.get(type="draft-iesg", slug="ad-eval"))
@ -202,7 +256,7 @@ class EditInfoTestCase(django.test.TestCase):
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.ad, new_ad)
self.assertEquals(draft.note, "New note")
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="telechat_date"))
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))
self.assertEquals(draft.docevent_set.count(), events_before + 3)
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertTrue(draft.name in outbox[-1]['Subject'])
@ -221,14 +275,14 @@ class EditInfoTestCase(django.test.TestCase):
)
# add to telechat
self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat"))
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))
data["telechat_date"] = TelechatDate.objects.active()[0].date.isoformat()
r = self.client.post(url, data)
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertTrue(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat"))
self.assertEquals(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, TelechatDate.objects.active()[0].date)
self.assertTrue(draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))
self.assertEqual(draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date, TelechatDate.objects.active()[0].date)
# change telechat
data["telechat_date"] = TelechatDate.objects.active()[1].date.isoformat()
@ -236,7 +290,7 @@ class EditInfoTestCase(django.test.TestCase):
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, TelechatDate.objects.active()[1].date)
self.assertEqual(draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date, TelechatDate.objects.active()[1].date)
# remove from agenda
data["telechat_date"] = ""
@ -244,7 +298,7 @@ class EditInfoTestCase(django.test.TestCase):
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date)
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date)
def test_start_iesg_process_on_draft(self):
make_test_data()
@ -311,6 +365,18 @@ class EditInfoTestCase(django.test.TestCase):
self.assertEquals(events[-3].type, "started_iesg_process")
self.assertEquals(len(outbox), mailbox_before)
def test_edit_consensus(self):
draft = make_test_data()
url = urlreverse('doc_edit_consensus', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
self.assertTrue(not draft.latest_event(ConsensusDocEvent, type="changed_consensus"))
r = self.client.post(url, dict(consensus="Yes"))
self.assertEquals(r.status_code, 302)
self.assertEqual(draft.latest_event(ConsensusDocEvent, type="changed_consensus").consensus, True)
class ResurrectTestCase(django.test.TestCase):
fixtures = ['names']
@ -406,6 +472,16 @@ class AddCommentTestCase(django.test.TestCase):
self.assertTrue("updated" in outbox[-1]['Subject'])
self.assertTrue(draft.name in outbox[-1]['Subject'])
# Make sure we can also do it as IANA
self.client.login(remote_user="iana")
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form textarea[name=comment]')), 1)
class EditPositionTestCase(django.test.TestCase):
fixtures = ['names']
@ -670,12 +746,21 @@ class BallotWriteupsTestCase(django.test.TestCase):
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
# add a IANA review note
draft.set_state(State.objects.get(type="draft-iana-review", slug="not-ok"))
DocEvent.objects.create(type="iana_review",
doc=draft,
by=Person.objects.get(user__username="iana"),
desc="IANA does not approve of this document, it does not make sense.",
)
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('textarea[name=ballot_writeup]')), 1)
self.assertEquals(len(q('input[type=submit][value*="Save Ballot Writeup"]')), 1)
self.assertTrue("IANA does not" in r.content)
# save
r = self.client.post(url, dict(
@ -869,6 +954,44 @@ class MakeLastCallTestCase(django.test.TestCase):
self.assertTrue("Last Call" in outbox[-3]['Subject'])
self.assertTrue("Last Call" in draft.message_set.order_by("-time")[0].subject)
class RequestPublicationTestCase(django.test.TestCase):
fixtures = ['names']
def test_request_publication(self):
draft = make_test_data()
draft.stream = StreamName.objects.get(slug="iab")
draft.group = Group.objects.get(acronym="iab")
draft.intended_std_level = IntendedStdLevelName.objects.get(slug="inf")
draft.save()
draft.set_state(State.objects.get(type="draft-stream-iab", slug="approved"))
url = urlreverse('doc_request_publication', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "iabchair", url)
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
subject = q('input#id_subject')[0].get("value")
self.assertTrue("Document Action" in subject)
body = q('.request-publication #id_body').text()
self.assertTrue("Informational" in body)
self.assertTrue("IAB" in body)
# approve
mailbox_before = len(outbox)
r = self.client.post(url, dict(subject=subject, body=body))
self.assertEquals(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.get_state_slug("draft-stream-iab"), "rfc-edit")
self.assertEquals(len(outbox), mailbox_before + 2)
self.assertTrue("Document Action" in outbox[-2]['Subject'])
self.assertTrue("Document Action" in draft.message_set.order_by("-time")[0].subject)
# the IANA copy
self.assertTrue("Document Action" in outbox[-1]['Subject'])
class ExpireIDsTestCase(django.test.TestCase):
fixtures = ['names']
@ -1099,341 +1222,6 @@ class ExpireLastCallTestCase(django.test.TestCase):
self.assertEquals(draft.docevent_set.count(), events_before + 1)
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertTrue("Last Call Expired" in outbox[-1]["Subject"])
TEST_RFC_INDEX = '''<?xml version="1.0" encoding="UTF-8"?>
<rfc-index xmlns="http://www.rfc-editor.org/rfc-index"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.rfc-editor.org/rfc-index
http://www.rfc-editor.org/rfc-index.xsd">
<bcp-entry>
<doc-id>BCP0110</doc-id>
<is-also>
<doc-id>RFC4170</doc-id>
</is-also>
</bcp-entry>
<bcp-entry>
<doc-id>BCP0111</doc-id>
<is-also>
<doc-id>RFC4181</doc-id>
<doc-id>RFC4841</doc-id>
</is-also>
</bcp-entry>
<fyi-entry>
<doc-id>FYI0038</doc-id>
<is-also>
<doc-id>RFC3098</doc-id>
</is-also>
</fyi-entry>
<rfc-entry>
<doc-id>RFC1938</doc-id>
<title>A One-Time Password System</title>
<author>
<name>N. Haller</name>
</author>
<author>
<name>C. Metz</name>
</author>
<date>
<month>May</month>
<year>1996</year>
</date>
<format>
<file-format>ASCII</file-format>
<char-count>44844</char-count>
<page-count>18</page-count>
</format>
<keywords>
<kw>OTP</kw>
<kw>authentication</kw>
<kw>S/KEY</kw>
</keywords>
<abstract><p>This document describes a one-time password authentication system (OTP). [STANDARDS-TRACK]</p></abstract>
<obsoleted-by>
<doc-id>RFC2289</doc-id>
</obsoleted-by>
<current-status>PROPOSED STANDARD</current-status>
<publication-status>PROPOSED STANDARD</publication-status>
<stream>Legacy</stream>
</rfc-entry>
<rfc-entry>
<doc-id>RFC2289</doc-id>
<title>A One-Time Password System</title>
<author>
<name>N. Haller</name>
</author>
<author>
<name>C. Metz</name>
</author>
<author>
<name>P. Nesser</name>
</author>
<author>
<name>M. Straw</name>
</author>
<date>
<month>February</month>
<year>1998</year>
</date>
<format>
<file-format>ASCII</file-format>
<char-count>56495</char-count>
<page-count>25</page-count>
</format>
<keywords>
<kw>ONE-PASS</kw>
<kw>authentication</kw>
<kw>OTP</kw>
<kw>replay</kw>
<kw>attach</kw>
</keywords>
<abstract><p>This document describes a one-time password authentication system (OTP). The system provides authentication for system access (login) and other applications requiring authentication that is secure against passive attacks based on replaying captured reusable passwords. [STANDARDS- TRACK]</p></abstract>
<obsoletes>
<doc-id>RFC1938</doc-id>
</obsoletes>
<is-also>
<doc-id>STD0061</doc-id>
</is-also>
<current-status>STANDARD</current-status>
<publication-status>DRAFT STANDARD</publication-status>
<stream>Legacy</stream>
</rfc-entry>
<rfc-entry>
<doc-id>RFC3098</doc-id>
<title>How to Advertise Responsibly Using E-Mail and Newsgroups or - how NOT to $$$$$ MAKE ENEMIES FAST! $$$$$</title>
<author>
<name>T. Gavin</name>
</author>
<author>
<name>D. Eastlake 3rd</name>
</author>
<author>
<name>S. Hambridge</name>
</author>
<date>
<month>April</month>
<year>2001</year>
</date>
<format>
<file-format>ASCII</file-format>
<char-count>64687</char-count>
<page-count>28</page-count>
</format>
<keywords>
<kw>internet</kw>
<kw>marketing</kw>
<kw>users</kw>
<kw>service</kw>
<kw>providers</kw>
<kw>isps</kw>
</keywords>
<abstract><p>This memo offers useful suggestions for responsible advertising techniques that can be used via the internet in an environment where the advertiser, recipients, and the Internet Community can coexist in a productive and mutually respectful fashion. This memo provides information for the Internet community.</p></abstract>
<draft>draft-ietf-run-adverts-02</draft>
<is-also>
<doc-id>FYI0038</doc-id>
</is-also>
<current-status>INFORMATIONAL</current-status>
<publication-status>INFORMATIONAL</publication-status>
<stream>Legacy</stream>
</rfc-entry>
<rfc-entry>
<doc-id>RFC4170</doc-id>
<title>Tunneling Multiplexed Compressed RTP (TCRTP)</title>
<author>
<name>B. Thompson</name>
</author>
<author>
<name>T. Koren</name>
</author>
<author>
<name>D. Wing</name>
</author>
<date>
<month>November</month>
<year>2005</year>
</date>
<format>
<file-format>ASCII</file-format>
<char-count>48990</char-count>
<page-count>24</page-count>
</format>
<keywords>
<kw>real-time transport protocol</kw>
</keywords>
<abstract><p>This document describes a method to improve the bandwidth utilization of RTP streams over network paths that carry multiple Real-time Transport Protocol (RTP) streams in parallel between two endpoints, as in voice trunking. The method combines standard protocols that provide compression, multiplexing, and tunneling over a network path for the purpose of reducing the bandwidth used when multiple RTP streams are carried over that path. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</p></abstract>
<draft>draft-ietf-avt-tcrtp-08</draft>
<is-also>
<doc-id>BCP0110</doc-id>
</is-also>
<current-status>BEST CURRENT PRACTICE</current-status>
<publication-status>BEST CURRENT PRACTICE</publication-status>
<stream>IETF</stream>
<area>rai</area>
<wg_acronym>avt</wg_acronym>
</rfc-entry>
<rfc-entry>
<doc-id>RFC4181</doc-id>
<title>Guidelines for Authors and Reviewers of MIB Documents</title>
<author>
<name>C. Heard</name>
<title>Editor</title>
</author>
<date>
<month>September</month>
<year>2005</year>
</date>
<format>
<file-format>ASCII</file-format>
<char-count>102521</char-count>
<page-count>42</page-count>
</format>
<keywords>
<kw>standards-track specifications</kw>
<kw>management information base</kw>
<kw>review</kw>
</keywords>
<abstract><p>This memo provides guidelines for authors and reviewers of IETF standards-track specifications containing MIB modules. Applicable portions may be used as a basis for reviews of other MIB documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</p></abstract>
<draft>draft-ietf-ops-mib-review-guidelines-04</draft>
<updated-by>
<doc-id>RFC4841</doc-id>
</updated-by>
<is-also>
<doc-id>BCP0111</doc-id>
</is-also>
<current-status>BEST CURRENT PRACTICE</current-status>
<publication-status>BEST CURRENT PRACTICE</publication-status>
<stream>IETF</stream>
<area>rtg</area>
<wg_acronym>ospf</wg_acronym>
<errata-url>http://www.rfc-editor.org/errata_search.php?rfc=4181</errata-url>
</rfc-entry>
<rfc-entry>
<doc-id>RFC4841</doc-id>
<title>RFC 4181 Update to Recognize the IETF Trust</title>
<author>
<name>C. Heard</name>
<title>Editor</title>
</author>
<date>
<month>March</month>
<year>2007</year>
</date>
<format>
<file-format>ASCII</file-format>
<char-count>4414</char-count>
<page-count>3</page-count>
</format>
<keywords>
<kw>management information base</kw>
<kw> standards-track specifications</kw>
<kw>mib review</kw>
</keywords>
<abstract><p>This document updates RFC 4181, "Guidelines for Authors and Reviewers of MIB Documents", to recognize the creation of the IETF Trust. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</p></abstract>
<draft>draft-heard-rfc4181-update-00</draft>
<updates>
<doc-id>RFC4181</doc-id>
</updates>
<is-also>
<doc-id>BCP0111</doc-id>
</is-also>
<current-status>BEST CURRENT PRACTICE</current-status>
<publication-status>BEST CURRENT PRACTICE</publication-status>
<stream>IETF</stream>
<wg_acronym>NON WORKING GROUP</wg_acronym>
</rfc-entry>
<std-entry>
<doc-id>STD0061</doc-id>
<title>A One-Time Password System</title>
<is-also>
<doc-id>RFC2289</doc-id>
</is-also>
</std-entry>
</rfc-index>
'''
TEST_QUEUE = '''<rfc-editor-queue xmlns="http://www.rfc-editor.org/rfc-editor-queue">
<section name="IETF STREAM: WORKING GROUP STANDARDS TRACK">
<entry xml:id="draft-ietf-sipping-app-interaction-framework">
<draft>draft-ietf-sipping-app-interaction-framework-05.txt</draft>
<date-received>2005-10-17</date-received>
<state>EDIT</state>
<normRef>
<ref-name>draft-ietf-sip-gruu</ref-name>
<ref-state>IN-QUEUE</ref-state>
</normRef>
<authors>J. Rosenberg</authors>
<title>
A Framework for Application Interaction in the Session Initiation Protocol (SIP)
</title>
<bytes>94672</bytes>
<source>Session Initiation Proposal Investigation</source>
</entry>
</section>
<section name="IETF STREAM: NON-WORKING GROUP STANDARDS TRACK">
<entry xml:id="draft-ietf-sip-gruu">
<draft>draft-ietf-sip-gruu-15.txt</draft>
<date-received>2007-10-15</date-received>
<state>MISSREF</state>
<normRef>
<ref-name>draft-ietf-sip-outbound</ref-name>
<ref-state>NOT-RECEIVED</ref-state>
</normRef>
<authors>J. Rosenberg</authors>
<title>
Obtaining and Using Globally Routable User Agent (UA) URIs (GRUU) in the Session Initiation Protocol (SIP)
</title>
<bytes>95501</bytes>
<source>Session Initiation Protocol</source>
</entry>
</section>
<section name="IETF STREAM: WORKING GROUP INFORMATIONAL/EXPERIMENTAL/BCP">
</section>
<section name="IETF STREAM: NON-WORKING GROUP INFORMATIONAL/EXPERIMENTAL/BCP">
<entry xml:id="draft-thomson-beep-async">
<draft>draft-thomson-beep-async-02.txt</draft>
<date-received>2009-05-12</date-received>
<state>EDIT</state>
<state>IANA</state>
<authors>M. Thomson</authors>
<title>
Asynchronous Channels for the Blocks Extensible Exchange Protocol (BEEP)
</title>
<bytes>17237</bytes>
<source>IETF - NON WORKING GROUP</source>
</entry>
</section>
<section name="IAB STREAM">
</section>
<section name="IRTF STREAM">
</section>
<section name="INDEPENDENT SUBMISSIONS">
</section>
</rfc-editor-queue>
'''
class MirrorScriptTestCases(unittest.TestCase,RealDatabaseTest):
def setUp(self):
self.setUpRealDatabase()
def tearDown(self):
self.tearDownRealDatabase()
def testRfcIndex(self):
print " Testing rfc-index.xml parsing"
from ietf.idrfc.mirror_rfc_index import parse
data = parse(StringIO.StringIO(TEST_RFC_INDEX))
self.assertEquals(len(data), 6)
print "OK"
def testRfcEditorQueue(self):
print " Testing queue2.xml parsing"
from ietf.idrfc.mirror_rfc_editor_queue import parse_all
(drafts,refs) = parse_all(StringIO.StringIO(TEST_QUEUE))
self.assertEquals(len(drafts), 3)
self.assertEquals(len(refs), 3)
print "OK"
class IndividualInfoFormsTestCase(django.test.TestCase):

View file

@ -49,12 +49,13 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/emailposition/$', views_ballot.send_ballot_comment, name='doc_send_ballot_comment'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/$', views_doc.document_ballot, name="doc_ballot"),
(r'^(?P<name>[A-Za-z0-9._+-]+)/doc.json$', views_doc.document_debug),
(r'^(?P<name>[A-Za-z0-9._+-]+)/doc.json$', views_doc.document_json),
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballotpopup/$', views_doc.ballot_for_popup),
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot.tsv$', views_doc.ballot_tsv),
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot.json$', views_doc.ballot_json),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/$', views_edit.change_state, name='doc_change_state'), # IESG state
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/(?P<state_type>iana-action|iana-review)/$', views_edit.change_iana_state, name='doc_change_iana_state'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/info/$', views_edit.edit_info, name='doc_edit_info'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/requestresurrect/$', views_edit.request_resurrect, name='doc_request_resurrect'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/resurrect/$', views_edit.resurrect, name='doc_resurrect'),
@ -66,6 +67,8 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/telechat/$', views_edit.telechat_date, name='doc_change_telechat_date'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/iesgnote/$', views_edit.edit_iesg_note, name='doc_change_iesg_note'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ad/$', views_edit.edit_ad, name='doc_change_ad'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/consensus/$', views_edit.edit_consensus, name='doc_edit_consensus'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_edit.request_publication, name='doc_request_publication'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/deferballot/$', views_ballot.defer_ballot, name='doc_defer_ballot'),

View file

@ -2,6 +2,7 @@ from django.conf import settings
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
from ietf.idrfc.mails import *
from ietf.ietfauth.decorators import has_role
def add_document_comment(request, doc, text, ballot=None):
if request:
@ -175,3 +176,24 @@ def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_i
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
update_telechat = update_telechatREDESIGN
def can_edit_intended_std_level(doc, user):
return user.is_authenticated() and (
has_role(user, ["Secretariat", "Area Director"]) or
doc.group.role_set.filter(name__in=("chair", "auth", "delegate"), person__user=user)
)
def can_edit_consensus(doc, user):
return user.is_authenticated() and (
has_role(user, ["Secretariat", "Area Director"]) or
doc.group.role_set.filter(name__in=("chair", "auth", "delegate"), person__user=user)
)
def nice_consensus(consensus):
mapping = {
None: "Unknown",
True: "Yes",
False: "No"
}
return mapping[consensus]

View file

@ -818,8 +818,8 @@ def ballot_writeupnotesREDESIGN(request, name):
msg = generate_issue_ballot_mail(request, doc, ballot)
send_mail_preformatted(request, msg)
email_iana(request, doc, 'drafts-eval@icann.org', msg)
send_mail_preformatted(request, msg, extra=extra_automation_headers(doc),
override={ "To": "IANA <drafts-eval@icann.org>" })
e = DocEvent(doc=doc, by=login)
e.by = login
@ -1106,7 +1106,8 @@ def approve_ballotREDESIGN(request, name):
send_mail_preformatted(request, announcement)
if action == "to_announcement_list":
email_iana(request, doc, "drafts-approval@icann.org", announcement)
send_mail_preformatted(request, announcement, extra=extra_automation_headers(doc),
override={ "To": "IANA <drafts-approval@icann.org>" })
msg = infer_message(announcement)
msg.by = login
@ -1131,62 +1132,10 @@ class MakeLastCallForm(forms.Form):
@group_required('Secretariat')
def make_last_call(request, name):
"""Make last call for Internet Draft, sending out announcement."""
doc = get_object_or_404(InternetDraft, filename=name)
if not doc.idinternal:
raise Http404()
login = IESGLogin.objects.get(login_name=request.user.username)
ballot = doc.idinternal.ballot
docs = [i.document() for i in doc.idinternal.ballot_set()]
announcement = ballot.last_call_text
if request.method == 'POST':
form = MakeLastCallForm(request.POST)
if form.is_valid():
send_mail_preformatted(request, announcement)
email_iana(request, doc, "drafts-lastcall@icann.org", announcement)
doc.idinternal.change_state(IDState.objects.get(document_state_id=IDState.IN_LAST_CALL), None)
doc.idinternal.event_date = date.today()
doc.idinternal.save()
log_state_changed(request, doc, login)
doc.lc_sent_date = form.cleaned_data['last_call_sent_date']
doc.lc_expiration_date = form.cleaned_data['last_call_expiration_date']
doc.save()
comment = "Last call has been made for %s ballot and state has been changed to %s" % (doc.filename, doc.idinternal.cur_state.state)
email_owner(request, doc, doc.idinternal.job_owner, login, comment)
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
else:
initial = {}
initial["last_call_sent_date"] = date.today()
expire_days = 14
if doc.group_id == Acronym.INDIVIDUAL_SUBMITTER:
expire_days = 28
initial["last_call_expiration_date"] = date.today() + timedelta(days=expire_days)
form = MakeLastCallForm(initial=initial)
return render_to_response('idrfc/make_last_call.html',
dict(doc=doc,
docs=docs,
form=form),
context_instance=RequestContext(request))
@group_required('Secretariat')
def make_last_callREDESIGN(request, name):
"""Make last call for Internet Draft, sending out announcement."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.get_state("draft-iesg"):
raise Http404()
raise Http404
login = request.user.get_profile()
@ -1199,7 +1148,8 @@ def make_last_callREDESIGN(request, name):
form = MakeLastCallForm(request.POST)
if form.is_valid():
send_mail_preformatted(request, announcement)
email_iana(request, doc, "drafts-lastcall@icann.org", announcement)
send_mail_preformatted(request, announcement, extra=extra_automation_headers(doc),
override={ "To": "IANA <drafts-lastcall@icann.org>" })
msg = infer_message(announcement)
msg.by = login
@ -1234,7 +1184,14 @@ def make_last_callREDESIGN(request, name):
e.time = datetime.datetime.combine(form.cleaned_data['last_call_sent_date'], e.time.time())
e.expires = form.cleaned_data['last_call_expiration_date']
e.save()
# update IANA Review state
prev_state = doc.get_state("draft-iana-review")
if not prev_state:
next_state = State.objects.get(type="draft-iana-review", slug="need-rev")
doc.set_state(next_state)
add_state_change_event(doc, login, prev_state, next_state)
return HttpResponseRedirect(doc.get_absolute_url())
else:
initial = {}
@ -1251,7 +1208,3 @@ def make_last_callREDESIGN(request, name):
dict(doc=doc,
form=form),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
make_last_call = make_last_callREDESIGN

View file

@ -46,6 +46,7 @@ from django.conf import settings
from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, DocumentComment
from ietf.idtracker.templatetags.ietf_filters import format_textarea, fill
from ietf.idrfc import markup_txt
from ietf.idrfc.utils import *
from ietf.idrfc.models import RfcIndex, DraftVersions
from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper
from ietf.ietfworkflows.utils import get_full_info_for_draft
@ -371,7 +372,8 @@ def document_ballot(request, name, ballot_id=None):
),
context_instance=RequestContext(request))
def document_debug(request, name):
def document_json(request, name):
# old interface
r = re.compile("^rfc([1-9][0-9]*)$")
m = r.match(name)
if m:
@ -381,7 +383,36 @@ def document_debug(request, name):
else:
id = get_object_or_404(InternetDraft, filename=name)
doc = IdWrapper(draft=id)
return HttpResponse(doc.to_json(), mimetype='text/plain')
from idrfc_wrapper import jsonify_helper
if isinstance(doc, RfcWrapper):
data = jsonify_helper(doc, ['rfc_number', 'title', 'publication_date', 'maturity_level', 'obsoleted_by','obsoletes','updated_by','updates','also','has_errata','stream_name','file_types','in_ietf_process', 'friendly_state'])
else:
data = jsonify_helper(doc, ['draft_name', 'draft_status', 'latest_revision', 'rfc_number', 'title', 'tracker_id', 'publication_date','rfc_editor_state', 'replaced_by', 'replaces', 'in_ietf_process', 'file_types', 'group_acronym', 'stream_id','friendly_state', 'abstract', 'ad_name'])
if doc.in_ietf_process():
data['ietf_process'] = doc.ietf_process.dict()
# add some more fields using the new interface
d = get_object_or_404(Document, docalias__name=name)
data["authors"] = [
dict(name=e.person.name,
email=e.address,
affiliation=e.person.affiliation)
for e in Email.objects.filter(documentauthor__document=d).select_related("person").order_by("documentauthor__order")
]
e = d.latest_event(ConsensusDocEvent, type="changed_consensus")
data["consensus"] = e.consensus if e else None
data["stream"] = d.stream.name if d.stream else None
data["shepherd"] = d.shepherd.formatted_email() if d.shepherd else None
def state_name(s):
return s.name if s else None
data["iana_review_state"] = state_name(d.get_state("draft-iana-review"))
data["iana_action_state"] = state_name(d.get_state("draft-iana-action"))
return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain')
def _get_html(key, filename, split=True):
return get_document_content(key, filename, split=split, markup=True)
@ -437,7 +468,14 @@ def document_main_idrfc(request, name, tab):
info['is_rfc'] = False
info['conflict_reviews'] = [ rel.source for alias in id.docalias_set.all() for rel in alias.relateddocument_set.filter(relationship='conflrev') ]
info['rfc_editor_state'] = id.get_state("draft-rfceditor")
info['iana_review_state'] = id.get_state("draft-iana-review")
info['iana_action_state'] = id.get_state("draft-iana-action")
e = id.latest_event(ConsensusDocEvent, type="changed_consensus")
info["consensus"] = nice_consensus(e and e.consensus)
info["can_edit_consensus"] = can_edit_consensus(id, request.user)
info["can_edit_intended_std_level"] = can_edit_intended_std_level(id, request.user)
(content1, content2) = _get_html(
str(name)+","+str(id.revision)+",html",
os.path.join(settings.INTERNET_DRAFT_PATH, name+"-"+id.revision+".txt"))

View file

@ -3,7 +3,7 @@
import re, os
from datetime import datetime, date, time, timedelta
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.core.urlresolvers import reverse as urlreverse
from django.template.loader import render_to_string
@ -13,10 +13,10 @@ from django.utils.html import strip_tags
from django.db.models import Max
from django.conf import settings
from ietf.utils.mail import send_mail_text
from ietf.utils.mail import send_mail_text, send_mail_message
from ietf.ietfauth.decorators import group_required
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ietfauth.decorators import has_role
from ietf.ietfauth.decorators import has_role, role_required
from ietf.idtracker.models import *
from ietf.iesg.models import *
from ietf.idrfc.mails import *
@ -26,10 +26,13 @@ from ietf.idrfc.lastcall import request_last_call
from ietf.ietfworkflows.models import Stream
from ietf.ietfworkflows.utils import update_stream
from ietf.ietfworkflows.streams import get_stream_from_draft
from ietf.ietfworkflows.accounts import can_edit_state
from ietf.doc.models import *
from ietf.doc.utils import *
from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName
from ietf.person.models import Person, Email
from ietf.message.models import Message
class ChangeStateForm(forms.Form):
pass
@ -58,20 +61,21 @@ def change_stateREDESIGN(request, name):
if request.method == 'POST':
form = ChangeStateForm(request.POST)
if form.is_valid():
state = form.cleaned_data['state']
next_state = form.cleaned_data['state']
prev_state = doc.get_state("draft-iesg")
tag = form.cleaned_data['substate']
comment = form.cleaned_data['comment'].strip()
prev = doc.get_state("draft-iesg")
# tag handling is a bit awkward since the UI still works
# as if IESG tags are a substate
prev_tag = doc.tags.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))
prev_tag = prev_tag[0] if prev_tag else None
if state != prev or tag != prev_tag:
if next_state != prev_state or tag != prev_tag:
save_document_in_history(doc)
doc.set_state(state)
doc.set_state(next_state)
if prev_tag:
doc.tags.remove(prev_tag)
@ -79,7 +83,7 @@ def change_stateREDESIGN(request, name):
if tag:
doc.tags.add(tag)
e = log_state_changed(request, doc, login, prev, prev_tag)
e = log_state_changed(request, doc, login, prev_state, prev_tag)
if comment:
c = DocEvent(type="added_comment")
@ -96,7 +100,15 @@ def change_stateREDESIGN(request, name):
email_state_changed(request, doc, e.desc)
email_owner(request, doc, doc.ad, login, e.desc)
if state.slug == "lc-req":
if prev_state and prev_state.slug in ("ann", "rfcqueue") and next_state.slug not in ("rfcqueue", "pub"):
email_pulled_from_rfc_queue(request, doc, comment, prev_state, next_state)
if next_state.slug in ("iesg-eva", "lc"):
if not doc.get_state_slug("draft-iana-review"):
doc.set_state(State.objects.get(type="draft-iana-review", slug="rev-need"))
if next_state.slug == "lc-req":
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
@ -129,6 +141,7 @@ def change_stateREDESIGN(request, name):
return render_to_response('idrfc/change_stateREDESIGN.html',
dict(form=form,
doc=doc,
state=state,
prev_state=prev_state,
next_states=next_states,
to_iesg_eval=to_iesg_eval),
@ -138,6 +151,52 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
change_state = change_stateREDESIGN
ChangeStateForm = ChangeStateFormREDESIGN
class ChangeIanaStateForm(forms.Form):
state = forms.ModelChoiceField(State.objects.all(), required=False)
def __init__(self, state_type, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
choices = State.objects.filter(type=state_type).order_by("order").values_list("pk", "name")
self.fields['state'].choices = [("", "-------")] + list(choices)
@role_required('Secretariat', 'IANA')
def change_iana_state(request, name, state_type):
"""Change IANA review state of Internet Draft. Normally, this is done via
automatic sync, but this form allows one to set it manually."""
doc = get_object_or_404(Document, docalias__name=name)
state_type = doc.type_id + "-" + state_type
prev_state = doc.get_state(state_type)
if request.method == 'POST':
form = ChangeIanaStateForm(state_type, request.POST)
if form.is_valid():
next_state = form.cleaned_data['state']
if next_state != prev_state:
save_document_in_history(doc)
doc.set_state(next_state)
e = add_state_change_event(doc, request.user.get_profile(), prev_state, next_state)
doc.time = e.time
doc.save()
return HttpResponseRedirect(doc.get_absolute_url())
else:
form = ChangeIanaStateForm(state_type, initial=dict(state=prev_state.pk if prev_state else None))
return render_to_response('idrfc/change_iana_state.html',
dict(form=form,
doc=doc),
context_instance=RequestContext(request))
class ChangeStreamForm(forms.Form):
stream = forms.ModelChoiceField(StreamName.objects.exclude(slug="legacy"), required=False)
comment = forms.CharField(widget=forms.Textarea, required=False)
@ -197,13 +256,15 @@ class ChangeIntentionForm(forms.Form):
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.filter(used=True), empty_label="(None)", required=True, label="Intended RFC status")
comment = forms.CharField(widget=forms.Textarea, required=False)
@group_required('Area_Director','Secretariat')
def change_intention(request, name):
"""Change the intended publication status of a Document of type 'draft' , notifying parties
as necessary and logging the change as a comment."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.type_id=='draft':
raise Http404()
if doc.type_id != 'draft':
raise Http404
if not can_edit_intended_std_level(doc, request.user):
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
login = request.user.get_profile()
@ -617,7 +678,7 @@ def add_comment(request, name):
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@group_required('Area_Director', 'Secretariat', 'IANA')
@group_required('Area_Director', 'Secretariat', 'IANA', 'RFC Editor')
def add_commentREDESIGN(request, name):
"""Add comment to history of document."""
doc = get_object_or_404(Document, docalias__name=name)
@ -827,3 +888,116 @@ def edit_ad(request, name):
},
context_instance = RequestContext(request))
class ConsensusForm(forms.Form):
consensus = forms.ChoiceField(choices=(("", "Unknown"), ("Yes", "Yes"), ("No", "No")), required=True)
def edit_consensus(request, name):
"""Change whether the draft is a consensus document or not."""
doc = get_object_or_404(Document, type="draft", name=name)
if not can_edit_consensus(doc, request.user):
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
prev_consensus = e and e.consensus
if request.method == 'POST':
form = ConsensusForm(request.POST)
if form.is_valid():
if form.cleaned_data["consensus"] != bool(prev_consensus):
e = ConsensusDocEvent(doc=doc, type="changed_consensus", by=request.user.get_profile())
e.consensus = form.cleaned_data["consensus"] == "Yes"
e.desc = "Changed consensus to <b>%s</b> from %s" % (nice_consensus(e.consensus),
nice_consensus(prev_consensus))
e.save()
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
else:
form = ConsensusForm(initial=dict(consensus=nice_consensus(prev_consensus).replace("Unknown", "")))
return render_to_response('idrfc/change_consensus.html',
{'form': form,
'doc': doc,
},
context_instance = RequestContext(request))
class PublicationForm(forms.Form):
subject = forms.CharField(max_length=200, required=True)
body = forms.CharField(widget=forms.Textarea, required=True)
def request_publication(request, name):
"""Request publication by RFC Editor for a document which hasn't
been through the IESG ballot process."""
doc = get_object_or_404(Document, type="draft", name=name, stream__in=("iab", "ise", "irtf"))
if not can_edit_state(request.user, doc):
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
m = Message()
m.frm = request.user.get_profile().formatted_email()
m.to = "RFC Editor <rfc-editor@rfc-editor.org>"
m.by = request.user.get_profile()
next_state = State.objects.get(type="draft-stream-%s" % doc.stream.slug, slug="rfc-edit")
if request.method == 'POST' and not request.POST.get("reset"):
form = PublicationForm(request.POST)
if form.is_valid():
m.subject = form.cleaned_data["subject"]
m.body = form.cleaned_data["body"]
m.save()
if doc.group.acronym != "none":
m.related_groups = [doc.group]
m.related_docs = [doc]
send_mail_message(request, m)
# IANA copy
m.to = "IANA <drafts-approval@icann.org>"
send_mail_message(request, m, extra=extra_automation_headers(doc))
e = DocEvent(doc=doc, type="requested_publication", by=request.user.get_profile())
e.desc = "Sent request for publication to the RFC Editor"
e.save()
# change state
prev_state = doc.get_state(next_state.type)
doc.set_state(next_state)
e = add_state_change_event(doc, request.user.get_profile(), prev_state, next_state)
doc.time = e.time
doc.save()
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
else:
if doc.intended_std_level_id in ("std", "ds", "ps", "bcp"):
action = "Protocol Action"
else:
action = "Document Action"
from ietf.idrfc.templatetags.mail_filters import std_level_prompt
subject = "%s: '%s' to %s (%s-%s.txt)" % (action, doc.title, std_level_prompt(doc), doc.name, doc.rev)
body = generate_publication_request(request, doc)
form = PublicationForm(initial=dict(subject=subject,
body=body))
return render_to_response('idrfc/request_publication.html',
dict(form=form,
doc=doc,
message=m,
next_state=next_state,
),
context_instance = RequestContext(request))

View file

@ -108,14 +108,6 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
def can_edit_state(user, draft):
streamed = get_streamed_draft(draft)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES and (not streamed or not streamed.stream):
person = get_person_for_user(user)
if not person:
return False
return (is_secretariat(user) or
is_wgchair(person) or
is_wgdelegate(person))
return (is_secretariat(user) or
is_authorized_in_draft_stream(user, draft))

View file

@ -74,6 +74,8 @@ def edit_actions(context, wrapper):
if can_edit_state(user, draft):
actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name))))
if draft.stream_id in ("iab", "ise", "irtf"):
actions.append(("Request publication", urlreverse('doc_request_publication', kwargs=dict(name=doc.draft_name))))
if can_manage_shepherd_of_a_document(user, draft):
actions.append(("Change shepherd", urlreverse('doc_managing_shepherd', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))

View file

@ -97,7 +97,7 @@
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="iana-crd" model="name.doctagname">
<object pk="iana" model="name.doctagname">
<field type="CharField" name="name">IANA coordination</field>
<field type="TextField" name="desc">RFC-Editor/IANA Registration Coordination</field>
<field type="BooleanField" name="used">True</field>
@ -325,36 +325,6 @@
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="no" model="name.groupballotpositionname">
<field type="CharField" name="name">No</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="yes" model="name.groupballotpositionname">
<field type="CharField" name="name">Yes</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="abstain" model="name.groupballotpositionname">
<field type="CharField" name="name">Abstain</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="block" model="name.groupballotpositionname">
<field type="CharField" name="name">Block</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="norecord" model="name.groupballotpositionname">
<field type="CharField" name="name">No record</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">True</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="bof" model="name.groupstatename">
<field type="CharField" name="name">BOF</field>
<field type="TextField" name="desc"></field>
@ -751,8 +721,11 @@
<object pk="draft-iesg" model="doc.statetype">
<field type="CharField" name="label">IESG state</field>
</object>
<object pk="draft-iana" model="doc.statetype">
<field type="CharField" name="label">IANA state</field>
<object pk="draft-iana-action" model="doc.statetype">
<field type="CharField" name="label">IANA Action state</field>
</object>
<object pk="draft-iana-review" model="doc.statetype">
<field type="CharField" name="label">IANA Review state</field>
</object>
<object pk="draft-rfceditor" model="doc.statetype">
<field type="CharField" name="label">RFC Editor state</field>
@ -1003,6 +976,141 @@
<field type="IntegerField" name="order">6</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="104" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">rfcedack</field>
<field type="CharField" name="name">RFC-Ed-Ack</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="103" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">waitrfc</field>
<field type="CharField" name="name">Waiting on RFC Editor</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA has notified the RFC Editor that the actions have been completed</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="102" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">waitwgc</field>
<field type="CharField" name="name">Waiting on WGC</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA is waiting on the IETF Working Group Chairs to respond</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="101" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">waitad</field>
<field type="CharField" name="name">Waiting on ADs</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA is waiting on the IETF Area Directors to respond</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="100" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">waitauth</field>
<field type="CharField" name="name">Waiting on Authors</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA is waiting on the document's authors to respond</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="99" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">inprog</field>
<field type="CharField" name="name">In Progress</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA is currently processing the actions for this document</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="98" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">newdoc</field>
<field type="CharField" name="name">New Document</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">A new document has been received by IANA, but no actions have been taken</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="105" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">onhold</field>
<field type="CharField" name="name">On Hold</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA has suspended work on the document</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="106" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-action</field>
<field type="SlugField" name="slug">noic</field>
<field type="CharField" name="name">No IC</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">Request completed. There were no IANA actions for this document</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="107" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-review</field>
<field type="SlugField" name="slug">need-rev</field>
<field type="CharField" name="name">IANA Review Needed</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc"></field>
<field type="IntegerField" name="order">1</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="108" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-review</field>
<field type="SlugField" name="slug">ok-act</field>
<field type="CharField" name="name">IANA OK - Actions Needed</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly.</field>
<field type="IntegerField" name="order">2</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="109" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-review</field>
<field type="SlugField" name="slug">ok-noact</field>
<field type="CharField" name="name">IANA OK - No Actions Needed</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">Document requires no IANA action, and the IANA Considerations section indicates this correctly.</field>
<field type="IntegerField" name="order">3</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="110" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-review</field>
<field type="SlugField" name="slug">not-ok</field>
<field type="CharField" name="name">IANA Not OK</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">IANA has issues with the text of the IANA Considerations section of the document.</field>
<field type="IntegerField" name="order">4</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="111" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iana-review</field>
<field type="SlugField" name="slug">changed</field>
<field type="CharField" name="name">Version Changed - Review Needed</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">Document revision has changed after review by IANA.</field>
<field type="IntegerField" name="order">5</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="112" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-rfceditor</field>
<field type="SlugField" name="slug">auth48-done</field>
<field type="CharField" name="name">AUTH48-DONE</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">Final approvals are complete</field>
<field type="IntegerField" name="order">0</field>
<field to="doc.state" name="next_states" rel="ManyToManyRel"></field>
</object>
<object pk="16" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-iesg</field>
<field type="SlugField" name="slug">pub-req</field>
@ -1185,7 +1293,7 @@
</object>
<object pk="27" model="doc.state">
<field to="doc.statetype" name="type" rel="ManyToOneRel">draft-rfceditor</field>
<field type="SlugField" name="slug">iana-crd</field>
<field type="SlugField" name="slug">iana</field>
<field type="CharField" name="name">IANA</field>
<field type="BooleanField" name="used">True</field>
<field type="TextField" name="desc">RFC-Editor/IANA Registration Coordination</field>
@ -1780,4 +1888,4 @@
<field type="IntegerField" name="order">3</field>
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="block"></object><object pk="abstain"></object><object pk="norecord"></object></field>
</object>
</django-objects>
</django-objects>

View file

@ -45,6 +45,8 @@ IESG Note
(Insert IESG Note here or remove section)
IANA Note
{% if iana %}
{% load ietf_filters %}{% filter wordwrap:"68"|indent:2 %}{{ iana }}{% endfilter %}
{% endif %}
(Insert IANA Note here or remove section)
{% endautoescape%}

View file

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block title %}Change whether {{ doc.name }}-{{ doc.rev }} is the result of a consensus process{% endblock %}
{% block content %}
<h1>Change whether {{ doc.name }}-{{ doc.rev }} is the result of a consensus process</h1>
<form action="" method="POST">
<table>
{{ form.as_table }}
<tr>
<td></td>
<td class="actions">
<a href="{% url doc_view name=doc.name %}">Back</a>
<input type="submit" value="Submit"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block title %}Change IANA state of {{ doc }}{% endblock %}
{% block morecss %}
form table .actions { text-align: right; padding-top: 1em; }
{% endblock %}
{% block content %}
<h1>Change IANA state of {{ doc }}</h1>
<form action="" method="post">
<table>
{{ form.as_table }}
<tr>
<td colspan="2" class="actions">
<a href="{{ doc.get_absolute_url }}">Back</a>
<input type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -3,9 +3,9 @@
{% block title %}Change state of {{ doc }}{% endblock %}
{% block morecss %}
form.change-state select {
width: 22em;
}
form.change-state select { width: 22em; }
form.change-state #id_comment { width: 30em; }
form.change-state .cancel-pub-note { width: 30em; color: #a00; }
form.change-state .actions {
text-align: right;
padding-top: 10px;
@ -29,6 +29,14 @@ form.change-state .actions {
<form class="change-state" action="" method="post">
<table>
{{ form.as_table }}
{% if state and state.slug == "rfcqueue" %}
<tr>
<td></td>
<td class="cancel-pub-note">Note: if you pull the draft out of the
{{ state.name }} state, the RFC Editor and IANA will be notified
by email with this comment so they can update their queues.</td>
</tr>
{% endif %}
<tr>
<td colspan="2" class="actions">
<a href="{{ doc.get_absolute_url }}">Back</a>

View file

@ -56,12 +56,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% with info.conflict_reviews as r %}{% if r %}<tr><td>IETF Conflict Review:</td><td> {% filter urlize_ietf_docs %}{{ r|join:","}}{% endfilter %}</td></tr>{% endif %} {% endwith %}
<tr><td>Intended RFC status:</td><td>
{% with user|in_group:"Area_Director,Secretariat" as add_link %}
{% if add_link %}<a class="editlink" href="{% url doc_change_intended_status name=doc.draft_name %}">{% endif %}
<a{% if info.can_edit_intended_std_level %} class="editlink" href="{% url doc_change_intended_status name=doc.draft_name %}"{% endif %}>
{{ doc.underlying_document.intended_std_level|default:"-" }}
{% if add_link %}</a>{% endif %}
{% endwith %}
</td></tr>
</a>
</td></tr>
<tr><td>Other versions:</td>
{% ifequal doc.draft_status "Active" %}
@ -118,6 +116,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endif %}
<tr><td>Document shepherd:</td><td>{{ stream_info.shepherd|default:"" }}</td></tr>
<tr><td>Consensus:</td><td><a title="Whether the document is the result of a community consensus process as defined in RFC 5741" {% if info.can_edit_consensus %}class="editlink" href="{% url doc_edit_consensus name=doc.draft_name %}"{% endif %}>{{ info.consensus }}</a></td></tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
@ -127,7 +127,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% else %}
{{ doc.friendly_state|safe }}
{% endif %}
{% if doc.rfc_editor_state %}<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{doc.draft_name}}">{{ doc.rfc_editor_state|escape }}</a>{% endif %}
{% if info.iana_review_state %}<br />IANA Review State: <a class="editlink" {% if user|in_group:"Secretariat,IANA" %}href="{% url doc_change_iana_state name=doc.draft_name state_type="iana-review" %}"{% endif %}>{{ info.iana_review_state.name|escape }}</a>{% endif %}
{% if info.iana_action_state %}<br />IANA Action State: <a class="editlink" {% if user|in_group:"Secretariat,IANA" %}href="{% url doc_change_iana_state name=doc.draft_name state_type="iana-action" %}"{% endif %}>{{ info.iana_action_state.name|escape }}</a>{% endif %}
{% if info.rfc_editor_state %}<br />RFC Editor State: <a href="http://www.rfc-editor.org/queue2.html#{{ doc.draft_name }}">{{ info.rfc_editor_state|escape }}</a>{% endif %}
{% ifequal doc.draft_status "Expired" %}
{% if doc.resurrect_requested_by %}(resurrect requested by {{ doc.resurrect_requested_by }}){% endif %}
{% endifequal %}

View file

@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% include "idrfc/doc_diffs.html" %}
{% endif %}
<h2 style="margin-top:1em;">Document history</h2>
{% if user|in_group:"Area_Director,Secretariat,IANA" and doc.in_ietf_process %}
{% if user|in_group:"Area_Director,Secretariat,IANA,RFC Editor" and doc.in_ietf_process %}
<div style="margin-bottom:8px" id="history_actions">
<span id="doc_add_comment_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url doc_add_comment name=doc.draft_name %}">Add comment</a></span></span>
</div>

View file

@ -53,7 +53,7 @@
{% endif %}
<h2>Document history</h2>
{% if user|has_role:"Area Director,Secretariat,IANA" %}
{% if user|has_role:"Area Director,Secretariat,IANA,RFC Editor" %}
<div class="history-actions">
<a href="{% url doc_add_comment name=doc.name %}">Add comment</a>
</div>

View file

@ -22,7 +22,7 @@ with no "Discuss" positions, are needed for approval.
DISCUSSES AND COMMENTS
======================
{% filter wordwrap:79 %}{% for pos in ad_feedback %}{{ pos.ad.get_name }}:
{% filter wordwrap:79 %}{% for pos in ad_feedback %}{{ pos.ad }}:
{% if pos.discuss %}Discuss [{{ pos.discuss_time|date:"Y-m-d" }}]:
{{ pos.discuss }}

View file

@ -0,0 +1,16 @@
{% load mail_filters %}{% autoescape off %}{% filter wordwrap:73 %}
The document "{{ doc.title }}" <{{ doc.name }}> from the {{ doc.stream }} stream is ready for publication as {{ doc|std_level_prompt }}.{% if group_description %}
This document is the product of the {{ group_description }}.{% endif %}{% endfilter %}
URL: {{ doc_url }}
No IANA allocation in the document requires IETF Consensus or Standards Action.
[OPTIONAL: include summary of related discussion of this document in an IETF WG or in the IESG.]
[OPTIONAL: include statement of the purpose of publishing this document, its intended audience, its merits and significance.]
[OPTIONAL: include suggested names and contact information for one or more competent and independent potential reviewers for the document (this can speed the review and approval process).]
{% endautoescape %}

View file

@ -0,0 +1,9 @@
{% autoescape off %}{{ doc.name }} is no longer in the {{ prev_state.name }} state.
{% if comment %}Explanation:
{{ comment }}
{% endif %}
URL: {{ url }}
{% endautoescape %}

View file

@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block title %}Request publication for {{ doc }}{% endblock %}
{% block morecss %}
form.request-publication #id_subject,
form.request-publication #id_body { width: 50em; }
form.request-publication #id_body { height: 30em; }
{% endblock %}
{% block content %}
<h1>Request publication for {{ doc }}</h1>
<p>Send a publication request to the RFC Editor for {{ doc }} and move
it to the <em>{{ next_state.name }}</em> stream state.</p>
<p>Please edit the message and remove any parts in brackets you do not
fill in. For independent submissions, see the <a
href="http://www.rfc-editor.org/indsubs.html">guidelines</a>.</p>
<form class="request-publication" action="" method="POST">
<table>
<tr><td>From:</td> <td>{{ message.frm }}</td></tr>
<tr><td>To:</td> <td>{{ message.to }}</td></tr>
<tr><td>Subject:</td> <td>{{ form.subject }} {{ form.subject.errors }}</td></tr>
<tr><td style="vertical-align: top">Message:</td> <td>{{ form.body }} {{ form.body.errors }}</td></tr>
<tr class="actions"><td></td>
<td>
<a href="{% url doc_view name=doc.name %}">Back</a>
<input name="reset" type="submit" value="Reset"/>
<input type="submit" value="Send request to the RFC Editor"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -26,7 +26,7 @@ test_mode = False
outbox = []
def empty_outbox():
outbox[:] = []
outbox[:] = []
def add_headers(msg):
if not(msg.has_key('Message-ID')):
@ -187,16 +187,34 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F
msg['X-Tracker-Bcc']=bcc
copy_email(msg, copy_to)
def send_mail_preformatted(request, preformatted):
def send_mail_preformatted(request, preformatted, extra={}, override={}):
"""Parse preformatted string containing mail with From:, To:, ...,
and send it through the standard IETF mail interface (inserting
extra headers as needed)."""
msg = message_from_string(preformatted.encode("utf-8"))
extra = copy.copy(msg)
for key in ['To', 'From', 'Subject', ]:
del extra[key]
send_mail_text(request, msg['To'], msg["From"], msg["Subject"], msg.get_payload(), extra=extra)
def send_mail_message(request, message):
for k, v in override.iteritems():
if k in msg:
del msg[k]
msg[k] = v
headers = copy.copy(msg)
for key in ['To', 'From', 'Subject']:
del headers[key]
for k, v in extra.iteritems():
if k in headers:
del headers[k]
headers[k] = v
send_mail_text(request, msg['To'], msg["From"], msg["Subject"], msg.get_payload(), extra=headers)
def send_mail_message(request, message, extra={}):
"""Send a Message object."""
# note that this doesn't handle MIME messages at the moment
send_mail_text(request, message.to, message.frm, message.subject, message.body, cc=message.cc, bcc=message.bcc, extra={ 'Reply-to': message.reply_to })
e = extra.copy()
if message.reply_to:
e['Reply-to'] = message.reply_to
send_mail_text(request, message.to, message.frm, message.subject,
message.body, cc=message.cc, bcc=message.bcc, extra=e)