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:
Lars Eggert 2022-05-10 20:37:14 +03:00 committed by GitHub
parent 47b89c1112
commit 5f8d4ed718
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 287 additions and 124 deletions

View file

@ -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]",

View file

@ -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:

View file

@ -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)

View file

@ -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.

View file

@ -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"

View file

@ -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>

View file

@ -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 %}

View file

@ -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" %}

View file

@ -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">

View file

@ -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. #}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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,
)

View file

@ -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):