Permalink
Browse files

* Have two distinct hooks: pre_save for css files and post_save for a…

…ny other resource that might be referenced from a css

* Rewritten the file re-writing function
* Update the file's hash and size based on it's new content
  • Loading branch information...
kux committed Nov 18, 2012
1 parent 5cb0915 commit 665658cfd6f68bd30c2d7f875866390d0961697a
Showing with 76 additions and 66 deletions.
  1. +75 −64 filertags/signals.py
  2. +1 −2 filertags/tests.py
View
@@ -1,11 +1,11 @@
import hashlib
import os.path
import re
-import StringIO
import urlparse
from django.core.cache import cache
from django.core.files.uploadedfile import UploadedFile
+from django.core.files.base import ContentFile
from django.db.models import signals
from filer.models.filemodels import File
@@ -34,35 +34,75 @@ def _get_commented_regions(content):
return [(m.start(), m.end()) for m in re.finditer(_COMMENT_REGEX, content)]
-def _is_not_on_s3(file_):
+def _is_in_memory(file_):
return isinstance(file_, UploadedFile)
def _rewrite_file_content(filer_file, new_content):
- if _is_not_on_s3(filer_file.file.file):
+ if _is_in_memory(filer_file.file.file):
filer_file.file.seek(0)
filer_file.file.write(new_content)
else:
+ # file_name = filer_file.original_filename
storage = filer_file.file.storage
- fp = StringIO.StringIO()
- fp.write(new_content)
- fp.seek(0)
- storage.save(filer_file.file.name, fp)
+ fp = ContentFile(new_content, filer_file.file.name)
+ filer_file.file.file = fp
+ filer_file.file.name = storage.save(filer_file.file.name, fp)
sha = hashlib.sha1()
sha.update(new_content)
filer_file.sha1 = sha.hexdigest()
filer_file._file_size = len(new_content)
-def _resolve_resource_urls(css_file):
- logical_folder_path = _construct_logical_folder_path(css_file)
+def _is_css(filer_file):
+ if filer_file.name:
+ return filer_file.name.endswith('.css')
+ else:
+ return filer_file.original_filename.endswith('.css')
+
+
+def resolve_resource_urls(instance, **kwargs):
+ """Post save hook for css files uploaded to filer.
+ It's purpose is to resolve the actual urls of resources referenced
+ in css files.
+
+ django-filer has two concepts of urls:
+ * the logical url: media/images/foobar.png
+ * the actual url: filer_public/2012/11/22/foobar.png
+
+ The css as written by the an end user uses logical urls:
+ .button.nice {
+ background: url('../images/misc/foobar.png');
+ -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
+ }
+
+ In order for the resources to be found, the logical urls need to be
+ replaced with the actual urls.
+
+ Whenever a css is saved it parses the content and rewrites all logical
+ urls to their actual urls; the logical url is still being saved
+ as a comment that follows the actual url. This comment is needed for
+ the behaviour described at point 2.
+
+ After url rewriting the above css snippet will look like:
+ .button.nice {
+ background: url('filer_public/2012/11/22/foobar.png') /* logicalurl('media/images/misc/foobar.png') /* ;
+ -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
+ }
+ """
+ if not _is_css(instance):
+ return
+ css_file = instance
+ if _is_in_clipboard(css_file):
+ return
content = css_file.file.read()
if content.startswith(_ALREADY_PARSED_MARKER):
# this css' resource urls have already been resolved
# this happens when moving the css in and out of the clipboard
# multiple times
return
+ logical_folder_path = _construct_logical_folder_path(css_file)
commented_regions = _get_commented_regions(content)
local_cache = {}
@@ -91,7 +131,24 @@ def change_urls(match):
_rewrite_file_content(css_file, new_content)
-def _update_referencing_css_files(resource_file):
+def update_referencing_css_files(instance, **kwargs):
+ """Post save hook for any resource uploaded to filer that
+ might be referenced by a css.
+ The purpose of this hook is to update the actual url in all css files that
+ reference the resource pointed by 'instance'.
+
+ References are found by looking for comments such as:
+ /* logicalurl('media/images/misc/foobar.png') */
+
+ If the url between parentheses matches the logical url of the resource
+ being saved, the actual url (which percedes the comment)
+ is being updated.
+ """
+ if _is_css(instance):
+ return
+ resource_file = instance
+ if _is_in_clipboard(resource_file):
+ return
if resource_file.name:
resource_name = resource_file.name
else:
@@ -100,70 +157,23 @@ def _update_referencing_css_files(resource_file):
_construct_logical_folder_path(resource_file),
resource_name)
css_files = File.objects.filter(original_filename__endswith=".css")
-
for css in css_files:
logical_url_snippet = _LOGICAL_URL_TEMPLATE % logical_file_path
url_updating_regex = "%s %s" % (
_RESOURCE_URL_REGEX.pattern, re.escape(logical_url_snippet))
repl = "url('%s') %s" % (resource_file.url, logical_url_snippet)
try:
- new_content = re.sub(url_updating_regex, repl, css.file.read())
+ content = css.file.read()
+ new_content = re.sub(url_updating_regex, repl, content)
except IOError:
# the filer database might have File entries that reference
# files no longer phisically exist
# TODO: find the root cause of missing filer files
continue
else:
- _rewrite_file_content(css, new_content)
-
-
-def resolve_css_resource_urls(instance, **kwargs):
- """Post save hook for filer resources.
- It's purpose is to resolve the actual urls of resources referenced
- in css files.
-
- django-filer has two concepts of urls:
- * the logical url: media/images/foobar.png
- * the actual url: filer_public/2012/11/22/foobar.png
-
- The css as written by the an end user uses logical urls:
- .button.nice {
- background: url('../images/misc/foobar.png');
- -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
- }
-
- In order for the resources to be found, the logical urls need to be
- replaced with the actual urls.
-
- This post save hook does this in two ways:
- 1) whenever a css is saved it parses the content and rewrites all logical
- urls to their actual urls; the logical url is still being saved
- as a comment that follows the actual url. This comment is needed for
- the behaviour described at point 2.
-
- After url rewriting the above css snippet will look like:
- .button.nice {
- background: url('filer_public/2012/11/22/foobar.png') /* logicalurl('media/images/misc/foobar.png') /* ;
- -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
- }
-
- 2) when any other kind of resource is saved, all css files are parsed for
- references to the resource being saved. If found, the actual url is
- being rewritten.
-
- References are found by looking for comments such as:
- /* logicalurl('media/images/misc/foobar.png') */
-
- If the url between parentheses matches the logical url of the resource
- being saved, the actual url (which percedes the comment)
- is being updated.
- """
- if _is_in_clipboard(instance):
- return
- if instance.original_filename.endswith('.css'):
- _resolve_resource_urls(instance)
- else:
- _update_referencing_css_files(instance)
+ if content != new_content:
+ _rewrite_file_content(css, new_content)
+ css.save()
def clear_urls_cache(instance, **kwargs):
@@ -175,8 +185,9 @@ def clear_urls_cache(instance, **kwargs):
cache.delete(cache_key)
-signals.pre_save.connect(resolve_css_resource_urls, sender=File)
-signals.pre_save.connect(resolve_css_resource_urls, sender=Image)
+signals.pre_save.connect(resolve_resource_urls, sender=File)
+signals.post_save.connect(update_referencing_css_files, sender=File)
+signals.post_save.connect(update_referencing_css_files, sender=Image)
signals.post_save.connect(clear_urls_cache, sender=File)
signals.post_save.connect(clear_urls_cache, sender=Image)
View
@@ -7,8 +7,7 @@
from filer.models.imagemodels import Image
from filer.models.filemodels import File
-from filer.tests.helpers import (create_superuser, create_folder_structure,
- create_image, create_clipboard_item)
+from filer.tests.helpers import create_superuser, create_image
from filertags.signals import _ALREADY_PARSED_MARKER

0 comments on commit 665658c

Please sign in to comment.