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" %}
+ class="btn btn-primary" + href="{% url 'ietf.doc.views_statement.new_statement' %}"> Start New Statement
@@ -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()