Skip to content

Commit 1ba8789

Browse files
authored
feat: Render the document shepherd writeup templates at two new URLs (ietf-tools#4225)
* feat: Render the document shepherd writeup templates at two new URL. Those being `/doc/shepherdwriteuptemplate/group` and `/doc/shepherdwriteuptemplate/individual`. * Address review comments from @jennifer-richards * Fixes * Remove debug statement * Make bleach sanitizer not strip the `start` attribute of `ol` tags Also rearrange the code a bit * Don't sanitize the `python_markdown` output, it destroys wanted formatting * Restore bleach * Don't bleach tag `id`s.
1 parent ce050fc commit 1ba8789

8 files changed

Lines changed: 147 additions & 49 deletions

File tree

ietf/doc/tests_draft.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,27 @@ def test_doc_change_shepherd_email(self):
11631163
self.assertEqual(r.status_code, 302)
11641164
doc = Document.objects.get(name=self.docname)
11651165
self.assertEqual(comment_event, doc.latest_event(DocEvent, type="added_comment"))
1166-
1166+
1167+
def test_doc_view_shepherd_writeup_templates(self):
1168+
url = urlreverse(
1169+
"ietf.doc.views_doc.document_shepherd_writeup_template",
1170+
kwargs=dict(type="group"),
1171+
)
1172+
1173+
r = self.client.get(url)
1174+
self.assertEqual(r.status_code, 200)
1175+
q = PyQuery(r.content)
1176+
self.assertEqual(len(q('h1:contains("for Group Documents")')), 1)
1177+
1178+
url = urlreverse(
1179+
"ietf.doc.views_doc.document_shepherd_writeup_template",
1180+
kwargs=dict(type="individual"),
1181+
)
1182+
1183+
r = self.client.get(url)
1184+
self.assertEqual(r.status_code, 200)
1185+
q = PyQuery(r.content)
1186+
self.assertEqual(len(q('h1:contains("for Individual Documents")')), 1)
11671187

11681188
def test_doc_view_shepherd_writeup(self):
11691189
url = urlreverse('ietf.doc.views_doc.document_shepherd_writeup',kwargs=dict(name=self.docname))

ietf/doc/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
url(r'^email-aliases/?$', views_doc.email_aliases),
6161
url(r'^downref/?$', views_downref.downref_registry),
6262
url(r'^downref/add/?$', views_downref.downref_registry_add),
63+
url(
64+
r"^shepherdwriteup-template/(?P<type>\w+)/?$",
65+
views_doc.document_shepherd_writeup_template,
66+
),
67+
6368
url(r'^stats/newrevisiondocevent/?$', views_stats.chart_newrevisiondocevent),
6469
url(r'^stats/newrevisiondocevent/conf/?$', views_stats.chart_conf_newrevisiondocevent),
6570
url(r'^stats/newrevisiondocevent/data/?$', views_stats.chart_data_newrevisiondocevent),

ietf/doc/views_doc.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,25 @@ def document_shepherd_writeup(request, name):
11391139
),
11401140
)
11411141

1142+
1143+
def document_shepherd_writeup_template(request, type):
1144+
writeup = markdown.markdown(
1145+
render_to_string(
1146+
"doc/shepherd_writeup.txt",
1147+
dict(stream="ietf", type="individ" if type == "individual" else "group"),
1148+
)
1149+
)
1150+
return render(
1151+
request,
1152+
"doc/shepherd_writeup_template.html",
1153+
dict(
1154+
writeup=writeup,
1155+
stream="ietf",
1156+
type="individ" if type == "individual" else "group",
1157+
),
1158+
)
1159+
1160+
11421161
def document_references(request, name):
11431162
doc = get_object_or_404(Document,docalias__name=name)
11441163
refs = doc.references()

ietf/doc/views_draft.py

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -940,49 +940,68 @@ def clean_content(self):
940940
def clean_txt(self):
941941
return get_cleaned_text_file_content(self.cleaned_data["txt"])
942942

