diff --git a/timezones/app_settings.py b/timezones/app_settings.py index 74f6c8f..ef8a3a8 100644 --- a/timezones/app_settings.py +++ b/timezones/app_settings.py @@ -13,7 +13,6 @@ # AA Time Zones from timezones import __title__ -from timezones.helper.static_files import calculate_integrity_hash logger = LoggerAddTag(my_logger=get_extension_logger(name=__name__), prefix=__title__) @@ -26,30 +25,3 @@ def allianceauth_discordbot_active(): """ return apps.is_installed("aadiscordbot") - - -class IntegrityHash: - """ - Integrity hashes for static files - """ - - logger.debug("Calculating integrity hashes for static files") - - # CSS - CSS = {"timezones.min.css": calculate_integrity_hash("css/timezones.min.css")} - - # JavaScript - JS = {"timezones.min.js": calculate_integrity_hash("js/timezones.min.js")} - - # External Libraries - EXTERNAL_LIBS = { - "jquery.timeago.min.js": calculate_integrity_hash( - "libs/jquery-timeago/1.6.7/jquery.timeago.min.js" - ), - "moment-timezone-with-data-1970-2030.min.js": calculate_integrity_hash( - "libs/moment-timezone/0.5.36/moment-timezone-with-data-1970-2030.min.js" - ), - "weather-icons.min.css": calculate_integrity_hash( - "libs/weather-icons/2.0.10/css/weather-icons.min.css" - ), - } diff --git a/timezones/helper/static_files.py b/timezones/helper/static_files.py index da3f6d7..9388885 100644 --- a/timezones/helper/static_files.py +++ b/timezones/helper/static_files.py @@ -3,9 +3,11 @@ """ # Standard Library -import base64 -import hashlib import os +from pathlib import Path + +# Third Party +from sri import Algorithm, calculate_integrity # Alliance Auth from allianceauth.services.hooks import get_extension_logger @@ -24,6 +26,8 @@ def calculate_integrity_hash(relative_file_path: str) -> str: """ Calculates the integrity hash for a given static file + :param self: + :type self: :param relative_file_path: The file path relative to the `aa-timezones/timezones/static/timezones` folder :type relative_file_path: str :return: The integrity hash @@ -31,14 +35,6 @@ def calculate_integrity_hash(relative_file_path: str) -> str: """ file_path = os.path.join(AA_TIMEZONES_STATIC_DIR, relative_file_path) - - logger.debug(f"Calculating integrity hash for file: {file_path}") - - with open(file=file_path, encoding="utf-8") as static_file: - file_hash = hashlib.sha512() - - content = static_file.read() - file_hash.update(content.encode("utf-8")) - integrity_hash = f"sha512-{base64.b64encode(file_hash.digest()).decode()}" + integrity_hash = calculate_integrity(Path(file_path), Algorithm.SHA512) return integrity_hash diff --git a/timezones/templates/timezones/bundles/aa-timezones-css.html b/timezones/templates/timezones/bundles/aa-timezones-css.html index b013d3e..24f8272 100644 --- a/timezones/templates/timezones/bundles/aa-timezones-css.html +++ b/timezones/templates/timezones/bundles/aa-timezones-css.html @@ -1,8 +1,3 @@ {% load timezones %} - +{% timezones_static 'css/timezones.min.css' %} diff --git a/timezones/templates/timezones/bundles/aa-timezones-js.html b/timezones/templates/timezones/bundles/aa-timezones-js.html index c50e06d..3ab5ef4 100644 --- a/timezones/templates/timezones/bundles/aa-timezones-js.html +++ b/timezones/templates/timezones/bundles/aa-timezones-js.html @@ -1,7 +1,3 @@ {% load timezones %} - +{% timezones_static 'js/timezones.min.js' %} diff --git a/timezones/templates/timezones/bundles/jquery-timeago-js.html b/timezones/templates/timezones/bundles/jquery-timeago-js.html index 3432a52..f734fcb 100644 --- a/timezones/templates/timezones/bundles/jquery-timeago-js.html +++ b/timezones/templates/timezones/bundles/jquery-timeago-js.html @@ -1,8 +1,3 @@ -{% load static %} {% load timezones %} - +{% timezones_static 'libs/jquery-timeago/1.6.7/jquery.timeago.min.js' %} diff --git a/timezones/templates/timezones/bundles/moment-timezone-js.html b/timezones/templates/timezones/bundles/moment-timezone-js.html index 20fbb4f..04e0f4c 100644 --- a/timezones/templates/timezones/bundles/moment-timezone-js.html +++ b/timezones/templates/timezones/bundles/moment-timezone-js.html @@ -1,8 +1,3 @@ -{% load static %} {% load timezones %} - +{% timezones_static 'libs/moment-timezone/0.5.36/moment-timezone-with-data-1970-2030.min.js' %} diff --git a/timezones/templates/timezones/bundles/weather-icons-css.html b/timezones/templates/timezones/bundles/weather-icons-css.html index 65f68cf..ba12353 100644 --- a/timezones/templates/timezones/bundles/weather-icons-css.html +++ b/timezones/templates/timezones/bundles/weather-icons-css.html @@ -1,9 +1,3 @@ -{% load static %} {% load timezones %} - +{% timezones_static 'libs/weather-icons/2.0.10/css/weather-icons.min.css' %} diff --git a/timezones/templatetags/timezones.py b/timezones/templatetags/timezones.py index 69b8f40..b4e9b90 100644 --- a/timezones/templatetags/timezones.py +++ b/timezones/templatetags/timezones.py @@ -2,54 +2,73 @@ Versioned static URLs to break browser caches when changing the app version """ +# Standard Library +import os + # Django from django.template.defaulttags import register from django.templatetags.static import static +from django.utils.safestring import mark_safe + +# Alliance Auth +from allianceauth.services.hooks import get_extension_logger + +# Alliance Auth (External Libs) +from app_utils.logging import LoggerAddTag # AA Time Zones -from timezones import __version__ -from timezones.app_settings import IntegrityHash +from timezones import __title__, __version__ +from timezones.helper.static_files import calculate_integrity_hash + +logger = LoggerAddTag(my_logger=get_extension_logger(__name__), prefix=__title__) @register.simple_tag -def timezones_static(path: str) -> str: +def timezones_static(relative_file_path: str) -> str | None: """ Versioned static URL - :param path: Path to the static file relative to the static folder - :type path: str + :param relative_file_path: The file path relative to the `aa-timezones/timezones/static/timezones` folder + :type relative_file_path: str :return: Versioned static URL :rtype: str """ - static_url = static(path) - versioned_url = static_url + "?v=" + __version__ + logger.debug(f"Getting versioned static URL for: {relative_file_path}") - return versioned_url + file_type = os.path.splitext(relative_file_path)[1][1:] + logger.debug(f"File extension: {file_type}") -@register.simple_tag -def timezones_static_integrity_hash( - static_file_type: str, relative_file_path: str -) -> str: - """ - Returns the integrity hash for a file + # Only support CSS and JS files + if file_type not in ["css", "js"]: + raise ValueError(f"Unsupported file type: {file_type}") - :param static_file_type: The type of static file - :type static_file_type: str - :param relative_file_path: The file path relative to the `aa-timezones/timezones/static/timezones` folder - :type relative_file_path: str - :return: Integrity hash - :rtype: str - """ + integrity_hash = calculate_integrity_hash(relative_file_path) + static_file_path = os.path.join("timezones", relative_file_path) + static_url = static(static_file_path) + + # Versioned URL for CSS and JS files + # Add version query parameter to break browser caches when changing the app version + # Do not add version query parameter for libs as they are already versioned through their file path + versioned_url = ( + static_url + if relative_file_path.startswith("libs/") + else static_url + "?v=" + __version__ + ) - if static_file_type == "css": - return IntegrityHash.CSS.get(relative_file_path) + # Return the versioned URL with integrity hash for CSS + if file_type == "css": + logger.debug(f"Integrity hash for {relative_file_path}: {integrity_hash}") - if static_file_type == "js": - return IntegrityHash.JS.get(relative_file_path) + return mark_safe( + f'' + ) - if static_file_type == "lib": - return IntegrityHash.EXTERNAL_LIBS.get(relative_file_path) + # Return the versioned URL with integrity hash for JS files + if file_type == "js": + return mark_safe( + f'' + ) - raise ValueError(f"Unsupported static file type: {static_file_type}") + return None diff --git a/timezones/tests/test_templatetags.py b/timezones/tests/test_templatetags.py index 7629b24..9a2deef 100644 --- a/timezones/tests/test_templatetags.py +++ b/timezones/tests/test_templatetags.py @@ -25,13 +25,12 @@ def test_versioned_static(self): context = Context({"version": __version__}) template_to_render = Template( - "{% load timezones %}" - "{% timezones_static 'timezones/css/timezones.min.css' %}" + "{% load timezones %}" "{% timezones_static 'css/timezones.min.css' %}" ) rendered_template = template_to_render.render(context) - self.assertInHTML( - needle=f'/static/timezones/css/timezones.min.css?v={context["version"]}', - haystack=rendered_template, + self.assertIn( + member=f'/static/timezones/css/timezones.min.css?v={context["version"]}', + container=rendered_template, )