Skip to content

Commit 9dc13aa

Browse files
committed
Merged in personal/rjs/r6117-pubreq:
- Made Publication Request (for documents from IETF working groups) an explicit action rather than a side-effect. - Simplified the working group state edit form. - Added hints to the WG state edit form to use the document's main page to request publication. - If a document is moved into IESG processing directly by the secretariat or an AD (old processing path), set working group state accordingly. - Legacy-Id: 6120
1 parent 2a2389d commit 9dc13aa

11 files changed

Lines changed: 340 additions & 114 deletions

File tree

ietf/doc/tests_draft.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,24 @@ def test_start_iesg_process_on_draft(self):
328328
self.assertEquals(events[-3].type, "started_iesg_process")
329329
self.assertEquals(len(outbox), mailbox_before)
330330

331+
# Redo, starting in publication requested to make sure WG state is also set
332+
draft.unset_state('draft-iesg')
333+
draft.set_state(State.objects.get(type='draft-stream-ietf',slug='writeupw'))
334+
draft.stream = StreamName.objects.get(slug='ietf')
335+
draft.save()
336+
r = self.client.post(url,
337+
dict(intended_std_level=str(draft.intended_std_level_id),
338+
ad=ad.pk,
339+
create_in_state=State.objects.get(used=True, type="draft-iesg", slug="pub-req").pk,
340+
notify="test@example.com",
341+
note="This is a note",
342+
telechat_date="",
343+
))
344+
self.assertEquals(r.status_code, 302)
345+
draft = Document.objects.get(name=draft.name)
346+
self.assertEquals(draft.get_state_slug('draft-iesg'),'pub-req')
347+
self.assertEquals(draft.get_state_slug('draft-stream-ietf'),'sub-pub')
348+
331349
def test_edit_consensus(self):
332350
draft = make_test_data()
333351

@@ -867,6 +885,66 @@ def setUp(self):
867885
self.docname='draft-ietf-mars-test'
868886
self.doc = Document.objects.get(name=self.docname)
869887

888+
class SubmitToIesgTestCase(django.test.TestCase):
889+
fixtures = ['names']
890+
891+
def verify_permissions(self):
892+
893+
def verify_fail(remote_user):
894+
if remote_user:
895+
self.client.login(remote_user=remote_user)
896+
r = self.client.get(url)
897+
self.assertEquals(r.status_code,404)
898+
899+
def verify_can_see(remote_user):
900+
self.client.login(remote_user=remote_user)
901+
r = self.client.get(url)
902+
self.assertEquals(r.status_code,200)
903+
q = PyQuery(r.content)
904+
self.assertEquals(len(q('form input[name="confirm"]')),1)
905+
906+
url = urlreverse('doc_to_iesg', kwargs=dict(name=self.docname))
907+
908+
for username in [None,'plain','iana','iab chair']:
909+
verify_fail(username)
910+
911+
for username in ['marschairman','secretary','ad']:
912+
verify_can_see(username)
913+
914+
def cancel_submission(self):
915+
url = urlreverse('doc_to_iesg', kwargs=dict(name=self.docname))
916+
self.client.login(remote_user='marschairman')
917+
918+
r = self.client.post(url, dict(cancel="1"))
919+
self.assertEquals(r.status_code, 302)
920+
921+
doc = Document.objects.get(pk=self.doc.pk)
922+
self.assertTrue(doc.get_state('draft-iesg')==None)
923+
924+
def confirm_submission(self):
925+
url = urlreverse('doc_to_iesg', kwargs=dict(name=self.docname))
926+
self.client.login(remote_user='marschairman')
927+
928+
docevent_count_pre = self.doc.docevent_set.count()
929+
mailbox_before = len(outbox)
930+
931+
r = self.client.post(url, dict(confirm="1"))
932+
self.assertEquals(r.status_code, 302)
933+
934+
doc = Document.objects.get(pk=self.doc.pk)
935+
self.assertTrue(doc.get_state('draft-iesg').slug=='pub-req')
936+
self.assertTrue(doc.get_state('draft-stream-ietf').slug=='sub-pub')
937+
self.assertTrue(doc.ad!=None)
938+
self.assertTrue(doc.docevent_set.count() != docevent_count_pre)
939+
self.assertEquals(len(outbox), mailbox_before + 1)
940+
self.assertTrue("Publication has been requested" in outbox[-1]['Subject'])
941+
942+
def setUp(self):
943+
make_test_data()
944+
self.docname='draft-ietf-mars-test'
945+
self.doc = Document.objects.get(name=self.docname)
946+
self.doc.unset_state('draft-iesg')
947+
870948
class RequestPublicationTestCase(django.test.TestCase):
871949
fixtures = ['names']
872950

