Skip to content

Commit becd10a

Browse files
committed
Merged [5570] from rjsparks@nostrum.com:
Moves the interface to edit shepherds and sheperd writeups out of the wgtracker interface and into the doc views (via idrfc). Added a little to the simple authorization code (can_edit_) in idrfc. Added a way to reset the writeup to the current templates (leaving extension points to take templates for non-ietf stream documents). Fixes bug ietf-tools#967. Implementation notes: Currently reused EmailsField -- in the long run a better approach will be to build a similar PersonField. This also fixes tickets ietf-tools#834 and ietf-tools#691 (which were effectively duplicates of ietf-tools#967) - Legacy-Id: 5631 Note: SVN reference [5570] has been migrated to Git commit 3bc9e9a
1 parent 68b697c commit becd10a

12 files changed

Lines changed: 235 additions & 225 deletions

File tree

ietf/idrfc/testsREDESIGN.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,89 @@ def test_doc_change_ad(self):
13661366
self.assertEquals(self.doc.ad,ad2)
13671367
self.assertTrue(self.doc.latest_event(DocEvent,type="added_comment").desc.startswith('Shepherding AD changed'))
13681368

1369+
def test_doc_change_shepherd(self):
1370+
url = urlreverse('doc_edit_shepherd',kwargs=dict(name=self.docname))
1371+
1372+
login_testing_unauthorized(self, "plain", url)
1373+
1374+
r = self.client.get(url)
1375+
self.assertEquals(r.status_code,403)
1376+
1377+
# get as the secretariat (and remain secretariat)
1378+
login_testing_unauthorized(self, "secretary", url)
1379+
1380+
r = self.client.get(url)
1381+
self.assertEquals(r.status_code,200)
1382+
q = PyQuery(r.content)
1383+
self.assertEquals(len(q('form input[id=id_shepherd]')),1)
1384+
1385+
# change the shepherd
1386+
plain = Person.objects.get(name='Plain Man')
1387+
plain_email = plain.email_set.all()[0]
1388+
r = self.client.post(url,dict(shepherd=plain_email))
1389+
self.assertEquals(r.status_code,302)
1390+
self.doc = Document.objects.get(name=self.docname)
1391+
self.assertEquals(self.doc.shepherd,plain)
1392+
self.assertTrue(self.doc.latest_event(DocEvent,type="added_comment").desc.startswith('Document shepherd changed to Plain Man'))
1393+
1394+
ad = Person.objects.get(name='Aread Irector')
1395+
two_answers = "%s,%s" % (plain_email, ad.email_set.all()[0])
1396+
r = self.client.post(url,(dict(shepherd=two_answers)))
1397+
self.assertEquals(r.status_code,200)
1398+
q = PyQuery(r.content)
1399+
self.assertTrue(len(q('form ul.errorlist')) > 0)
1400+
1401+
def test_doc_view_shepherd_writeup(self):
1402+
url = urlreverse('doc_shepherd_writeup',kwargs=dict(name=self.docname))
1403+
1404+
# get as a shepherd
1405+
self.client.login(remote_user="plain")
1406+
1407+
r = self.client.get(url)
1408+
self.assertEquals(r.status_code,200)
1409+
q = PyQuery(r.content)
1410+
self.assertEquals(len(q('span[id=doc_edit_shepherd_writeup]')),1)
1411+
1412+
# Try again when no longer a shepherd.
1413+
1414+
self.doc.shepherd = None
1415+
r = self.client.get(url)
1416+
self.assertEquals(r.status_code,200)
1417+
q = PyQuery(r.content)
1418+
self.assertEquals(len(q('span[id=doc_edit_shepherd_writeup]')),1)
1419+
1420+
def test_doc_change_shepherd_writeup(self):
1421+
url = urlreverse('doc_edit_shepherd_writeup',kwargs=dict(name=self.docname))
1422+
1423+
# get
1424+
login_testing_unauthorized(self, "secretary", url)
1425+
1426+
r = self.client.get(url)
1427+
self.assertEquals(r.status_code,200)
1428+
q = PyQuery(r.content)
1429+
self.assertEquals(len(q('form textarea[id=id_content]')),1)
1430+
1431+
# direct edit
1432+
r = self.client.post(url,dict(content='here is a new writeup',submit_response="1"))
1433+
print r.content
1434+
self.assertEquals(r.status_code,302)
1435+
self.doc = Document.objects.get(name=self.docname)
1436+
self.assertTrue(self.doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup").text.startswith('here is a new writeup'))
1437+
1438+
# file upload
1439+
test_file = StringIO.StringIO("This is a different writeup.")
1440+
test_file.name = "unnamed"
1441+
r = self.client.post(url,dict(txt=test_file,submit_response="1"))
1442+
self.assertEquals(r.status_code, 302)
1443+
doc = Document.objects.get(name=self.docname)
1444+
self.assertTrue(self.doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup").text.startswith('This is a different writeup.'))
1445+
1446+
# template reset
1447+
r = self.client.post(url,dict(txt=test_file,reset_text="1"))
1448+
self.assertEquals(r.status_code, 200)
1449+
q = PyQuery(r.content)
1450+
self.assertTrue(q('textarea')[0].text.startswith("As required by RFC 4858"))
1451+
13691452
def setUp(self):
13701453
make_test_data()
13711454
self.docname='draft-ietf-mars-test'

ietf/idrfc/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
url(r'^(?P<name>[A-Za-z0-9._+-]+)/((?P<rev>[0-9-]+)/)?$', views_doc.document_main, name="doc_view"),
5050
url(r'^(?P<name>[A-Za-z0-9._+-]+)/history/$', views_doc.document_history, name="doc_history"),
5151
url(r'^(?P<name>[A-Za-z0-9._+-]+)/writeup/$', views_doc.document_writeup, name="doc_writeup"),
52+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/shepherdwriteup/$', views_doc.document_shepherd_writeup, name="doc_shepherd_writeup"),
5253
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/position/$', views_ballot.edit_position),
5354
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'),
5455
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"),
@@ -72,6 +73,8 @@
7273
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/iesgnote/$', views_edit.edit_iesg_note, name='doc_change_iesg_note'),
7374
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ad/$', views_edit.edit_ad, name='doc_change_ad'),
7475
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/consensus/$', views_edit.edit_consensus, name='doc_edit_consensus'),
76+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/shepherd/$', views_edit.edit_shepherd, name='doc_edit_shepherd'),
77+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/shepherdwriteup/$', views_edit.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'),
7578
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_edit.request_publication, name='doc_request_publication'),
7679

