diff --git a/openedx/core/djangoapps/theming/storage.py b/openedx/core/djangoapps/theming/storage.py index e0b566210a..d128c65b12 100644 --- a/openedx/core/djangoapps/theming/storage.py +++ b/openedx/core/djangoapps/theming/storage.py @@ -179,6 +179,71 @@ class ThemeCachedFilesMixin(CachedFilesMixin): processed_asset_name = self._processed_asset_name(name) return super(ThemeCachedFilesMixin, self)._url(hashed_name_func, processed_asset_name, force, hashed_files) + def url_converter(self, name, hashed_files, template=None): + """ + This is an override of url_converter from CachedFilesMixin. + It changes one line near the end of the method (see the NOTE) in order + to return absolute urls instead of relative urls. This behavior is + necessary for theme overrides, as we get 404 on assets with relative + urls on a themed site. + """ + if template is None: + template = self.default_template + + def converter(matchobj): + """ + Convert the matched URL to a normalized and hashed URL. + This requires figuring out which files the matched URL resolves + to and calling the url() method of the storage. + """ + matched, url = matchobj.groups() + + # Ignore absolute/protocol-relative and data-uri URLs. + if re.match(r'^[a-z]+:', url): + return matched + + # Ignore absolute URLs that don't point to a static file (dynamic + # CSS / JS?). Note that STATIC_URL cannot be empty. + if url.startswith('/') and not url.startswith(settings.STATIC_URL): + return matched + + # Strip off the fragment so a path-like fragment won't interfere. + url_path, fragment = urldefrag(url) + + if url_path.startswith('/'): + # Otherwise the condition above would have returned prematurely. + assert url_path.startswith(settings.STATIC_URL) + target_name = url_path[len(settings.STATIC_URL):] + else: + # We're using the posixpath module to mix paths and URLs conveniently. + source_name = name if os.sep == '/' else name.replace(os.sep, '/') + target_name = posixpath.join(posixpath.dirname(source_name), url_path) + + # Determine the hashed name of the target file with the storage backend. + hashed_url = self._url( + self._stored_name, unquote(target_name), + force=True, hashed_files=hashed_files, + ) + + # NOTE: + # The line below was commented out so that absolute urls are used instead of relative urls to make themed + # assets work correctly. + # + # The line is commented and not removed to make future django upgrade easier and show exactly what is + # changed in this method override + # + #transformed_url = '/'.join(url_path.split('/')[:-1] + hashed_url.split('/')[-1:]) + transformed_url = hashed_url # This line was added. + + # Restore the fragment that was stripped off earlier. + if fragment: + transformed_url += ('?#' if '?#' in url else '#') + fragment + + # Return the hashed version to the file + return template % unquote(transformed_url) + + return converter + class ThemePipelineMixin(PipelineMixin): """