ietf/doc/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/state/(?P<state_type>iana-action|iana-review)/$', views_draft.change_iana_state, name='doc_change_iana_state'),
7070
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/info/$', views_draft.edit_info, name='doc_edit_info'),
7171
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/requestresurrect/$', views_draft.request_resurrect, name='doc_request_resurrect'),
72+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/submit-to-iesg/$', views_draft.to_iesg, name='doc_to_iesg'),
7273
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/resurrect/$', views_draft.resurrect, name='doc_resurrect'),
7374
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/addcomment/$', views_doc.add_comment, name='doc_add_comment'),
7475

ietf/doc/views_doc.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,11 @@ def document_main(request, name, rev=None):
318318
label += " (note that intended status is not set)"
319319
actions.append((label, urlreverse('doc_request_publication', kwargs=dict(name=doc.name))))
320320

321-
if doc.get_state_slug() != "expired" and doc.stream_id in ("ietf",) and can_edit and not iesg_state:
322-
actions.append(("Begin IESG Processing", urlreverse('doc_edit_info', kwargs=dict(name=doc.name)) + "?new=1"))
321+
if doc.get_state_slug() != "expired" and doc.stream_id in ("ietf",):
322+
if not iesg_state and can_edit:
323+
actions.append(("Begin IESG Processing", urlreverse('doc_edit_info', kwargs=dict(name=doc.name)) + "?new=1"))
324+
elif can_edit_stream_info and (not iesg_state or iesg_state.slug == 'watching'):
325+
actions.append(("Submit to IESG for Publication", urlreverse('doc_to_iesg', kwargs=dict(name=doc.name))))
323326