7780
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'),

ietf/idrfc/utils.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,20 @@ def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_i
177177
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
178178
update_telechat = update_telechatREDESIGN
179179

180-
def can_edit_intended_std_level(doc, user):
180+
def can_edit_base(doc, user):
181181
return user.is_authenticated() and (
182182
has_role(user, ["Secretariat", "Area Director"]) or
183183
doc.group.role_set.filter(name__in=("chair", "auth", "delegate"), person__user=user)
184184
)
185185

186-
def can_edit_consensus(doc, user):
186+
can_edit_intended_std_level = can_edit_base
187+
can_edit_consensus = can_edit_base
188+
can_edit_shepherd = can_edit_base
189+
190+
def can_edit_shepherd_writeup(doc, user):
187191
return user.is_authenticated() and (
188-
has_role(user, ["Secretariat", "Area Director"]) or
189-
doc.group.role_set.filter(name__in=("chair", "auth", "delegate"), person__user=user)
192+
can_edit_base(doc,user) or
193+
(doc.shepherd==user.person)
190194
)
191195

192196
def nice_consensus(consensus):

ietf/idrfc/views_doc.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,21 @@ def document_writeup(request, name):
317317
),
318318
context_instance=RequestContext(request))
319319

320+
def document_shepherd_writeup(request, name):
321+
doc = get_object_or_404(Document, docalias__name=name)
322+
lastwriteup = doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup")
323+
if lastwriteup:
324+
writeup_text = lastwriteup.text
325+
else:
326+
writeup_text = "(There is no shepherd's writeup available for this document)"
327+
return render_to_response("idrfc/shepherd_writeup.html",
328+
dict(doc=doc,
329+
writeup=writeup_text,
330+
can_edit=can_edit_shepherd_writeup(doc,request.user)
331+
),
332+
context_instance=RequestContext(request))
333+
334+
320335
def document_ballot_content(request, doc, ballot_id, editable=True):
321336
"""Render HTML string with content of ballot page."""
322337
all_ballots = list(BallotDocEvent.objects.filter(doc=doc, type="created_ballot").order_by("time"))
@@ -501,6 +516,7 @@ def document_main_idrfc(request, name, tab):
501516
info["consensus"] = nice_consensus(e and e.consensus)
502517
info["can_edit_consensus"] = can_edit_consensus(id, request.user)
503518
info["can_edit_intended_std_level"] = can_edit_intended_std_level(id, request.user)
519+
info["can_edit_shepherd"] = can_edit_shepherd(id, request.user)
504520

