From 4027cc54e9d3f3b3f8fc7702c42333d2288b1393 Mon Sep 17 00:00:00 2001 From: xian Date: Wed, 29 Apr 2009 19:58:35 -0500 Subject: [PATCH] url() normalizing is go. --- compressor/filters/css_absolute.py | 27 ++++++++++++++++++++++-- compressor/templatetags/compress.py | 29 +++++++++++++++++--------- tests/core/tests.py | 32 ++++++++++++++++++++++------- tests/media/css/url/2/url2.css | 4 ++++ tests/media/css/url/url1.css | 4 ++++ 5 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 tests/media/css/url/2/url2.css create mode 100644 tests/media/css/url/url1.css diff --git a/compressor/filters/css_absolute.py b/compressor/filters/css_absolute.py index 77780d2..919ced8 100644 --- a/compressor/filters/css_absolute.py +++ b/compressor/filters/css_absolute.py @@ -1,3 +1,6 @@ +import os +import re + from compressor.filters import FilterBase, FilterError from compressor.conf import settings @@ -6,5 +9,25 @@ class CssAbsoluteFilter(FilterBase): def input(self, filename=None, **kwargs): if not filename or not filename.startswith(settings.MEDIA_ROOT): return self.content - filename = filename[len(settings.MEDIA_ROOT):] - return "\n".join([filename, self.content]) + self.media_path = filename[len(settings.MEDIA_ROOT):] + self.media_path = self.media_path.lstrip('/') + self.media_url = settings.MEDIA_URL.rstrip('/') + self.has_http = False + if self.media_url.startswith('http://'): + self.has_http = True + self.media_url = self.media_url[7:] + self.directory_name = '/'.join([self.media_url, os.path.dirname(self.media_path)]) + url_pattern = re.compile(r'url\(([^\)]+)\)') + output = url_pattern.sub(self.url_converter, self.content) + return output + + def url_converter(self, matchobj): + url = matchobj.group(1) + url = url.strip(' \'"') + if url.startswith('http://') or url.startswith('/'): + return "url('%s')" % url + full_url = '/'.join([self.directory_name, url]) + full_url = os.path.normpath(full_url) + if self.has_http: + full_url = "http://%s" % full_url + return "url('%s')" % full_url diff --git a/compressor/templatetags/compress.py b/compressor/templatetags/compress.py index f77f86d..d743d3f 100644 --- a/compressor/templatetags/compress.py +++ b/compressor/templatetags/compress.py @@ -3,13 +3,16 @@ from hashlib import sha1 as hash from BeautifulSoup import BeautifulSoup from django import template +from django.conf import settings as django_settings from django.template.loader import render_to_string - + from compressor.conf import settings from compressor import filters register = template.Library() +class UncompressableFileError(Exception): + pass class CompressedNode(template.Node): @@ -23,15 +26,13 @@ class CompressedNode(template.Node): def content_hash(self): """docstring for content_hash""" pass - + def split_contents(self): raise NotImplementedError('split_contents must be defined in a subclass') def get_filename(self, url): if not url.startswith(settings.MEDIA_URL): - # TODO: Put a proper exception here. Maybe one that only shows up - # if debug is on. - raise Exception('FIX THIS EXCPETIONS@!@') + raise UncompressableFileError('"%s" is not in COMPRESS_MEDIA_URL ("%s") and can not be compressed' % (url, settings.COMPRESS_MEDIA_URL)) basename = url[len(settings.MEDIA_URL):] filename = os.path.join(settings.MEDIA_ROOT, basename) return filename @@ -58,7 +59,7 @@ class CompressedNode(template.Node): def concat(self): return "\n".join(self.get_hunks()) - + def filter(self, content, method, **kwargs): content = content for f in self.filters: @@ -74,7 +75,7 @@ class CompressedNode(template.Node): if getattr(self, '_output', ''): return self._output output = self.concat() - filter_method = getattr(self, 'filter_method', None) + filter_method = getattr(self, 'filter_method', None) if self.filters: output = self.filter(output, 'output') self._output = output @@ -90,7 +91,7 @@ class CompressedNode(template.Node): filepath = "%s/%s/%s" % (settings.OUTPUT_DIR.strip('/'), self.ouput_prefix, filename) return filepath new_filepath = property(get_new_filepath) - + def save_file(self): filename = "%s/%s" % (settings.MEDIA_ROOT.rstrip('/'), self.new_filepath) if os.path.exists(filename): @@ -129,7 +130,11 @@ class CompressedCssNode(CompressedNode): split = self.soup.findAll({'link' : True, 'style' : True}) for elem in split: if elem.name == 'link' and elem['rel'] == 'stylesheet': - self.split_content.append(('file', self.get_filename(elem['href']))) + try: + self.split_content.append(('file', self.get_filename(elem['href']))) + except UncompressableFileError: + if django_settings.DEBUG: + raise if elem.name == 'style': self.split_content.append(('hunk', elem.string)) return self.split_content @@ -150,7 +155,11 @@ class CompressedJsNode(CompressedNode): split = self.soup.findAll('script') for elem in split: if elem.has_key('src'): - self.split_content.append(('file', self.get_filename(elem['src']))) + try: + self.split_content.append(('file', self.get_filename(elem['src']))) + except UncompressableFileError: + if django_settings.DEBUG: + raise else: self.split_content.append(('hunk', elem.string)) return self.split_content diff --git a/tests/core/tests.py b/tests/core/tests.py index 5af94c8..fc67566 100644 --- a/tests/core/tests.py +++ b/tests/core/tests.py @@ -3,10 +3,11 @@ import os from django.test import TestCase from compressor.templatetags.compress import CompressedCssNode, CompressedJsNode from compressor.conf import settings +from django.conf import settings as django_settings class CompressedNodeTestCase(TestCase): - + def setUp(self): settings.COMPRESS = True self.css = """ @@ -21,7 +22,7 @@ class CompressedNodeTestCase(TestCase): """ self.jsNode = CompressedJsNode(self.js) - + def test_css_split(self): out = [ ('file', os.path.join(settings.MEDIA_ROOT, u'css/one.css')), @@ -76,16 +77,33 @@ class CompressedNodeTestCase(TestCase): def test_js_return_if_on(self): output = u'' self.assertEqual(output, self.jsNode.render()) - + class CssAbsolutizingTestCase(TestCase): def setUp(self): settings.COMPRESS = True + django_settings.DEBUG = True + settings.MEDIA_URL = '/media/' self.css = """ - + + """ self.cssNode = CompressedCssNode(self.css) - def test_fail(self): - settings.COMPRESS = False - self.assertEqual(self.js, self.jsNode.render()) + def test_css_absolute_filter(self): + from compressor.filters.css_absolute import CssAbsoluteFilter + filename = os.path.join(settings.MEDIA_ROOT, 'css/url/test.css') + content = "p { background: url('../../images/image.gif') }" + output = "p { background: url('%simages/image.gif') }" % settings.MEDIA_URL + filter = CssAbsoluteFilter(content) + self.assertEqual(output, filter.input(filename=filename)) + settings.MEDIA_URL = 'http://media.example.com/' + filename = os.path.join(settings.MEDIA_ROOT, 'css/url/test.css') + output = "p { background: url('%simages/image.gif') }" % settings.MEDIA_URL + self.assertEqual(output, filter.input(filename=filename)) + + def test_css_hunks(self): + out = [u"p { background: url('/media/images/test.png'); }\np { background: url('/media/images/test.png'); }\np { background: url('/media/images/test.png'); }\np { background: url('/media/images/test.png'); }\n", + u"p { background: url('/media/images/test.png'); }\np { background: url('/media/images/test.png'); }\np { background: url('/media/images/test.png'); }\np { background: url('/media/images/test.png'); }\n", + ] + self.assertEqual(out, self.cssNode.hunks) diff --git a/tests/media/css/url/2/url2.css b/tests/media/css/url/2/url2.css new file mode 100644 index 0000000..1114622 --- /dev/null +++ b/tests/media/css/url/2/url2.css @@ -0,0 +1,4 @@ +p { background: url('../../../images/test.png'); } +p { background: url(../../../images/test.png); } +p { background: url( ../../../images/test.png ); } +p { background: url( '../../../images/test.png' ); } diff --git a/tests/media/css/url/url1.css b/tests/media/css/url/url1.css new file mode 100644 index 0000000..d202a47 --- /dev/null +++ b/tests/media/css/url/url1.css @@ -0,0 +1,4 @@ +p { background: url('../../images/test.png'); } +p { background: url(../../images/test.png); } +p { background: url( ../../images/test.png ); } +p { background: url( '../../images/test.png' ); }