943+
943944
@login_required
944945
def edit_shepherd_writeup(request, name):
945946
"""Change this document's shepherd writeup"""
946947
doc = get_object_or_404(Document, type="draft", name=name)
947948

948949
can_edit_stream_info = is_authorized_in_doc_stream(request.user, doc)
949-
can_edit_shepherd_writeup = ( can_edit_stream_info
950+
can_edit_shepherd_writeup = (
951+
can_edit_stream_info
950952
or (doc.shepherd and user_is_person(request.user, doc.shepherd.person))
951-
or has_role(request.user, ["Area Director"]))
953+
or has_role(request.user, ["Area Director"])
954+
)
952955

953956
if not can_edit_shepherd_writeup:
954-
permission_denied(request, "You do not have the necessary permissions to view this page")
957+
permission_denied(
958+
request, "You do not have the necessary permissions to view this page"
959+
)
955960

956961
login = request.user.person
957962

958-
if request.method == 'POST':
963+
if request.method == "POST":
959964
if "submit_response" in request.POST:
960965
form = ShepherdWriteupUploadForm(request.POST, request.FILES)
961966
if form.is_valid():
962-
963-
from_file = form.cleaned_data['txt']
967+
968+
from_file = form.cleaned_data["txt"]
964969
if from_file:
965-
writeup = from_file
970+
writeup = from_file
966971
else:
967-
writeup = form.cleaned_data['content']
968-
e = WriteupDocEvent(doc=doc, rev=doc.rev, by=login, type="changed_protocol_writeup")
972+
writeup = form.cleaned_data["content"]
973+
e = WriteupDocEvent(
974+
doc=doc, rev=doc.rev, by=login, type="changed_protocol_writeup"
975+
)
969976

970-
# Add the shepherd writeup to description if the document is in submitted for publication state
977+
# Add the shepherd writeup to description,
978+
# if the document is in submitted for publication state
971979
stream_state = doc.get_state("draft-stream-%s" % doc.stream_id)
972-
iesg_state = doc.get_state("draft-iesg")
973-
if (iesg_state or (stream_state and stream_state.slug=='sub-pub')):
980+
iesg_state = doc.get_state("draft-iesg")
981+
if iesg_state or (stream_state and stream_state.slug == "sub-pub"):
974982
e.desc = writeup
975983
else:
976984
e.desc = "Changed document writeup"
977985

978986
e.text = writeup
979987
e.save()
980-
981-
return redirect('ietf.doc.views_doc.document_main', name=doc.name)
982988

983-
elif "reset_text" in request.POST:
989+
return redirect("ietf.doc.views_doc.document_main", name=doc.name)
984990

985-
init = { "content": render_to_string("doc/shepherd_writeup.txt",dict(doc=doc))}
991+
elif "reset_text" in request.POST:
992+
init = {
993+
"content": render_to_string(
994+
"doc/shepherd_writeup.txt",
995+
dict(
996+
doc=doc,
997+
type="individ"
998+
if not doc.group.type.slug or doc.group.type.slug != "ietf"
999+
else "group",
1000+
stream=doc.stream.slug,
1001+
group=doc.group.type.slug,
1002+
),
1003+
)
1004+
}
9861005
form = ShepherdWriteupUploadForm(initial=init)
9871006

9881007
# Protect against handcrufted malicious posts
@@ -993,21 +1012,36 @@ def edit_shepherd_writeup(request, name):
9931012
form = None
9941013

9951014
if not form:
996-
init = { "content": ""}
1015+
init = {"content": ""}
9971016