505521
(content1, content2) = _get_html(
506522
str(name)+","+str(id.revision)+",html",

ietf/idrfc/views_edit.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
from ietf.idrfc.mails import *
2424
from ietf.idrfc.utils import *
2525
from ietf.idrfc.lastcall import request_last_call
26+
from ietf.utils.textupload import get_cleaned_text_file_content
27+
from ietf.person.forms import EmailsField
28+
2629

2730
from ietf.ietfworkflows.models import Stream
2831
from ietf.ietfworkflows.utils import update_stream
@@ -866,6 +869,122 @@ def edit_iesg_note(request, name):
866869
),
867870
context_instance=RequestContext(request))
868871

872+
class ShepherdWriteupUploadForm(forms.Form):
873+
content = forms.CharField(widget=forms.Textarea, label="Shepherd writeup", help_text="Edit the shepherd writeup", required=False)
874+
txt = forms.FileField(label=".txt format", help_text="Or upload a .txt file", required=False)
875+
876+
def clean_content(self):
877+
return self.cleaned_data["content"].replace("\r", "")
878+
879+
def clean_txt(self):
880+
return get_cleaned_text_file_content(self.cleaned_data["txt"])
881+
882+
def edit_shepherd_writeup(request, name):
883+
"""Change this document's shepherd writeup"""
884+
doc = get_object_or_404(Document, type="draft", name=name)
885+
886+
if not can_edit_shepherd_writeup(doc, request.user):
887+
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
888+
889+
login = request.user.get_profile()
890+
891+
if request.method == 'POST':
892+
if "submit_response" in request.POST:
893+
form = ShepherdWriteupUploadForm(request.POST, request.FILES)
894+
if form.is_valid():
895+
896+
from_file = form.cleaned_data['txt']
897+
if from_file:
898+
writeup = from_file
899+
else:
900+
writeup = form.cleaned_data['content']
901+
902+
e = WriteupDocEvent(doc=doc, by=login, type="changed_protocol_writeup")
903+
e.desc = "Changed document writeup"
904+
e.text = writeup
905+
e.save()
906+
907+
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
908+
909+
elif "reset_text" in request.POST:
910+
911+
init = { "content": render_to_string("doc/shepherd_writeup.txt",dict(doc=doc))}
912+
form = ShepherdWriteupUploadForm(initial=init)
913+
914+
# Protect against handcrufted malicious posts
915+
else:
916+
form = None
917+
918+
else:
919+
form = None
920+
921+
if not form:
922+
init = { "content": ""}
923+
924+
previous_writeup = doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup")
925+
if previous_writeup:
926+
init["content"] = previous_writeup.text
927+
else:
928+
init["content"] = render_to_string("doc/shepherd_writeup.txt",
929+
dict(doc=doc),
930+
)
931+
form = ShepherdWriteupUploadForm(initial=init)
932+
933+
return render_to_response('idrfc/change_shepherd_writeup.html',
934+
{'form': form,
935+
'doc' : doc,
936+
},
937+
context_instance=RequestContext(request))
938+
939+
class ShepherdForm(forms.Form):
940+
shepherd = EmailsField(label="Shepherd", required=False)
941+
942+
def clean_shepherd(self):
943+
data = self.cleaned_data['shepherd']
944+
if len(data)>1:
945+
raise forms.ValidationError("Please choose at most one shepherd.")
946+
return data
947+
948+
def edit_shepherd(request, name):
949+
"""Change the shepherd for a Document"""
950+
# TODO - this shouldn't be type="draft" specific
951+
doc = get_object_or_404(Document, type="draft", name=name)
952+
953+
if not can_edit_shepherd(doc, request.user):
954+
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
955+
956+
if request.method == 'POST':
957+
form = ShepherdForm(request.POST)
958+
if form.is_valid():
959+
960+
if form.cleaned_data['shepherd']:
961+
doc.shepherd = form.cleaned_data['shepherd'][0].person
962+
else:
963+
doc.shepherd = None
964+
doc.save()
965+
966+
login = request.user.get_profile()
967+
c = DocEvent(type="added_comment", doc=doc, by=login)
968+
c.desc = "Document shepherd changed to "+doc.shepherd.name
969+
c.save()
970+
971+
return HttpResponseRedirect(urlreverse('doc_view', kwargs={'name': doc.name}))
972+
973+
else:
974+
current_shepherd = None
975+
if doc.shepherd:
976+
e = doc.shepherd.email_set.order_by("-active", "-time")
977+
if e:
978+
current_shepherd=e[0].pk
979+
init = { "shepherd": current_shepherd}
980+
form = ShepherdForm(initial=init)
981+
982+
return render_to_response('idrfc/change_shepherd.html',
983+
{'form': form,
984+
'doc': doc,
985+
},
986+
context_instance = RequestContext(request))
987+
869988
class AdForm(forms.Form):
870989
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'),
871990
label="Shepherding AD", empty_label="(None)", required=True)

