fix: add more HTML validation & fixes (#3891)
* Update vnu.jar * Fix py2 -> py3 issue * Run pyupgrade * test: Add default-jdk to images * test: Add option to also validate HTML with vnu.jar Since it's already installed in bin. Don't do this by default, since it increases the time needed for tests by ~50%. * fix: Stop the urlizer from urlizing in linkified mailto: text * More HTML fixes * More HTML validation fixes * And more HTML fixes * Fix floating badge * Ignore unicode errors * Only URLize docs that are existing * Final fixes * Don't URLize everything during test-crawl * Feed HTML into vnu using python rather than Java to speed things up * Allow test-crawl to start vnu on a different port * Increase retry count to vnu. Restore batch size to 30. * More HTML validation fixes * Use urllib3 to make requests to vnu, since overriding requests_mock is tricky * Undo commit of unmodified file * Also urlize ftp links * Fix matching of file name * More HTML fixes * Add `is_valid_url` filter * weekday -> data-weekday * urlencode URLs * Add and use vnu_fmt_message. Bump vnu max buffer. * Simplify doc_exists * Don't add tab link to mail archive if the URL is invalid * Run urlize_ietf_docs before linkify Reduces the possibility of generating incorrect HTML * Undo superfluous change * Runner fixes * Consolidate vnu message filtering into vnu_filter_message * Correctly handle multiple persons with same name * Minimze diff * Fix HTML nits * Print source snippet in vnu_fmt_message * Only escape if there is something to escape * Fix snippet * Skip crufty old IPR declarations * Only include modal when needed. Add handles. * Fix wordwrap+linkification * Update ietf/doc/templatetags/ietf_filters.py * Update ietf/doc/templatetags/tests_ietf_filters.py * Don't right-align second column
This commit is contained in:
parent
f778058005
commit
5598762608
493
bin/test-crawl
493
bin/test-crawl
|
@ -3,7 +3,7 @@
|
|||
# Copyright The IETF Trust 2013-2019, All Rights Reserved
|
||||
|
||||
|
||||
import os, sys, re, datetime, argparse, traceback, json, subprocess
|
||||
import os, sys, re, datetime, argparse, traceback, json
|
||||
import html5lib
|
||||
import random
|
||||
|
||||
|
@ -31,7 +31,7 @@ parser.add_argument('--no-follow', dest='follow', action='store_false', default=
|
|||
parser.add_argument('--validator-nu', dest='validator_nu', action='store_true',
|
||||
help='Use validator.nu instead of html5lib for HTML validation')
|
||||
parser.add_argument('--pedantic', action='store_true',
|
||||
help='Stop the crawl on the first HTML validation issue')
|
||||
help='Stop the crawl on the first error or warning')
|
||||
parser.add_argument('--random', action='store_true',
|
||||
help='Crawl URLs randomly')
|
||||
parser.add_argument('--validate-all', dest='validate_all', action='store_true', default=False,
|
||||
|
@ -44,6 +44,7 @@ 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
|
||||
|
@ -65,6 +66,7 @@ import debug # pyflakes:ignore
|
|||
from ietf.name.models import DocTypeName
|
||||
from ietf.utils.html import unescape
|
||||
from ietf.utils.test_utils import unicontent
|
||||
from ietf.utils.test_runner import start_vnu_server, vnu_validate, vnu_fmt_message, vnu_filter_message
|
||||
|
||||
# --- Constants ---
|
||||
|
||||
|
@ -156,55 +158,38 @@ def check_html_valid(url, response, args):
|
|||
key = re.sub("/submit/status/nnnn/[0-9a-f]+/", "/submit/status/nnnn/bar/", key)
|
||||
key = re.sub("/team/[a-z0-9-]+/", "/team/foo/", key)
|
||||
key = re.sub("/wg/[a-z0-9-]+/", "/wg/foo/", key)
|
||||
key = re.sub("\?.*$", "", key)
|
||||
key = re.sub(r"\?.*$", "", key)
|
||||
|
||||
for slug in doc_types:
|
||||
key = re.sub("/%s-.*/"%slug, "/%s-nnnn/"%slug, key)
|
||||
|
||||
if not key in validated_urls:
|
||||
note('Validate: %-32s: %s' % (url[:32], key))
|
||||
# These URLs have known issues, skip them until those are fixed
|
||||
for pattern in (
|
||||
'/secr',
|
||||
'admin/',
|
||||
'/doc/.*/edit/info/',
|
||||
'rfc542$',
|
||||
'rfc776$',
|
||||
'draft-leroux-pce-pcecp-interarea-reqs',
|
||||
'draft-fujiwara-dnsop-resolver-update',
|
||||
):
|
||||
if re.search(pattern, url):
|
||||
validated_urls[key] = True
|
||||
log("%s blacklisted; skipping HTML validation" % url)
|
||||
return
|
||||
|
||||
if hasattr(response, "content"):
|
||||
content = response.content
|
||||
else:
|
||||
content = response.streaming_content
|
||||
validated_urls[key] = True
|
||||
if args.validator_nu:
|
||||
v = subprocess.Popen(["java", "-jar", basedir + "/bin/vnu.jar",
|
||||
"--format", "json", "-"],
|
||||
stdin=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
for m in json.loads(v.communicate(content)[1])["messages"]:
|
||||
t = m["subType"] if m["type"] == "info" else m["type"]
|
||||
tags.append("\n%s\tLine %d: %s" %
|
||||
(t.upper(), m["lastLine"], m["message"]))
|
||||
tags.append("\n\t%s" % m["extract"].replace('\n', ' '))
|
||||
tags.append("\n\t%s%s" %
|
||||
(" " * m["hiliteStart"], "^" * m["hiliteLength"]))
|
||||
ret = vnu_validate(content, response["Content-Type"], port=8887)
|
||||
assert ret
|
||||
for m in json.loads(ret)["messages"]:
|
||||
if "lastLine" not in m:
|
||||
tag = m # just dump the raw JSON for now
|
||||
else:
|
||||
tag = vnu_fmt_message(url, m, content.decode())
|
||||
# disregard some HTML issues that are (usually) due to invalid
|
||||
# database content
|
||||
if not re.search('Forbidden code point|Bad value|seamless|The first child|Duplicate ID|The first occurrence of ID', m["message"]):
|
||||
if not vnu_filter_message(m, True, False):
|
||||
warnings += 1
|
||||
tags.append(tag)
|
||||
else:
|
||||
try:
|
||||
parser.parse(content)
|
||||
except Exception as e:
|
||||
for err in parser.errors:
|
||||
pos, code, data = err
|
||||
tags.append(u"WARN invalid html at line, pos %s: %s" % (pos, e))
|
||||
pos, _, _ = err
|
||||
tags.append("WARN invalid html at line, pos {}: {}".format(pos, e))
|
||||
warnings += 1
|
||||
|
||||
def skip_extract_from(url):
|
||||
|
@ -219,410 +204,24 @@ def skip_extract_from(url):
|
|||
|
||||
def skip_url(url):
|
||||
for pattern in (
|
||||
"^/community/[0-9]+/remove_document/",
|
||||
"^/community/personal/",
|
||||
r"^/community/[0-9]+/remove_document/",
|
||||
r"^/community/personal/",
|
||||
# Skip most of the slow pdf composite generation urls and svg urls
|
||||
"^/meeting/[0-9]+/agenda/[0-9b-z].*-drafts\\.pdf",
|
||||
"^/wg/[a-z0-9-]+/deps/svg/",
|
||||
# This bad url occurs in an uploaded html agenda:
|
||||
r"/site/ietfdhcwg/_/rsrc/1311005436000/system/app/css/overlay.css\?cb=simple100%250150goog-ws-left",
|
||||
r"/dir/tsvdir/reviews/",
|
||||
r"draft-touch-msword-template-v2\.0",
|
||||
|
||||
r"^/meeting/[0-9]+/agenda/[0-9b-z].*-drafts\\.pdf",
|
||||
r"^/wg/[a-z0-9-]+/deps/svg/",
|
||||
# Skip other bad urls
|
||||
r"^/dir/tsvdir/reviews/",
|
||||
r"^/ipr/\d{,3}/history/",
|
||||
# Skip most html conversions, not worth the time
|
||||
"^/doc/html/draft-[0-9ac-z]",
|
||||
"^/doc/html/draft-b[0-9b-z]",
|
||||
"^/doc/pdf/draft-[0-9ac-z]",
|
||||
"^/doc/pdf/draft-b[0-9b-z]",
|
||||
"^/doc/html/charter-.*",
|
||||
"^/doc/html/status-.*",
|
||||
"^/doc/html/rfc.*",
|
||||
# These will always 404, but include only those not excluded above
|
||||
# r"^/doc/html/charter-ietf-cicm",
|
||||
# r"^/doc/html/charter-ietf-dcon",
|
||||
# r"^/doc/html/charter-ietf-fun",
|
||||
# r"^/doc/html/charter-ietf-multrans",
|
||||
# r"^/doc/html/charter-ietf-sdn",
|
||||
# r"^/doc/html/charter-ietf-woes",
|
||||
# r"^/doc/html/draft-allan-mpls-loadbal-06",
|
||||
# r"^/doc/html/draft-allocchio-mail11-00",
|
||||
# r"^/doc/html/draft-almquist-leak-01",
|
||||
# r"^/doc/html/draft-almquist-nexthop-01",
|
||||
# r"^/doc/html/draft-armitage-ion-mars-scsp-06",
|
||||
r"^/doc/html/draft-balakrishnan-cm-03",
|
||||
r"^/doc/html/draft-ballardie-cbt-02",
|
||||
# r"^/doc/html/draft-bellovin-ipng-shape-of-bits-00",
|
||||
# r"^/doc/html/draft-bellovin-itrace-04",
|
||||
# r"^/doc/html/draft-bhaskar-pim-ss-04",
|
||||
# r"^/doc/html/draft-bhattach-diot-pimso-04",
|
||||
# r"^/doc/html/draft-bierman-rmonmib-apmcaps-04",
|
||||
# r"^/doc/html/draft-blaze-ipsp-trustmgt-04",
|
||||
# r"^/doc/html/draft-blumenthal-snmpv2a-community-00",
|
||||
# r"^/doc/html/draft-borenstein-agc-spec-00",
|
||||
# r"^/doc/html/draft-borenstein-kidcode-00",
|
||||
# r"^/doc/html/draft-borenstein-mailcap-00",
|
||||
# r"^/doc/html/draft-borenstein-pgp-mime-00",
|
||||
# r"^/doc/html/draft-brockhaus-lamps-cmp-updates-00",
|
||||
# r"^/doc/html/draft-brockhaus-lamps-cmp-updates-03",
|
||||
# r"^/doc/html/draft-brockhaus-lamps-lightweight-cmp-profile-00",
|
||||
# r"^/doc/html/draft-brockhaus-lamps-lightweight-cmp-profile-03",
|
||||
# r"^/doc/html/draft-brown-supplpkgs-04",
|
||||
# r"^/doc/html/draft-brownlee-acct-arch-report-03",
|
||||
# r"^/doc/html/draft-cain-igmp-00",
|
||||
# r"^/doc/html/draft-calhoun-aaa-diameter-comp-01",
|
||||
# r"^/doc/html/draft-calhoun-mobileip-fa-tokens-04",
|
||||
# r"^/doc/html/draft-calhoun-mobileip-min-lat-handoff-02",
|
||||
# r"^/doc/html/draft-callon-addflow-support-clnp-00",
|
||||
# r"^/doc/html/draft-callon-routing-00",
|
||||
# r"^/doc/html/draft-carpenter-ipng-nosi-00",
|
||||
# r"^/doc/html/draft-carpenter-percep-00",
|
||||
# r"^/doc/html/draft-casati-gtp-03",
|
||||
# r"^/doc/html/draft-ceuppens-mpls-optical-04",
|
||||
# r"^/doc/html/draft-chapin-clnp-ISO8473-00",
|
||||
# r"^/doc/html/draft-cheng-modular-ikmp-00",
|
||||
# r"^/doc/html/draft-cole-appm-01",
|
||||
# r"^/doc/html/draft-conta-ipv6-icmp-igmp-00",
|
||||
# r"^/doc/html/draft-coya-test-01",
|
||||
# r"^/doc/html/draft-crocker-cidrd-myth-00",
|
||||
# r"^/doc/html/draft-crocker-pci-00",
|
||||
# r"^/doc/html/draft-crocker-stif-00",
|
||||
# r"^/doc/html/draft-daigle-napstr-05",
|
||||
# r"^/doc/html/draft-davie-intserv-compress-01",
|
||||
# r"^/doc/html/draft-davie-tag-switching-atm-04",
|
||||
# r"^/doc/html/draft-davin-qosrep-00",
|
||||
# r"^/doc/html/draft-davin-rsvfms-00",
|
||||
# r"^/doc/html/draft-dckmtr-proxy-00",
|
||||
# r"^/doc/html/draft-doolan-tdp-spec-04",
|
||||
# r"^/doc/html/draft-duerst-ruby-04",
|
||||
# r"^/doc/html/draft-dusse-pem-message-00",
|
||||
# r"^/doc/html/draft-dutt-rap-rsvp-proxy-01",
|
||||
# r"^/doc/html/draft-eastlake-muse-05",
|
||||
# r"^/doc/html/draft-elmalki-soliman-hmipv4v6-04",
|
||||
# r"^/doc/html/draft-ema-vpim-simplev3-02",
|
||||
# r"^/doc/html/draft-esaki-co-cl-ip-forw-atm-01",
|
||||
# r"^/doc/html/draft-etf-marid-protocol-00",
|
||||
# r"^/doc/html/draft-faltstrom-macmime-00",
|
||||
# r"^/doc/html/draft-faltstrom-whois-05",
|
||||
# r"^/doc/html/draft-fielding-http-spec-01",
|
||||
# r"^/doc/html/draft-flick-interfaces-mib-00",
|
||||
# r"^/doc/html/draft-flick-repeater-dev-mib-00",
|
||||
# r"^/doc/html/draft-floyd-cc-alt",
|
||||
# r"^/doc/html/draft-ford-bigten-bt-format-00",
|
||||
# r"^/doc/html/draft-ford-sdrp-sipp16-format-00",
|
||||
# r"^/doc/html/draft-freed-ftbp-00",
|
||||
# r"^/doc/html/draft-freed-newenc-01",
|
||||
# r"^/doc/html/draft-gellens-imapext-annotate-01",
|
||||
# r"^/doc/html/draft-gharai-hdtv-video-04",
|
||||
# r"^/doc/html/draft-glenn-layer-security-00",
|
||||
# r"^/doc/html/draft-hares-idrp-familytree-00",
|
||||
# r"^/doc/html/draft-harrington-control-mib-00",
|
||||
# r"^/doc/html/draft-harrington-data-filter-mib-00",
|
||||
# r"^/doc/html/draft-haskin-intra-route-server-00",
|
||||
# r"^/doc/html/draft-helenius-ppp-subnet-04",
|
||||
# r"^/doc/html/draft-hinden-ipng-addr-00",
|
||||
# r"^/doc/html/draft-holbrook-ssm-04",
|
||||
# r"^/doc/html/draft-hollenbeck-epp-tcp-02",
|
||||
# r"^/doc/html/draft-houldsworth-ip6-nsap-use-00",
|
||||
# r"^/doc/html/draft-houldsworth-sc6-hot-finland-00",
|
||||
# r"^/doc/html/draft-huitema-shipworm-01",
|
||||
# r"^/doc/html/draft-iab-liaisons-00",
|
||||
# r"^/doc/html/draft-iab-mou2jtc1-03",
|
||||
# r"^/doc/html/draft-iab-standards-processv3-00",
|
||||
# r"^/doc/html/draft-ietf-aft-socks-md5-auth-00",
|
||||
# r"^/doc/html/draft-ietf-bgpdepl-minutes-93feb-00",
|
||||
# r"^/doc/html/draft-ietf-bmwg-overallperf-00",
|
||||
# r"^/doc/html/draft-ietf-bridge-sr-obj-00",
|
||||
# r"^/doc/html/draft-ietf-cat-altftp-00",
|
||||
# r"^/doc/html/draft-ietf-cip-apisocket-00",
|
||||
# r"^/doc/html/draft-ietf-cipso-ipsec-option-00",
|
||||
# r"^/doc/html/draft-ietf-decnetiv-mib-implement-00",
|
||||
# r"^/doc/html/draft-ietf-dhc-problem-stmt-00",
|
||||
# r"^/doc/html/draft-ietf-dns-idpr-02",
|
||||
# r"^/doc/html/draft-ietf-dns-ixfr-01",
|
||||
# r"^/doc/html/draft-ietf-dnsind-dynDNS-arch-00",
|
||||
# r"^/doc/html/draft-ietf-dnsind-dynDNS-impl-00",
|
||||
# r"^/doc/html/draft-ietf-dtn-tcpclv4-00",
|
||||
# r"^/doc/html/draft-ietf-dtn-tcpclv4-15",
|
||||
# r"^/doc/html/draft-ietf-dtn-tcpclv4-18",
|
||||
# r"^/doc/html/draft-ietf-dtn-tcpclv4-19",
|
||||
# r"^/doc/html/draft-ietf-ethermib-objects-00",
|
||||
# r"^/doc/html/draft-ietf-fax-tiff-f-reg-01",
|
||||
# r"^/doc/html/draft-ietf-geopriv-dhcp-lo-option-01",
|
||||
# r"^/doc/html/draft-ietf-html-charset-harmful-00",
|
||||
# r"^/doc/html/draft-ietf-iafa-templates-00",
|
||||
# r"^/doc/html/draft-ietf-idmr-mtree-00",
|
||||
# r"^/doc/html/draft-ietf-idmr-pim-dense-spec-00",
|
||||
# r"^/doc/html/draft-ietf-idmr-pim-dm-spec-08",
|
||||
# r"^/doc/html/draft-ietf-idr-bgp-tcp-md5bad-01",
|
||||
# r"^/doc/html/draft-ietf-idr-community-00",
|
||||
# r"^/doc/html/draft-ietf-idr-rifs-00",
|
||||
# r"^/doc/html/draft-ietf-ids-iwps-design-spec-01",
|
||||
# r"^/doc/html/draft-ietf-ids-pilots-00",
|
||||
# r"^/doc/html/draft-ietf-iesg-evolutionplan-00",
|
||||
# r"^/doc/html/draft-ietf-iiir-html-01",
|
||||
# r"^/doc/html/draft-ietf-iiir-http-00",
|
||||
# r"^/doc/html/draft-ietf-ipae-new-ip-00",
|
||||
# r"^/doc/html/draft-ietf-ipidrp-sip-01",
|
||||
# r"^/doc/html/draft-ietf-iplpdn-multi-isdn-02",
|
||||
# r"^/doc/html/draft-ietf-iplpdn-para-negotiation-02",
|
||||
# r"^/doc/html/draft-ietf-iplpdn-shortcutrouting-02",
|
||||
# r"^/doc/html/draft-ietf-iplpdn-simple-multi-01",
|
||||
# r"^/doc/html/draft-ietf-ipp-indp-04",
|
||||
# r"^/doc/html/draft-ietf-ipsec-ike-base-mode-03",
|
||||
# r"^/doc/html/draft-ietf-ipsec-intragkm-03",
|
||||
# r"^/doc/html/draft-ietf-ipsp-spsl-04",
|
||||
# r"^/doc/html/draft-ietf-ipsra-pic-07",
|
||||
# r"^/doc/html/draft-ietf-isis-atipx-00",
|
||||
# r"^/doc/html/draft-ietf-isis-multilevel-routing-00",
|
||||
# r"^/doc/html/draft-ietf-isis-nbma-00",
|
||||
# r"^/doc/html/draft-ietf-isis-tcpip-01",
|
||||
# r"^/doc/html/draft-ietf-isn-aup-01",
|
||||
# r"^/doc/html/draft-ietf-lsma-scenarios-03",
|
||||
# r"^/doc/html/draft-ietf-mailext-lang-char-00",
|
||||
# r"^/doc/html/draft-ietf-mhsds-822dir-03",
|
||||
# r"^/doc/html/draft-ietf-mhsds-convert-01",
|
||||
# r"^/doc/html/draft-ietf-mhsds-mhsprofile-04",
|
||||
# r"^/doc/html/draft-ietf-mhsds-mhsuse-03",
|
||||
# r"^/doc/html/draft-ietf-mmusic-agree-00",
|
||||
# r"^/doc/html/draft-ietf-mobileip-aaa-req-00",
|
||||
# r"^/doc/html/draft-ietf-mobileip-addr-ext-00",
|
||||
# r"^/doc/html/draft-ietf-mobileip-integrated-00",
|
||||
# r"^/doc/html/draft-ietf-mobileip-mib-fa-01",
|
||||
# r"^/doc/html/draft-ietf-mobileip-mib-ha-01",
|
||||
# r"^/doc/html/draft-ietf-mobileip-mib-mn-01",
|
||||
# r"^/doc/html/draft-ietf-mobileip-mib-sec-01",
|
||||
# r"^/doc/html/draft-ietf-msi-api-03",
|
||||
# r"^/doc/html/draft-ietf-nasreq-nasrequirements-01",
|
||||
# r"^/doc/html/draft-ietf-netdata-implement-03",
|
||||
# r"^/doc/html/draft-ietf-netdata-netdata-04",
|
||||
# r"^/doc/html/draft-ietf-nimrod-dns-01",
|
||||
# r"^/doc/html/draft-ietf-nisi-nicdoc-00",
|
||||
# r"^/doc/html/draft-ietf-nisi-nics-00",
|
||||
# r"^/doc/html/draft-ietf-nntp-news-01",
|
||||
# r"^/doc/html/draft-ietf-oncrpc-remote-06",
|
||||
# r"^/doc/html/draft-ietf-osids-dirtree-00",
|
||||
# r"^/doc/html/draft-ietf-osids-dsanaming-02",
|
||||
# r"^/doc/html/draft-ietf-osids-requirements-00",
|
||||
# r"^/doc/html/draft-ietf-osids-simple-stack-00",
|
||||
# r"^/doc/html/draft-ietf-osids-treestructure-00",
|
||||
# r"^/doc/html/draft-ietf-osinsap-format-01",
|
||||
# r"^/doc/html/draft-ietf-osix500-directories-01",
|
||||
# r"^/doc/html/draft-ietf-ospf-extattr-00",
|
||||
# r"^/doc/html/draft-ietf-ospf-ipv6-ext-00",
|
||||
# r"^/doc/html/draft-ietf-ospf-pmp-if-00",
|
||||
# r"^/doc/html/draft-ietf-otp-ver-03",
|
||||
# r"^/doc/html/draft-ietf-pana-aaa-interworking-00",
|
||||
# r"^/doc/html/draft-ietf-pem-notary-00",
|
||||
# r"^/doc/html/draft-ietf-pim-ipv6-04",
|
||||
# r"^/doc/html/draft-ietf-pim-simplekmp-02",
|
||||
# r"^/doc/html/draft-ietf-pint-conf-02",
|
||||
# r"^/doc/html/draft-ietf-pip-vector-00",
|
||||
# r"^/doc/html/draft-ietf-poised-nomcomm-00",
|
||||
# r"^/doc/html/draft-ietf-pppext-aha-auth-00",
|
||||
# r"^/doc/html/draft-ietf-pppext-ipcp-network-04",
|
||||
# r"^/doc/html/draft-ietf-pppext-kap-auth-00",
|
||||
# r"^/doc/html/draft-ietf-pppext-kapv4-auth-00",
|
||||
# r"^/doc/html/draft-ietf-ripv2-ripng-00",
|
||||
# r"^/doc/html/draft-ietf-rmon-trap-00",
|
||||
# r"^/doc/html/draft-ietf-rmonmib-rmon2hc-01",
|
||||
# r"^/doc/html/draft-ietf-roamops-actng-08",
|
||||
# r"^/doc/html/draft-ietf-rohc-rtp-rocco-performance-01",
|
||||
# r"^/doc/html/draft-ietf-rohc-rtp-rocco-video-01",
|
||||
# r"^/doc/html/draft-ietf-rolc-nhrp-mib-00",
|
||||
# r"^/doc/html/draft-ietf-rreq-iprouters-04",
|
||||
# r"^/doc/html/draft-ietf-rsvp-policy-ext-05",
|
||||
# r"^/doc/html/draft-ietf-rsvp-state-compression-04",
|
||||
# r"^/doc/html/draft-ietf-sdr-IPv6-pack-00",
|
||||
# r"^/doc/html/draft-ietf-sdr-pl-00",
|
||||
# r"^/doc/html/draft-ietf-sdr-route-construction-01",
|
||||
# r"^/doc/html/draft-ietf-sdr-route-setup-00",
|
||||
# r"^/doc/html/draft-ietf-sdr-speakers-attribute-00",
|
||||
# r"^/doc/html/draft-ietf-sip-64bit-plan-00",
|
||||
# r"^/doc/html/draft-ietf-sip-dnss-00",
|
||||
# r"^/doc/html/draft-ietf-sip-ospf-00",
|
||||
# r"^/doc/html/draft-ietf-sip-rip-01",
|
||||
# r"^/doc/html/draft-ietf-sip-unicast-addr-00",
|
||||
# r"^/doc/html/draft-ietf-sipp-auto-addr-00",
|
||||
# r"^/doc/html/draft-ietf-sipp-bsd-api-02",
|
||||
# r"^/doc/html/draft-ietf-sipp-dhcpopt-01",
|
||||
# r"^/doc/html/draft-ietf-sipp-discovery-04",
|
||||
# r"^/doc/html/draft-ietf-sipp-discovery-formats-00",
|
||||
# r"^/doc/html/draft-ietf-sipp-dns-01",
|
||||
# r"^/doc/html/draft-ietf-sipp-dns-ext-00",
|
||||
# r"^/doc/html/draft-ietf-sipp-icmp-igmp-00",
|
||||
# r"^/doc/html/draft-ietf-sipp-routing-addr-02",
|
||||
# r"^/doc/html/draft-ietf-sipp-sst-overview-00",
|
||||
# r"^/doc/html/draft-ietf-sipping-overload-design",
|
||||
# r"^/doc/html/draft-ietf-smime-certdist-06",
|
||||
# r"^/doc/html/draft-ietf-smtpext-pipeline-02",
|
||||
# r"^/doc/html/draft-ietf-snmp-isdn-cisco-00",
|
||||
# r"^/doc/html/draft-ietf-snmpsec-m2mv2-01",
|
||||
# r"^/doc/html/draft-ietf-snmpsec-mibv2-00",
|
||||
# r"^/doc/html/draft-ietf-snmpsec-protov2-01",
|
||||
# r"^/doc/html/draft-ietf-snmpsec-tmv2-00",
|
||||
# r"^/doc/html/draft-ietf-stjohns-ipso-00",
|
||||
# r"^/doc/html/draft-ietf-svrloc-discovery-11",
|
||||
# r"^/doc/html/draft-ietf-tcplw-extensions-00",
|
||||
# r"^/doc/html/draft-ietf-tcplw-high-performance-01",
|
||||
# r"^/doc/html/draft-ietf-telnet-authker-v5-01",
|
||||
# r"^/doc/html/draft-ietf-telnet-compression-00",
|
||||
# r"^/doc/html/draft-ietf-telnet-encryption-02",
|
||||
# r"^/doc/html/draft-ietf-tewg-measure-07",
|
||||
# r"^/doc/html/draft-ietf-thinosi-profile-00",
|
||||
# r"^/doc/html/draft-ietf-tnfs-spec-03",
|
||||
# r"^/doc/html/draft-ietf-ucp-connectivity-01",
|
||||
# r"^/doc/html/draft-ietf-udlr-life-03",
|
||||
# r"^/doc/html/draft-ietf-ufdl-spec-01",
|
||||
# r"^/doc/html/draft-ietf-uri-roy-urn-urc-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urc-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urc-sgml-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urc-spec-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urc-trivial-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn-issues-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn-madsen-critique-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn-res-descript-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn-res-thoughts-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn-syntax-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn-x-dns-2-00",
|
||||
# r"^/doc/html/draft-ietf-uri-urn2urc-00",
|
||||
# r"^/doc/html/draft-ietf-uri-yaurn-00",
|
||||
# r"^/doc/html/draft-ietf-userdoc2-fyi-biblio-00",
|
||||
# r"^/doc/html/draft-ietf-uswg-fyi1-02",
|
||||
# r"^/doc/html/draft-ietf-whip-reqs-summary-01",
|
||||
# r"^/doc/html/draft-ietf-x400ops-admd-03",
|
||||
# r"^/doc/html/draft-ietf-x400ops-dnsx400rout-02",
|
||||
# r"^/doc/html/draft-ietf-x400ops-tbl-dist-00",
|
||||
# r"^/doc/html/draft-ietf-x400ops-tbl-dist-part1-01",
|
||||
# r"^/doc/html/draft-ietf-x400ops-tbl-dist-part2-01",
|
||||
# r"^/doc/html/draft-ipsec-isakmp-mode-cfg-02",
|
||||
# r"^/doc/html/draft-johnson-imhp-00",
|
||||
# r"^/doc/html/draft-jseng-utf5-02",
|
||||
# r"^/doc/html/draft-just-ldapv3-rescodes-03",
|
||||
# r"^/doc/html/draft-karrenberg-proposal-00",
|
||||
# r"^/doc/html/draft-kastenholz-loki-00",
|
||||
# r"^/doc/html/draft-kempf-scope-rules-04",
|
||||
# r"^/doc/html/draft-klyne-conneg-feature-match-03",
|
||||
# r"^/doc/html/draft-koch-dnsind-local-compression-01",
|
||||
# r"^/doc/html/draft-kzm-rap-sppi-04",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-adminv2-alt-00",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-coex-alt-00",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-conf-alt-00",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-intro-alt-00",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-mib-alt-00",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-smi-alt-00",
|
||||
# r"^/doc/html/draft-kzm-snmpv2-usec-conf-alt-00",
|
||||
# r"^/doc/html/draft-larson-bad-dns-res-01",
|
||||
# r"^/doc/html/draft-lear-foglamps-03",
|
||||
# r"^/doc/html/draft-leech-socks-protocol-v4-01",
|
||||
# r"^/doc/html/draft-levi-snmp-mid-level-mgr-00",
|
||||
# r"^/doc/html/draft-levi-snmp-script-language-00",
|
||||
# r"^/doc/html/draft-levinson-sgml-02",
|
||||
# r"^/doc/html/draft-li-bigten-addr-format-00",
|
||||
# r"^/doc/html/draft-li-tap-ipv7-00",
|
||||
# r"^/doc/html/draft-lloyd-ip6-iso-itu-reg-00",
|
||||
# r"^/doc/html/draft-macker-mdp-framework-05",
|
||||
# r"^/doc/html/draft-mahoney-snmpv2-features-00",
|
||||
# r"^/doc/html/draft-mahoney-snmpv2-proto-alt-00",
|
||||
# r"^/doc/html/draft-martensson-rocco-video-04",
|
||||
# r"^/doc/html/draft-mccann-mobileip-sessionid-04",
|
||||
# r"^/doc/html/draft-megginson-ldup-lcup-01",
|
||||
# r"^/doc/html/draft-metzger-ah-sha-00",
|
||||
# r"^/doc/html/draft-mpls-rsvpte-attributes-00",
|
||||
# r"^/doc/html/draft-myers-imap-imsp-01",
|
||||
# r"^/doc/html/draft-myers-imap-mbox-00",
|
||||
# r"^/doc/html/draft-myers-smtp-mult-01",
|
||||
# r"^/doc/html/draft-nelson-model-mailext-00",
|
||||
# r"^/doc/html/draft-newman-imap-annotate-00",
|
||||
# r"^/doc/html/draft-nguyen-bgp-ipv6-vpn-03",
|
||||
# r"^/doc/html/draft-nordmark-ipv6-aaa-hooks-04",
|
||||
# r"^/doc/html/draft-nyckelgard-isl-arch-04",
|
||||
# r"^/doc/html/draft-ohta-address-allocation-01",
|
||||
# r"^/doc/html/draft-ohta-dynamic-dns-00",
|
||||
# r"^/doc/html/draft-ohta-ip-over-atm-02",
|
||||
# r"^/doc/html/draft-ohta-mime-charset-names-00",
|
||||
# r"^/doc/html/draft-ohta-shared-media-02",
|
||||
# r"^/doc/html/draft-ohta-simple-dns-01",
|
||||
# r"^/doc/html/draft-ohta-text-encoding-01",
|
||||
# r"^/doc/html/draft-ohta-translation-instr-01",
|
||||
# r"^/doc/html/draft-ooms-cl-multicast-03",
|
||||
# r"^/doc/html/draft-ops-rfc2011-update-01",
|
||||
# r"^/doc/html/draft-ouldbrahim-bgpvpn-auto-03",
|
||||
# r"^/doc/html/draft-palme-autosub-06",
|
||||
# r"^/doc/html/draft-pan-diffserv-mib-01",
|
||||
# r"^/doc/html/draft-perkins-cnlp-support-00",
|
||||
# r"^/doc/html/draft-perkins-homeaddr-dhcpopt-00",
|
||||
# r"^/doc/html/draft-perkins-opaque-04",
|
||||
# r"^/doc/html/draft-polk-slp-loc-auth-server-04",
|
||||
# r"^/doc/html/draft-popp-cnrp-goals-01",
|
||||
# r"^/doc/html/draft-pusateri-igmp-mib-00",
|
||||
# r"^/doc/html/draft-pusateri-ipmulti-mib-00",
|
||||
# r"^/doc/html/draft-reddy-opsawg-mud-tls-00",
|
||||
# r"^/doc/html/draft-reddy-opsawg-mud-tls-03",
|
||||
# r"^/doc/html/draft-reichmeyer-polterm-terminology-04",
|
||||
# r"^/doc/html/draft-rekhter-arch-sipp16-addr-00",
|
||||
# r"^/doc/html/draft-rekhter-bigten-addr-arch-00",
|
||||
# r"^/doc/html/draft-rekhter-direct-provider-01",
|
||||
# r"^/doc/html/draft-rekhter-idr-over-atm-00",
|
||||
# r"^/doc/html/draft-rekhter-lsr-mobile-hosts-00",
|
||||
# r"^/doc/html/draft-rekhter-select-providers-02",
|
||||
# r"^/doc/html/draft-rekhter-sops-02",
|
||||
# r"^/doc/html/draft-rekhter-stratum-aggregation-01",
|
||||
# r"^/doc/html/draft-renwick-hippiarp-01",
|
||||
# r"^/doc/html/draft-renwick-hippimib-01",
|
||||
# r"^/doc/html/draft-rfced-info-corson-00",
|
||||
# r"^/doc/html/draft-rfced-info-katsube-oops-00",
|
||||
# r"^/doc/html/draft-rfced-info-perkins-05",
|
||||
# r"^/doc/html/draft-rfced-info-pi-vs-pa-addrspac-00",
|
||||
# r"^/doc/html/draft-rfced-info-senie-00",
|
||||
# r"^/doc/html/draft-ronc-domain-phb-set-ldap-rep-04",
|
||||
# r"^/doc/html/draft-ronc-domain-phb-set-specification-04",
|
||||
# r"^/doc/html/draft-rose-limit-01",
|
||||
# r"^/doc/html/draft-rose-smxp-spec-00",
|
||||
# r"^/doc/html/draft-rosen-ppvpn-l2vpn-01",
|
||||
# r"^/doc/html/draft-rosen-tag-stack-05",
|
||||
# r"^/doc/html/draft-rosenberg-mmusic-sdp-offer-answer-01",
|
||||
# r"^/doc/html/draft-rosenberg-sip-tunnels-01",
|
||||
# r"^/doc/html/draft-salzr-ldap-repsig-01",
|
||||
# r"^/doc/html/draft-sandick-pimsm-ssmrules-04",
|
||||
# r"^/doc/html/draft-schroeppel-dnsind-ecc-04",
|
||||
# r"^/doc/html/draft-simpson-exchanges-00",
|
||||
# r"^/doc/html/draft-simpson-ipv6-deploy-00",
|
||||
# r"^/doc/html/draft-simpson-ipv6-discovery-req-00",
|
||||
# r"^/doc/html/draft-simpson-ipv6-hc-00",
|
||||
# r"^/doc/html/draft-simpson-sipp-64-bit-plan-00",
|
||||
# r"^/doc/html/draft-sinnreich-interdomain-sip-qos-osp-02",
|
||||
# r"^/doc/html/draft-slutsman-aicd-02",
|
||||
# r"^/doc/html/draft-speer-avt-layered-video-05",
|
||||
# r"^/doc/html/draft-stein-green-commerce-model-00",
|
||||
# r"^/doc/html/draft-svanbro-rohc-lower-layer-guidelines-04",
|
||||
# r"^/doc/html/draft-templin-atn-aero-interface-00",
|
||||
# r"^/doc/html/draft-templin-atn-aero-interface-21",
|
||||
# r"^/doc/html/draft-teraoka-ipv6-mobility-sup-07",
|
||||
# r"^/doc/html/draft-thayer-seccomp-04",
|
||||
# r"^/doc/html/draft-traina-bgp-confed-00",
|
||||
# r"^/doc/html/draft-treese-class-desc-00",
|
||||
# r"^/doc/html/draft-vaudreuil-binaryheaders-01",
|
||||
# r"^/doc/html/draft-vaudreuil-enum-e164dir-05",
|
||||
# r"^/doc/html/draft-veizades-ipng-svrloc-00",
|
||||
# r"^/doc/html/draft-villamizar-isis-omp-01",
|
||||
# r"^/doc/html/draft-waldbusser-conventions-01",
|
||||
# r"^/doc/html/draft-waldbusser-rmonmib-apm-04",
|
||||
# r"^/doc/html/draft-waldbusser-ssecimpl-01",
|
||||
# r"^/doc/html/draft-waters-snmpv1-sec-mech-00",
|
||||
# r"^/doc/html/draft-weider-comindex-00",
|
||||
# r"^/doc/html/draft-wijnen-snmpv2-snmpv2t-00",
|
||||
# r"^/doc/html/draft-woundy-dhcpleasequery-04",
|
||||
# r"^/doc/html/draft-wright-policy-mpls-04",
|
||||
# r"^/doc/html/draft-yu-asn1-pitfalls-04",
|
||||
# r"^/doc/html/draft-yu-rpd-00",
|
||||
# r"^/doc/html/draft-zaccone-nat-rsip-gen-arch-02",
|
||||
# r"^/doc/html/draft-zaccone-nat-transp-fram-02",
|
||||
# r"^/doc/html/draft-zaccone-nat-transport-03",
|
||||
# r"^/doc/html/status-change-icmpv6-dns-ipv6-to-internet-standard",
|
||||
|
||||
r"^/doc/html/draft-[0-9ac-z]",
|
||||
r"^/doc/html/draft-b[0-9b-z]",
|
||||
r"^/doc/pdf/draft-[0-9ac-z]",
|
||||
r"^/doc/pdf/draft-b[0-9b-z]",
|
||||
r"^/doc/html/charter-.*",
|
||||
r"^/doc/html/status-.*",
|
||||
r"^/doc/html/rfc.*",
|
||||
r"^/static/coverage/",
|
||||
r"^/meeting/6[0-4]/agenda",
|
||||
r"^https?://www.ietf.org/",
|
||||
r"^/meeting/\d{,2}/agenda", # no agendas < 100
|
||||
):
|
||||
if re.search(pattern, url):
|
||||
return True
|
||||
|
@ -691,8 +290,16 @@ logfile = None
|
|||
if args.logfile:
|
||||
logfile = open(args.logfile, "w")
|
||||
|
||||
vnu = None
|
||||
|
||||
# --- Main ---
|
||||
|
||||
def do_exit(code):
|
||||
if vnu:
|
||||
vnu.terminate()
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if (args.user):
|
||||
# log in as user, to have the respective HTML generated by the templates
|
||||
|
@ -702,7 +309,7 @@ if __name__ == "__main__":
|
|||
if (response.status_code != 200):
|
||||
log("Could not log in as %s, HTML response %d" %
|
||||
(args.user, response.status_code))
|
||||
sys.exit(1)
|
||||
do_exit(1)
|
||||
|
||||
# Run django system checks and checks from ietf.checks:
|
||||
error_list = django.core.checks.run_checks()
|
||||
|
@ -718,10 +325,13 @@ if __name__ == "__main__":
|
|||
for entry in error_list:
|
||||
print(entry)
|
||||
|
||||
if args.validator_nu:
|
||||
vnu = start_vnu_server(port=8887)
|
||||
|
||||
while urls:
|
||||
if args.random:
|
||||
# popitem() is documented to be random, but really isn't
|
||||
url = random.choice(urls.keys())
|
||||
url = random.choice(list(urls.keys()))
|
||||
referrer = urls.pop(url)
|
||||
else:
|
||||
url, referrer = urls.popitem()
|
||||
|
@ -746,10 +356,10 @@ if __name__ == "__main__":
|
|||
elapsed = datetime.datetime.now() - request_start
|
||||
except KeyboardInterrupt:
|
||||
log(" ... was fetching %s" % url)
|
||||
sys.exit(1)
|
||||
do_exit(1)
|
||||
except:
|
||||
elapsed = datetime.datetime.now() - request_start
|
||||
tags = [ u"FAIL (from [ %s ])" % (",\n\t".join(get_referrers(url))) ]
|
||||
tags = [ "FAIL (from [ %s ])" % (",\n\t".join(get_referrers(url))) ]
|
||||
log("%2d:%02d:%02d %7d %6d %s %6.3fs %s %s" % (hrs,min,sec, len(visited), len(urls), 500, elapsed.total_seconds(), url, " ".join(tags)))
|
||||
log("=============")
|
||||
log(traceback.format_exc())
|
||||
|
@ -760,7 +370,7 @@ if __name__ == "__main__":
|
|||
|
||||
if r.status_code in (301, 302):
|
||||
u = strip_url(r["Location"])
|
||||
if u not in visited and u not in urls:
|
||||
if not url.startswith("/") and u not in visited and u not in urls:
|
||||
urls[u] = referrer # referrer is original referrer, not redirected url
|
||||
referrers[u] = referrer
|
||||
|
||||
|
@ -799,8 +409,9 @@ if __name__ == "__main__":
|
|||
log("=============")
|
||||
|
||||
else:
|
||||
tags.append(u"FAIL (from %s)" % (referrer, ))
|
||||
errors += 1
|
||||
tags.append("FAIL (from {})".format(referrer))
|
||||
if not url.startswith("/person/"): # FIXME: those fail sometimes
|
||||
errors += 1
|
||||
|
||||
if elapsed.total_seconds() > slow_threshold:
|
||||
tags.append("SLOW")
|
||||
|
@ -816,7 +427,7 @@ if __name__ == "__main__":
|
|||
|
||||
log("%2d:%02d:%02d %7d %6d %s %6.3fs %s %s" % (hrs,min,sec, len(visited), len(urls), r.status_code, elapsed.total_seconds(), url, " ".join(tags)))
|
||||
if ((errors or warnings) and args.pedantic):
|
||||
sys.exit(1)
|
||||
do_exit(1)
|
||||
|
||||
if logfile:
|
||||
logfile.close()
|
||||
|
@ -824,7 +435,7 @@ if __name__ == "__main__":
|
|||
|
||||
if errors > 0:
|
||||
sys.stderr.write("Found %s errors, grep output for FAIL for details\n" % errors)
|
||||
sys.exit(1)
|
||||
do_exit(1)
|
||||
else:
|
||||
sys.stderr.write("Found no errors.\n")
|
||||
if warnings > 0:
|
||||
|
|
BIN
bin/vnu.jar
BIN
bin/vnu.jar
Binary file not shown.
|
@ -23,6 +23,7 @@ RUN apt-get install -qy \
|
|||
bash \
|
||||
build-essential \
|
||||
curl \
|
||||
default-jdk \
|
||||
docker-ce-cli \
|
||||
enscript \
|
||||
gawk \
|
||||
|
|
|
@ -32,6 +32,7 @@ RUN apt-get install -qy \
|
|||
bash \
|
||||
build-essential \
|
||||
curl \
|
||||
default-jdk \
|
||||
docker-ce-cli \
|
||||
enscript \
|
||||
fish \
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import datetime
|
||||
import re
|
||||
import os
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from email.utils import parseaddr
|
||||
|
@ -17,10 +18,13 @@ from django.utils.html import strip_tags
|
|||
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
|
||||
|
||||
from ietf.doc.models import BallotDocEvent
|
||||
from ietf.doc.models import BallotDocEvent, DocAlias
|
||||
from ietf.doc.models import ConsensusDocEvent
|
||||
from ietf.utils.html import sanitize_fragment
|
||||
from ietf.utils import log
|
||||
|
@ -184,49 +188,113 @@ def rfceditor_info_url(rfcnum : str):
|
|||
"""Link to the RFC editor info page for an RFC"""
|
||||
return urljoin(settings.RFC_EDITOR_INFO_BASE_URL, f'rfc{rfcnum}')
|
||||
|
||||
|
||||
def doc_exists(name):
|
||||
"""Check whether a given document exists"""
|
||||
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)
|
||||
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)
|
||||
if extension_split:
|
||||
name = extension_split.group(1)
|
||||
|
||||
if find_unique(name):
|
||||
return True
|
||||
|
||||
# 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)
|
||||
if rev_split:
|
||||
name = rev_split.group(1)
|
||||
if find_unique(name):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def link_charter_doc_match1(match):
|
||||
if not doc_exists(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]):
|
||||
return match[0]
|
||||
return f'<a href="/doc/{match[1][:-1]}/{match[2]}/">{match[0]}</a>'
|
||||
|
||||
|
||||
def link_non_charter_doc_match(match):
|
||||
if len(match[3])==2 and match[3].isdigit():
|
||||
if not doc_exists(match[0]):
|
||||
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>'
|
||||
else:
|
||||
return f'<a href="/doc/{match[2]}{match[3]}/">{match[0]}</a>'
|
||||
|
||||
@register.filter(name='urlize_ietf_docs', is_safe=True, needs_autoescape=True)
|
||||
|
||||
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):
|
||||
return match[0]
|
||||
return f'<a href="/doc/{match[2].strip().lower()}{match[3]}/">{match[1]}</a>'
|
||||
|
||||
|
||||
@register.filter(name="urlize_ietf_docs", is_safe=True, needs_autoescape=True)
|
||||
def urlize_ietf_docs(string, autoescape=None):
|
||||
"""
|
||||
Make occurrences of RFC NNNN and draft-foo-bar links to /doc/.
|
||||
"""
|
||||
if autoescape and not isinstance(string, SafeData):
|
||||
string = escape(string)
|
||||
exp1 = r"\b(charter-(?:[\d\w\.+]+-)*)(\d\d-\d\d)(\.txt)?\b"
|
||||
exp2 = r"\b(charter-(?:[\d\w\.+]+-)*)(\d\d)(\.txt)?\b"
|
||||
if "<" in string:
|
||||
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"
|
||||
if re.search(exp1, string):
|
||||
string = re.sub(
|
||||
exp1,
|
||||
lambda x: f'<a href="/doc/{x[1][:-1]}/{x[2]}/">{x[0]}</a>',
|
||||
link_charter_doc_match1,
|
||||
string,
|
||||
flags=re.IGNORECASE | re.ASCII,
|
||||
)
|
||||
elif re.search(exp2, string):
|
||||
string = re.sub(
|
||||
exp2,
|
||||
lambda x: f'<a href="/doc/{x[1][:-1]}/{x[2]}/">{x[0]}</a>',
|
||||
link_charter_doc_match2,
|
||||
string,
|
||||
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\.+]+-)*)([\d\w\.+]+?)(\.txt)?)\b(?![-@])",
|
||||
link_non_charter_doc_match,
|
||||
string,
|
||||
flags=re.IGNORECASE | re.ASCII,
|
||||
)
|
||||
string = re.sub(
|
||||
# r"\b((RFC|BCP|STD|FYI|(?:draft-|bofreq-|conflict-review-|status-change-|charter-)[-\d\w.+]+)\s*0*(\d+))\b",
|
||||
r"\b(?<!-)((RFC|BCP|STD|FYI)\s*0*(\d+))\b",
|
||||
lambda x: f'<a href="/doc/{x[2].strip().lower()}{x[3]}/">{x[1]}</a>',
|
||||
r"\b(?<![/\-:=#])((RFC|BCP|STD|FYI)\s*0*(\d+))\b",
|
||||
link_other_doc_match,
|
||||
string,
|
||||
flags=re.IGNORECASE | re.ASCII,
|
||||
)
|
||||
return mark_safe(string)
|
||||
|
||||
urlize_ietf_docs = stringfilter(urlize_ietf_docs)
|
||||
|
||||
@register.filter(name='urlize_related_source_list', is_safe=True, needs_autoescape=True)
|
||||
|
@ -444,7 +512,7 @@ def format_snippet(text, trunc_words=25):
|
|||
@register.simple_tag
|
||||
def doc_edit_button(url_name, *args, **kwargs):
|
||||
"""Given URL name/args/kwargs, looks up the URL just like "url" tag and returns a properly formatted button for the document material tables."""
|
||||
return mark_safe('<a class="btn btn-primary btn-sm" type="button" href="%s">Edit</a>' % (urlreverse(url_name, args=args, kwargs=kwargs)))
|
||||
return mark_safe('<a class="btn btn-primary btn-sm" href="%s">Edit</a>' % (urlreverse(url_name, args=args, kwargs=kwargs)))
|
||||
|
||||
@register.filter
|
||||
def textify(text):
|
||||
|
@ -765,3 +833,16 @@ def absurl(viewname, **kwargs):
|
|||
Uses settings.IDTRACKER_BASE_URL as the base.
|
||||
"""
|
||||
return urljoin(settings.IDTRACKER_BASE_URL, urlreverse(viewname, kwargs=kwargs))
|
||||
|
||||
|
||||
@register.filter
|
||||
def is_valid_url(url):
|
||||
"""
|
||||
Check if the given URL is syntactically valid
|
||||
"""
|
||||
validate_url = URLValidator()
|
||||
try:
|
||||
validate_url(url)
|
||||
except ValidationError:
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -51,6 +51,14 @@ class IetfFiltersTests(TestCase):
|
|||
(
|
||||
"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>'
|
||||
),
|
||||
(
|
||||
'<a href="mailto:draft-ietf-some-names@ietf.org">draft-ietf-some-names@ietf.org</a>',
|
||||
'<a href="mailto:draft-ietf-some-names@ietf.org">draft-ietf-some-names@ietf.org</a>',
|
||||
),
|
||||
(
|
||||
"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"
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ from ietf.person.models import Email
|
|||
from ietf.review.utils import can_manage_review_requests_for_team
|
||||
from ietf.utils import log
|
||||
from ietf.utils.history import get_history_object_for, copy_many_to_many_for_history
|
||||
from ietf.doc.templatetags.ietf_filters import is_valid_url
|
||||
from functools import reduce
|
||||
|
||||
def save_group_in_history(group):
|
||||
|
@ -208,7 +209,8 @@ def construct_group_menu_context(request, group, selected, group_type, others):
|
|||
entries.append(("Photos", urlreverse("ietf.group.views.group_photos", kwargs=kwargs)))
|
||||
entries.append(("Email expansions", urlreverse("ietf.group.views.email", kwargs=kwargs)))
|
||||
if group.list_archive.startswith("http:") or group.list_archive.startswith("https:") or group.list_archive.startswith("ftp:"):
|
||||
entries.append((mark_safe("List archive »"), group.list_archive))
|
||||
if is_valid_url(group.list_archive):
|
||||
entries.append((mark_safe("List archive »"), group.list_archive))
|
||||
|
||||
|
||||
# actions
|
||||
|
|
|
@ -35,7 +35,7 @@ from ietf.utils.validators import ( validate_file_size, validate_mime_type,
|
|||
|
||||
# need to insert empty option for use in ChoiceField
|
||||
# countries.insert(0, ('', '-'*9 ))
|
||||
countries.insert(0, ('', ''))
|
||||
countries.insert(0, ('', '-' * 9))
|
||||
timezones.insert(0, ('', '-' * 9))
|
||||
|
||||
# -------------------------------------------------
|
||||
|
|
|
@ -22,7 +22,9 @@ VALID_BLUESHEET_EXTENSIONS = ('.pdf','.jpg','.jpeg')
|
|||
|
||||
class RecordingForm(forms.Form):
|
||||
external_url = forms.URLField(label='Url')
|
||||
session = forms.ModelChoiceField(queryset=Session.objects,empty_label='')
|
||||
session = forms.ModelChoiceField(queryset=Session.objects)
|
||||
session.widget.attrs['class'] = "select2-field"
|
||||
session.widget.attrs['data-minimum-input-length'] = 0
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.meeting = kwargs.pop('meeting')
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
/* proceedings-recordings.js - utility functions */
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#id_session').select2({ placeholder: 'Type group acronym or part of session name', width: '450px' });;
|
||||
});
|
|
@ -11,7 +11,6 @@
|
|||
{% block extrahead %}{{ block.super }}
|
||||
<script src="{% static 'ietf/js/jquery-ui.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/select2.js' %}"></script>
|
||||
<script src="{% static 'secr/js/proceedings-recording.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}{{ block.super }}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
.utc();
|
||||
item.end_ts = moment.unix(this.getAttribute("data-end-time"))
|
||||
.utc();
|
||||
if (this.hasAttribute("weekday")) {
|
||||
if (this.hasAttribute("data-weekday")) {
|
||||
item.format = 2;
|
||||
} else {
|
||||
item.format = 1;
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
<input id="subject" class="form-control" type="text" placeholder="{{ subject }}" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="body">Body</label>
|
||||
<pre id="body" class="border p-3">{{ body|maybewordwrap }}</pre>
|
||||
<p class="form-label">Body</p>
|
||||
<pre class="border p-3">{{ body|maybewordwrap }}</pre>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger">Send</button>
|
||||
<a class="btn btn-secondary float-end"
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre class="ballot pasted">{{ p.discuss|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="ballot pasted">{{ p.discuss|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -148,7 +148,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre class="ballot pasted">{{ p.comment|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="ballot pasted">{{ p.comment|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -199,11 +199,11 @@
|
|||
</div>
|
||||
{% if p.pos.blocking and p.discuss %}
|
||||
<div class="card-body">
|
||||
<pre class="ballot pasted">{{ p.discuss|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="ballot pasted">{{ p.discuss|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body">
|
||||
<pre class="ballot pasted">{{ p.comment|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="ballot pasted">{{ p.comment|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -107,13 +107,13 @@
|
|||
{% if resources %}
|
||||
{% for resource in resources|dictsort:"display_name" %}
|
||||
{% if resource.name.type.slug == 'url' or resource.name.type.slug == 'email' %}
|
||||
<a href="{{ resource.value }}" title="{{ resource.name.name }}">
|
||||
<a href="{{ resource.value|urlencode }}" title="{{ resource.name.name }}">
|
||||
{% firstof resource.display_name resource.name.name %}
|
||||
</a>
|
||||
<br>
|
||||
{# Maybe make how a resource displays itself a method on the class so templates aren't doing this switching #}
|
||||
{% else %}
|
||||
<span title="{{ resource.name.name }}">{% firstof resource.display_name resource.name.name %}: {{ resource.value }}</span>
|
||||
<span title="{{ resource.name.name }}">{% firstof resource.display_name resource.name.name %}: {{ resource.value|escape }}</span>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -230,7 +230,7 @@
|
|||
{{ doc.canonical_name }}-{{ doc.rev }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre>{{ content|maybewordwrap|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ content|maybewordwrap|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
{{ doc.name }}-{{ doc.rev }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre>{{ content|maybewordwrap|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ content|maybewordwrap|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -366,23 +366,30 @@
|
|||
{% if resources or doc.group and doc.group.list_archive %}
|
||||
{% for resource in resources|dictsort:"display_name" %}
|
||||
{% if resource.name.type.slug == 'url' or resource.name.type.slug == 'email' %}
|
||||
<a href="{{ resource.value }}" title="{{ resource.name.name }}">
|
||||
<a href="{{ resource.value|urlencode }}" title="{{ resource.name.name }}">
|
||||
{% firstof resource.display_name resource.name.name %}
|
||||
</a>
|
||||
<br>
|
||||
{# Maybe make how a resource displays itself a method on the class so templates aren't doing this switching #}
|
||||
{% else %}
|
||||
<span title="{{ resource.name.name }}">
|
||||
{% firstof resource.display_name resource.name.name %}: {{ resource.value }}
|
||||
{% firstof resource.display_name resource.name.name %}: {{ resource.value|escape }}
|
||||
</span>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if doc.group and doc.group.list_archive %}
|
||||
<a href="{{ doc.group.list_archive }}?q={{ doc.name }}">
|
||||
Mailing list discussion
|
||||
</a>
|
||||
<br>
|
||||
{% if doc.group.list_archive|startswith:settings.MAILING_LIST_ARCHIVE_URL %}
|
||||
<a href="{{ doc.group.list_archive }}?q={{ doc.name }}">
|
||||
Mailing list discussion
|
||||
</a>
|
||||
{% elif doc.group.list_archive|is_valid_url %}
|
||||
<a href="{{ doc.group.list_archive }}">
|
||||
Mailing list discussion
|
||||
</a>
|
||||
{% else %}
|
||||
{{ doc.group.list_archive|urlencode }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% load origin %}
|
||||
{% load static %}
|
||||
{% load ietf_filters textfilters %}
|
||||
{% block title %}{{ doc.title }}{% endblock %}
|
||||
{% block title %}{{ doc.title|default:"Untitled" }}{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
{{ top|safe }}
|
||||
|
@ -36,7 +36,7 @@
|
|||
{% doc_edit_button 'ietf.doc.views_material.edit_material' name=doc.name action="title" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<th scope="row">{{ doc.title }}</th>
|
||||
<th scope="row">{{ doc.title|default:'<span class="text-muted">(None)</span>' }}</th>
|
||||
</tr>
|
||||
{% if doc.abstract or doc.type_id == 'slides' and can_manage_material and not snapshot %}
|
||||
<tr>
|
||||
|
@ -122,7 +122,7 @@
|
|||
{% if content_is_html %}
|
||||
{{ content|sanitize|safe }}
|
||||
{% else %}
|
||||
<pre>{{ content|maybewordwrap|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ content|maybewordwrap|urlize_ietf_docs|linkify }}</pre>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Not available as plain text.
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
<div class="card mt-5">
|
||||
<div class="card-header">{{ doc.name }}-{{ doc.rev }}</div>
|
||||
<div class="card-body">
|
||||
<pre class="pasted">{{ content|maybewordwrap|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="pasted">{{ content|maybewordwrap|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -162,7 +162,7 @@
|
|||
{{ doc.name }}-{{ doc.rev }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre>{{ content|maybewordwrap|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ content|maybewordwrap|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% origin %}
|
||||
{% load ietf_filters %}
|
||||
<h1>
|
||||
{{ doc.title }}
|
||||
{{ doc.title|default:"(Untitled)" }}
|
||||
<br>
|
||||
<small class="text-muted">{{ name }}</small>
|
||||
</h1>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</h2>
|
||||
{% for name, text, url in writeups %}
|
||||
{% if name %}<h3 class="mt-5">{{ name }}</h3>{% endif %}
|
||||
{% if text %}<pre class="border p-3 my-3">{{ text|linkify|urlize_ietf_docs }}</pre>{% endif %}
|
||||
{% if text %}<pre class="border p-3 my-3">{{ text|urlize_ietf_docs|linkify }}</pre>{% endif %}
|
||||
{% if can_edit %}
|
||||
<p>
|
||||
<a href="{{ url }}" class="btn btn-primary">
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="ref">Referenced RFC</th>
|
||||
<th scope="col" data-sort="id">Internet-Draft making the reference</th>
|
||||
<th scope="col" data-sort="refby">Internet-Draft making the reference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
{% endif %}
|
||||
{% if doc.note %}
|
||||
<br>
|
||||
<i>Note: {{ doc.note|linkify|urlize_ietf_docs|linebreaksbr }}</i>
|
||||
<i>Note: {{ doc.note|urlize_ietf_docs|linkify|linebreaksbr }}</i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th scope="row">Requested rev.</th>
|
||||
<th scope="row">Requested revision</th>
|
||||
<td>
|
||||
{% if review_req.requested_rev %}
|
||||
{{ review_req.requested_rev }}
|
||||
|
@ -299,12 +299,10 @@
|
|||
<th scope="row">
|
||||
</th>
|
||||
<th scope="row">
|
||||
Reviewed rev.
|
||||
Reviewed revision
|
||||
</th>
|
||||
<td>
|
||||
<a href="{% url "ietf.doc.views_doc.document_main" name=review_req.doc.name rev=assignment.reviewed_rev %}">
|
||||
{{ assignment.reviewed_rev }}
|
||||
</a>
|
||||
<a href="{% url "ietf.doc.views_doc.document_main" name=review_req.doc.name rev=assignment.reviewed_rev %}">{{ assignment.reviewed_rev }}</a>
|
||||
{% if assignment.reviewed_rev != review_req.doc.rev %}(document currently at {{ review_req.doc.rev }}){% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -314,7 +312,7 @@
|
|||
<th scope="row">
|
||||
</th>
|
||||
<th scope="row">
|
||||
Review result
|
||||
Result
|
||||
</th>
|
||||
<td class="{% if assignment.result.name|slice:5 == 'Ready' %}text-success{% else %}text-danger{% endif %}">
|
||||
{{ assignment.result.name }}
|
||||
|
@ -326,7 +324,7 @@
|
|||
<th scope="row">
|
||||
</th>
|
||||
<th scope="row">
|
||||
Review completed:
|
||||
Completed
|
||||
</th>
|
||||
<td>
|
||||
{{ assignment.completed_on|date:"Y-m-d" }}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<br>
|
||||
<small class="text-muted">{{ doc.canonical_name }}-{{ doc.rev }}</small>
|
||||
</h1>
|
||||
<pre class="border p-3 my-3">{{writeup|linkify|urlize_ietf_docs|wordwrap:"80"}}</pre>
|
||||
<pre class="border p-3 my-3">{{writeup|maybewordwrap|urlize_ietf_docs|linkify}}</pre>
|
||||
{% if can_edit %}
|
||||
<a class="btn btn-primary"
|
||||
href="{% url 'ietf.doc.views_draft.edit_shepherd_writeup' name=doc.name %}">Edit</a>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
</h2>
|
||||
{% if area.description %}
|
||||
<p>
|
||||
{{ area.description|linkify|urlize_ietf_docs|safe }}
|
||||
{{ area.description|urlize_ietf_docs|linkify|safe }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
{{ rpt.time|date:"Y-m-d" }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre class="pasted">{{ rpt.desc|default:"(none)"|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="pasted">{{ rpt.desc|default:"(none)"|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@ -38,7 +38,7 @@
|
|||
{{ rpt.time|date:"Y-m-d" }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre class="pasted">{{ rpt.desc|default:"(none)"|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="pasted">{{ rpt.desc|default:"(none)"|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -148,10 +148,10 @@
|
|||
{% for resource in resources|dictsort:"display_name" %}
|
||||
{# Maybe make how a resource displays itself a method on the class so templates aren't doing this switching #}
|
||||
{% if resource.name.type.slug == 'url' or resource.name.type.slug == 'email' %}
|
||||
<a href="{{ resource.value }}" title="{{ resource.name.name }}">
|
||||
<a href="{{ resource.value|urlencode }}" title="{{ resource.name.name }}">
|
||||
{% firstof resource.display_name resource.name.name %}</a>{% else %}
|
||||
<span title="{{ resource.name.name }}">
|
||||
{% firstof resource.display_name resource.name.name %}: {{ resource.value }}
|
||||
{% firstof resource.display_name resource.name.name %}: {{ resource.value|escape }}
|
||||
</span>{% endif %}{% if not forloop.last %}{% if resource.display_name|length < 15 and resource.name.name|length < 15 and resources|length <= 3 %},{% else %}<br>{% endif %}{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
@ -309,7 +309,7 @@
|
|||
{{ group.type.desc.title }}
|
||||
</h2>
|
||||
{# the linebreaks filter adds <p>, no surrounding <p> necessary: #}
|
||||
{{ group.charter_text|linkify|urlize_ietf_docs|linebreaks }}
|
||||
{{ group.charter_text|urlize_ietf_docs|linkify|linebreaks }}
|
||||
{% else %}
|
||||
<h2 class="mt-3">
|
||||
{% if requested_close or group.state_id == "conclude" %}Final{% endif %}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<pre class="border p-3 my-3 pasted">{{ status_update.desc|default:"(none)"|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="border p-3 my-3 pasted">{{ status_update.desc|default:"(none)"|urlize_ietf_docs|linkify }}</pre>
|
||||
{% if can_provide_status_update %}
|
||||
<a id="edit_button"
|
||||
class="btn btn-primary"
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>Status update for {{ group.type.name }} {{ group.acronym }} at {{ meeting }}</h1>
|
||||
<pre class="border p-3 my-3 pasted">{{ status_update.desc|default:"(none)"|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre class="border p-3 my-3 pasted">{{ status_update.desc|default:"(none)"|urlize_ietf_docs|linkify }}</pre>
|
||||
<a class="btn btn-secondary float-end"
|
||||
href="{% url "ietf.meeting.views.proceedings" num=meeting.number %}">Back</a>
|
||||
{% endblock %}
|
|
@ -9,12 +9,12 @@
|
|||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>
|
||||
{{ group.name }} ({{ group.acronym }})
|
||||
{% if group.state_id == "dormant" or group.state_id == "conclude" %}
|
||||
<span class="badge bg-warning float-end">Concluded {{ group.type.name }}</span>
|
||||
<span class="badge bg-warning float-end ms-3">Concluded {{ group.type.name }}</span>
|
||||
{% endif %}
|
||||
{% if group.state_id == "replaced" %}<span class="badge bg-warning float-end">Replaced {{ group.type.name }}</span>{% endif %}
|
||||
{% if group.state_id == "proposed" %}<span class="badge bg-info float-end">Proposed {{ group.type.name }}</span>{% endif %}
|
||||
{% if group.state_id == "replaced" %}<span class="badge bg-warning float-end ms-3">Replaced {{ group.type.name }}</span>{% endif %}
|
||||
{% if group.state_id == "proposed" %}<span class="badge bg-info float-end ms-3">Proposed {{ group.type.name }}</span>{% endif %}
|
||||
{{ group.name }} ({{ group.acronym }})
|
||||
</h1>
|
||||
<ul class="nav nav-tabs my-3">
|
||||
{% for name, url in menu_entries %}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<div class="row request-metadata">
|
||||
<div class="col-sm-6">
|
||||
<p class="lead">
|
||||
{{ r.doc.title|linkify|urlize_ietf_docs }}
|
||||
{{ r.doc.title|urlize_ietf_docs|linkify }}
|
||||
</p>
|
||||
{% if r.pk != None %}
|
||||
<span class="fw-bold">Requested:</span>
|
||||
|
@ -143,7 +143,7 @@
|
|||
</div>
|
||||
<div class="col-sm-6">
|
||||
<span class="fw-bold">Abstract:</span>
|
||||
{{ r.doc.abstract|linkify|urlize_ietf_docs }}
|
||||
{{ r.doc.abstract|urlize_ietf_docs|linkify }}
|
||||
</div>
|
||||
</div>
|
||||
{% if r.form.non_field_errors %}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr class="table-info" id="unassigned-review-requests">
|
||||
<th scope="col" colspan="7">Unassigned review requests</th>
|
||||
<th scope="col" colspan="6">Unassigned review requests</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
|
@ -149,7 +149,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr class="table-info">
|
||||
<th scope="col" colspan="8">
|
||||
<th scope="col" colspan="6">
|
||||
Closed review requests
|
||||
</th>
|
||||
</tr>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load ietf_filters %}
|
||||
{% load ietf_filters textfilters %}
|
||||
{% block pagehead %}<link rel="alternate" type="application/atom+xml" href="/feed/iesg-agenda/">{% endblock %}
|
||||
{% block title %}IESG agenda: {{ date }}{% endblock %}
|
||||
{% block content %}
|
||||
|
@ -107,7 +107,7 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% if num|startswith:"6." and user|has_role:"Area Director,IAB Chair,Secretariat" %}
|
||||
<pre class="border p-3">{{ section.text|wordwrap:"80" }}</pre>
|
||||
<pre class="border p-3">{{ section.text|maybewordwrap|urlize_ietf_docs|linkify }}</pre>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<small class="text-muted">{{ user.username }}</small>
|
||||
</h1>
|
||||
{% csrf_token %}
|
||||
{% if person.apikeys.all %}
|
||||
<table class="table table-sm tablesorter">
|
||||
<thead>
|
||||
<tr >
|
||||
|
@ -49,13 +50,12 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td>You have no personal API keys.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>You have no personal API keys.</p>
|
||||
{% endif %}
|
||||
<a href="{% url 'ietf.ietfauth.views.apikey_create' %}"
|
||||
class="btn btn-primary add-apikey my-3">
|
||||
Get a new personal API key
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
<td>
|
||||
{% if e.message %}
|
||||
{% if e.response_due %}<span class="badge bg-info">Response due {{ e.response_due|date:"Y-m-d" }}</span>{% endif %}
|
||||
<pre>{{ e.message|render_message_for_history|format_history_text:"100" }}</pre>
|
||||
{# FIXME: can't do format_history_text, because that inserts a <div> into the <pre>, which is illegal. Need to rework the snippeting. #}
|
||||
<pre>{{ e.message|render_message_for_history|urlize_ietf_docs|linkify }}</pre>
|
||||
{% else %}
|
||||
{{ e.desc|format_history_text }}
|
||||
{% endif %}
|
||||
|
|
|
@ -201,7 +201,7 @@
|
|||
Body
|
||||
</th>
|
||||
<td>
|
||||
<pre>{{ liaison.body|maybewordwrap:"80"|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ liaison.body|maybewordwrap:"80"|urlize_ietf_docs|linkify }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
|
|
@ -52,7 +52,14 @@
|
|||
{% for fieldset in form.fieldsets %}
|
||||
<h2>{{ fieldset.name }}</h2>
|
||||
{% for field in fieldset %}
|
||||
{% bootstrap_field field layout="horizontal" %}
|
||||
{% if field.id_for_label != "id_attachments" %}
|
||||
{% bootstrap_field field layout="horizontal" %}
|
||||
{% else %}
|
||||
<div class="row mb-3">
|
||||
<p class="col-md-2 fw-bold col-form-label">{{ field.label }}</p>
|
||||
<div class="col-md-10">{{ field }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<a class="btn btn-danger float-end"
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
{{ schedule.meeting.agenda_info_note|removetags:"h1"|safe }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% include 'meeting/tz-display.html' with meeting_timezone=timezone only %}
|
||||
{% include 'meeting/tz-display.html' with id_suffix="" meeting_timezone=timezone only %}
|
||||
{% include "meeting/agenda_filter.html" with filter_categories=filter_categories customize_button_text="Filter this agenda view..." always_show=personalize %}
|
||||
{% include "meeting/agenda_personalize_buttonlist.html" with meeting=schedule.meeting personalize=personalize only %}
|
||||
<div class="input-group mb-3">
|
||||
|
@ -84,7 +84,7 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
<a class="btn btn-outline-primary {% if non_area_keywords|length == 0 %}disabled{% endif %}"
|
||||
href="{% url "ietf.meeting.views.agenda_ical" num=schedule.meeting.number %}?show={{ non_area_keywords|join:',' }}">
|
||||
href="{% if non_area_keywords %}{% url 'ietf.meeting.views.agenda_ical' num=schedule.meeting.number %}?show={{ non_area_keywords|join:',' }}{% else %}#{% endif %}">
|
||||
Download non-area events
|
||||
</a>
|
||||
</div>
|
||||
|
@ -181,18 +181,20 @@
|
|||
{% if item.session.current_status == 'canceled' %}
|
||||
<span class="badge bg-danger float-end">CANCELLED</span>
|
||||
{% else %}
|
||||
<div class="float-end ps-2">
|
||||
{% if item.slot_type.slug == 'other' %}
|
||||
{% if item.session.agenda or item.session.remote_instructions or item.session.agenda_note %}
|
||||
{% include "meeting/session_buttons_include.html" with show_agenda=True item=item schedule=schedule %}
|
||||
{% else %}
|
||||
{% for slide in item.session.slides %}
|
||||
<a href="{{ slide.get_href }}">{{ slide.title|clean_whitespace }}</a>
|
||||
<br>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if item.slot_type.slug == 'other' %}
|
||||
{% if item.session.agenda or item.session.remote_instructions or item.session.agenda_note %}
|
||||
<div class="float-end ps-2">
|
||||
{% include "meeting/session_buttons_include.html" with show_agenda=True item=item schedule=schedule %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div>
|
||||
{% for slide in item.session.slides %}
|
||||
<a href="{{ slide.get_href }}">{{ slide.title|clean_whitespace }}</a>
|
||||
<br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -300,7 +302,7 @@
|
|||
<div class="timetooltip reschedtimetooltip">
|
||||
<div data-start-time="{{ item.session.rescheduled_to.utc_start_time|date:"U" }}"
|
||||
data-end-time="{{ item.session.rescheduled_to.utc_end_time|date:"U" }}"
|
||||
{% if item.timeslot.time|date:"l" != item.session.rescheduled_to.time|date:"l" %} weekday="1"{% endif %}>
|
||||
{% if item.timeslot.time|date:"l" != item.session.rescheduled_to.time|date:"l" %} data-weekday="1"{% endif %}>
|
||||
{% if "-utc" in request.path %}
|
||||
{{ item.session.rescheduled_to.utc_start_time|date:"l G:i"|upper }}-{{ item.session.rescheduled_to.utc_end_time|date:"G:i" }}
|
||||
{% else %}
|
||||
|
@ -313,7 +315,7 @@
|
|||
{% endif %}
|
||||
{% if item.session.agenda_note|first_url|conference_url %}
|
||||
<br>
|
||||
<a href={{ item.session.agenda_note|first_url }}>{{ item.session.agenda_note|slice:":23" }}
|
||||
<a href="{{ item.session.agenda_note|first_url }}">{{ item.session.agenda_note|slice:":23" }}
|
||||
</a>
|
||||
{% elif item.session.agenda_note %}
|
||||
<br>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
{% if session.all_meeting_sessions_cancelled %}
|
||||
<td colspan="{% if user|has_role:'Secretariat' or user_groups %}7{% else %}6{% endif %}">
|
||||
<td colspan="{% if user|has_role:'Secretariat' or user_groups %}6{% else %}5{% endif %}">
|
||||
<span class="badge bg-danger">Session cancelled</span>
|
||||
</td>
|
||||
{% else %}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ d.name.desc|linkify|urlize_ietf_docs }}
|
||||
{{ d.name.desc|urlize_ietf_docs|linkify }}
|
||||
{% if first and d.name.slug == 'openreg' or first and d.name.slug == 'earlybird' %}
|
||||
<a href="https://www.ietf.org/how/meetings/register/">Register here</a>.
|
||||
{% endif %}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
{% load textfilters %}
|
||||
{% origin %}
|
||||
{% with item=session.official_timeslotassignment acronym=session.historic_group.acronym %}
|
||||
{% include "meeting/session_agenda_include.html" with slug=item.slug session=session timeslot=item.timeslot only %}
|
||||
<div role="group" class="btn-group btn-group-sm">
|
||||
{% if session.agenda and show_agenda %}
|
||||
{% include "meeting/session_agenda_include.html" with slug=item.slug session=session timeslot=item.timeslot only %}
|
||||
{# Note: if called with show_agenda=True, calling template must load agenda_materials.js, needed by session_agenda_include.html #}
|
||||
{# agenda pop-up button #}
|
||||
<a class="btn btn-outline-primary"
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% if meeting.type.slug == 'interim' and session.remote_instructions %}
|
||||
<div>
|
||||
<div class="my-3">
|
||||
<b>Remote instructions:</b>
|
||||
{% if session.agenda_note|first_url|conference_url %}
|
||||
<a href="{{ session.agenda_note|first_url }}" title="Online conference">
|
||||
|
@ -125,6 +125,7 @@
|
|||
{% for pres in session.filtered_slides %}
|
||||
<tr data-name="{{ pres.document.name }}" title="Drag to reorder.">
|
||||
{% url 'ietf.doc.views_doc.document_main' name=pres.document.name as url %}
|
||||
<td><i class="bi bi-grip-vertical"></i></td>
|
||||
<td>
|
||||
<a href="{{ pres.document.get_href }}">{{ pres.document.title }}</a>
|
||||
<a href="{{ url }}">({{ pres.document.name }})</a>
|
||||
|
@ -158,7 +159,7 @@
|
|||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if can_manage_materials %}<div class="small">Drag-and-drop to reorder slides</div>{% endif %}
|
||||
{% if can_manage_materials %}<div class="form-text">Drag-and-drop to reorder slides</div>{% endif %}
|
||||
<h3 class="mt-4">Drafts</h3>
|
||||
<table class="table table-sm table-striped">
|
||||
{% if session.filtered_drafts %}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include 'meeting/tz-display.html' with meeting_timezone=None only %}
|
||||
{% include 'meeting/tz-display.html' with id_suffix="" meeting_timezone=None only %}
|
||||
{% include 'meeting/agenda_filter.html' with filter_categories=filter_categories customize_button_text="Customize the meeting list..." only %}
|
||||
{% cache 600 upcoming-meetings entries.count %}
|
||||
{% if entries %}
|
||||
|
|
|
@ -36,5 +36,5 @@
|
|||
{{ message.subject|linkify }}
|
||||
</dd>
|
||||
</dl>
|
||||
<pre>{{ message.body|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ message.body|urlize_ietf_docs|linkify }}</pre>
|
||||
{% endblock %}
|
|
@ -21,7 +21,7 @@
|
|||
{% for position in positions %}
|
||||
<div role="tabpanel" class="tab-pane {% if forloop.first %} active{% endif %}"
|
||||
id="{{ position.name|slugify }}">
|
||||
{{ position.get_questionnaire|linkify|urlize_ietf_docs|linebreaks }}
|
||||
{{ position.get_questionnaire|urlize_ietf_docs|linkify|linebreaks }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<pre class="border p-3 pasted">{{ template.content|linkify|urlize_ietf_docs|linebreaksbr }}</pre>
|
||||
<pre class="border p-3 pasted">{{ template.content|urlize_ietf_docs|linkify|linebreaksbr }}</pre>
|
||||
<a class="btn btn-secondary my-3"
|
||||
href="{% if return_url %}{{ return_url }}{% else %}../{% endif %}">Back</a>
|
||||
{% endblock %}
|
|
@ -13,7 +13,7 @@
|
|||
{% origin %}
|
||||
{% if persons|length > 1 %}
|
||||
<p class="alert alert-warning my-3">
|
||||
More than one person with this name has been found. Showing all:
|
||||
More than one person with this name has been found. Showing all.
|
||||
</p>
|
||||
{% endif %}
|
||||
{% for person in persons %}
|
||||
|
@ -29,15 +29,18 @@
|
|||
{% if person.photo %}
|
||||
<div class="float-end ms-3 mb-3">{% include "person/photo.html" with person=person %}</div>
|
||||
{% endif %}
|
||||
{{ person.biography|linkify|urlize_ietf_docs|apply_markup:"restructuredtext" }}
|
||||
{{ person.biography|apply_markup:"restructuredtext"|urlize_ietf_docs|linkify }}
|
||||
</div>
|
||||
{% if person.role_set.exists %}
|
||||
<h2 class="mt-5" id="roles">Roles</h2>
|
||||
<h2 class="mt-5" id="roles-{{ forloop.counter }}">Roles</h2>
|
||||
{% if person.role_set.all|active_roles %}
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<th scope="col" data-sort="role">Role</th>
|
||||
<th scope="col" data-sort="group">Group</th>
|
||||
<th scope="col" data-sort="email">Email</th>
|
||||
<tr>
|
||||
<th scope="col" data-sort="role">Role</th>
|
||||
<th scope="col" data-sort="group">Group</th>
|
||||
<th scope="col" data-sort="email">Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for role in person.role_set.all|active_roles %}
|
||||
|
@ -51,18 +54,21 @@
|
|||
<a href="mailto:{{ role.email.address }}">{{ role.email.address }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
{{ person.first_name }} has no active roles as of {{ today }}.
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>{{ person.first_name }} has no active roles as of {{ today }}.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if person.personextresource_set.exists %}
|
||||
<h2 class="mt-5" id="extresources">External Resources</h2>
|
||||
<h2 class="mt-5" id="extresources-{{ forloop.counter }}">External Resources</h2>
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<th scope="col" data-sort="name">Name</th>
|
||||
<th scope="col" data-sort="value">Value</th>
|
||||
<tr>
|
||||
<th scope="col" data-sort="name">Name</th>
|
||||
<th scope="col" data-sort="value">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for extres in person.personextresource_set.all %}
|
||||
|
@ -76,16 +82,18 @@
|
|||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<h2 class="mt-5" id="rfcs">
|
||||
<h2 class="mt-5" id="rfcs-{{ forloop.counter }}">
|
||||
RFCs <small class="text-muted">({{ person.rfcs|length }})</small>
|
||||
</h2>
|
||||
{% if person.rfcs %}
|
||||
<table class="table table-sm table-striped tablesorter">
|
||||
<thead>
|
||||
<th scope="col" data-sort="rfc">RFC</th>
|
||||
<th scope="col" data-sort="date">Date</th>
|
||||
<th scope="col" data-sort="title">Title</th>
|
||||
<th scope="col" data-sort="citedby">Cited by</th>
|
||||
<tr>
|
||||
<th scope="col" data-sort="rfc">RFC</th>
|
||||
<th scope="col" data-sort="date">Date</th>
|
||||
<th scope="col" data-sort="title">Title</th>
|
||||
<th scope="col" data-sort="citedby">Cited by</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for doc in person.rfcs %}
|
||||
|
@ -113,7 +121,7 @@
|
|||
{% else %}
|
||||
{{ person.first_name }} has no RFCs as of {{ today }}.
|
||||
{% endif %}
|
||||
<h2 class="mt-5" id="drafts">
|
||||
<h2 class="mt-5" id="drafts-{{ forloop.counter }}">
|
||||
Active Drafts <small class="text-muted">({{ person.active_drafts|length }})</small>
|
||||
</h2>
|
||||
{% if person.active_drafts.exists %}
|
||||
|
@ -146,11 +154,11 @@
|
|||
{% else %}
|
||||
{{ person.first_name }} has no expired drafts as of {{ today }}.
|
||||
{% endif %}
|
||||
{% if persons|length == 1 and person.has_drafts %}
|
||||
{% if person.has_drafts %}
|
||||
<h2 class="mt-5">
|
||||
Draft Activity
|
||||
</h2>
|
||||
<div id="chart">
|
||||
<div id="chart-{{ forloop.counter }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
@ -161,15 +169,17 @@
|
|||
<script src="{% static 'ietf/js/highcharts-exporting.js' %}"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$.getJSON('{% url "ietf.doc.views_stats.chart_conf_person_drafts" id=persons.0.id %}', function (conf) {
|
||||
// Create the chart
|
||||
chart = Highcharts.stockChart('chart', conf);
|
||||
chart.showLoading();
|
||||
$.getJSON('{% url "ietf.doc.views_stats.chart_data_person_drafts" id=persons.0.id %}', function (data) {
|
||||
chart.series[0].setData(data);
|
||||
chart.hideLoading();
|
||||
{% for person in persons %}
|
||||
$.getJSON('{% url "ietf.doc.views_stats.chart_conf_person_drafts" id=person.id %}', function (conf) {
|
||||
// Create the chart
|
||||
chart = Highcharts.stockChart('chart-{{ forloop.counter }}', conf);
|
||||
chart.showLoading();
|
||||
$.getJSON('{% url "ietf.doc.views_stats.chart_data_person_drafts" id=person.id %}', function (data) {
|
||||
chart.series[0].setData(data);
|
||||
chart.hideLoading();
|
||||
});
|
||||
});
|
||||
});
|
||||
{% endfor %}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
Message
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
<pre>{{ message.message.body|linkify|urlize_ietf_docs|linebreaksbr }}</pre>
|
||||
<pre>{{ message.message.body|urlize_ietf_docs|linkify|linebreaksbr }}</pre>
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
Attachment
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="{{ check.checker|slugify }}-message">
|
||||
<pre>{{ check.message|linkify|urlize_ietf_docs }}</pre>
|
||||
<pre>{{ check.message|urlize_ietf_docs|linkify }}</pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
|
|
|
@ -51,6 +51,8 @@ import subprocess
|
|||
import tempfile
|
||||
import copy
|
||||
import factory.random
|
||||
import urllib3
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from fnmatch import fnmatch
|
||||
from pathlib import Path
|
||||
|
@ -95,6 +97,108 @@ template_coverage_collection = None
|
|||
code_coverage_collection = None
|
||||
url_coverage_collection = None
|
||||
|
||||
|
||||
def start_vnu_server(port=8888):
|
||||
"Start a vnu validation server on the indicated port"
|
||||
vnu = subprocess.Popen(
|
||||
[
|
||||
"java",
|
||||
"-Dnu.validator.servlet.bind-address=127.0.0.1",
|
||||
"-Dnu.validator.servlet.max-file-size=16777216",
|
||||
"-cp",
|
||||
"bin/vnu.jar",
|
||||
"nu.validator.servlet.Main",
|
||||
f"{port}",
|
||||
],
|
||||
stdout=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
print(" Waiting for vnu server to start up...", end="")
|
||||
while vnu_validate(b"", content_type="", port=port) is None:
|
||||
print(".", end="")
|
||||
time.sleep(1)
|
||||
print()
|
||||
return vnu
|
||||
|
||||
|
||||
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"
|
||||
gzippeddata = gzip.compress(html)
|
||||
try:
|
||||
req = http.request(
|
||||
"POST",
|
||||
f"http://127.0.0.1:{port}/?"
|
||||
+ urlencode({"out": "json", "asciiquotes": "yes"}),
|
||||
headers={
|
||||
"Content-Type": content_type,
|
||||
"Accept-Encoding": "gzip",
|
||||
"Content-Encoding": "gzip",
|
||||
"Content-Length": str(len(gzippeddata)),
|
||||
},
|
||||
body=gzippeddata,
|
||||
)
|
||||
except (
|
||||
urllib3.exceptions.NewConnectionError,
|
||||
urllib3.exceptions.MaxRetryError,
|
||||
ConnectionRefusedError,
|
||||
):
|
||||
return None
|
||||
|
||||
assert req.status == 200
|
||||
return req.data.decode("utf-8")
|
||||
|
||||
|
||||
def vnu_fmt_message(file, msg, content):
|
||||
"Convert a vnu JSON message into a printable string"
|
||||
ret = f"\n{file}:\n"
|
||||
if "extract" in msg:
|
||||
ret += msg["extract"].replace("\n", " ") + "\n"
|
||||
ret += " " * msg["hiliteStart"]
|
||||
ret += "^" * msg["hiliteLength"] + "\n"
|
||||
ret += " " * msg["hiliteStart"]
|
||||
ret += f"{msg['type']}: {msg['message']}\n"
|
||||
if "firstLine" in msg and "lastLine" in msg:
|
||||
ret += f'Source snippet, lines {msg["firstLine"]-5} to {msg["lastLine"]+4}:\n'
|
||||
lines = content.splitlines()
|
||||
for line in range(msg["firstLine"] - 5, msg["lastLine"] + 5):
|
||||
ret += f"{line}: {lines[line]}\n"
|
||||
return ret
|
||||
|
||||
|
||||
def vnu_filter_message(msg, filter_db_issues, filter_test_issues):
|
||||
"True if the vnu message is a known false positive"
|
||||
if filter_db_issues and re.search(
|
||||
r"""^Forbidden\ code\ point\ U\+|
|
||||
'href'\ on\ element\ 'a':\ Percentage\ \("%"\)\ is\ not\ followed|
|
||||
^Saw\ U\+\d+\ in\ stream|
|
||||
^Document\ uses\ the\ Unicode\ Private\ Use\ Area""",
|
||||
msg["message"],
|
||||
flags=re.VERBOSE,
|
||||
):
|
||||
return True
|
||||
|
||||
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""",
|
||||
msg["message"],
|
||||
flags=re.VERBOSE,
|
||||
):
|
||||
return True
|
||||
|
||||
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""",
|
||||
msg["message"],
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def load_and_run_fixtures(verbosity):
|
||||
loadable = [f for f in settings.GLOBAL_TEST_FIXTURES if "." not in f]
|
||||
call_command('loaddata', *loadable, verbosity=int(verbosity)-1, database="default")
|
||||
|
@ -196,7 +300,7 @@ class ValidatingTemplate(Template):
|
|||
return content
|
||||
|
||||
fingerprint = hash(content) + sys.maxsize + 1 # make hash positive
|
||||
if fingerprint in self.backend.validation_cache:
|
||||
if not settings.validate_html_harder and fingerprint in self.backend.validation_cache:
|
||||
# already validated this HTML fragment, skip it
|
||||
# as an optimization, make page a bit smaller by not returning HTML for the menus
|
||||
# FIXME: figure out why this still includes base/menu.html
|
||||
|
@ -590,8 +694,11 @@ class IetfTestRunner(DiscoverRunner):
|
|||
parser.add_argument('--no-validate-html',
|
||||
action='store_false', dest="validate_html", default=True,
|
||||
help='Do not validate all generated HTML with html-validate.org')
|
||||
parser.add_argument('--validate-html-harder',
|
||||
action='store_true', dest="validate_html_harder", default=False,
|
||||
help='Validate all generated HTML with additional validators (slow)')
|
||||
|
||||
def __init__(self, skip_coverage=False, save_version_coverage=None, html_report=None, permit_mixed_migrations=None, show_logging=None, validate_html=None, **kwargs):
|
||||
def __init__(self, skip_coverage=False, save_version_coverage=None, html_report=None, permit_mixed_migrations=None, show_logging=None, validate_html=None, validate_html_harder=None, **kwargs):
|
||||
#
|
||||
self.check_coverage = not skip_coverage
|
||||
self.save_version_coverage = save_version_coverage
|
||||
|
@ -599,6 +706,7 @@ class IetfTestRunner(DiscoverRunner):
|
|||
self.permit_mixed_migrations = permit_mixed_migrations
|
||||
self.show_logging = show_logging
|
||||
settings.validate_html = self if validate_html else None
|
||||
settings.validate_html_harder = self if validate_html and validate_html_harder else None
|
||||
settings.show_logging = show_logging
|
||||
#
|
||||
self.root_dir = os.path.dirname(settings.BASE_DIR)
|
||||
|
@ -718,7 +826,7 @@ class IetfTestRunner(DiscoverRunner):
|
|||
print(" Not validating any generated HTML; "
|
||||
"please do so at least once before committing changes")
|
||||
else:
|
||||
print(" Validating all HTML generated during the tests")
|
||||
print(" Validating all HTML generated during the tests", end="")
|
||||
self.batches = {"doc": [], "frag": []}
|
||||
|
||||
# keep the html-validate configs here, so they can be kept in sync easily
|
||||
|
@ -768,6 +876,13 @@ class IetfTestRunner(DiscoverRunner):
|
|||
self.config_file[kind].flush()
|
||||
Path(self.config_file[kind].name).chmod(0o644)
|
||||
|
||||
if not settings.validate_html_harder:
|
||||
print("")
|
||||
self.vnu = None
|
||||
else:
|
||||
print(" (extra pedantically)")
|
||||
self.vnu = start_vnu_server()
|
||||
|
||||
super(IetfTestRunner, self).setup_test_environment(**kwargs)
|
||||
|
||||
def teardown_test_environment(self, **kwargs):
|
||||
|
@ -789,12 +904,18 @@ class IetfTestRunner(DiscoverRunner):
|
|||
else:
|
||||
with open(self.coverage_file, "w") as file:
|
||||
json.dump(self.coverage_master, file, indent=2, sort_keys=True)
|
||||
super(IetfTestRunner, self).teardown_test_environment(**kwargs)
|
||||
|
||||
if settings.validate_html:
|
||||
for kind in self.batches:
|
||||
self.validate(kind)
|
||||
try:
|
||||
self.validate(kind)
|
||||
except Exception:
|
||||
pass
|
||||
self.config_file[kind].close()
|
||||
if self.vnu:
|
||||
self.vnu.terminate()
|
||||
|
||||
super(IetfTestRunner, self).teardown_test_environment(**kwargs)
|
||||
|
||||
def validate(self, kind):
|
||||
if not self.batches[kind]:
|
||||
|
@ -843,9 +964,10 @@ class IetfTestRunner(DiscoverRunner):
|
|||
errors += (
|
||||
f'\n{result["filePath"]}:\n'
|
||||
+ "".join(source_lines[line - 5 : line])
|
||||
+ "-" * (msg["column"] - 1)
|
||||
+ "^" * msg["size"]
|
||||
+ f' {msg["ruleId"]}: {msg["message"]} '
|
||||
+ " " * (msg["column"] - 1)
|
||||
+ "^" * msg["size"] + "\n"
|
||||
+ " " * (msg["column"] - 1)
|
||||
+ f'{msg["ruleId"]}: {msg["message"]} '
|
||||
+ f'on line {line}:{msg["column"]}\n'
|
||||
+ "".join(source_lines[line : line + 5])
|
||||
+ "\n"
|
||||
|
@ -853,6 +975,28 @@ class IetfTestRunner(DiscoverRunner):
|
|||
|
||||
if errors:
|
||||
testcase.fail(errors)
|
||||
|
||||
if settings.validate_html_harder:
|
||||
if kind == "frag":
|
||||
return
|
||||
files = [
|
||||
os.path.join(d, f)
|
||||
for d, dirs, files in os.walk(tmpdir.name)
|
||||
for f in files
|
||||
]
|
||||
for file in files:
|
||||
with open(file, "rb") as f:
|
||||
content = f.read()
|
||||
result = vnu_validate(content)
|
||||
assert result
|
||||
for msg in json.loads(result)["messages"]:
|
||||
if vnu_filter_message(msg, False, True):
|
||||
continue
|
||||
errors = vnu_fmt_message(file, msg, content)
|
||||
|
||||
if errors:
|
||||
testcase.fail(errors)
|
||||
|
||||
tmpdir.cleanup()
|
||||
|
||||
def get_test_paths(self, test_labels):
|
||||
|
|
|
@ -17,8 +17,10 @@ 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.append("ftp") # we still have some ftp links
|
||||
bleach_linker = bleach.Linker(
|
||||
url_re=bleach.linkifier.build_url_re(tlds=tlds_sorted),
|
||||
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
|
||||
)
|
||||
|
|
|
@ -152,7 +152,6 @@
|
|||
"ietf/secr/static/images/tooltag-arrowright.webp",
|
||||
"ietf/secr/static/images/tooltag-arrowright_over.webp",
|
||||
"ietf/secr/static/js/dynamic_inlines.js",
|
||||
"ietf/secr/static/js/proceedings-recording.js",
|
||||
"ietf/secr/static/js/session_form.js",
|
||||
"ietf/secr/static/js/sessions.js",
|
||||
"ietf/secr/static/js/utils.js"
|
||||
|
|
Loading…
Reference in a new issue