998-
previous_writeup = doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup")
1017+
previous_writeup = doc.latest_event(
1018+
WriteupDocEvent, type="changed_protocol_writeup"
1019+
)
9991020
if previous_writeup:
10001021
init["content"] = previous_writeup.text
10011022
else:
1002-
init["content"] = render_to_string("doc/shepherd_writeup.txt",
1003-
dict(doc=doc),
1004-
)
1023+
init["content"] = render_to_string(
1024+
"doc/shepherd_writeup.txt",
1025+
dict(
1026+
doc=doc,
1027+
type="individ"
1028+
if not doc.group.type.slug or doc.group.type.slug != "ietf"
1029+
else "group",
1030+
stream=doc.stream.slug,
1031+
group=doc.group.type.slug,
1032+
),
1033+
)
10051034
form = ShepherdWriteupUploadForm(initial=init)
10061035

1007-
return render(request, 'doc/draft/change_shepherd_writeup.html',
1008-
{'form': form,
1009-
'doc' : doc,
1010-
})
1036+
return render(
1037+
request,
1038+
"doc/draft/change_shepherd_writeup.html",
1039+
{
1040+
"form": form,
1041+
"doc": doc,
1042+
},
1043+
)
1044+
10111045

10121046
class ShepherdForm(forms.Form):
10131047
shepherd = SearchableEmailField(required=False, only_users=True)