ietf/ietfworkflows/templatetags/ietf_streams.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,6 @@ def edit_actions(context, wrapper):
7777
if draft.stream_id in ("iab", "ise", "irtf"):
7878
actions.append(("Request publication", urlreverse('doc_request_publication', kwargs=dict(name=doc.draft_name))))
7979

80-
if can_manage_shepherd_of_a_document(user, draft):
81-
actions.append(("Change shepherd", urlreverse('doc_managing_shepherd', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
82-
83-
if can_manage_writeup_of_a_document(user, draft):
84-
actions.append(("Change stream writeup", urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
85-
else:
86-
actions.append(("View writeup", urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
87-
8880
return dict(actions=actions)
8981

9082

ietf/ietfworkflows/templatetags/ietf_streams_redesign.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,6 @@ def edit_actions(context, wrapper):
6969
if can_edit_stream(user, draft):
7070
actions.append(("Change stream", urlreverse('edit_stream', kwargs=dict(name=doc.draft_name))))
7171

72-
if can_manage_shepherd_of_a_document(user, draft):
73-
actions.append(("Change shepherd", urlreverse('doc_managing_shepherd', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
74-
75-
if can_manage_writeup_of_a_document(user, draft):
76-
actions.append(("Change stream writeup", urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
77-
else:
78-
actions.append(("View writeup", urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.filename))))
79-
8072
return dict(actions=actions)
8173

8274

ietf/templates/idrfc/doc_tab_document_id.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@
115115
{% endif %}
116116
{% endifequal %}
117117
{% endif %}
118-
<tr><td>Document shepherd:</td><td>{{ stream_info.shepherd|default:"" }}</td></tr>
118+
<tr><td>Document shepherd:</td><td><a {% if info.can_edit_shepherd %}class="editlink" href="{% url doc_edit_shepherd name=doc.draft_name %}"{% endif %}>{{ doc.underlying_document.shepherd|default:"(None)" }}</a></td></tr>
119+
<tr><td></td><td><a href="{% url doc_shepherd_writeup name=doc.draft_name %}">Shepherd writeup</a></td></tr>
119120

120121
{% if info.consensus %}
121122
<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>

ietf/templates/wgchairs/shepherd_document_row.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
<a href="{% url doc_view doc.filename %}">{{ doc.title }}</a>
66
</td>
77
<td class="shepherd">
8-
<a href="{% url doc_managing_shepherd wg.group_acronym.acronym doc %}">Change shepherd</a>
8+
<a href="{% url doc_edit_shepherd doc %}">Change shepherd</a>
99
</td>
1010
<td class="writeup">
1111
{% writeup doc %}
12-
<a href="{% url doc_managing_writeup wg.group_acronym.acronym doc %}" style="display: block;" href="">[Edit]</a>
12+
<a href="{% url doc_edit_shepherd_writeup doc %}" style="display: block;" href="">[Edit]</a>
1313
</td>
1414
<td class="writeup_date">
1515
{% writeupdate doc %}

0 commit comments

Comments
 (0)