From 1c2ccdbf1f161fd69c88e52594f5ef7fc42c13f8 Mon Sep 17 00:00:00 2001 From: Daniel Valenzuela Date: Thu, 21 Nov 2024 10:01:18 -0300 Subject: [PATCH] fix: render library assets with whitespace --- common/djangoapps/static_replace/__init__.py | 2 +- .../test/test_static_replace.py | 10 ++++++++++ .../xblock/runtime/learning_core_runtime.py | 20 +++++++++++++++---- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/common/djangoapps/static_replace/__init__.py b/common/djangoapps/static_replace/__init__.py index 310ad8343242..cfab4dde8375 100644 --- a/common/djangoapps/static_replace/__init__.py +++ b/common/djangoapps/static_replace/__init__.py @@ -11,7 +11,7 @@ from xmodule.contentstore.content import StaticContent log = logging.getLogger(__name__) -XBLOCK_STATIC_RESOURCE_PREFIX = '/static/xblock' +XBLOCK_STATIC_RESOURCE_PREFIX = '/static/xblock/' def _url_replace_regex(prefix): diff --git a/common/djangoapps/static_replace/test/test_static_replace.py b/common/djangoapps/static_replace/test/test_static_replace.py index 1bdb770cad31..c71f8fa15c6a 100644 --- a/common/djangoapps/static_replace/test/test_static_replace.py +++ b/common/djangoapps/static_replace/test/test_static_replace.py @@ -86,6 +86,16 @@ def processor(__, prefix, quote, rest): # pylint: disable=redefined-outer-name assert process_static_urls(STATIC_SOURCE, processor) == '"test/static/file.png"' +def test_process_url_no_match_starts_with_xblock(): + def processor(original, prefix, quote, rest): # pylint: disable=unused-argument, redefined-outer-name + return quote + 'test' + prefix + rest + quote + assert process_static_urls( + '"/static/xblock-file.png"', + processor, + data_dir=DATA_DIRECTORY + ) == '"test/static/xblock-file.png"' + + @patch('django.http.HttpRequest', autospec=True) def test_static_urls(mock_request): mock_request.build_absolute_uri = lambda url: 'http://' + url diff --git a/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py b/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py index dde2084e54cd..44dedcf42874 100644 --- a/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py +++ b/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py @@ -6,6 +6,7 @@ import logging from collections import defaultdict from datetime import datetime, timezone +from urllib.parse import unquote from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db.transaction import atomic @@ -446,9 +447,20 @@ def _lookup_asset_url(self, block: XBlock, asset_path: str) -> str | None: .get(key=f"static/{asset_path}") ) except ObjectDoesNotExist: - # This means we see a path that _looks_ like it should be a static - # asset for this Component, but that static asset doesn't really - # exist. - return None + try: + # Retry with unquoted path. We don't always unquote because it would not + # be backwards-compatible, but we need to try both. + asset_path = unquote(asset_path) + content = ( + component_version + .componentversioncontent_set + .filter(content__has_file=True) + .get(key=f"static/{asset_path}") + ) + except ObjectDoesNotExist: + # This means we see a path that _looks_ like it should be a static + # asset for this Component, but that static asset doesn't really + # exist. + return None return self._absolute_url_for_asset(component_version, asset_path)