ietf/templates/doc/shepherd_writeup.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{# Keep in sync with https://github.com/ietf-chairs/chairs.ietf.org/blob/main/documents/qa-style-writeup-template.md #}{% if doc.stream %}{% if doc.stream.slug == 'ietf' %}# Document Shepherd Write-Up
1+
{# Keep in sync with https://github.com/ietf-chairs/chairs.ietf.org/blob/main/documents/qa-style-writeup-template.md #}{% if stream %}{% if stream == 'ietf' %}# Document Shepherd Write-Up{% if type != "individ" %} for Group Documents{% else %} for Individual Documents{% endif %}
22

33
*This version is dated 4 July 2022.*
44

@@ -13,7 +13,7 @@ Note that some numbered items contain multiple related questions; please be sure
1313
to answer all of them.
1414

1515
## Document History
16-
{% if doc.group.type.slug == 'individ' %}
16+
{% if type == 'individ' %}
1717
1. Was the document considered in any WG, and if so, why was it not adopted as a
1818
work item there?
1919

@@ -37,7 +37,7 @@ to answer all of them.
3737
either in the document itself (as [RFC 7942][3] recommends) or elsewhere
3838
(where)?
3939

40-
### Additional Reviews
40+
## Additional Reviews
4141

4242
5. Do the contents of this document closely interact with technologies in other
4343
IETF working groups or external organizations, and would it therefore benefit
@@ -58,7 +58,7 @@ to answer all of them.
5858
final version of the document written in a formal language, such as XML code,
5959
BNF rules, MIB definitions, CBOR's CDDL, etc.
6060

61-
### Document Shepherd Checks
61+
## Document Shepherd Checks
6262

6363
9. Based on the shepherd's review of the document, is it their opinion that this
6464
document is needed, clearly written, complete, correctly designed, and ready
@@ -139,5 +139,5 @@ to answer all of them.
139139
[15]: https://authors.ietf.org/en/content-guidelines-overview
140140
[16]: https://www.ietf.org/about/groups/iesg/statements/normative-informative-references/
141141
[17]: https://datatracker.ietf.org/doc/downref/
142-
{% else %}There is no default shepherd write-up template for the {{doc.stream}} stream.
142+
{% else %}There is no default shepherd write-up template for the {{stream}} stream.
143143
{% endif %}{% else %}There is no stream set for this document (thus, no default shepherd write-up template).{% endif %}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% extends "base.html" %}
2+
{# Copyright The IETF Trust 2015, All Rights Reserved #}
3+
{% load origin %}
4+
{% load ietf_filters %}
5+
{% load textfilters htmlfilters %}
6+
{% block title %}Document Shepherd Write-Up{% if type == "group" %} for Group Documents{% elif type == "individual" %} for Individual Documents{% endif %}{% endblock %}
7+
{% block content %}
8+
{% origin %}
9+
{{ writeup|urlize_ietf_docs|linkify }}
10+
{% endblock %}

ietf/utils/markdown.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import markdown as python_markdown
99

1010
from django.utils.safestring import mark_safe
11-
from markdown.extensions.extra import ExtraExtension
1211

1312
from ietf.doc.templatetags.ietf_filters import urlize_ietf_docs
1413
from ietf.utils.text import bleach_cleaner, bleach_linker
@@ -20,7 +19,7 @@ def markdown(text):
2019
urlize_ietf_docs(
2120
bleach_cleaner.clean(
2221
python_markdown.markdown(
23-
text, extensions=[ExtraExtension(), "nl2br"]
22+
text, extensions=["extra", "nl2br", "sane_lists", "toc"]
2423
)
2524
)
2625
)

ietf/utils/text.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,36 @@
2323
protocols = copy.copy(bleach.sanitizer.ALLOWED_PROTOCOLS)
2424
protocols.append("ftp") # we still have some ftp links
2525
protocols.append("xmpp") # we still have some xmpp links
26+
27+
tags = set(copy.copy(bleach.sanitizer.ALLOWED_TAGS)).union(
28+
{
29+
# fmt: off
30+
'a', 'abbr', 'acronym', 'address', 'b', 'big',
31+
'blockquote', 'body', 'br', 'caption', 'center', 'cite', 'code', 'col',
32+
'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'font',
33+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'i', 'ins', 'kbd',
34+
'li', 'ol', 'p', 'pre', 'q', 's', 'samp', 'small', 'span', 'strike', 'style',
35+
'strong', 'sub', 'sup', 'table', 'title', 'tbody', 'td', 'tfoot', 'th', 'thead',
36+
'tr', 'tt', 'u', 'ul', 'var'
37+
# fmt: on
38+
}
39+
)
40+
41+
attributes = copy.copy(bleach.sanitizer.ALLOWED_ATTRIBUTES)
42+
attributes["*"] = ["id"]
43+
attributes["ol"] = ["start"]
44+
45+
bleach_cleaner = bleach.sanitizer.Cleaner(
46+
tags=tags, attributes=attributes, protocols=protocols, strip=True
47+
)
48+
2649
validate_url = URLValidator()
2750

2851

2952
def check_url_validity(attrs, new=False):
30-
if (None, 'href') not in attrs:
53+
if (None, "href") not in attrs:
3154
return None
32-
url = attrs[(None, 'href')]
55+
url = attrs[(None, "href")]
3356
try:
3457
if url.startswith("http"):
3558
validate_url(url)
@@ -41,22 +64,10 @@ def check_url_validity(attrs, new=False):
4164
bleach_linker = bleach.Linker(
4265
callbacks=[check_url_validity],
4366
url_re=bleach.linkifier.build_url_re(tlds=tlds_sorted, protocols=protocols),
44-
email_re=bleach.linkifier.build_email_re(tlds=tlds_sorted), # type: ignore
45-
parse_email=True
46-
)
47-
48-
tags = (
49-
'a', 'abbr', 'acronym', 'address', 'b', 'big',
50-
'blockquote', 'body', 'br', 'caption', 'center', 'cite', 'code', 'col',
51-
'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'font',
52-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'i', 'ins', 'kbd',
53-
'li', 'ol', 'p', 'pre', 'q', 's', 'samp', 'small', 'span', 'strike', 'style',
54-
'strong', 'sub', 'sup', 'table', 'title', 'tbody', 'td', 'tfoot', 'th', 'thead',
55-
'tr', 'tt', 'u', 'ul', 'var'
67+
email_re=bleach.linkifier.build_email_re(tlds=tlds_sorted), # type: ignore
68+
parse_email=True,
5669
)
5770

58-
bleach_cleaner = bleach.sanitizer.Cleaner(tags=tags, protocols=protocols, strip=True)
59-
6071

6172
@keep_lazy(str)
6273
def xslugify(value):

0 commit comments

Comments
 (0)