diff --git a/ietf/group/views.py b/ietf/group/views.py
index f59b270a08..0c89302c6a 100644
--- a/ietf/group/views.py
+++ b/ietf/group/views.py
@@ -2201,7 +2201,7 @@ def statements(request, acronym, group_type=None):
).values_list("state__slug", flat=True)[:1]
)
)
- .order_by("-published")
+ .order_by("status", "-published")
)
return render(
request,
diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py
index 10494d323f..dd651c2941 100644
--- a/ietf/nomcom/utils.py
+++ b/ietf/nomcom/utils.py
@@ -184,7 +184,7 @@ def retrieve_nomcom_private_key(request, year):
if not private_key:
return private_key
- command = "%s bf -d -in /dev/stdin -k \"%s\" -a"
+ command = "%s aes-128-ecb -d -in /dev/stdin -k \"%s\" -a -iter 1000"
code, out, error = pipe(
command % (
settings.OPENSSL_COMMAND,
@@ -208,7 +208,7 @@ def store_nomcom_private_key(request, year, private_key):
if not private_key:
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = ''
else:
- command = "%s bf -e -in /dev/stdin -k \"%s\" -a"
+ command = "%s aes-128-ecb -e -in /dev/stdin -k \"%s\" -a -iter 1000"
code, out, error = pipe(
command % (
settings.OPENSSL_COMMAND,
diff --git a/ietf/settings.py b/ietf/settings.py
index d76a8749b2..64679ca1d8 100644
--- a/ietf/settings.py
+++ b/ietf/settings.py
@@ -1,4 +1,4 @@
-# Copyright The IETF Trust 2007-2024, All Rights Reserved
+# Copyright The IETF Trust 2007-2025, All Rights Reserved
# -*- coding: utf-8 -*-
@@ -9,6 +9,7 @@
import os
import sys
import datetime
+import pathlib
import warnings
from hashlib import sha384
from typing import Any, Dict, List, Tuple # pyflakes:ignore
@@ -27,8 +28,12 @@
warnings.filterwarnings("ignore", message="Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated", module="bleach")
warnings.filterwarnings("ignore", message="HTTPResponse.getheader\\(\\) is deprecated", module='selenium.webdriver')
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-sys.path.append(os.path.abspath(BASE_DIR + "/.."))
+base_path = pathlib.Path(__file__).resolve().parent
+BASE_DIR = str(base_path)
+
+project_path = base_path.parent
+PROJECT_DIR = str(project_path)
+sys.path.append(PROJECT_DIR)
from ietf import __version__
import debug
@@ -717,12 +722,13 @@ def skip_unreadable_post(record):
]
# These are filename globs. They are used by test_parse_templates() and
-# get_template_paths()
+# get_template_paths(). Globs are applied via pathlib.Path().match, using
+# the path to the template from the project root.
TEST_TEMPLATE_IGNORE = [
- ".*", # dot-files
- "*~", # tilde temp-files
- "#*", # files beginning with a hashmark
- "500.html" # isn't loaded by regular loader, but checked by test_500_page()
+ ".*", # dot-files
+ "*~", # tilde temp-files
+ "#*", # files beginning with a hashmark
+ "500.html", # isn't loaded by regular loader, but checked by test_500_page()
]
TEST_COVERAGE_MAIN_FILE = os.path.join(BASE_DIR, "../release-coverage.json")
diff --git a/ietf/static/js/list.js b/ietf/static/js/list.js
index c16111ba63..756a75001a 100644
--- a/ietf/static/js/list.js
+++ b/ietf/static/js/list.js
@@ -247,7 +247,8 @@ $(document)
$(table)
.find(".sort")
- .on("click", function () {
+ .on("click", function (ev) {
+ ev.preventDefault()
var order = $(this)
.hasClass("asc") ? "desc" : "asc";
$.each(list_instance, (_, e) => {
diff --git a/ietf/templates/group/statements.html b/ietf/templates/group/statements.html
index 035c3bc967..6bbe3cb394 100644
--- a/ietf/templates/group/statements.html
+++ b/ietf/templates/group/statements.html
@@ -12,8 +12,8 @@
{{group.acronym|upper}} Statements
{% if request.user|has_role:"Secretariat" %}
@@ -25,16 +25,25 @@ {{group.acronym|upper}} Statements
Statement |
+{% regroup statements by status as grouped_statements %}
+{% for statement_group in grouped_statements %}
- {% for statement in statements %}
+
+ |
+ {{ statement_group.grouper|title }} {{"Statement"|plural:statement_group.list }} ({{ statement_group.list|length }} {{"hit"|plural:statement_group.list }})
+ |
+
+
+
+ {% for statement in statement_group.list %}
| {{ statement.published|date:"Y-m-d" }} |
{{statement.title}}
- {% if statement.status == "replaced" %}Replaced{% endif %}
|
- {% endfor %}
+ {% endfor %}
+{% endfor %}
{% endblock %}
{% block js %}
diff --git a/ietf/utils/test_runner.py b/ietf/utils/test_runner.py
index bfe5a56597..d0a5496283 100644
--- a/ietf/utils/test_runner.py
+++ b/ietf/utils/test_runner.py
@@ -1,4 +1,4 @@
-# Copyright The IETF Trust 2009-2020, All Rights Reserved
+# Copyright The IETF Trust 2009-2025, All Rights Reserved
# -*- coding: utf-8 -*-
#
# Portion Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
@@ -54,7 +54,6 @@
import urllib3
import warnings
-from fnmatch import fnmatch
from typing import Callable, Optional
from urllib.parse import urlencode
@@ -414,8 +413,9 @@ def do_append(res, p0, p1, item):
res.append((str(item.pattern), item))
return res
+
_all_templates = None
-def get_template_paths(apps=None):
+def get_template_paths(apps=None) -> list[str]:
global _all_templates
if not _all_templates:
# TODO: Add app templates to the full list, if we are using
@@ -424,25 +424,26 @@ def get_template_paths(apps=None):
templatepaths = settings.TEMPLATES[0]['DIRS']
for templatepath in templatepaths:
for dirpath, dirs, files in os.walk(templatepath):
- if ".svn" in dirs:
- dirs.remove(".svn")
- relative_path = dirpath[len(templatepath)+1:]
- for file in files:
- ignore = False
- for pattern in settings.TEST_TEMPLATE_IGNORE:
- if fnmatch(file, pattern):
- ignore = True
- break
- if ignore:
- continue
- if relative_path != "":
- file = os.path.join(relative_path, file)
- templates.add(file)
- if apps:
- templates = [ t for t in templates if t.split(os.path.sep)[0] in apps ]
- _all_templates = templates
+ # glob against path from PROJECT_DIR
+ project_path = pathlib.Path(
+ dirpath.removeprefix(settings.PROJECT_DIR).lstrip("/")
+ )
+ # label entries with name relative to templatepath
+ relative_path = pathlib.Path(
+ dirpath.removeprefix(templatepath).lstrip("/")
+ )
+ if apps and relative_path.parts[0] not in apps:
+ continue # skip uninteresting apps
+ for filename in files:
+ file_path = project_path / filename
+ if not any(
+ file_path.match(pat) for pat in settings.TEST_TEMPLATE_IGNORE
+ ):
+ templates.add(relative_path / filename)
+ _all_templates = [str(t) for t in templates]
return _all_templates
+
def save_test_results(failures, test_labels):
# Record the test result in a file, in order to be able to check the
# results and avoid re-running tests if we've already run them with OK
diff --git a/ietf/utils/tests.py b/ietf/utils/tests.py
index 872aa366b9..ce1842236d 100644
--- a/ietf/utils/tests.py
+++ b/ietf/utils/tests.py
@@ -19,7 +19,6 @@
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
-from fnmatch import fnmatch
from importlib import import_module
from textwrap import dedent
from tempfile import mkdtemp
@@ -320,7 +319,7 @@ class TemplateChecksTestCase(TestCase):
def setUp(self):
super().setUp()
set_coverage_checking(False)
- self.paths = list(get_template_paths())
+ self.paths = get_template_paths() # already filtered ignores
self.paths.sort()
for path in self.paths:
try:
@@ -335,11 +334,7 @@ def tearDown(self):
def test_parse_templates(self):
errors = []
for path in self.paths:
- for pattern in settings.TEST_TEMPLATE_IGNORE:
- if fnmatch(path, pattern):
- continue
- if not path in self.templates:
-
+ if path not in self.templates:
try:
get_template(path)
except Exception as e:
@@ -709,6 +704,14 @@ def test_render_author_name(self):
)),
"Joanna Q. Public",
)
+ self.assertEqual(
+ XMLDraft.render_author_name(lxml.etree.Element(
+ "author",
+ fullname=chr(340)+"ich",
+ asciiFullname="Rich UTF-8",
+ )),
+ chr(340)+"ich (Rich UTF-8)",
+ )
self.assertEqual(
XMLDraft.render_author_name(lxml.etree.Element(
"author",
diff --git a/ietf/utils/xmldraft.py b/ietf/utils/xmldraft.py
index 7ef6605c78..f555a0a16a 100644
--- a/ietf/utils/xmldraft.py
+++ b/ietf/utils/xmldraft.py
@@ -233,6 +233,12 @@ def render_author_name(author_elt):
# Use fullname attribute, if present
fullname = author_elt.attrib.get("fullname", "").strip()
if fullname:
+ # If any 8bit chars in the fullname, try to append the author's
+ # name in ascii.
+ if any([x >= 0x80 for x in fullname.encode('utf8')]):
+ asciifullname = author_elt.attrib.get("asciiFullname", "").strip()
+ if asciifullname:
+ fullname = fullname + ' (' + asciifullname + ')'
return fullname
surname = author_elt.attrib.get("surname", "").strip()
initials = author_elt.attrib.get("initials", "").strip()