More HTML nitfixing (#3934)
* Unicode messages are triggered by both db content and tests * Make ids unique * Avoid "No value found" message on page * Strip HTML from history entries, it's often broken * Check HTML sources for occurrences of "** No value found for" and fix them * Fix another occurrence of "** No value found for" * Fix more occurrences of "** No value found for" * Fix document revision stripping * Force breaks of long (garbage) words * Check URL validity before urlizing them * Handle some additional corner cases * Linkify action items * Don't create profile/email links for System * Handle headings with HTML elements in them better * Fix comment * Fix another occurrence of "** No value found for" * Better I-D URLization that handles more edge cases. Also, test for them. * Remove print * Handle charters better * Cache for one day
This commit is contained in:
parent
47b89c1112
commit
5f8d4ed718
|
@ -44,7 +44,6 @@ args = parser.parse_args()
|
|||
|
||||
# Import Django, call setup()
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", args.settings or "ietf.settings_testcrawl")
|
||||
os.environ["DJANGO_URLIZE_IETF_DOCS_PRODUCTION"] = "1"
|
||||
|
||||
import django
|
||||
import django.test
|
||||
|
@ -175,7 +174,7 @@ def check_html_valid(url, response, args):
|
|||
assert ret
|
||||
for m in json.loads(ret)["messages"]:
|
||||
if "lastLine" not in m:
|
||||
tag = m # just dump the raw JSON for now
|
||||
tag = m["message"]
|
||||
else:
|
||||
tag = vnu_fmt_message(url, m, content.decode())
|
||||
# disregard some HTML issues that are (usually) due to invalid
|
||||
|
@ -211,7 +210,7 @@ def skip_url(url):
|
|||
r"^/wg/[a-z0-9-]+/deps/svg/",
|
||||
# Skip other bad urls
|
||||
r"^/dir/tsvdir/reviews/",
|
||||
r"^/ipr/\d{,3}/history/",
|
||||
# r"^/ipr/\d{,3}/history/",
|
||||
# Skip most html conversions, not worth the time
|
||||
r"^/doc/html/draft-[0-9ac-z]",
|
||||
r"^/doc/html/draft-b[0-9b-z]",
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import datetime
|
||||
import re
|
||||
import os
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from email.utils import parseaddr
|
||||
|
@ -19,7 +18,6 @@ from django.utils.encoding import force_text
|
|||
from django.utils.encoding import force_str # pyflakes:ignore force_str is used in the doctests
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.core.cache import cache
|
||||
from django.core.validators import URLValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -29,7 +27,7 @@ from ietf.doc.models import ConsensusDocEvent
|
|||
from ietf.utils.html import sanitize_fragment
|
||||
from ietf.utils import log
|
||||
from ietf.doc.utils import prettify_std_name
|
||||
from ietf.utils.text import wordwrap, fill, wrap_text_if_unwrapped, bleach_linker
|
||||
from ietf.utils.text import wordwrap, fill, wrap_text_if_unwrapped, bleach_linker, bleach_cleaner, validate_url
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -189,69 +187,82 @@ def rfceditor_info_url(rfcnum : str):
|
|||
return urljoin(settings.RFC_EDITOR_INFO_BASE_URL, f'rfc{rfcnum}')
|
||||
|
||||
|
||||
def doc_exists(name):
|
||||
"""Check whether a given document exists"""
|
||||
def doc_canonical_name(name):
|
||||
"""Check whether a given document exists, and return its canonical name"""
|
||||
|
||||
def find_unique(n):
|
||||
key = hash(n)
|
||||
found = cache.get(key)
|
||||
if not found:
|
||||
exact = DocAlias.objects.filter(name=n).first()
|
||||
found = exact.name if exact else "_"
|
||||
cache.set(key, found)
|
||||
cache.set(key, found, timeout=60*60*24) # cache for one day
|
||||
return None if found == "_" else found
|
||||
|
||||
# all documents exist when tests are running
|
||||
if settings.SERVER_MODE == 'test':
|
||||
# unless we are running test-crawl, which would otherwise 404
|
||||
if "DJANGO_URLIZE_IETF_DOCS_PRODUCTION" not in os.environ:
|
||||
return True
|
||||
|
||||
# chop away extension
|
||||
extension_split = re.search(r"^(.+)\.(txt|ps|pdf)$", name)
|
||||
extension_split = re.search(r"^(.+)\.(txt|ps|pdf|html)$", name)
|
||||
if extension_split:
|
||||
name = extension_split.group(1)
|
||||
|
||||
if find_unique(name):
|
||||
return True
|
||||
return name
|
||||
|
||||
# check for embedded rev - this may be ambiguous, so don't
|
||||
# chop it off if we don't find a match
|
||||
rev_split = re.search("^(.+)-([0-9]{2,})$", name)
|
||||
rev_split = re.search(r"^(charter-.+)-(\d{2}-\d{2})$", name) or re.search(
|
||||
r"^(.+)-(\d{2}|[1-9]\d{2,})$", name
|
||||
)
|
||||
if rev_split:
|
||||
name = rev_split.group(1)
|
||||
if find_unique(name):
|
||||
return True
|
||||
return name
|
||||
|
||||
return False
|
||||
return ""
|
||||
|
||||
|
||||
def link_charter_doc_match1(match):
|
||||
if not doc_exists(match[0]):
|
||||
if not doc_canonical_name(match[0]):
|
||||
return match[0]
|
||||
return f'<a href="/doc/{match[1][:-1]}/{match[2]}/">{match[0]}</a>'
|
||||
|
||||
|
||||
def link_charter_doc_match2(match):
|
||||
if not doc_exists(match[0]):
|
||||
if not doc_canonical_name(match[0]):
|
||||
return match[0]
|
||||
return f'<a href="/doc/{match[1][:-1]}/{match[2]}/">{match[0]}</a>'
|
||||
|
||||
|
||||
def link_non_charter_doc_match(match):
|
||||
if not doc_exists(match[0]):
|
||||
name = match[0]
|
||||
cname = doc_canonical_name(name)
|
||||
if not cname:
|
||||
return match[0]
|
||||
if len(match[3]) == 2 and match[3].isdigit():
|
||||
return f'<a href="/doc/{match[2][:-1]}/{match[3]}/">{match[0]}</a>'
|
||||
if name == cname:
|
||||
return f'<a href="/doc/{cname}/">{match[0]}</a>'
|
||||
|
||||
# if we get here, the name probably has a version number and/or extension at the end
|
||||
rev_split = re.search(r"^(" + re.escape(cname) + r")-(\d{2,})", name)
|
||||
if rev_split:
|
||||
name = rev_split.group(1)
|
||||
else:
|
||||
return f'<a href="/doc/{match[2]}{match[3]}/">{match[0]}</a>'
|
||||
return f'<a href="/doc/{cname}/">{match[0]}</a>'
|
||||
|
||||
cname = doc_canonical_name(name)
|
||||
if not cname:
|
||||
return match[0]
|
||||
if name == cname:
|
||||
return f'<a href="/doc/{cname}/{rev_split.group(2)}/">{match[0]}</a>'
|
||||
|
||||
# if we get here, we can't linkify
|
||||
return match[0]
|
||||
|
||||
|
||||
def link_other_doc_match(match):
|
||||
# there may be whitespace in the match
|
||||
doc = re.sub(r"\s+", "", match[0])
|
||||
if not doc_exists(doc):
|
||||
doc = match[2].strip().lower()
|
||||
rev = match[3]
|
||||
if not doc_canonical_name(doc + rev):
|
||||
return match[0]
|
||||
return f'<a href="/doc/{match[2].strip().lower()}{match[3]}/">{match[1]}</a>'
|
||||
return f'<a href="/doc/{doc}{rev}/">{match[1]}</a>'
|
||||
|
||||
|
||||
@register.filter(name="urlize_ietf_docs", is_safe=True, needs_autoescape=True)
|
||||
|
@ -264,8 +275,8 @@ def urlize_ietf_docs(string, autoescape=None):
|
|||
string = escape(string)
|
||||
else:
|
||||
string = mark_safe(string)
|
||||
exp1 = r"\b(?<![/\-:=#])(charter-(?:[\d\w\.+]+-)*)(\d\d-\d\d)(\.txt)?\b"
|
||||
exp2 = r"\b(?<![/\-:=#])(charter-(?:[\d\w\.+]+-)*)(\d\d)(\.txt)?\b"
|
||||
exp1 = r"\b(?<![/\-:=#])(charter-(?:[\d\w\.+]+-)*)(\d{2}-\d{2})(\.(?:txt|ps|pdf|html))?\b"
|
||||
exp2 = r"\b(?<![/\-:=#])(charter-(?:[\d\w\.+]+-)*)(\d{2})(\.(?:txt|ps|pdf|html))?\b"
|
||||
if re.search(exp1, string):
|
||||
string = re.sub(
|
||||
exp1,
|
||||
|
@ -281,7 +292,8 @@ def urlize_ietf_docs(string, autoescape=None):
|
|||
flags=re.IGNORECASE | re.ASCII,
|
||||
)
|
||||
string = re.sub(
|
||||
r"\b(?<![/\-:=#])(((?:draft-|bofreq-|conflict-review-|status-change-)(?:[\d\w\.+]+-)*)([\d\w\.+]+?)(\.txt)?)\b(?![-@])",
|
||||
r"\b(?<![/\-:=#])((?:draft-|bofreq-|conflict-review-|status-change-)[\d\w\.+-]+(?![-@]))",
|
||||
# r"\b(?<![/\-:=#])(((?:draft-|bofreq-|conflict-review-|status-change-)(?:[\d\w\.+]+-)*)([\d\w\.+]+?)(\.(?:txt|ps|pdf|html))?)\b(?![-@])",
|
||||
link_non_charter_doc_match,
|
||||
string,
|
||||
flags=re.IGNORECASE | re.ASCII,
|
||||
|
@ -295,6 +307,7 @@ def urlize_ietf_docs(string, autoescape=None):
|
|||
)
|
||||
return mark_safe(string)
|
||||
|
||||
|
||||
urlize_ietf_docs = stringfilter(urlize_ietf_docs)
|
||||
|
||||
@register.filter(name='urlize_related_source_list', is_safe=True, needs_autoescape=True)
|
||||
|
@ -492,10 +505,8 @@ def ad_area(user):
|
|||
@register.filter
|
||||
def format_history_text(text, trunc_words=25):
|
||||
"""Run history text through some cleaning and add ellipsis if it's too long."""
|
||||
full = mark_safe(text)
|
||||
if "</a>" not in full:
|
||||
full = urlize_ietf_docs(full)
|
||||
full = bleach_linker.linkify(full)
|
||||
full = mark_safe(bleach_cleaner.clean(text))
|
||||
full = bleach_linker.linkify(urlize_ietf_docs(full))
|
||||
|
||||
return format_snippet(full, trunc_words)
|
||||
|
||||
|
@ -840,7 +851,6 @@ def is_valid_url(url):
|
|||
"""
|
||||
Check if the given URL is syntactically valid
|
||||
"""
|
||||
validate_url = URLValidator()
|
||||
try:
|
||||
validate_url(url)
|
||||
except ValidationError:
|
||||
|
|
|
@ -1,56 +1,96 @@
|
|||
# Copyright The IETF Trust 2022, All Rights Reserved
|
||||
|
||||
from ietf.doc.templatetags.ietf_filters import urlize_ietf_docs
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.doc.factories import (
|
||||
WgDraftFactory,
|
||||
IndividualDraftFactory,
|
||||
CharterFactory,
|
||||
NewRevisionDocEventFactory,
|
||||
)
|
||||
from ietf.doc.models import State, DocEvent, DocAlias
|
||||
from ietf.doc.templatetags.ietf_filters import urlize_ietf_docs, is_valid_url
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.test_utils import TestCase
|
||||
|
||||
import debug # pyflakes: ignore
|
||||
import debug # pyflakes: ignore
|
||||
|
||||
# TODO: most other filters need test cases, too
|
||||
|
||||
|
||||
class IetfFiltersTests(TestCase):
|
||||
def test_is_valid_url(self):
|
||||
cases = [(settings.IDTRACKER_BASE_URL, True), ("not valid", False)]
|
||||
for url, result in cases:
|
||||
self.assertEqual(is_valid_url(url), result)
|
||||
|
||||
def test_urlize_ietf_docs(self):
|
||||
wg_id = WgDraftFactory()
|
||||
wg_id.set_state(State.objects.get(type="draft", slug="rfc"))
|
||||
wg_id.std_level_id = "bcp"
|
||||
wg_id.save_with_history(
|
||||
[
|
||||
DocEvent.objects.create(
|
||||
doc=wg_id,
|
||||
rev=wg_id.rev,
|
||||
type="published_rfc",
|
||||
by=Person.objects.get(name="(System)"),
|
||||
)
|
||||
]
|
||||
)
|
||||
DocAlias.objects.create(name="rfc123456").docs.add(wg_id)
|
||||
DocAlias.objects.create(name="bcp123456").docs.add(wg_id)
|
||||
DocAlias.objects.create(name="std123456").docs.add(wg_id)
|
||||
DocAlias.objects.create(name="fyi123456").docs.add(wg_id)
|
||||
|
||||
id = IndividualDraftFactory(name="draft-me-rfc123456bis")
|
||||
id_num = IndividualDraftFactory(name="draft-rosen-rfcefdp-update-2026")
|
||||
id_num_two = IndividualDraftFactory(name="draft-spaghetti-idr-deprecate-8-9-10")
|
||||
id_plus = IndividualDraftFactory(name="draft-odell-8+8")
|
||||
id_plus_end = IndividualDraftFactory(name="draft-durand-gse+")
|
||||
id_dot = IndividualDraftFactory(name="draft-ietf-pem-ansix9.17")
|
||||
charter = CharterFactory()
|
||||
e = NewRevisionDocEventFactory(doc=charter, rev="01")
|
||||
charter.rev = e.rev
|
||||
charter.save_with_history([e])
|
||||
e = NewRevisionDocEventFactory(doc=charter, rev="01-00")
|
||||
charter.rev = e.rev
|
||||
charter.save_with_history([e])
|
||||
|
||||
cases = [
|
||||
("no change", "no change"),
|
||||
("bcp1", '<a href="/doc/bcp1/">bcp1</a>'),
|
||||
("Std 003", '<a href="/doc/std3/">Std 003</a>'),
|
||||
("bCp123456", '<a href="/doc/bcp123456/">bCp123456</a>'),
|
||||
("Std 00123456", '<a href="/doc/std123456/">Std 00123456</a>'),
|
||||
(
|
||||
"FYI02 changes Std 003",
|
||||
'<a href="/doc/fyi2/">FYI02</a> changes <a href="/doc/std3/">Std 003</a>',
|
||||
"FyI 0123456 changes std 00123456",
|
||||
'<a href="/doc/fyi123456/">FyI 0123456</a> changes <a href="/doc/std123456/">std 00123456</a>',
|
||||
),
|
||||
("rfc2119", '<a href="/doc/rfc2119/">rfc2119</a>'),
|
||||
("Rfc 02119", '<a href="/doc/rfc2119/">Rfc 02119</a>'),
|
||||
("draft-abc-123", '<a href="/doc/draft-abc-123/">draft-abc-123</a>'),
|
||||
("rfc123456", '<a href="/doc/rfc123456/">rfc123456</a>'),
|
||||
("Rfc 0123456", '<a href="/doc/rfc123456/">Rfc 0123456</a>'),
|
||||
(wg_id.name, f'<a href="/doc/{wg_id.name}/">{wg_id.name}</a>'),
|
||||
(
|
||||
"draft-ietf-rfc9999-bis-01.txt",
|
||||
'<a href="/doc/draft-ietf-rfc9999-bis/01/">draft-ietf-rfc9999-bis-01.txt</a>',
|
||||
f"{id.name}-{id.rev}.txt",
|
||||
f'<a href="/doc/{id.name}/{id.rev}/">{id.name}-{id.rev}.txt</a>',
|
||||
),
|
||||
(
|
||||
"foo RFC 9999 draft-ietf-rfc9999-bis-01 bar",
|
||||
'foo <a href="/doc/rfc9999/">RFC 9999</a> <a href="/doc/draft-ietf-rfc9999-bis/01/">draft-ietf-rfc9999-bis-01</a> bar',
|
||||
f"foo RFC 123456 {id.name}-{id.rev} bar",
|
||||
f'foo <a href="/doc/rfc123456/">RFC 123456</a> <a href="/doc/{id.name}/{id.rev}/">{id.name}-{id.rev}</a> bar',
|
||||
),
|
||||
(
|
||||
"New version available: <b>draft-bryan-sipping-p2p-03.txt</b>",
|
||||
'New version available: <b><a href="/doc/draft-bryan-sipping-p2p/03/">draft-bryan-sipping-p2p-03.txt</a></b>',
|
||||
f"New version available: <b>{id.name}-{id.rev}.txt</b>",
|
||||
f'New version available: <b><a href="/doc/{id.name}/{id.rev}/">{id.name}-{id.rev}.txt</a></b>',
|
||||
),
|
||||
(
|
||||
"New version available: <b>charter-ietf-6man-04.txt</b>",
|
||||
'New version available: <b><a href="/doc/charter-ietf-6man/04/">charter-ietf-6man-04.txt</a></b>'
|
||||
f"New version available: <b>{charter.name}-{charter.rev}.txt</b>",
|
||||
f'New version available: <b><a href="/doc/{charter.name}/{charter.rev}/">{charter.name}-{charter.rev}.txt</a></b>',
|
||||
),
|
||||
(
|
||||
"New version available: <b>charter-ietf-6man-03-07.txt</b>",
|
||||
'New version available: <b><a href="/doc/charter-ietf-6man/03-07/">charter-ietf-6man-03-07.txt</a></b>'
|
||||
f"New version available: <b>{charter.name}-01-00.txt</b>",
|
||||
f'New version available: <b><a href="/doc/{charter.name}/01-00/">{charter.name}-01-00.txt</a></b>',
|
||||
),
|
||||
(
|
||||
"repository https://github.com/tlswg/draft-ietf-tls-ticketrequest",
|
||||
'repository https://github.com/tlswg/draft-ietf-tls-ticketrequest'
|
||||
),
|
||||
(
|
||||
"draft-madanapalli-nd-over-802.16-problems",
|
||||
'<a href="/doc/draft-madanapalli-nd-over-802.16-problems/">draft-madanapalli-nd-over-802.16-problems</a>'
|
||||
),
|
||||
(
|
||||
"draft-madanapalli-nd-over-802.16-problems-02.txt",
|
||||
'<a href="/doc/draft-madanapalli-nd-over-802.16-problems/02/">draft-madanapalli-nd-over-802.16-problems-02.txt</a>'
|
||||
"repository https://github.com/tlswg/draft-ietf-tls-ticketrequest",
|
||||
),
|
||||
(
|
||||
'<a href="mailto:draft-ietf-some-names@ietf.org">draft-ietf-some-names@ietf.org</a>',
|
||||
|
@ -58,19 +98,50 @@ class IetfFiltersTests(TestCase):
|
|||
),
|
||||
(
|
||||
"http://ieee802.org/1/files/public/docs2015/cn-thaler-Qcn-draft-PAR.pdf",
|
||||
"http://ieee802.org/1/files/public/docs2015/cn-thaler-Qcn-draft-PAR.pdf"
|
||||
)
|
||||
"http://ieee802.org/1/files/public/docs2015/cn-thaler-Qcn-draft-PAR.pdf",
|
||||
),
|
||||
(
|
||||
f"{id_num.name}.pdf",
|
||||
f'<a href="/doc/{id_num.name}/">{id_num.name}.pdf</a>',
|
||||
),
|
||||
(
|
||||
f"{id_num.name}-{id_num.rev}.txt",
|
||||
f'<a href="/doc/{id_num.name}/{id_num.rev}/">{id_num.name}-{id_num.rev}.txt</a>',
|
||||
),
|
||||
(
|
||||
f"{id_num_two.name}.pdf",
|
||||
f'<a href="/doc/{id_num_two.name}/">{id_num_two.name}.pdf</a>',
|
||||
),
|
||||
(
|
||||
f"{id_num_two.name}-{id_num_two.rev}.txt",
|
||||
f'<a href="/doc/{id_num_two.name}/{id_num_two.rev}/">{id_num_two.name}-{id_num_two.rev}.txt</a>',
|
||||
),
|
||||
(
|
||||
f"{id_plus.name}",
|
||||
f'<a href="/doc/{id_plus.name}/">{id_plus.name}</a>',
|
||||
),
|
||||
(
|
||||
f"{id_plus.name}-{id_plus.rev}.txt",
|
||||
f'<a href="/doc/{id_plus.name}/{id_plus.rev}/">{id_plus.name}-{id_plus.rev}.txt</a>',
|
||||
),
|
||||
(
|
||||
f"{id_plus_end.name}",
|
||||
f'<a href="/doc/{id_plus_end.name}/">{id_plus_end.name}</a>',
|
||||
),
|
||||
(
|
||||
f"{id_plus_end.name}-{id_plus_end.rev}.txt",
|
||||
f'<a href="/doc/{id_plus_end.name}/{id_plus_end.rev}/">{id_plus_end.name}-{id_plus_end.rev}.txt</a>',
|
||||
),
|
||||
(
|
||||
f"{id_dot.name}",
|
||||
f'<a href="/doc/{id_dot.name}/">{id_dot.name}</a>',
|
||||
),
|
||||
(
|
||||
f"{id_dot.name}-{id_dot.rev}.txt",
|
||||
f'<a href="/doc/{id_dot.name}/{id_dot.rev}/">{id_dot.name}-{id_dot.rev}.txt</a>',
|
||||
),
|
||||
]
|
||||
|
||||
# Some edge cases scraped from existing old draft names
|
||||
for name in [
|
||||
# "draft-odell-8+8", # This fails since + matches the right side of \b
|
||||
# "draft-durand-gse+", # same failure
|
||||
"draft-kim-xcast+-few-2-few",
|
||||
#"draft-ietf-pem-ansix9.17", # Fails because of not being greedy with . before txt
|
||||
]:
|
||||
cases.append((name,f'<a href="/doc/{name}/">{name}</a>'))
|
||||
|
||||
for input, output in cases:
|
||||
#debug.show("(urlize_ietf_docs(input),output)")
|
||||
self.assertEqual(urlize_ietf_docs(input), output)
|
||||
|
|
|
@ -199,12 +199,15 @@ $(function () {
|
|||
.find("h1:visible, h2:visible, h3:visible, h4:visible, h5:visible, h6:visible, .nav-heading:visible")
|
||||
.not(".navskip")
|
||||
.each(function () {
|
||||
// Some headings have complex HTML in them - only use first part in that case.
|
||||
const text = $(this)
|
||||
// Some headings have line breaks in them - only use first line in that case.
|
||||
const frag = $(this)
|
||||
.html()
|
||||
.split("<")
|
||||
.shift()
|
||||
.trim();
|
||||
.split("<br")
|
||||
.shift();
|
||||
const text = $.parseHTML(frag)
|
||||
.map(x => $(x)
|
||||
.text())
|
||||
.join(" ");
|
||||
|
||||
if (text === undefined || text === "") {
|
||||
// Nothing to do for empty headings.
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
<div class="card-header bg-danger text-light">
|
||||
<div>
|
||||
<b>{{ p.pos.name }}</b>
|
||||
({{ p.discuss_time|date:"Y-m-d" }}{% if not p.for_current_revision %}{% if p.discuss_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
({{ p.discuss_time|date:"Y-m-d" }}{% if not p.for_current_revision and p.get_dochistory.rev %}{% if p.discuss_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
{% if p.send_email %}
|
||||
<i class="bi bi-envelope float-end"
|
||||
title="Email requested to be sent for this discuss"></i>
|
||||
|
@ -131,7 +131,7 @@
|
|||
<div class="card-header {{ p.pos|pos_to_label_format }}">
|
||||
<div>
|
||||
<b>Comment</b>
|
||||
({{ p.comment_time|date:"Y-m-d" }}{% if not p.for_current_revision %}{% if p.comment_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
({{ p.comment_time|date:"Y-m-d" }}{% if not p.for_current_revision and p.get_dochistory.rev %}{% if p.comment_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
{% if p.send_email %}
|
||||
<i class="bi bi-envelope-check float-end"
|
||||
title="Email requested to be sent for this comment"></i>
|
||||
|
@ -178,9 +178,9 @@
|
|||
<b>{{ p.pos.name }} </b>
|
||||
{% if p.pos.blocking and p.discuss %}
|
||||
<b>[Treat as non-blocking comment]</b>
|
||||
({{ p.discuss_time|date:"Y-m-d" }}{% if not p.for_current_revision %}{% if p.discuss_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
({{ p.discuss_time|date:"Y-m-d" }}{% if not p.for_current_revision and p.get_dochistory.rev %}{% if p.discuss_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
{% else %}
|
||||
({{ p.comment_time|date:"Y-m-d" }}{% if not p.for_current_revision %}{% if p.comment_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
({{ p.comment_time|date:"Y-m-d" }}{% if not p.for_current_revision and p.get_dochistory.rev %}{% if p.comment_time %} {% endif %}for -{{ p.get_dochistory.rev }}{% endif %})
|
||||
{% endif %}
|
||||
{% if p.send_email %}
|
||||
<i class="bi bi-envelope float-end"
|
||||
|
|
|
@ -56,7 +56,13 @@
|
|||
{% doc_edit_button 'ietf.doc.views_material.edit_material' name=doc.name action="state" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ doc.get_state.name }}</td>
|
||||
<td>
|
||||
{% if doc.get_state.name %}
|
||||
{{ doc.get_state.name }}
|
||||
{% else %}
|
||||
<span class="text-muted">(None)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if other_types %}
|
||||
<tr>
|
||||
|
|
|
@ -25,7 +25,13 @@
|
|||
<td></td>
|
||||
<th scope="row">State</th>
|
||||
<td class="edit"></td>
|
||||
<td>{{ doc.get_state.name }}</td>
|
||||
<td>
|
||||
{% if doc.get_state.name %}
|
||||
{{ doc.get_state.name }}
|
||||
{% else %}
|
||||
<span class="text-muted">(None)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if other_reviews %}
|
||||
|
|
|
@ -68,7 +68,9 @@
|
|||
{% if group.charter %}
|
||||
<a href="{% url "ietf.doc.views_doc.document_main" name=group.charter.name %}">
|
||||
{{ group.charter.name }}-{{ group.charter.rev }}</a>
|
||||
<span class="badge bg-info">{{ group.charter.get_state.name }}</span>
|
||||
{% if group.charter.get_state.name %}
|
||||
<span class="badge bg-info">{{ group.charter.get_state.name }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">(None)</span>
|
||||
{% if user|has_role:"Area Director,Secretariat" %}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
{% elif num|sectionlevel == 3 %}
|
||||
<h4 class="mt-3" id="sec-{{ num|slugify }}">{{ num }} {{ section.title|safe }}</h4>
|
||||
{% endif %}
|
||||
{% if num == "1.4" %}<pre>{{ section.text }}</pre>{% endif %}
|
||||
{% if num == "1.4" %}<pre>{{ section.text|urlize_ietf_docs|linkify }}</pre>{% endif %}
|
||||
{% if num >= "2" and num < "5" %}
|
||||
{% if num == "2" %}
|
||||
<p class="alert alert-info my-3">
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td>{% person_link e.by %}</td>
|
||||
<td>
|
||||
<td class="text-break">
|
||||
{% if e.message %}
|
||||
{% if e.response_due %}<span class="badge bg-info">Response due {{ e.response_due|date:"Y-m-d" }}</span>{% endif %}
|
||||
{# FIXME: can't do format_history_text, because that inserts a <div> into the <pre>, which is illegal. Need to rework the snippeting. #}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block title %}IPR Details - {{ ipr.title }}{% endblock %}
|
||||
{% block pagehead %}
|
||||
<meta name="description"
|
||||
content="IPR disclosure #{{ ipr.ipr_id }}: {{ ipr.title }} ({{ ipr.time|date:'Y' }})">
|
||||
content="IPR disclosure{% if ipr.ipr_id %} #{{ ipr.ipr_id }}{% endif %}: {{ ipr.title }} ({{ ipr.time|date:'Y' }})">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
@ -170,7 +170,13 @@
|
|||
{% endif %}
|
||||
<tbody>
|
||||
<tr>
|
||||
{% if prev %}<td class="ipr-prev">{{ prev.holder_legal_name }}</td>{% endif %}
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{% if prev.holder_legal_name %}
|
||||
{{ prev.holder_legal_name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">Holder legal name</th>
|
||||
<td class="ipr-this">{{ ipr.holder_legal_name }}</td>
|
||||
</tr>
|
||||
|
@ -207,7 +213,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.holder_contact_name }}
|
||||
{% if prev.holder_contact_name %}
|
||||
{{ prev.holder_contact_name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -220,7 +228,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.holder_contact_email|linkify }}
|
||||
{% if prev.holder_contact_email %}
|
||||
{{ prev.holder_contact_email|linkify }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -233,14 +243,16 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.holder_contact_info|linebreaks }}
|
||||
{% if prev.holder_contact_info %}
|
||||
{{ prev.holder_contact_info|linkify|linebreaks }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
Holder contact info
|
||||
</th>
|
||||
<td class="ipr-this">
|
||||
{% if ipr.holder_contact_info %}{{ ipr.holder_contact_info|linebreaks }}{% endif %}
|
||||
{% if ipr.holder_contact_info %}{{ ipr.holder_contact_info|linkify|linebreaks }}{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -282,7 +294,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.ietfer_name }}
|
||||
{% if prev.ietfer_name %}
|
||||
{{ prev.ietfer_name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -295,7 +309,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.ietfer_contact_email|linkify }}
|
||||
{% if prev.ietfer_contact_email %}
|
||||
{{ prev.ietfer_contact_email|linkify }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -308,14 +324,16 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.ietfer_contact_info|linebreaks }}
|
||||
{% if prev.ietfer_contact_info %}
|
||||
{{ prev.ietfer_contact_info|linkify|linebreaks }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
Other info
|
||||
</th>
|
||||
<td class="ipr-this">
|
||||
{{ ipr.ietfer_contact_info|linebreaks }}
|
||||
{{ ipr.ietfer_contact_info|linkify|linebreaks }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -474,7 +492,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.patent_info|linebreaks }}
|
||||
{% if prev.patent_info %}
|
||||
{{ prev.patent_info|urlize_ietf_docs|linkify|linebreaks }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -483,7 +503,7 @@
|
|||
or Application/File number(s)
|
||||
</th>
|
||||
<td class="ipr-this">
|
||||
{{ ipr.patent_info|linebreaks }}
|
||||
{{ ipr.patent_info|urlize_ietf_docs|linkify|linebreaks }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -523,7 +543,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.has_patent_pending|yesno:"Yes,No" }}
|
||||
{% if prev.has_patent_pending %}
|
||||
{{ prev.has_patent_pending|yesno:"Yes,No" }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -592,10 +614,12 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{% if prev.licensing.slug == "provided-later" %}
|
||||
{{ prev.licensing.desc|slice:"2:"|slice:":117" }})
|
||||
{% else %}
|
||||
{{ prev.licensing.desc|slice:"2:" }}
|
||||
{% if prev.licensing.slug %}
|
||||
{% if prev.licensing.slug == "provided-later" %}
|
||||
{{ prev.licensing.desc|slice:"2:"|slice:":117" }})
|
||||
{% else %}
|
||||
{{ prev.licensing.desc|slice:"2:" }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
@ -613,7 +637,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.licensing_comments|default:"(No information submitted)"|linebreaks }}
|
||||
{% if prev.licensing_comments %}
|
||||
{{ prev.licensing_comments|default:"(No information submitted)"|urlize_ietf_docs|linkify|linebreaks }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -666,14 +692,16 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.statement|linebreaks }}
|
||||
{% if prev.statement %}
|
||||
{{ prev.statement|urlize_ietf_docs|linkify|linebreaks }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
Statement
|
||||
</th>
|
||||
<td class="ipr-this">
|
||||
{{ ipr.statement|linebreaks }}
|
||||
{{ ipr.statement|urlize_ietf_docs|linkify|linebreaks }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -714,7 +742,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.submitter_name }}
|
||||
{% if prev.submitter_name %}
|
||||
{{ prev.submitter_name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -727,7 +757,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.submitter_email|linkify }}
|
||||
{% if prev.submitter_email %}
|
||||
{{ prev.submitter_email|linkify }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -775,7 +807,9 @@
|
|||
<tr>
|
||||
{% if prev %}
|
||||
<td class="ipr-prev">
|
||||
{{ prev.notes|urlize_ietf_docs|linkify|linebreaks }}
|
||||
{% if prev.notes %}
|
||||
{{ prev.notes|urlize_ietf_docs|linkify|linebreaks }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<th scope="row" class="ipr-label {% if prev %}text-center table-secondary{% endif %}">
|
||||
|
@ -791,4 +825,4 @@
|
|||
<p class="my-3 alert alert-info">
|
||||
Only those sections of the relevant entry form where the submitter provided information are displayed above.
|
||||
</p>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -140,13 +140,13 @@
|
|||
{% if item.session_keyword %}
|
||||
<label class="d-none"
|
||||
aria-label="Select session"
|
||||
for="{{ item.session_keyword }}">
|
||||
for="{{ item.session_keyword }}-{{ item.slug }}">
|
||||
</label>
|
||||
<input type="checkbox"
|
||||
class="pickview form-check-input"
|
||||
title="Select session"
|
||||
name="selected-sessions"
|
||||
id="{{ item.session_keyword }}"
|
||||
id="{{ item.session_keyword }}-{{ item.slug }}"
|
||||
value="{{ item.session_keyword }}"
|
||||
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
|
||||
data-filter-item="{{ item.session_keyword }}">
|
||||
|
@ -268,7 +268,9 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-none d-sm-block">{{ item.session.historic_group.historic_parent.acronym }}</div>
|
||||
{% if item.session.historic_group.historic_parent.acronym %}
|
||||
<div class="d-none d-sm-block">{{ item.session.historic_group.historic_parent.acronym }}</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if item.session.historic_group %}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
{% if email and email == "system@datatracker.ietf.org" or name and name == "(System)" %}
|
||||
<span class="text-muted">(System)</span>
|
||||
{% else %}
|
||||
<span {% if class %}class="{{ class }}"
|
||||
{% endif %}>{% if email or name %}<a {% if class %}class="text-reset"{% endif %}
|
||||
title="{% if title %}{{ title }}{% else %}Datatracker profile of {{ name }}.{% endif %}"
|
||||
|
@ -5,4 +8,5 @@
|
|||
href="mailto:{{ email|urlencode }}"
|
||||
aria-label="Compose email to {{ email }}."
|
||||
title="Compose email to {{ email }}.">
|
||||
<i class="bi bi-envelope"></i></a>{% endif %}{% else %}<span class="text-muted">(None)</span>{% endif %}</span>
|
||||
<i class="bi bi-envelope"></i></a>{% endif %}{% else %}<span class="text-muted">(None)</span>{% endif %}</span>
|
||||
{% endif %}
|
||||
|
|
|
@ -126,6 +126,11 @@ http = urllib3.PoolManager(retries=urllib3.Retry(99, redirect=False))
|
|||
|
||||
def vnu_validate(html, content_type="text/html", port=8888):
|
||||
"Pass the HTML to the vnu server running on the indicated port"
|
||||
if "** No value found for " in html.decode():
|
||||
return json.dumps(
|
||||
{"messages": [{"message": '"** No value found for" in source'}]}
|
||||
)
|
||||
|
||||
gzippeddata = gzip.compress(html)
|
||||
try:
|
||||
req = http.request(
|
||||
|
@ -183,8 +188,7 @@ def vnu_filter_message(msg, filter_db_issues, filter_test_issues):
|
|||
if filter_test_issues and re.search(
|
||||
r"""Ceci\ n'est\ pas\ une\ URL|
|
||||
^The\ '\w+'\ attribute\ on\ the\ '\w+'\ element\ is\ obsolete|
|
||||
^Section\ lacks\ heading|
|
||||
is\ not\ in\ Unicode\ Normalization\ Form\ C""",
|
||||
^Section\ lacks\ heading""",
|
||||
msg["message"],
|
||||
flags=re.VERBOSE,
|
||||
):
|
||||
|
@ -193,7 +197,8 @@ def vnu_filter_message(msg, filter_db_issues, filter_test_issues):
|
|||
return re.search(
|
||||
r"""document\ is\ not\ mappable\ to\ XML\ 1|
|
||||
^Attribute\ 'required'\ not\ allowed\ on\ element\ 'div'|
|
||||
^The\ 'type'\ attribute\ is\ unnecessary\ for\ JavaScript""",
|
||||
^The\ 'type'\ attribute\ is\ unnecessary\ for\ JavaScript|
|
||||
is\ not\ in\ Unicode\ Normalization\ Form\ C""",
|
||||
msg["message"],
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
|
|
@ -2,13 +2,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import bleach
|
||||
import bleach # type: ignore
|
||||
import copy
|
||||
import email
|
||||
import re
|
||||
import textwrap
|
||||
import tlds
|
||||
import unicodedata
|
||||
|
||||
from django.core.validators import URLValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.functional import keep_lazy
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
@ -17,14 +20,32 @@ import debug # pyflakes:ignore
|
|||
from .texescape import init as texescape_init, tex_escape_map
|
||||
|
||||
tlds_sorted = sorted(tlds.tld_set, key=len, reverse=True)
|
||||
protocols = bleach.sanitizer.ALLOWED_PROTOCOLS
|
||||
protocols = copy.copy(bleach.sanitizer.ALLOWED_PROTOCOLS)
|
||||
protocols.append("ftp") # we still have some ftp links
|
||||
validate_url = URLValidator()
|
||||
|
||||
|
||||
def check_url_validity(attrs, new=False):
|
||||
url = attrs[(None, 'href')]
|
||||
try:
|
||||
if url.startswith("http"):
|
||||
validate_url(url)
|
||||
except ValidationError:
|
||||
return None
|
||||
return attrs
|
||||
|
||||
|
||||
bleach_linker = bleach.Linker(
|
||||
callbacks=[check_url_validity],
|
||||
url_re=bleach.linkifier.build_url_re(tlds=tlds_sorted, protocols=protocols),
|
||||
email_re=bleach.linkifier.build_email_re(tlds=tlds_sorted), # type: ignore
|
||||
parse_email=True
|
||||
)
|
||||
|
||||
tags = copy.copy(bleach.sanitizer.ALLOWED_TAGS)
|
||||
tags.remove("a")
|
||||
bleach_cleaner = bleach.sanitizer.Cleaner(tags=tags, protocols=protocols)
|
||||
|
||||
|
||||
@keep_lazy(str)
|
||||
def xslugify(value):
|
||||
|
|
Loading…
Reference in a new issue