diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index a804c5626c..cf0c4a0e3a 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -113,13 +113,22 @@ class CachedFilesMixin(object): return matched name_parts = name.split('/') # Using posix normpath here to remove duplicates - result = url_parts = posixpath.normpath(url).split('/') - level = url.count('..') - if level: - result = name_parts[:-level - 1] + url_parts[level:] - elif name_parts[:-1]: - result = name_parts[:-1] + url_parts[-1:] - joined_result = '/'.join(result) + url = posixpath.normpath(url) + url_parts = url.split('/') + parent_level, sub_level = url.count('..'), url.count('/') + if url.startswith('/'): + sub_level -= 1 + url_parts = url_parts[1:] + if parent_level or not url.startswith('/'): + start, end = parent_level + 1, parent_level + else: + if sub_level: + if sub_level == 1: + parent_level -= 1 + start, end = parent_level, sub_level - 1 + else: + start, end = 1, sub_level - 1 + joined_result = '/'.join(name_parts[:-start] + url_parts[end:]) hashed_url = self.url(joined_result, force=True) # Return the hashed and normalized version to the file return 'url("%s")' % hashed_url diff --git a/tests/regressiontests/staticfiles_tests/project/documents/cached/css/img/window.png b/tests/regressiontests/staticfiles_tests/project/documents/cached/css/img/window.png new file mode 100644 index 0000000000..ba48325c0a Binary files /dev/null and b/tests/regressiontests/staticfiles_tests/project/documents/cached/css/img/window.png differ diff --git a/tests/regressiontests/staticfiles_tests/project/documents/cached/css/window.css b/tests/regressiontests/staticfiles_tests/project/documents/cached/css/window.css new file mode 100644 index 0000000000..9fea4e8a5e --- /dev/null +++ b/tests/regressiontests/staticfiles_tests/project/documents/cached/css/window.css @@ -0,0 +1,3 @@ +body { + background: #d3d6d8 url("img/window.png"); +} \ No newline at end of file diff --git a/tests/regressiontests/staticfiles_tests/project/documents/cached/img/relative.png b/tests/regressiontests/staticfiles_tests/project/documents/cached/img/relative.png new file mode 100644 index 0000000000..ba48325c0a Binary files /dev/null and b/tests/regressiontests/staticfiles_tests/project/documents/cached/img/relative.png differ diff --git a/tests/regressiontests/staticfiles_tests/project/documents/cached/relative.css b/tests/regressiontests/staticfiles_tests/project/documents/cached/relative.css index 40c4a256aa..afa658dbeb 100644 --- a/tests/regressiontests/staticfiles_tests/project/documents/cached/relative.css +++ b/tests/regressiontests/staticfiles_tests/project/documents/cached/relative.css @@ -1,2 +1,5 @@ @import url("../cached/styles.css"); -@import url("absolute.css"); \ No newline at end of file +@import url("absolute.css"); +body { + background: #d3d6d8 url(img/relative.png); +} \ No newline at end of file diff --git a/tests/regressiontests/staticfiles_tests/project/documents/cached/styles.css b/tests/regressiontests/staticfiles_tests/project/documents/cached/styles.css index 84936d1dcb..ca28fc35cd 100644 --- a/tests/regressiontests/staticfiles_tests/project/documents/cached/styles.css +++ b/tests/regressiontests/staticfiles_tests/project/documents/cached/styles.css @@ -1 +1 @@ -@import url("cached/other.css"); \ No newline at end of file +@import url("other.css"); \ No newline at end of file diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py index 40569484df..8a179141b9 100644 --- a/tests/regressiontests/staticfiles_tests/tests.py +++ b/tests/regressiontests/staticfiles_tests/tests.py @@ -300,11 +300,11 @@ class TestCollectionCachedStorage(BaseCollectionTestCase, """, "/static/test/file.dad0999e4f8f.txt") self.assertTemplateRenders(""" {% load static from staticfiles %}{% static "cached/styles.css" %} - """, "/static/cached/styles.5653c259030b.css") + """, "/static/cached/styles.93b1147e8552.css") def test_template_tag_simple_content(self): relpath = self.cached_file_path("cached/styles.css") - self.assertEqual(relpath, "cached/styles.5653c259030b.css") + self.assertEqual(relpath, "cached/styles.93b1147e8552.css") with storage.staticfiles_storage.open(relpath) as relfile: content = relfile.read() self.assertFalse("cached/other.css" in content, content) @@ -316,7 +316,7 @@ class TestCollectionCachedStorage(BaseCollectionTestCase, with storage.staticfiles_storage.open(relpath) as relfile: content = relfile.read() self.assertFalse("/static/cached/styles.css" in content) - self.assertTrue("/static/cached/styles.5653c259030b.css" in content) + self.assertTrue("/static/cached/styles.93b1147e8552.css" in content) def test_template_tag_denorm(self): relpath = self.cached_file_path("cached/denorm.css") @@ -324,16 +324,26 @@ class TestCollectionCachedStorage(BaseCollectionTestCase, with storage.staticfiles_storage.open(relpath) as relfile: content = relfile.read() self.assertFalse("..//cached///styles.css" in content) - self.assertTrue("/static/cached/styles.5653c259030b.css" in content) + self.assertTrue("/static/cached/styles.93b1147e8552.css" in content) def test_template_tag_relative(self): relpath = self.cached_file_path("cached/relative.css") - self.assertEqual(relpath, "cached/relative.298ff891a8d4.css") + self.assertEqual(relpath, "cached/relative.8dffb45d91f5.css") with storage.staticfiles_storage.open(relpath) as relfile: content = relfile.read() self.assertFalse("../cached/styles.css" in content) self.assertFalse('@import "styles.css"' in content) - self.assertTrue("/static/cached/styles.5653c259030b.css" in content) + self.assertTrue("/static/cached/styles.93b1147e8552.css" in content) + self.assertFalse("url(img/relative.png)" in content) + self.assertTrue("/static/cached/img/relative.acae32e4532b.png" in content) + + def test_template_tag_deep_relative(self): + relpath = self.cached_file_path("cached/css/window.css") + self.assertEqual(relpath, "cached/css/window.9db38d5169f3.css") + with storage.staticfiles_storage.open(relpath) as relfile: + content = relfile.read() + self.assertFalse('url(img/window.png)' in content) + self.assertTrue('url("/static/cached/css/img/window.acae32e4532b.png")' in content) def test_template_tag_url(self): relpath = self.cached_file_path("cached/url.css")