From f31053a5604b0d919a2abd66e6b3b5dda018ee21 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Thu, 12 Sep 2024 14:50:47 -0500 Subject: [PATCH 1/2] fix: update `django-storages` version (#2533) --- prod-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prod-requirements.txt b/prod-requirements.txt index a7c4022f1..cdc543952 100644 --- a/prod-requirements.txt +++ b/prod-requirements.txt @@ -4,5 +4,5 @@ raven==6.10.0 # Heroku Whitenoise==6.6.0 # 6.4.0 is first version that supports Django 4.2 -django-storages==1.42.2 # 1.42.2 is first version that supports Django 4.2 +django-storages==1.14.4 # 1.14.4 is first version that supports Django 4.2 boto3==1.26.165 From e89f46d04867e514d2702894b1bd8dcddbebc9e1 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Thu, 12 Sep 2024 16:53:51 -0400 Subject: [PATCH 2/2] Fix static (#2534) * re-encode plugins as utf-8 * upgrade to 4.2.x code for HashedFilesMixin and reapply patches --- custom_storages/storages.py | 84 +++++++++++++++++++++++++------------ static/js/plugins/IE7.js | 3 +- static/js/plugins/IE9.js | 2 +- 3 files changed, 60 insertions(+), 29 deletions(-) mode change 100755 => 100644 static/js/plugins/IE7.js mode change 100755 => 100644 static/js/plugins/IE9.js diff --git a/custom_storages/storages.py b/custom_storages/storages.py index c735fd530..567685603 100644 --- a/custom_storages/storages.py +++ b/custom_storages/storages.py @@ -23,6 +23,21 @@ class PipelineManifestStorage(PipelineMixin, ManifestFilesMixin, StaticFilesStor imports in comments. Ref: https://code.djangoproject.com/ticket/21080 """ + # Skip map files + # https://code.djangoproject.com/ticket/33353#comment:13 + patterns = ( + ( + "*.css", + ( + "(?Purl\\(['\"]{0,1}\\s*(?P.*?)[\"']{0,1}\\))", + ( + "(?P@import\\s*[\"']\\s*(?P.*?)[\"'])", + '@import url("%(url)s")', + ), + ), + ), + ) + def get_comment_blocks(self, content): """ Return a list of (start, end) tuples for each comment block. @@ -32,73 +47,85 @@ def get_comment_blocks(self, content): for match in re.finditer(r'\/\*.*?\*\/', content, flags=re.DOTALL) ] - def url_converter(self, name, hashed_files, template=None, comment_blocks=None): + + def is_in_comment(self, pos, comments): + for start, end in comments: + if start < pos and pos < end: + return True + if pos < start: + return False + return False + + + def url_converter(self, name, hashed_files, template=None, comment_blocks=[]): """ Return the custom URL converter for the given file name. """ - if comment_blocks is None: - comment_blocks = [] - 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() + matches = matchobj.groupdict() + matched = matches["matched"] + url = matches["url"] # Ignore URLs in comments. if self.is_in_comment(matchobj.start(), comment_blocks): return matched # Ignore absolute/protocol-relative and data-uri URLs. - if re.match(r'^[a-z]+:', url): + 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): + 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('/'): + # Ignore URLs without a path + if not url_path: + return matched + + 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):] + 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, '/') + 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, + self._stored_name, + unquote(target_name), + force=True, + hashed_files=hashed_files, ) - transformed_url = '/'.join(url_path.split('/')[:-1] + hashed_url.split('/')[-1:]) + transformed_url = "/".join( + url_path.split("/")[:-1] + hashed_url.split("/")[-1:] + ) # Restore the fragment that was stripped off earlier. if fragment: - transformed_url += ('?#' if '?#' in url else '#') + fragment + transformed_url += ("?#" if "?#" in url else "#") + fragment # Return the hashed version to the file - return template % unquote(transformed_url) + matches["url"] = unquote(transformed_url) + return template % matches return converter - def is_in_comment(self, pos, comments): - for start, end in comments: - if start < pos and pos < end: - return True - if pos < start: - return False - return False def _post_process(self, paths, adjustable_paths, hashed_files): # Sort the files by directory level @@ -122,7 +149,7 @@ def path_level(name): hashed_name = hashed_files[hash_key] # then get the original's file content.. - if hasattr(original_file, 'seek'): + if hasattr(original_file, "seek"): original_file.seek(0) hashed_file_exists = self.exists(hashed_name) @@ -131,12 +158,14 @@ def path_level(name): # ..to apply each replacement pattern to the content if name in adjustable_paths: old_hashed_name = hashed_name - content = original_file.read().decode(settings.FILE_CHARSET) + content = original_file.read().decode("utf-8") for extension, patterns in self._patterns.items(): if matches_patterns(path, (extension,)): comment_blocks = self.get_comment_blocks(content) for pattern, template in patterns: - converter = self.url_converter(name, hashed_files, template, comment_blocks) + converter = self.url_converter( + name, hashed_files, template, comment_blocks + ) try: content = pattern.sub(converter, content) except ValueError as exc: @@ -145,8 +174,9 @@ def path_level(name): self.delete(hashed_name) # then save the processed result content_file = ContentFile(content.encode()) - # Save intermediate file for reference - saved_name = self._save(hashed_name, content_file) + if self.keep_intermediate_files: + # Save intermediate file for reference + self._save(hashed_name, content_file) hashed_name = self.hashed_name(name, content_file) if self.exists(hashed_name): diff --git a/static/js/plugins/IE7.js b/static/js/plugins/IE7.js old mode 100755 new mode 100644 index ba86e3ae0..2884c7d6b --- a/static/js/plugins/IE7.js +++ b/static/js/plugins/IE7.js @@ -12,7 +12,7 @@ Unknown W Brackets, Benjamin Westfarer, Rob Eberhardt, Bill Edney, Kevin Newman, James Crompton, Matthew Mastracci, Doug Wright, Richard York, Kenneth Kolano, MegaZone, - Thomas Verelst, Mark 'Tarquin' Wilton-Jones, Rainer Åhlfors, + Thomas Verelst, Mark 'Tarquin' Wilton-Jones, Rainer Ã…hlfors, David Zulaica, Ken Kolano, Kevin Newman, Sjoerd Visscher, Ingo Chao */ @@ -2406,3 +2406,4 @@ IE7.loaded = true; })(); })(this, document); + diff --git a/static/js/plugins/IE9.js b/static/js/plugins/IE9.js old mode 100755 new mode 100644 index 4d99fd69e..9a50014ed --- a/static/js/plugins/IE9.js +++ b/static/js/plugins/IE9.js @@ -14,7 +14,7 @@ Unknown W Brackets, Benjamin Westfarer, Rob Eberhardt, Bill Edney, Kevin Newman, James Crompton, Matthew Mastracci, Doug Wright, Richard York, Kenneth Kolano, MegaZone, - Thomas Verelst, Mark 'Tarquin' Wilton-Jones, Rainer Åhlfors, + Thomas Verelst, Mark 'Tarquin' Wilton-Jones, Rainer Ã…hlfors, David Zulaica, Ken Kolano, Kevin Newman, Sjoerd Visscher, Ingo Chao */