Commit 30479815 authored by Jannis Leidel's avatar Jannis Leidel
Browse files

Fixed #18050 -- Fixed a rather glaring bug in the handling of @import...

Fixed #18050 -- Fixed a rather glaring bug in the handling of @import statements when using the cached staticfiles storage.
parent effc2cad
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -45,10 +45,11 @@ class StaticFilesStorage(FileSystemStorage):


class CachedFilesMixin(object):
    default_template = """url("%s")"""
    patterns = (
        ("*.css", (
            br"""(url\(['"]{0,1}\s*(.*?)["']{0,1}\))""",
            br"""(@import\s*["']\s*(.*?)["'])""",
            (br"""(@import\s*["']\s*(.*?)["'])""", """@import url("%s")"""),
        )),
    )

@@ -62,8 +63,12 @@ class CachedFilesMixin(object):
        self._patterns = SortedDict()
        for extension, patterns in self.patterns:
            for pattern in patterns:
                if isinstance(pattern, (tuple, list)):
                    pattern, template = pattern
                else:
                    template = self.default_template
                compiled = re.compile(pattern)
                self._patterns.setdefault(extension, []).append(compiled)
                self._patterns.setdefault(extension, []).append((compiled, template))

    def file_hash(self, name, content=None):
        """
@@ -140,10 +145,13 @@ class CachedFilesMixin(object):

        return unquote(final_url)

    def url_converter(self, name):
    def url_converter(self, name, template=None):
        """
        Returns the custom URL converter for the given file name.
        """
        if template is None:
            template = self.default_template

        def converter(matchobj):
            """
            Converts the matched URL depending on the parent level (`..`)
@@ -178,7 +186,8 @@ class CachedFilesMixin(object):
            relative_url = '/'.join(url.split('/')[:-1] + file_name)

            # Return the hashed version to the file
            return 'url("%s")' % unquote(relative_url)
            return template % unquote(relative_url)

        return converter

    def post_process(self, paths, dry_run=False, **options):
@@ -229,9 +238,9 @@ class CachedFilesMixin(object):
                # ..to apply each replacement pattern to the content
                if name in adjustable_paths:
                    content = original_file.read().decode(settings.FILE_CHARSET)
                    converter = self.url_converter(name)
                    for patterns in self._patterns.values():
                        for pattern in patterns:
                        for pattern, template in patterns:
                            converter = self.url_converter(name, template)
                            content = pattern.sub(converter, content)
                    if hashed_file_exists:
                        self.delete(hashed_name)
+1 −0
Original line number Diff line number Diff line
@import 'styles.css';
+7 −0
Original line number Diff line number Diff line
@@ -446,6 +446,13 @@ class TestCollectionCachedStorage(BaseCollectionTestCase,
            self.assertIn(b'url("img/relative.acae32e4532b.png")', content)
            self.assertIn(b"../cached/styles.93b1147e8552.css", content)

    def test_import_replacement(self):
        "See #18050"
        relpath = self.cached_file_path("cached/import.css")
        self.assertEqual(relpath, "cached/import.2b1d40b0bbd4.css")
        with storage.staticfiles_storage.open(relpath) as relfile:
            self.assertIn(b"""import url("styles.93b1147e8552.css")""", relfile.read())

    def test_template_tag_deep_relative(self):
        relpath = self.cached_file_path("cached/css/window.css")
        self.assertEqual(relpath, "cached/css/window.9db38d5169f3.css")