324327
return render_to_response("doc/document_draft.html",
325328
dict(doc=doc,

ietf/doc/views_draft.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,105 @@ def get_initial_notify(doc):
374374
receivers.append("%s@%s" % (doc.name, settings.TOOLS_SERVER))
375375
return ", ".join(receivers)
376376

377+
def to_iesg(request,name):
378+
""" Submit an IETF stream document to the IESG for publication """
379+
doc = get_object_or_404(Document, docalias__name=name, stream='ietf')
380+
381+
if doc.get_state_slug('draft') == "expired" or doc.get_state_slug('draft-iesg') == 'pub-req' :
382+
raise Http404()
383+
384+
if not is_authorized_in_doc_stream(request.user, doc):
385+
raise Http404()
386+
387+
target_state={
388+
'iesg' : State.objects.get(type='draft-iesg',slug='pub-req'),
389+
'wg' : State.objects.get(type='draft-stream-ietf',slug='sub-pub'),
390+
}
391+
392+
warn={}
393+
if not doc.intended_std_level:
394+
warn['intended_std_level'] = True
395+
if not doc.shepherd:
396+
warn['shepherd'] = True
397+
shepherd_writeup = doc.latest_event(WriteupDocEvent, type="changed_protocol_writeup")
398+
if not shepherd_writeup:
399+
warn['shepherd_writeup'] = True
400+
tags = doc.tags.filter(slug__in=get_tags_for_stream_id(doc.stream_id))
401+
if tags:
402+
warn['tags'] = True
403+
notify = doc.notify
404+
if not notify:
405+
notify = get_initial_notify(doc)
406+
ad = doc.ad or doc.group.ad
407+
408+
if request.method == 'POST':
409+
410+
if request.POST.get("confirm", ""):
411+
412+
save_document_in_history(doc)
413+
414+
login = request.user.get_profile()
415+
416+
changes = []
417+
418+
if not doc.get_state("draft-iesg"):
419+
420+
e = DocEvent()
421+
e.type = "started_iesg_process"
422+
e.by = login
423+
e.doc = doc
424+
e.desc = "IESG process started in state <b>%s</b>" % target_state['iesg'].name
425+
e.save()
426+
427+
if not doc.get_state('draft-iesg')==target_state['iesg']:
428+
doc.set_state(target_state['iesg'])
429+
changes.append("IESG state set to %s" % target_state['iesg'].name)
430+
if not doc.get_state('draft-ietf-stream')==target_state['wg']:
431+
doc.set_state(target_state['wg'])
432+
changes.append("Working group state set to %s" % target_state['wg'].name)
433+
434+
if not doc.ad == ad :
435+
doc.ad = ad
436+
changes.append("Responsible AD changed to %s" % doc.ad)
437+
438+
if not doc.notify == notify :
439+
doc.notify = notify
440+
changes.append("State Change Notice email list changed to %s" % doc.notify)
441+
442+
for c in changes:
443+
e = DocEvent(doc=doc, by=login)
444+
e.desc = c
445+
e.type = "changed_document"
446+
e.save()
447+
448+
# Is this still necessary? I remember Henrik planning to have the model take care of this.
449+
doc.time = datetime.datetime.now()
450+
451+
doc.save()
452+
453+
extra = {}
454+
extra['Cc'] = "%s-chairs@tools.ietf.org, iesg-secretary@ietf.org, %s" % (doc.group.acronym,doc.notify)
455+
send_mail(request=request,
456+
to = doc.ad.email_address(),
457+
frm = login.formatted_email(),
458+
subject = "Publication has been requested for %s-%s" % (doc.name,doc.rev),
459+
template = "doc/submit_to_iesg_email.txt",
460+
context = dict(doc=doc,login=login,url="%s%s"%(settings.IDTRACKER_BASE_URL,doc.get_absolute_url()),),
461+
extra = extra)
462+
463+
return HttpResponseRedirect(doc.get_absolute_url())
464+
465+
return render_to_response('doc/submit_to_iesg.html',
466+
dict(doc=doc,
467+
warn=warn,
468+
target_state=target_state,
469+
ad=ad,
470+
shepherd_writeup=shepherd_writeup,
471+
tags=tags,
472+
notify=notify,
473+
),
474+
context_instance=RequestContext(request))
475+
377476
@role_required('Area Director','Secretariat')
378477
def edit_info(request, name):
379478
"""Edit various Internet Draft attributes, notifying parties as
@@ -404,6 +503,18 @@ def edit_info(request, name):
404503
if new_document:
405504
doc.set_state(r['create_in_state'])
406505

506+
# Is setting the WG state here too much of a hidden side-effect?
507+
if r['create_in_state'].slug=='pub-req':
508+
if doc.stream and ( doc.stream.slug=='ietf' ) and doc.group and ( doc.group.type.name=='WG'):
509+
submitted_state = State.objects.get(type='draft-stream-ietf',slug='sub-pub')
510+
doc.set_state(submitted_state)
511+
e = DocEvent()
512+
e.type = "changed_document"
513+
e.by = login
514+
e.doc = doc
515+
e.desc = "Working group state set to %s" % submitted_state.name
516+
e.save()
517+
407518
# fix so Django doesn't barf in the diff below because these
408519
# fields can't be NULL
409520
doc.ad = r['ad']

ietf/ietfworkflows/forms.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ def save(self):
156156

157157
class DraftTagsStateForm(StreamDraftForm):
158158

159+
new_state = forms.ChoiceField(label='State')
160+
weeks = forms.IntegerField(label='Expected weeks in state',required=False)
159161
comment = forms.CharField(widget=forms.Textarea, required=False)
160-
new_state = forms.ChoiceField()
161-
weeks = forms.IntegerField(required=False)
162162
tags = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False)
163163

164164
template = 'ietfworkflows/state_form.html'
@@ -167,6 +167,9 @@ def __init__(self, *args, **kwargs):
167167
super(DraftTagsStateForm, self).__init__(*args, **kwargs)
168168
self.state = get_state_for_draft(self.draft)
169169
self.fields['new_state'].choices = self.get_states()
170+
self.fields['new_state'].initial = self.state.pk
171+
if self.draft.stream_id == 'ietf':
172+
self.fields['new_state'].help_text = "Only select 'Submitted to IESG for Publication' to correct errors. Use the document's main page to request publication."
170173
if self.is_bound:
171174
for key, value in self.data.items():
172175
if key.startswith('transition_'):

ietf/ietfworkflows/tests.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ def test_set_tags(self):
7272
unused = draft.group.unused_tags.values_list("slug", flat=True)
7373
for t in q("input[name=tags]"):
7474
self.assertTrue(t.attrib["value"] not in unused)
75-
self.assertEquals(len(q('form input[type=submit][name=only_tags]')), 1)
7675

7776
# set tags
7877
mailbox_before = len(outbox)
@@ -115,10 +114,26 @@ def test_set_state(self):
115114
self.assertTrue(t.attrib["value"] not in unused)
116115
self.assertEquals(len(q('select[name=new_state]')), 1)
117116

118-
# set state
117+
old_state = draft.get_state("draft-stream-%s" % draft.stream_id )
119118
new_state = State.objects.get(used=True, type="draft-stream-%s" % draft.stream_id, slug="parked")
119+
self.assertTrue(old_state!=new_state)
120120
mailbox_before = len(outbox)
121121
events_before = draft.docevent_set.count()
122+
123+
# First make sure cancel doesn't change anything
124+
r = self.client.post(url,
125+
dict(comment="some comment",
126+
weeks="10",
127+
tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))],
128+
new_state=new_state.pk,
129+
cancel="1",
130+
))
131+
self.assertEquals(r.status_code, 302)
132+
133+
draft = Document.objects.get(pk=draft.pk)
134+
self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), old_state)
135+
136+
# Set new state
122137
r = self.client.post(url,
123138
dict(comment="some comment",
124139
weeks="10",

ietf/ietfworkflows/views.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,16 @@ def _edit_draft_stream(request, draft, form_class=DraftTagsStateForm):
7070
if request.method == 'POST':
7171
form = form_class(user=user, draft=draft, data=request.POST)
7272
form.request = request
73+
if request.POST.get("cancel",""):
74+
return HttpResponseRedirect(draft.get_absolute_url())
7375
if form.is_valid():
7476
form.save()
75-
if form_class == NoWorkflowStateForm and settings.USE_DB_REDESIGN_PROXY_CLASSES:
76-
return HttpResponseRedirect(urlreverse('ietf.ietfworkflows.views.edit_state', kwargs={ 'name': draft.filename } ))
77+
78+
# This behavior surprises folks. Let's try running awhile without it.
79+
#if form_class == NoWorkflowStateForm and settings.USE_DB_REDESIGN_PROXY_CLASSES:
80+
# return HttpResponseRedirect(urlreverse('ietf.ietfworkflows.views.edit_state', kwargs={ 'name': draft.filename } ))
7781

78-
return HttpResponseRedirect('.')
82+
return HttpResponseRedirect(draft.get_absolute_url())
7983
else:
8084
form = form_class(user=user, draft=draft)
8185
form.request = request
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}
4+
Publication Request for {{doc.name}}-{{doc.rev}}
5+
{% endblock %}
6+
7+
{% block content %}
8+
<h1>Publication Request for {{doc.name}}-{{doc.rev}}</h1>
9+
10+
<div>
11+
Please verify the following information:
12+
</div>
13+
14+
<div style="margin-left:10px;margin-top:20px;margin-bottom:20px;">
15+
<table>
16+
17+
<tr>
18+
<td>Intended Status Level:</td>
19+
<td>{% if warn.intended_std_level %}<img src="/images/warning.png"/>{% endif %}</td>
20+
<td>{{doc.intended_std_level}}</td>
21+
</tr>
22+
23+
<tr>
24+
<td>Responsible AD:</td>
25+
<td></td>
26+
<td>{{ad}}</td>
27+
</tr>
28+
29+
<tr>
30+
<td>Document Shepherd:</td>
31+
<td>{% if warn.shepherd %}<img src="/images/warning.png"/>{% endif %}</td>
32+
<td>{{doc.shepherd}}</td>
33+
</tr>
34+
35+
<tr>
36+
<td>Shepherd Write-Up Exists:</td>
37+
<td>{% if warn.shepherd_writeup %}<img src="/images/warning.png"/>{% endif %}</td>
38+
<td>{%if shepherd_writeup %}Yes{%else%}No{%endif%}</td>
39+
</tr>
40+
41+
<tr>
42+
<td>Also Notify:</td>
43+
<td></td>
44+
<td>{% if notify %}{{notify}}{%else%}(None){%endif%}</td>
45+
</tr>
46+
47+
<tr>
48+
<td>Annotation Tags:</td>
49+
<td>{% if warn.tags %}<img src="/images/warning.png"/>{% endif %}</td>
50+
<td>{% if not tags %}(None){%else%}{% for tag in tags %}{{ tag }}{% if not forloop.last%}, {%endif%}{% endfor %}{% endif %}</td>
51+
</tr>
52+
53+
</table>
54+
</div>
55+
56+
57+
58+
{% if warn %}
59+
<div style="margin-top:20px; margin-bottom:20px;">
60+
<img src="/images/warning.png"/> indicates the document might not be ready for submission. Please check each instance carefully to see if changes need to be made to the document's state before submitting.
61+
</div>
62+
{% endif %}
63+
64+
<div>
65+
Upon submission:
66+
<ul>
67+
<li> the document will be placed into the IESG '{{target_state.iesg}}' state</li>
68+
<li> the document will be placed into the working group '{{target_state.wg}}' state</li>
69+
{% if not ad == doc.ad %}<li> the responsible AD will be set as above </li>{% endif %}
70+
{% if not notify == doc.notify %}<li> the document's state change notification list will be set as above </li>{% endif %}
71+
<li> an entry will be made noting the publication request in the document's history</li>
72+
<li> an email message will be sent to the working group chairs, the secretariat, and everyone listed above</li>
73+
</div>
74+
75+
<form action="" method="POST">
76+
<input type="submit" name="confirm" value="Request Publication"/>
77+
<input type="submit" name="cancel" value="Cancel"/>
78+
</form>
79+
80+
{% endblock %}

0 commit comments

Comments
 (0)