From 1f083d108ed84d1a97f87cf4e9e138cd357d097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Mali=C5=84ski?= Date: Sat, 7 Dec 2013 02:48:20 +0100 Subject: [PATCH] [#7] Extract core image processing to backends. --- README.rst | 5 +++ ckeditor/image/__init__.py | 0 ckeditor/image/dummy_backend.py | 6 ++++ ckeditor/image/pillow_backend.py | 37 +++++++++++++++++++ ckeditor/image_processing.py | 11 ++++++ ckeditor/templates/browse.html | 2 +- ckeditor/views.py | 47 +++++++------------------ ckeditor_demo/demo_application/tests.py | 18 +++++++--- ckeditor_demo/settings.py | 1 + ckeditor_demo_requirements.txt | 1 + setup.py | 1 - 11 files changed, 89 insertions(+), 40 deletions(-) create mode 100644 ckeditor/image/__init__.py create mode 100644 ckeditor/image/dummy_backend.py create mode 100644 ckeditor/image/pillow_backend.py create mode 100644 ckeditor/image_processing.py diff --git a/README.rst b/README.rst index eaf162847..d363bf59f 100644 --- a/README.rst +++ b/README.rst @@ -42,6 +42,11 @@ Required (r'^ckeditor/', include('ckeditor.urls')), +#. Set ``CKEDITOR_IMAGE_BACKEND`` to one of supported backends to enable thumbnails in ckeditor gallery. Supported backends: + + - ``pillow``: uses PIL or Pillow + + Optional ~~~~~~~~ #. All uploaded files are slugified by defaults, to disable this feature set ``CKEDITOR_SLUGIFY_FILENAME`` to ``False`` diff --git a/ckeditor/image/__init__.py b/ckeditor/image/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ckeditor/image/dummy_backend.py b/ckeditor/image/dummy_backend.py new file mode 100644 index 000000000..78fb12d90 --- /dev/null +++ b/ckeditor/image/dummy_backend.py @@ -0,0 +1,6 @@ +def create_thumbnail(file_object, format): + raise NotImplementedError + + +def is_image(filepath): + return False diff --git a/ckeditor/image/pillow_backend.py b/ckeditor/image/pillow_backend.py new file mode 100644 index 000000000..035e39cb2 --- /dev/null +++ b/ckeditor/image/pillow_backend.py @@ -0,0 +1,37 @@ +from io import BytesIO + +from django.core.files.storage import default_storage + +try: + from PIL import Image, ImageOps +except ImportError: + import Image + import ImageOps + +THUMBNAIL_SIZE = (75, 75) + + +def create_thumbnail(file_object, format): + image = Image.open(file_object) + + # Convert to RGB if necessary + # Thanks to Limodou on DjangoSnippets.org + # http://www.djangosnippets.org/snippets/20/ + if image.mode not in ('L', 'RGB'): + image = image.convert('RGB') + + # scale and crop to thumbnail + imagefit = ImageOps.fit(image, THUMBNAIL_SIZE, Image.ANTIALIAS) + thumbnail_io = BytesIO() + imagefit.save(thumbnail_io, format=format) + return thumbnail_io + + +def is_image(filepath): + image = default_storage.open(filepath) + try: + Image.open(image) + except IOError: + return False + else: + return True diff --git a/ckeditor/image_processing.py b/ckeditor/image_processing.py new file mode 100644 index 000000000..0f87e9419 --- /dev/null +++ b/ckeditor/image_processing.py @@ -0,0 +1,11 @@ +from django.conf import settings + + +def get_backend(): + backend = getattr(settings, "CKEDITOR_IMAGE_BACKEND", None) + + if backend == "pillow": + from ckeditor.image import pillow_backend as backend + else: + from ckeditor.image import dummy_backend as backend + return backend diff --git a/ckeditor/templates/browse.html b/ckeditor/templates/browse.html index ce161774d..76d7de75b 100644 --- a/ckeditor/templates/browse.html +++ b/ckeditor/templates/browse.html @@ -36,7 +36,7 @@

No images found. Upload images using the 'Image Button' dialog's 'Upload' ta {% for image in images %}
  • - +
    diff --git a/ckeditor/views.py b/ckeditor/views.py index 4382122a0..3996b426e 100644 --- a/ckeditor/views.py +++ b/ckeditor/views.py @@ -1,7 +1,6 @@ from datetime import datetime import mimetypes import os -from io import BytesIO from django.conf import settings from django.core.files.storage import default_storage @@ -12,30 +11,14 @@ from django.template.defaultfilters import slugify from django.template import RequestContext -try: - from PIL import Image, ImageOps -except ImportError: - import Image - import ImageOps - -THUMBNAIL_SIZE = (75, 75) +from ckeditor import image_processing CKEDITOR_UPLOAD_SLUGIFY_FILENAME = getattr(settings, "CKEDITOR_UPLOAD_SLUGIFY_FILENAME", True) -def is_image(filepath): - image = default_storage.open(filepath) - try: - Image.open(image) - except IOError: - return False - else: - return True - - def slugify_filename(filename): u""" Slugify filename """ name, ext = os.path.splitext(filename) @@ -58,21 +41,11 @@ def get_image_format(extension): def create_thumbnail(filename): thumbnail_filename = get_thumb_filename(filename) thumbnail_format = get_image_format(os.path.splitext(filename)[1]) - pil_format = thumbnail_format.split('/')[1] + file_format = thumbnail_format.split('/')[1] image = default_storage.open(filename) - image = Image.open(image) - - # Convert to RGB if necessary - # Thanks to Limodou on DjangoSnippets.org - # http://www.djangosnippets.org/snippets/20/ - if image.mode not in ('L', 'RGB'): - image = image.convert('RGB') - - # scale and crop to thumbnail - imagefit = ImageOps.fit(image, THUMBNAIL_SIZE, Image.ANTIALIAS) - thumbnail_io = BytesIO() - imagefit.save(thumbnail_io, format=pil_format) + backend = image_processing.get_backend() + thumbnail_io = backend.create_thumbnail(image, file_format) thumbnail = InMemoryUploadedFile( thumbnail_io, @@ -128,7 +101,8 @@ def upload(request): upload_filename = get_upload_filename(upload.name, request.user) saved_path = default_storage.save(upload_filename, upload) - if is_image(saved_path): + backend = image_processing.get_backend() + if backend.is_image(saved_path): create_thumbnail(saved_path) url = get_media_url(saved_path) @@ -184,9 +158,14 @@ def get_image_browse_urls(user=None): """ images = [] for filename in get_image_files(user=user): + src = get_media_url(filename) + if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None): + thumb = get_media_url(get_thumb_filename(filename)) + else: + thumb = src images.append({ - 'thumb': get_media_url(get_thumb_filename(filename)), - 'src': get_media_url(filename) + 'thumb': thumb, + 'src': src }) return images diff --git a/ckeditor_demo/demo_application/tests.py b/ckeditor_demo/demo_application/tests.py index 16d8f1d1d..7ce3904f3 100644 --- a/ckeditor_demo/demo_application/tests.py +++ b/ckeditor_demo/demo_application/tests.py @@ -6,6 +6,7 @@ from django.conf import settings from django.contrib.staticfiles.finders import find from django.test import LiveServerTestCase +from django.test.utils import override_settings from selenium.webdriver.firefox.webdriver import WebDriver @@ -86,7 +87,8 @@ def _assert_image_uploaded(self): assert os.path.isfile(expected_thumbnail_path) self._assert_uploaded_image_did_not_changed(expected_image_path) self._assert_thumbnail_is_not_empty(expected_thumbnail_path) - self._remove_uploads(expected_image_path, expected_thumbnail_path) + os.remove(expected_image_path) + os.remove(expected_thumbnail_path) def _get_upload_directory(self): date_path = datetime.now().strftime('%Y/%m/%d') @@ -111,6 +113,14 @@ def _assert_thumbnail_is_not_empty(self, path): size = os.path.getsize(path) assert size > 0 - def _remove_uploads(self, image, thumbnail): - os.remove(image) - os.remove(thumbnail) + +@override_settings(CKEDITOR_IMAGE_BACKEND=None) +class TestAdminPanelWidgetForDummyImageBackend(TestAdminPanelWidget): + def _assert_image_uploaded(self): + upload_directory = self._get_upload_directory() + expected_image_path = os.path.join(upload_directory, 'close.png') + expected_thumbnail_path = os.path.join(upload_directory, 'close_thumb.png') + assert os.path.isfile(expected_image_path) + assert not os.path.isfile(expected_thumbnail_path) + self._assert_uploaded_image_did_not_changed(expected_image_path) + os.remove(expected_image_path) diff --git a/ckeditor_demo/settings.py b/ckeditor_demo/settings.py index 6e1f8d82d..2956cd377 100644 --- a/ckeditor_demo/settings.py +++ b/ckeditor_demo/settings.py @@ -88,3 +88,4 @@ MEDIA_ROOT = os.path.join(tempfile.gettempdir(), 'ck_media') CKEDITOR_UPLOAD_PATH = "uploads/" +CKEDITOR_IMAGE_BACKEND = "pillow" diff --git a/ckeditor_demo_requirements.txt b/ckeditor_demo_requirements.txt index d4419f75f..dced06593 100644 --- a/ckeditor_demo_requirements.txt +++ b/ckeditor_demo_requirements.txt @@ -1,3 +1,4 @@ django==1.6.0 +Pillow==2.2.1 selenium==2.38.1 tox==1.6.1 diff --git a/setup.py b/setup.py index 7c538b2ca..443d4adb5 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,6 @@ def get_source_files(): 'ckeditor', ], install_requires=[ - 'Pillow', 'Django', ], classifiers=[