From f8241a8eb37c7a88ec25e950a0ccad3a5598b3a1 Mon Sep 17 00:00:00 2001 From: rristow Date: Tue, 18 Feb 2014 11:13:09 +0100 Subject: [PATCH 01/37] Avoid browser cache (Return code 304 Not Modified) --- src/plone/app/imagecropping/browser/editor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plone/app/imagecropping/browser/editor.py b/src/plone/app/imagecropping/browser/editor.py index 7ce71389..8eeaeabd 100644 --- a/src/plone/app/imagecropping/browser/editor.py +++ b/src/plone/app/imagecropping/browser/editor.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from DateTime.DateTime import DateTime from Products.ATContentTypes.interfaces.interfaces import IATContentType from Products.Five.browser import BrowserView from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile @@ -12,7 +13,6 @@ from zope.component._api import getUtility import json - JS_MESSAGES = """\ if(typeof(imagecropping) != "undefined") { imagecropping.i18n_message_ids = { @@ -157,6 +157,8 @@ def __call__(self): x2 = int(round(float(self.request.form.get('x2')))) y2 = int(round(float(self.request.form.get('y2')))) scale_name = self.request.form.get('scalename') + # Avoid browser cache + self.context.setModificationDate(DateTime()) cropping_util._crop(fieldname=self.fieldname, scale=scale_name, box=(x1, y1, x2, y2), From 15d680e9c2fe240a4008f3546c89d6121d5c204d Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 19 Feb 2014 21:24:06 +0100 Subject: [PATCH 02/37] simplified code a little bit --- src/plone/app/imagecropping/browser/editor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plone/app/imagecropping/browser/editor.py b/src/plone/app/imagecropping/browser/editor.py index 8eeaeabd..a5d9c9b5 100644 --- a/src/plone/app/imagecropping/browser/editor.py +++ b/src/plone/app/imagecropping/browser/editor.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from DateTime.DateTime import DateTime from Products.ATContentTypes.interfaces.interfaces import IATContentType from Products.Five.browser import BrowserView from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile @@ -157,12 +156,14 @@ def __call__(self): x2 = int(round(float(self.request.form.get('x2')))) y2 = int(round(float(self.request.form.get('y2')))) scale_name = self.request.form.get('scalename') - # Avoid browser cache - self.context.setModificationDate(DateTime()) cropping_util._crop(fieldname=self.fieldname, scale=scale_name, box=(x1, y1, x2, y2), interface=self.interface) + # Avoid browser cache + # an empty call of setModificationDate uses current timestamp + self.context.setModificationDate() + self.context.reindexObject() IStatusMessage(self.request).add( _(u"Successfully saved cropped area")) From f7a02373e659a497a873e8b78840a1ffec6b9c75 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 19 Feb 2014 21:32:37 +0100 Subject: [PATCH 03/37] factored out cropping for better testability --- src/plone/app/imagecropping/browser/editor.py | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/plone/app/imagecropping/browser/editor.py b/src/plone/app/imagecropping/browser/editor.py index a5d9c9b5..83cd4b9a 100644 --- a/src/plone/app/imagecropping/browser/editor.py +++ b/src/plone/app/imagecropping/browser/editor.py @@ -142,28 +142,31 @@ def image_url(self, fieldname="image"): height=int(self.default_editor_size[1])) return scaled_img and scaled_img.url or '' - def __call__(self): - form = self.request.form + def _crop(self): + coordinate = lambda x: int(round(float(self.request.form.get(x)))) + x1 = coordinate('x1') + y1 = coordinate('y1') + x2 = coordinate('x2') + y2 = coordinate('y2') + scale_name = self.request.form.get('scalename') cropping_util = self.context.restrictedTraverse('@@crop-image') + cropping_util._crop(fieldname=self.fieldname, + scale=scale_name, + box=(x1, y1, x2, y2), + interface=self.interface) + # Avoid browser cache + # an empty call of setModificationDate uses current timestamp + self.context.setModificationDate() + self.context.reindexObject() + def __call__(self): + form = self.request.form if form.get('form.button.Delete', None) is not None: - cropping_util._remove(self.fieldname, - self.request.form.get('scalename')) + cropping_util = self.context.restrictedTraverse('@@crop-image') + cropping_util._remove(self.fieldname, form.get('scalename')) IStatusMessage(self.request).add(_(u"Cropping area deleted")) if form.get('form.button.Save', None) is not None: - x1 = int(round(float(self.request.form.get('x1')))) - y1 = int(round(float(self.request.form.get('y1')))) - x2 = int(round(float(self.request.form.get('x2')))) - y2 = int(round(float(self.request.form.get('y2')))) - scale_name = self.request.form.get('scalename') - cropping_util._crop(fieldname=self.fieldname, - scale=scale_name, - box=(x1, y1, x2, y2), - interface=self.interface) - # Avoid browser cache - # an empty call of setModificationDate uses current timestamp - self.context.setModificationDate() - self.context.reindexObject() + self._crop() IStatusMessage(self.request).add( _(u"Successfully saved cropped area")) From 49ee3050789548165d98b9f5af85cdeb169f924e Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Thu, 20 Feb 2014 05:44:08 +0100 Subject: [PATCH 04/37] use Plone 4.3 for default buildout --- buildout.cfg | 6 ++---- travis.cfg | 11 +++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/buildout.cfg b/buildout.cfg index 8b186510..bc71374c 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -1,12 +1,10 @@ [buildout] extends = - https://raw.github.com/collective/buildout.plonetest/master/test-4.2.x.cfg + https://raw.github.com/collective/buildout.plonetest/master/test-4.3.x.cfg package-name = plone.app.imagecropping package-extras = [test] -#index = http://c.pypi.python.org - parts += omelette i18ndude @@ -54,4 +52,4 @@ input = inline: mode = 755 [versions] -plone.app.collection = 2.0b5 +plone.app.contenttypes = 1.0 diff --git a/travis.cfg b/travis.cfg index bbb27a60..d16dc14f 100644 --- a/travis.cfg +++ b/travis.cfg @@ -10,10 +10,9 @@ allow-hosts += code.google.com robotframework.googlecode.com -[versions] -plone.app.collection = 2.0b5 -plone.app.testing = >=4.2.2 -docutils = >=0.8.1 -plone.namedfile = >=2.0 -plone.dexterity = >=2.0 +#[versions] +#plone.app.testing = >=4.2.2 +#docutils = >=0.8.1 +#plone.namedfile = >=2.0 +#plone.dexterity = >=2.0 From 24c8e3aec9951b464f90c0a0dac953e8dd0a17c6 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Thu, 20 Feb 2014 05:45:00 +0100 Subject: [PATCH 05/37] started test for crop-view --- src/plone/app/imagecropping/tests/test_editor.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/plone/app/imagecropping/tests/test_editor.py b/src/plone/app/imagecropping/tests/test_editor.py index e3a0ebbb..4917e819 100644 --- a/src/plone/app/imagecropping/tests/test_editor.py +++ b/src/plone/app/imagecropping/tests/test_editor.py @@ -5,7 +5,6 @@ from plone.app.testing import TEST_USER_NAME from plone.app.testing import TEST_USER_PASSWORD from plone.testing.z2 import Browser -from Products.CMFPlone.utils import _createObjectByType import transaction import unittest2 as unittest @@ -24,21 +23,22 @@ def setUp(self): self.browser.handleErrors = False self.browser.addHeader('Authorization', 'Basic %s:%s' % ( TEST_USER_NAME, TEST_USER_PASSWORD,)) + self.createSingleImageType() def createSingleImageType(self): # create test image as testuser - _createObjectByType('Image', self.portal, 'testimage', - title=u"I'm a testing Image") + self.portal.invokeFactory('Image', 'testimage', + title=u"I'm a testing Image") + self.img = self.portal['testimage'] + self.img.reindexObject() transaction.commit() - self.img = self.portal.testimage f = file(join(dirname(tests.__file__), 'plone-logo.png')) self.img.setImage(f) f.close() def test_singleimage_editorview(self): """ """ - self.createSingleImageType() # is there the cropping action tab self.browser.open("%s/view" % self.img.absolute_url()) self.assertIn("Cropping", self.browser.contents) @@ -56,5 +56,11 @@ def test_singleimage_editorview(self): self.assertIn(u"Save cropping information", self.browser.contents) self.assertIn(u"Remove cropping information", self.browser.contents) + def test_editview_crop(self): + request = self.layer['request'] + request.form.update({'x1': 1.0, 'y1': 2.7, 'x2': 10.6, 'y2': 8.4}) + cropview = self.img.restrictedTraverse('@@croppingeditor') + cropview._crop() + def tearDown(self): self.portal.manage_delObjects(['testimage', ]) From ab47688609a808090e339a5707325ed1c8c7998e Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 26 Feb 2014 22:32:05 +0100 Subject: [PATCH 06/37] override scaling view to keep crops on metadata change --- buildout.cfg | 3 +++ .../app/imagecropping/browser/configure.zcml | 9 ++++++++ src/plone/app/imagecropping/browser/crop.py | 3 +-- src/plone/app/imagecropping/browser/editor.py | 4 ---- .../app/imagecropping/browser/scaling.py | 17 +++++++++++++++ src/plone/app/imagecropping/interfaces.py | 2 +- .../app/imagecropping/tests/test_cropping.py | 21 +++++++++++++++++++ .../app/imagecropping/tests/test_editor.py | 3 ++- src/plone/app/imagecropping/utils.py | 10 +++++++-- 9 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 src/plone/app/imagecropping/browser/scaling.py diff --git a/buildout.cfg b/buildout.cfg index bc71374c..2cb1449b 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -24,6 +24,9 @@ test-eggs += versions = versions +[versions] +Pillow = 2.3.0 + [omelette] recipe = collective.recipe.omelette eggs = ${instance:eggs} ${buildout:test-eggs} diff --git a/src/plone/app/imagecropping/browser/configure.zcml b/src/plone/app/imagecropping/browser/configure.zcml index e910f9d7..80418eb3 100644 --- a/src/plone/app/imagecropping/browser/configure.zcml +++ b/src/plone/app/imagecropping/browser/configure.zcml @@ -2,6 +2,7 @@ xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:i18n="http://xml.zope.org/namespaces/i18n" + xmlns:zcml="http://namespaces.zope.org/zcml" i18n_domain="plone"> @@ -43,4 +44,12 @@ permission="cmf.ModifyPortalContent" /> + + diff --git a/src/plone/app/imagecropping/browser/crop.py b/src/plone/app/imagecropping/browser/crop.py index b18e61ac..ec77c013 100644 --- a/src/plone/app/imagecropping/browser/crop.py +++ b/src/plone/app/imagecropping/browser/crop.py @@ -25,7 +25,6 @@ def _crop(self, fieldname, scale, box, interface=None): """switch between dexterity and Archetypes """ croputils = IImageCroppingUtils(self.context) - field = croputils.get_image_field(fieldname) data = croputils.get_image_data(fieldname) original_file = StringIO(data) @@ -38,7 +37,7 @@ def _crop(self, fieldname, scale, box, interface=None): cropped_image_file.seek(0) croputils.save_cropped( - fieldname, field, scale, cropped_image_file, interface) + fieldname, scale, cropped_image_file, interface) # store crop information in annotations self._store(fieldname, scale, box) diff --git a/src/plone/app/imagecropping/browser/editor.py b/src/plone/app/imagecropping/browser/editor.py index 83cd4b9a..31406097 100644 --- a/src/plone/app/imagecropping/browser/editor.py +++ b/src/plone/app/imagecropping/browser/editor.py @@ -154,10 +154,6 @@ def _crop(self): scale=scale_name, box=(x1, y1, x2, y2), interface=self.interface) - # Avoid browser cache - # an empty call of setModificationDate uses current timestamp - self.context.setModificationDate() - self.context.reindexObject() def __call__(self): form = self.request.form diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py new file mode 100644 index 00000000..46132f98 --- /dev/null +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -0,0 +1,17 @@ +from zope.annotation.interfaces import IAnnotations + +from plone.app.imagecropping import PAI_STORAGE_KEY +from plone.app.imaging.scaling import ImageScaling as BaseImageScaling + + +class ImageScaling(BaseImageScaling): + + def modified(self): + cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) + if not cropped: + return super(ImageScaling, self).modified() + else: + return 1 + + +# XXX need this for plone.namedfile and NEWSItem too diff --git a/src/plone/app/imagecropping/interfaces.py b/src/plone/app/imagecropping/interfaces.py index 9b22f993..8882a0d7 100644 --- a/src/plone/app/imagecropping/interfaces.py +++ b/src/plone/app/imagecropping/interfaces.py @@ -30,7 +30,7 @@ def get_image_size(fieldname, interface): (100, 200) """ - def save_cropped(fieldname, field, scale, image_file, interface=None): + def save_cropped(fieldname, scale, image_file, interface=None): """ Save the cropped iamge under the name of the selected scale in plone.scale.storage.AnnotationStorage, so that it is available in plone.app.imaging @@images view diff --git a/src/plone/app/imagecropping/tests/test_cropping.py b/src/plone/app/imagecropping/tests/test_cropping.py index 36dfc731..bd019ae8 100644 --- a/src/plone/app/imagecropping/tests/test_cropping.py +++ b/src/plone/app/imagecropping/tests/test_cropping.py @@ -117,3 +117,24 @@ def test_image_formats(self): # XXX: fixme # self.assertEqual(open(croppedData).format, 'JPEG', # "cropped scale does not have same format as the original") + + def test_modify_context(self): + """ See https://github.com/collective/plone.app.imagecropping/issues/21 + """ + + view = self.img.restrictedTraverse('@@crop-image') + traverse = self.portal.REQUEST.traverseName + scales = traverse(self.img, '@@images') + unscaled_thumb = scales.scale('image', 'thumb') + + # store cropped version for thumb and check if the result + # is a square now + view._crop(fieldname='image', scale='thumb', box=(14, 14, 218, 218)) + thumb = scales.scale('image', 'thumb') + self.failIfEqual(thumb.data, unscaled_thumb.data) + + self.img.setTitle('A new title') + self.img.reindexObject() + + thumb2 = scales.scale('image', 'thumb') + self.assertEqual(thumb.data, thumb2.data) diff --git a/src/plone/app/imagecropping/tests/test_editor.py b/src/plone/app/imagecropping/tests/test_editor.py index 4917e819..4c439014 100644 --- a/src/plone/app/imagecropping/tests/test_editor.py +++ b/src/plone/app/imagecropping/tests/test_editor.py @@ -58,7 +58,8 @@ def test_singleimage_editorview(self): def test_editview_crop(self): request = self.layer['request'] - request.form.update({'x1': 1.0, 'y1': 2.7, 'x2': 10.6, 'y2': 8.4}) + request.form.update({'x1': 1.0, 'y1': 2.7, 'x2': 10.6, 'y2': 8.4, + 'scalename': 'mini'}) cropview = self.img.restrictedTraverse('@@croppingeditor') cropview._crop() diff --git a/src/plone/app/imagecropping/utils.py b/src/plone/app/imagecropping/utils.py index bd812913..203acbd2 100644 --- a/src/plone/app/imagecropping/utils.py +++ b/src/plone/app/imagecropping/utils.py @@ -82,9 +82,10 @@ def get_image_size(self, fieldname, interface=None): return image_size def save_cropped( - self, fieldname, field, scale, image_file, interface=None): + self, fieldname, scale, image_file, interface=None): """ see interface """ + field = self.get_image_field(fieldname) handler = IImageScaleHandler(field) sizes = field.getAvailableSizes(self.context) w, h = sizes[scale] @@ -104,6 +105,10 @@ def crop_factory(fieldname, direction='keep', **parameters): result.close() return blob, image_format, dimensions + # Avoid browser cache + # calling reindexObject updates the modified metadate too + self.context.reindexObject() + # call storage with actual time in milliseconds # this always invalidates old scales storage = AnnotationStorage(self.context, self.now_millis) @@ -162,7 +167,7 @@ def get_image_size(self, fieldname, interface=None): return image_size def save_cropped( - self, fieldname, field, scale, image_file, interface=None): + self, fieldname, scale, image_file, interface=None): """ see interface """ sizes = getAllowedSizes() @@ -173,6 +178,7 @@ def crop_factory(fieldname, **parameters): if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() + field = self.get_image_field(fieldname) value = field.__class__( data, contentType=mimetype, From 362674ff33c9fb15bf258cd7c4fa37926ef5c237 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 5 Mar 2014 22:15:15 +0100 Subject: [PATCH 07/37] implement scaling for plone.namedfile --- buildout.cfg | 1 + .../app/imagecropping/browser/configure.zcml | 16 ++++++ .../app/imagecropping/browser/scaling.py | 54 +++++++++++++++++-- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/buildout.cfg b/buildout.cfg index 2cb1449b..b7145713 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -26,6 +26,7 @@ versions = versions [versions] Pillow = 2.3.0 +selenium = 2.40.0 [omelette] recipe = collective.recipe.omelette diff --git a/src/plone/app/imagecropping/browser/configure.zcml b/src/plone/app/imagecropping/browser/configure.zcml index 80418eb3..bc3d5042 100644 --- a/src/plone/app/imagecropping/browser/configure.zcml +++ b/src/plone/app/imagecropping/browser/configure.zcml @@ -52,4 +52,20 @@ allowed_interface="plone.app.imaging.interfaces.IImageScaling" permission="zope2.View" /> + + + + diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 46132f98..6107e876 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -6,12 +6,60 @@ class ImageScaling(BaseImageScaling): + _rescale = True + def modified(self): - cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) - if not cropped: + if self._rescale: return super(ImageScaling, self).modified() else: return 1 + + def scale(self, + fieldname=None, + scale=None, + height=None, + width=None, + **parameters): + cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) + if '%s_%s' % (fieldname, scale) in cropped: + self._rescale = False + else: + self._rescale = True + return super(ImageScaling, self).scale(fieldname, scale, height, width, **parameters) + + +try: + from plone.namedfile.scaling import ImageScaling as NFImageScaling + + from plone.namedfile.interfaces import IImageScaleTraversable + from plone.app.imagecropping.interfaces import IImageCropping + + class IImageCroppingScale(IImageScaleTraversable, IImageCropping): + pass + + class NamedfileImageScaling(NFImageScaling): + + _rescale = True + + def modified(self): + if self._rescale: + return super(ImageScaling, self).modified() + else: + return 1 + def scale(self, + fieldname=None, + scale=None, + height=None, + width=None, + direction='thumbnail', + **parameters): + cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) + if '%s_%s' % (fieldname, scale) in cropped: + self._rescale = False + else: + self._rescale = True + return super(ImageScaling, self).scale(fieldname, scale, height, width, direction, **parameters) -# XXX need this for plone.namedfile and NEWSItem too +except ImportError: + pass From ea7dc0b9082167feefd2c571125c74d230145df1 Mon Sep 17 00:00:00 2001 From: tomgross Date: Mon, 10 Mar 2014 21:18:22 +0100 Subject: [PATCH 08/37] Remove commented out version pins --- travis.cfg | 7 ------- 1 file changed, 7 deletions(-) diff --git a/travis.cfg b/travis.cfg index d16dc14f..4547045a 100644 --- a/travis.cfg +++ b/travis.cfg @@ -9,10 +9,3 @@ package-extras = [test] allow-hosts += code.google.com robotframework.googlecode.com - -#[versions] -#plone.app.testing = >=4.2.2 -#docutils = >=0.8.1 -#plone.namedfile = >=2.0 -#plone.dexterity = >=2.0 - From 300a3c62ea55ff70417e2da94a2055b738151b35 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Tue, 11 Mar 2014 22:05:45 +0100 Subject: [PATCH 09/37] fixed case where cropping annotation is none --- src/plone/app/imagecropping/browser/scaling.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 6107e876..79e68ef9 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -21,7 +21,7 @@ def scale(self, width=None, **parameters): cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) - if '%s_%s' % (fieldname, scale) in cropped: + if cropped and '%s_%s' % (fieldname, scale) in cropped: self._rescale = False else: self._rescale = True @@ -43,7 +43,7 @@ class NamedfileImageScaling(NFImageScaling): def modified(self): if self._rescale: - return super(ImageScaling, self).modified() + return super(NamedfileImageScaling, self).modified() else: return 1 @@ -55,11 +55,12 @@ def scale(self, direction='thumbnail', **parameters): cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) - if '%s_%s' % (fieldname, scale) in cropped: + if cropped and '%s_%s' % (fieldname, scale) in cropped: self._rescale = False else: self._rescale = True - return super(ImageScaling, self).scale(fieldname, scale, height, width, direction, **parameters) + return super(NamedfileImageScaling, self).scale( + fieldname, scale, height, width, direction, **parameters) except ImportError: pass From 1cbe543a0d2418a223ab562875a3141d4b96a41b Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Tue, 11 Mar 2014 22:24:30 +0100 Subject: [PATCH 10/37] we depend on plone.namedfile >= 2.0.1 --- test-4.2.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-4.2.cfg b/test-4.2.cfg index 9dc5a085..61c06f6c 100644 --- a/test-4.2.cfg +++ b/test-4.2.cfg @@ -2,3 +2,6 @@ extends = https://raw.github.com/collective/buildout.plonetest/master/test-4.2.x.cfg package-name = plone.app.imagecropping package-extras = [test] + +[versions] +plone.namedfile = 2.0.1 From f36fcaad7b75ab9aeb6353d9b1a1a420137ad258 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Tue, 11 Mar 2014 22:47:59 +0100 Subject: [PATCH 11/37] document changes --- CHANGES.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 87ba8470..7220ac80 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,7 +5,7 @@ Changelog ------------------- - Check for plone.namedfile not Dexterity. It can be used seperately - [tom_gross] + [tomgross] - Rename ``imagecropping_keywords.txt`` to ``keywords.robot`` to allow simple reusage in ``plone.app.robotframework``. @@ -20,6 +20,9 @@ Changelog - Prevent fieldname loosing for for current field [bogdangi] +- Fixed #21 (cropping was reset on modifying image) + [tomgross] + 0.1rc2 (2013-05-03) ------------------- From 204339d2771635d17d1ed29ce42125980c020763 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 12 Mar 2014 07:21:07 +0100 Subject: [PATCH 12/37] adjust schema of test content so tests pass on 4.2 and 4.3 --- .../types/dexterity_content_type_with_two_image_fields.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plone/app/imagecropping/profiles/testing/types/dexterity_content_type_with_two_image_fields.xml b/src/plone/app/imagecropping/profiles/testing/types/dexterity_content_type_with_two_image_fields.xml index a819cd45..cbac35a0 100644 --- a/src/plone/app/imagecropping/profiles/testing/types/dexterity_content_type_with_two_image_fields.xml +++ b/src/plone/app/imagecropping/profiles/testing/types/dexterity_content_type_with_two_image_fields.xml @@ -42,7 +42,6 @@ </schema> </model> - dexterity From 1588cd7a902d4f8a433eefb4e38dc9db3484ad07 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 12 Mar 2014 07:22:04 +0100 Subject: [PATCH 13/37] use latest seleium --- test-4.2.cfg | 1 + test-4.3.cfg | 3 +++ 2 files changed, 4 insertions(+) diff --git a/test-4.2.cfg b/test-4.2.cfg index 61c06f6c..67bde107 100644 --- a/test-4.2.cfg +++ b/test-4.2.cfg @@ -5,3 +5,4 @@ package-extras = [test] [versions] plone.namedfile = 2.0.1 +selenium = 2.40.0 diff --git a/test-4.3.cfg b/test-4.3.cfg index a67d3d88..f8a8d299 100644 --- a/test-4.3.cfg +++ b/test-4.3.cfg @@ -2,3 +2,6 @@ extends = https://raw.github.com/collective/buildout.plonetest/master/test-4.3.x.cfg package-name = plone.app.imagecropping package-extras = [test] + +[versions] +selenium = 2.40.0 From 791f64a1502bedda2a81339da82dcae6a059e601 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Wed, 12 Mar 2014 07:35:13 +0100 Subject: [PATCH 14/37] specify version pinned egg plone.namedfile as test dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index ab4fe40a..382750a0 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,7 @@ 'test': [ 'plone.app.testing[robot]>=4.2.2', 'plone.app.dexterity', + 'plone.namedfile>=2.0.1' ], }, entry_points=""" From f44cce0d683b3dbf7d66061144b6ef9712ed4705 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Thu, 13 Mar 2014 20:15:12 +0100 Subject: [PATCH 15/37] added test for plone 5.0 --- .travis.yml | 16 ++++------------ travis.cfg => test-5.0.cfg | 10 +++------- 2 files changed, 7 insertions(+), 19 deletions(-) rename travis.cfg => test-5.0.cfg (51%) diff --git a/.travis.yml b/.travis.yml index 25238db9..9742c434 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,21 +3,13 @@ python: 2.7 env: - PLONE_VERSION=4.2 - PLONE_VERSION=4.3 -matrix: - include: - - python: 2.6 - env: PLONE_VERSION=4.1 - allow_failures: - - python: 2.6 - env: PLONE_VERSION=4.1 before_install: - sudo apt-get install -y libjpeg-dev libgif-dev libpng-dev libz-dev -install: - - sed -ie "s#travis-4.x.cfg#travis-$PLONE_VERSION.x.cfg#" travis.cfg - mkdir -p buildout-cache/downloads - - python bootstrap.py -c travis.cfg - - bin/buildout -c travis.cfg annotate - - bin/buildout -c travis.cfg -N -q + - mkdir -p buildout-cache/eggs +install: + - python bootstrap.py -c test-$PLONE_VERSION.cfg + - bin/buildout -t 5 -Nc test-$PLONE_VERSION.cfg before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" diff --git a/travis.cfg b/test-5.0.cfg similarity index 51% rename from travis.cfg rename to test-5.0.cfg index 4547045a..12248bbb 100644 --- a/travis.cfg +++ b/test-5.0.cfg @@ -1,11 +1,7 @@ [buildout] -extends = https://raw.github.com/collective/buildout.plonetest/master/travis-4.x.cfg -test-eggs = - decorator - selenium +extends = https://raw.github.com/collective/buildout.plonetest/master/test-5.0.x.cfg package-name = plone.app.imagecropping package-extras = [test] -allow-hosts += - code.google.com - robotframework.googlecode.com +[versions] +selenium = 2.40.0 From c6f57c1e43e0145108abb1e8fad9fb3de7b7e6e9 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 06:38:29 +0100 Subject: [PATCH 16/37] buildout and egg hygiene --- buildout.cfg | 5 ++--- setup.py | 15 ++++++++++----- src/plone/app/imagecropping/__init__.py | 3 --- src/plone/app/imagecropping/configure.zcml | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/buildout.cfg b/buildout.cfg index b7145713..d2e06f88 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -26,7 +26,8 @@ versions = versions [versions] Pillow = 2.3.0 -selenium = 2.40.0 +selenium = 2.40.0 +plone.app.contenttypes = 1.0 [omelette] recipe = collective.recipe.omelette @@ -55,5 +56,3 @@ input = inline: cd - mode = 755 -[versions] -plone.app.contenttypes = 1.0 diff --git a/setup.py b/setup.py index 382750a0..578eb00f 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,17 @@ from setuptools import setup, find_packages import os +import pkg_resources version = '0.1rc3.dev0' + +test_requires = [ + 'plone.app.testing[robot]>=4.2.2', + ] + +if pkg_resources.get_distribution('Products.CMFPlone').version >= '4.2': + test_requires.append('plone.app.dexterity') + long_description = ( open('README.rst').read() + '\n' + @@ -53,11 +62,7 @@ 'Products.CMFPlone>=4.1' ], extras_require={ - 'test': [ - 'plone.app.testing[robot]>=4.2.2', - 'plone.app.dexterity', - 'plone.namedfile>=2.0.1' - ], + 'test': test_requires, }, entry_points=""" # -*- Entry points: -*- diff --git a/src/plone/app/imagecropping/__init__.py b/src/plone/app/imagecropping/__init__.py index 098ed596..c245064e 100644 --- a/src/plone/app/imagecropping/__init__.py +++ b/src/plone/app/imagecropping/__init__.py @@ -14,6 +14,3 @@ imagecroppingMessageFactory = MessageFactory("plone.app.imagecropping") PRODUCT_NAME = PAI_STORAGE_KEY = "plone.app.imagecropping" - -def initialize(context): - """Initializer called when used as a Zope 2 product.""" diff --git a/src/plone/app/imagecropping/configure.zcml b/src/plone/app/imagecropping/configure.zcml index 62241625..910d3df5 100644 --- a/src/plone/app/imagecropping/configure.zcml +++ b/src/plone/app/imagecropping/configure.zcml @@ -6,7 +6,7 @@ xmlns:zcml="http://namespaces.zope.org/zcml" i18n_domain="plone.app.imagecropping"> - + Date: Fri, 14 Mar 2014 06:39:01 +0100 Subject: [PATCH 17/37] remove buildout config which is a duplicate of buildout.cfg --- instance-4.3.cfg | 60 ------------------------------------------------ 1 file changed, 60 deletions(-) delete mode 100644 instance-4.3.cfg diff --git a/instance-4.3.cfg b/instance-4.3.cfg deleted file mode 100644 index cfc15f45..00000000 --- a/instance-4.3.cfg +++ /dev/null @@ -1,60 +0,0 @@ -[buildout] -extensions = mr.developer - -extends = - https://raw.github.com/collective/buildout.plonetest/master/test-4.3.x.cfg - -package-name = plone.app.imagecropping -package-extras = [test] - -#index = http://c.pypi.python.org - -parts += - omelette - i18ndude - rebuild_pot - -eggs += - plone.app.dexterity [grok] - plone.app.contenttypes - plone.reload - -test-eggs += - Products.ATContentTypes [test] - plone.app.contenttypes [test] - plone.app.dexterity [test] - -versions = versions - -[test] -defaults = ['-s', '${buildout:package-name}', '--auto-color', '--auto-progress'] - -[omelette] -recipe = collective.recipe.omelette -eggs = ${instance:eggs} ${buildout:test-eggs} - -[i18ndude] -recipe = zc.recipe.egg -eggs = i18ndude - -[rebuild_pot] -recipe = collective.recipe.template -output = ${buildout:directory}/bin/rebuild_pot -input = inline: - #!/bin/bash - DOMAIN="plone.app.imagecropping" - BASE_PATH=${buildout:directory}/src/plone/app/imagecropping - touch $BASE_PATH/locales/$DOMAIN.pot - ${buildout:directory}/bin/i18ndude rebuild-pot --pot $BASE_PATH/locales/$DOMAIN.pot --create $DOMAIN ${buildout:directory}/src/ - - cd $BASE_PATH - # sync all locales - find locales -maxdepth 1 -mindepth 1 -type d \ - | grep -v .svn \ - | sed -e "s/locales\/\(.*\)$/\1/" \ - | xargs -I % ${buildout:directory}/bin/i18ndude sync --pot $BASE_PATH/locales/$DOMAIN.pot $BASE_PATH/locales/%/LC_MESSAGES/$DOMAIN.po - cd - -mode = 755 - -[versions] -plone.app.collection = 2.0b5 From 81bf404401718c95b047bf31b7d06ddf0733b0c0 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 07:48:37 +0100 Subject: [PATCH 18/37] don't depend on plone.namedfile >= 2.0.1 --- .travis.yml | 13 +++- setup.py | 9 ++- .../app/imagecropping/browser/scaling.py | 73 +++++++++++-------- test-4.2.cfg | 1 - 4 files changed, 61 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9742c434..d1439c50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,19 @@ language: python -python: 2.7 +python: + - 2.6 + - 2.7 env: + - PLONE_VERSION=4.1 - PLONE_VERSION=4.2 - PLONE_VERSION=4.3 +matrix: + exclude: + - python: 2.7 + env: PLONE=4.1 + - python: 2.6 + env: PLONE=4.2 + - python: 2.6 + env: PLONE=4.3 before_install: - sudo apt-get install -y libjpeg-dev libgif-dev libpng-dev libz-dev - mkdir -p buildout-cache/downloads diff --git a/setup.py b/setup.py index 578eb00f..54af7a4d 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,17 @@ from setuptools import setup, find_packages import os import pkg_resources +from distutils.version import LooseVersion -version = '0.1rc3.dev0' - +version = '1.0.dev0' test_requires = [ 'plone.app.testing[robot]>=4.2.2', ] -if pkg_resources.get_distribution('Products.CMFPlone').version >= '4.2': - test_requires.append('plone.app.dexterity') +plone_version = pkg_resources.get_distribution('Products.CMFPlone').version +if LooseVersion(plone_version) >= LooseVersion('4.2'): + test_requires.append('plone.app.contenttypes') long_description = ( open('README.rst').read() diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 79e68ef9..54e64748 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -3,8 +3,10 @@ from plone.app.imagecropping import PAI_STORAGE_KEY from plone.app.imaging.scaling import ImageScaling as BaseImageScaling +import pkg_resources +from distutils.version import LooseVersion -class ImageScaling(BaseImageScaling): +class ScalingOverrides(object): _rescale = True @@ -13,54 +15,67 @@ def modified(self): return super(ImageScaling, self).modified() else: return 1 - + + def need_rescale(self, fieldname, scale): + cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) + if cropped and '%s_%s' % (fieldname, scale) in cropped: + self._rescale = False + else: + self._rescale = True + + + +class ImageScaling(ScalingOverrides, BaseImageScaling): + def scale(self, fieldname=None, scale=None, height=None, width=None, **parameters): - cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) - if cropped and '%s_%s' % (fieldname, scale) in cropped: - self._rescale = False - else: - self._rescale = True + self.need_rescale(fieldname, scale) return super(ImageScaling, self).scale(fieldname, scale, height, width, **parameters) - try: from plone.namedfile.scaling import ImageScaling as NFImageScaling from plone.namedfile.interfaces import IImageScaleTraversable from plone.app.imagecropping.interfaces import IImageCropping + plone_namedfile_version = pkg_resources.get_distribution('plone_namedfile').version + class IImageCroppingScale(IImageScaleTraversable, IImageCropping): pass - class NamedfileImageScaling(NFImageScaling): - - _rescale = True + class NamedfileImageScaling(ScalingOverrides, NFImageScaling): + """ Override plone.namedfile scaling view - def modified(self): - if self._rescale: - return super(NamedfileImageScaling, self).modified() - else: - return 1 + This view checks, if image crops are available and + prevents rescaling in this case. + """ - def scale(self, - fieldname=None, - scale=None, - height=None, - width=None, + if LooseVersion(plone_namedfile_version) >= LooseVersion('2.0.1'): + def scale(self, + fieldname=None, + scale=None, + height=None, + width=None, direction='thumbnail', - **parameters): - cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) - if cropped and '%s_%s' % (fieldname, scale) in cropped: - self._rescale = False - else: - self._rescale = True - return super(NamedfileImageScaling, self).scale( - fieldname, scale, height, width, direction, **parameters) + **parameters): + self.need_rescale(fieldname, scale) + return super(NamedfileImageScaling, self).scale( + fieldname, scale, height, width, direction, **parameters) + else: + def scale(self, + fieldname=None, + scale=None, + height=None, + width=None, + **parameters): + self.need_rescale(fieldname, scale) + return super(NamedfileImageScaling, self).scale( + fieldname, scale, height, width, **parameters) + except ImportError: pass diff --git a/test-4.2.cfg b/test-4.2.cfg index 67bde107..20555b64 100644 --- a/test-4.2.cfg +++ b/test-4.2.cfg @@ -4,5 +4,4 @@ package-name = plone.app.imagecropping package-extras = [test] [versions] -plone.namedfile = 2.0.1 selenium = 2.40.0 From 64054d98e32be017d802432989937aba3586b9da Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 08:09:11 +0100 Subject: [PATCH 19/37] fix test buildouts --- setup.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index 54af7a4d..c5be6625 100644 --- a/setup.py +++ b/setup.py @@ -5,14 +5,6 @@ version = '1.0.dev0' -test_requires = [ - 'plone.app.testing[robot]>=4.2.2', - ] - -plone_version = pkg_resources.get_distribution('Products.CMFPlone').version -if LooseVersion(plone_version) >= LooseVersion('4.2'): - test_requires.append('plone.app.contenttypes') - long_description = ( open('README.rst').read() + '\n' + @@ -33,7 +25,6 @@ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Plone", - "Framework :: Plone :: 4.1", "Framework :: Plone :: 4.2", "Framework :: Plone :: 4.3", "Intended Audience :: End Users/Desktop", @@ -41,7 +32,6 @@ "Operating System :: OS Independent", "Programming Language :: JavaScript", "Programming Language :: Python", - "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Topic :: Software Development :: Libraries :: Python Modules", ], @@ -63,7 +53,10 @@ 'Products.CMFPlone>=4.1' ], extras_require={ - 'test': test_requires, + 'test': [ + 'plone.app.testing[robot]>=4.2.2', + 'plone.app.dexterity', + ], }, entry_points=""" # -*- Entry points: -*- From 7b134d4f12f5e85571a5592ac144e9a7d0566de0 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 08:11:02 +0100 Subject: [PATCH 20/37] reduce travis test variants --- .travis.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index d1439c50..9742c434 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,8 @@ language: python -python: - - 2.6 - - 2.7 +python: 2.7 env: - - PLONE_VERSION=4.1 - PLONE_VERSION=4.2 - PLONE_VERSION=4.3 -matrix: - exclude: - - python: 2.7 - env: PLONE=4.1 - - python: 2.6 - env: PLONE=4.2 - - python: 2.6 - env: PLONE=4.3 before_install: - sudo apt-get install -y libjpeg-dev libgif-dev libpng-dev libz-dev - mkdir -p buildout-cache/downloads From 4fc6094519ad2965185567dda87cc8f1ed898358 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 08:31:42 +0100 Subject: [PATCH 21/37] fixed typo --- .travis.yml | 2 +- src/plone/app/imagecropping/browser/scaling.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9742c434..fffe5c3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - mkdir -p buildout-cache/eggs install: - python bootstrap.py -c test-$PLONE_VERSION.cfg - - bin/buildout -t 5 -Nc test-$PLONE_VERSION.cfg + - bin/buildout -t 10 -Nc test-$PLONE_VERSION.cfg before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 54e64748..9cf5bf75 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -42,7 +42,7 @@ def scale(self, from plone.namedfile.interfaces import IImageScaleTraversable from plone.app.imagecropping.interfaces import IImageCropping - plone_namedfile_version = pkg_resources.get_distribution('plone_namedfile').version + plone_namedfile_version = pkg_resources.get_distribution('plone.namedfile').version class IImageCroppingScale(IImageScaleTraversable, IImageCropping): pass From 79b45a2c14371fae77fabb52a9428e98dd2cd7af Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 08:57:14 +0100 Subject: [PATCH 22/37] reverted over optimization --- .../app/imagecropping/browser/scaling.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 9cf5bf75..459dd8fc 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -6,16 +6,11 @@ import pkg_resources from distutils.version import LooseVersion + class ScalingOverrides(object): _rescale = True - def modified(self): - if self._rescale: - return super(ImageScaling, self).modified() - else: - return 1 - def need_rescale(self, fieldname, scale): cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) if cropped and '%s_%s' % (fieldname, scale) in cropped: @@ -24,9 +19,14 @@ def need_rescale(self, fieldname, scale): self._rescale = True - class ImageScaling(ScalingOverrides, BaseImageScaling): + def modified(self): + if self._rescale: + return super(ImageScaling, self).modified() + else: + return 1 + def scale(self, fieldname=None, scale=None, @@ -54,6 +54,12 @@ class NamedfileImageScaling(ScalingOverrides, NFImageScaling): prevents rescaling in this case. """ + def modified(self): + if self._rescale: + return super(NamedfileImageScaling, self).modified() + else: + return 1 + if LooseVersion(plone_namedfile_version) >= LooseVersion('2.0.1'): def scale(self, fieldname=None, From 02f521ca485f006940e63d390ee247f516617b25 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 10:11:30 +0100 Subject: [PATCH 23/37] added z3c.blobfile to eggs to activate plone.supermodel handler --- test-4.2.cfg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test-4.2.cfg b/test-4.2.cfg index 20555b64..780e9d5c 100644 --- a/test-4.2.cfg +++ b/test-4.2.cfg @@ -5,3 +5,7 @@ package-extras = [test] [versions] selenium = 2.40.0 + +[test] +eggs += + z3c.blobfile From 6120df2c2039317197cf5dc9f3e969ee44d68b53 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 14 Mar 2014 13:53:17 +0100 Subject: [PATCH 24/37] use bugfix version of plone.app.z3cform --- test-4.2.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/test-4.2.cfg b/test-4.2.cfg index 780e9d5c..8ab9bd5c 100644 --- a/test-4.2.cfg +++ b/test-4.2.cfg @@ -5,6 +5,7 @@ package-extras = [test] [versions] selenium = 2.40.0 +plone.app.z3cform = 0.7.0 [test] eggs += From 75ee18b55217224d4b3926db0e0070184fb8105d Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Sat, 15 Mar 2014 06:44:12 +0100 Subject: [PATCH 25/37] remove plone.namedfile switch. we depend on plone.namedfile >= 2.0.1 --- setup.py | 1 + .../app/imagecropping/browser/scaling.py | 35 ++++++------------- test-4.2.cfg | 5 +-- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index c5be6625..39c5ca9c 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,7 @@ 'test': [ 'plone.app.testing[robot]>=4.2.2', 'plone.app.dexterity', + 'plone.namedfile>=2.0.1', ], }, entry_points=""" diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 459dd8fc..5f9d69c5 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -3,9 +3,6 @@ from plone.app.imagecropping import PAI_STORAGE_KEY from plone.app.imaging.scaling import ImageScaling as BaseImageScaling -import pkg_resources -from distutils.version import LooseVersion - class ScalingOverrides(object): @@ -42,7 +39,6 @@ def scale(self, from plone.namedfile.interfaces import IImageScaleTraversable from plone.app.imagecropping.interfaces import IImageCropping - plone_namedfile_version = pkg_resources.get_distribution('plone.namedfile').version class IImageCroppingScale(IImageScaleTraversable, IImageCropping): pass @@ -60,27 +56,16 @@ def modified(self): else: return 1 - if LooseVersion(plone_namedfile_version) >= LooseVersion('2.0.1'): - def scale(self, - fieldname=None, - scale=None, - height=None, - width=None, - direction='thumbnail', - **parameters): - self.need_rescale(fieldname, scale) - return super(NamedfileImageScaling, self).scale( - fieldname, scale, height, width, direction, **parameters) - else: - def scale(self, - fieldname=None, - scale=None, - height=None, - width=None, - **parameters): - self.need_rescale(fieldname, scale) - return super(NamedfileImageScaling, self).scale( - fieldname, scale, height, width, **parameters) + def scale(self, + fieldname=None, + scale=None, + height=None, + width=None, + direction='thumbnail', + **parameters): + self.need_rescale(fieldname, scale) + return super(NamedfileImageScaling, self).scale( + fieldname, scale, height, width, direction, **parameters) except ImportError: diff --git a/test-4.2.cfg b/test-4.2.cfg index 8ab9bd5c..30ecd98e 100644 --- a/test-4.2.cfg +++ b/test-4.2.cfg @@ -5,8 +5,5 @@ package-extras = [test] [versions] selenium = 2.40.0 -plone.app.z3cform = 0.7.0 +plone.namedfile = 2.0.1 -[test] -eggs += - z3c.blobfile From 5d79d9b4607994130789a20a312353ecacf4c5e0 Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Sat, 15 Mar 2014 06:47:14 +0100 Subject: [PATCH 26/37] document changes --- CHANGES.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7220ac80..d028ad38 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,7 +4,8 @@ Changelog 0.1rc3 (unreleased) ------------------- -- Check for plone.namedfile not Dexterity. It can be used seperately +- Check for plone.namedfile not Dexterity. It can be used seperately. + *If plone.namedfile is used it needs to be at least version 2.0.1* [tomgross] - Rename ``imagecropping_keywords.txt`` to ``keywords.robot`` to allow simple @@ -23,6 +24,8 @@ Changelog - Fixed #21 (cropping was reset on modifying image) [tomgross] +- Only test Plone 4.2 and 4.3 with Python 2.7 on Travis-CI + [tomgross] 0.1rc2 (2013-05-03) ------------------- From 4868874af33e197690030218b29bdb96b06a6025 Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Fri, 4 Apr 2014 15:13:43 +0200 Subject: [PATCH 27/37] register customized scale view for all archetypes --- src/plone/app/imagecropping/browser/configure.zcml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/plone/app/imagecropping/browser/configure.zcml b/src/plone/app/imagecropping/browser/configure.zcml index bc3d5042..79036a12 100644 --- a/src/plone/app/imagecropping/browser/configure.zcml +++ b/src/plone/app/imagecropping/browser/configure.zcml @@ -45,21 +45,14 @@ /> - - + Date: Fri, 4 Apr 2014 15:14:52 +0200 Subject: [PATCH 28/37] test scales stored in attributestorage too, and add test for invalidating scales upon resetting the image --- .../app/imagecropping/tests/test_cropping.py | 61 ++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/plone/app/imagecropping/tests/test_cropping.py b/src/plone/app/imagecropping/tests/test_cropping.py index bd019ae8..a5fc4fc0 100644 --- a/src/plone/app/imagecropping/tests/test_cropping.py +++ b/src/plone/app/imagecropping/tests/test_cropping.py @@ -24,6 +24,23 @@ def setUp(self): f = file(join(dirname(tests.__file__), 'plone-logo.png')) self.img.setImage(f) f.close() + + + def _jpegImage(self): + """convert our testimage to jpeg format + and return it's data + """ + + from cStringIO import StringIO + from PIL.Image import open + + img = open(file(join(dirname(tests.__file__), 'plone-logo.png'))) + out = StringIO() + img.save(out, format='JPEG', quality=75) + out.seek(0) + result = out.getvalue() + out.close() + return result def test_image_and_annotation(self): """check that our cropping view is able to store a cropped image @@ -96,16 +113,12 @@ def test_image_formats(self): self.assertEqual(open(croppedData).format, 'PNG', "cropped scale does not have same format as the original") - # create a jpeg image out of the png file: - img = open(file(join(dirname(tests.__file__), 'plone-logo.png'))) - out = StringIO() - img.save(out, format='JPEG', quality=75) - out.seek(0) + # create a jpeg image out of the png file # and test if created scale is jpeg too _createObjectByType('Image', self.portal, 'testjpeg') jpg = self.portal.testjpeg - jpg.setImage(out.getvalue()) + jpg.setImage(self._jpegImage()) org_data = StringIO(jpg.getImage().data) self.assertEqual(open(org_data).format, 'JPEG') @@ -130,11 +143,45 @@ def test_modify_context(self): # store cropped version for thumb and check if the result # is a square now view._crop(fieldname='image', scale='thumb', box=(14, 14, 218, 218)) + + # images accessed via context/@@images/image/thumb + # stored in plone.scale annotation + # see https://github.com/plone/plone.scale/pull/3#issuecomment-28597087 thumb = scales.scale('image', 'thumb') self.failIfEqual(thumb.data, unscaled_thumb.data) + + #images accessed via context/image_thumb + #stored in attribute_storage + thumb_attr = traverse(self.img, 'image_thumb') + self.failIfEqual(thumb_attr.data, unscaled_thumb.data) + + #import pdb;pdb.set_trace() + self.assertEqual((thumb.width, thumb.height), (thumb_attr.width, thumb_attr.height)) + + self.img.setTitle('A new title') self.img.reindexObject() + thumb2 = scales.scale('image', 'thumb') - self.assertEqual(thumb.data, thumb2.data) + self.assertEqual(thumb.data, thumb2.data, 'context/@@images/image/thumb accessor lost cropped scale') + + thumb2_attr = traverse(self.img, 'image_thumb') + self.assertEqual((thumb.width, thumb.height), + (thumb2_attr.width, thumb2_attr.height), + 'context/image_thumb accessor lost cropped scale') + + + + # set a different image, this should invalidate scales + self.img.setImage(self._jpegImage()) + + jpeg_thumb_attr = traverse(self.img, 'image_thumb') + self.failIfEqual((jpeg_thumb_attr.width, jpeg_thumb_attr.height), + (128, 128), + 'context/image_thumb returns old cropped scale after setting a new image') + + jpeg_thumb = scales.scale('image', 'thumb') + self.failIfEqual((jpeg_thumb.width, jpeg_thumb.height), (128, 128), + 'context/@@images/image/thumb returns old cropped scale after setting a new image') From bf34b7db076e50f376b6fc20fbdc7b14aaa8833b Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Thu, 15 May 2014 13:20:54 +0200 Subject: [PATCH 29/37] remove scales information, if image changes --- .../app/imagecropping/browser/configure.zcml | 2 +- src/plone/app/imagecropping/configure.zcml | 3 +++ src/plone/app/imagecropping/utils.py | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/plone/app/imagecropping/browser/configure.zcml b/src/plone/app/imagecropping/browser/configure.zcml index 79036a12..6a39172e 100644 --- a/src/plone/app/imagecropping/browser/configure.zcml +++ b/src/plone/app/imagecropping/browser/configure.zcml @@ -46,7 +46,7 @@ + + diff --git a/src/plone/app/imagecropping/utils.py b/src/plone/app/imagecropping/utils.py index 203acbd2..e1fd31cf 100644 --- a/src/plone/app/imagecropping/utils.py +++ b/src/plone/app/imagecropping/utils.py @@ -4,15 +4,23 @@ from Products.ATContentTypes.interfaces.interfaces import IATContentType from Products.Archetypes.interfaces.field import IImageField from ZODB.blob import Blob +from plone.app.blob.config import blobScalesAttr from plone.app.blob.interfaces import IBlobImageField +from plone.app.imagecropping import PAI_STORAGE_KEY +from plone.app.imagecropping.interfaces import IImageCropping from plone.app.imagecropping.interfaces import IImageCroppingUtils from plone.app.imaging.interfaces import IImageScaleHandler +from plone.app.imaging.traverse import ImageTraverser as BaseImageTraverser from plone.app.imaging.utils import getAllowedSizes from plone.scale.scale import scaleImage from plone.scale.storage import AnnotationStorage + +from zope.annotation.interfaces import IAnnotations from zope.component import adapts from zope.interface import implements from zope.interface.declarations import providedBy +from zope.publisher.interfaces import IRequest + import time @@ -204,3 +212,14 @@ def crop_factory(fieldname, **parameters): width=w, height=h, ) + + +class ImageTraverser(BaseImageTraverser): + + adapts(IImageCropping, IRequest) + + def publishTraverse(self, request, name): + # remove scales information, if image has changed + if not hasattr(aq_base(self.context), blobScalesAttr): + del IAnnotations(self.context)[PAI_STORAGE_KEY] + return super(ImageTraverser, self).publishTraverse(request, name) From d60b1e2d61365bfff3777bbcc8ee60b45ab0713c Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 16 May 2014 16:37:25 +0200 Subject: [PATCH 30/37] fix situation, where no crop information is available --- .gitignore | 1 + src/plone/app/imagecropping/utils.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 18256b14..f11ce7d9 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ lib # Local development local.cfg +*.swp diff --git a/src/plone/app/imagecropping/utils.py b/src/plone/app/imagecropping/utils.py index e1fd31cf..617a0285 100644 --- a/src/plone/app/imagecropping/utils.py +++ b/src/plone/app/imagecropping/utils.py @@ -220,6 +220,7 @@ class ImageTraverser(BaseImageTraverser): def publishTraverse(self, request, name): # remove scales information, if image has changed - if not hasattr(aq_base(self.context), blobScalesAttr): - del IAnnotations(self.context)[PAI_STORAGE_KEY] + if ((not hasattr(aq_base(self.context), blobScalesAttr)) and + (PAI_STORAGE_KEY in IAnnotations(self.context))): + del IAnnotations(self.context)[PAI_STORAGE_KEY] return super(ImageTraverser, self).publishTraverse(request, name) From bc1001cf154c30acd90813c06b4d44ada6f3ec81 Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Mon, 19 May 2014 10:16:10 +0200 Subject: [PATCH 31/37] remove todo --- src/plone/app/imagecropping/browser/editor.pt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plone/app/imagecropping/browser/editor.pt b/src/plone/app/imagecropping/browser/editor.pt index 8d4eacd2..7dc2ffb1 100644 --- a/src/plone/app/imagecropping/browser/editor.pt +++ b/src/plone/app/imagecropping/browser/editor.pt @@ -30,7 +30,6 @@ This operation is non-destructive which means, your original image data doesn't get lost when cropping a scale. By removing a cropping area, the scale gets back to its default functionality: resize, no crop.

- xxx check if self/scales is empty and show a message in this case
Date: Mon, 19 May 2014 13:19:53 +0200 Subject: [PATCH 33/37] our test type needs this interface so NamedfileImageScaling is looked up for `@@images` --- CHANGES.txt | 2 +- src/plone/app/imagecropping/testing.zcml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4b21df48..5e6da55c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,7 +30,7 @@ Changelog [bogdangi] - Fixed #21 (cropping was reset on modifying image) - [tomgross] + [tomgross, fRiSi] - Only test Plone 4.2 and 4.3 with Python 2.7 on Travis-CI [tomgross] diff --git a/src/plone/app/imagecropping/testing.zcml b/src/plone/app/imagecropping/testing.zcml index a3aab033..f8d0a9f7 100644 --- a/src/plone/app/imagecropping/testing.zcml +++ b/src/plone/app/imagecropping/testing.zcml @@ -15,6 +15,7 @@ + From 172bfb45348305ee8d799b67455a3e5653a1a92d Mon Sep 17 00:00:00 2001 From: Tom Gross Date: Fri, 30 May 2014 15:43:21 +0200 Subject: [PATCH 34/37] removed unneeded interface --- src/plone/app/imagecropping/testing.zcml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plone/app/imagecropping/testing.zcml b/src/plone/app/imagecropping/testing.zcml index f8d0a9f7..012692e1 100644 --- a/src/plone/app/imagecropping/testing.zcml +++ b/src/plone/app/imagecropping/testing.zcml @@ -14,7 +14,6 @@ /> - From 729c0c5eff39a88b5cf7e43af902eeeec536b04c Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Fri, 30 May 2014 15:24:30 +0200 Subject: [PATCH 35/37] fix indentation --- src/plone/app/imagecropping/utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plone/app/imagecropping/utils.py b/src/plone/app/imagecropping/utils.py index 617a0285..131282ae 100644 --- a/src/plone/app/imagecropping/utils.py +++ b/src/plone/app/imagecropping/utils.py @@ -216,11 +216,11 @@ def crop_factory(fieldname, **parameters): class ImageTraverser(BaseImageTraverser): - adapts(IImageCropping, IRequest) - - def publishTraverse(self, request, name): - # remove scales information, if image has changed - if ((not hasattr(aq_base(self.context), blobScalesAttr)) and - (PAI_STORAGE_KEY in IAnnotations(self.context))): - del IAnnotations(self.context)[PAI_STORAGE_KEY] - return super(ImageTraverser, self).publishTraverse(request, name) + adapts(IImageCropping, IRequest) + + def publishTraverse(self, request, name): + # remove scales information, if image has changed + if ((not hasattr(aq_base(self.context), blobScalesAttr)) and + (PAI_STORAGE_KEY in IAnnotations(self.context))): + del IAnnotations(self.context)[PAI_STORAGE_KEY] + return super(ImageTraverser, self).publishTraverse(request, name) From 9e0d4b0d410f6b0abff5775efb54cf9d5db08081 Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Fri, 30 May 2014 15:29:31 +0200 Subject: [PATCH 36/37] add documenation and try to use meaningful names --- .../app/imagecropping/browser/scaling.py | 37 ++++++++++++++----- src/plone/app/imagecropping/utils.py | 4 ++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/plone/app/imagecropping/browser/scaling.py b/src/plone/app/imagecropping/browser/scaling.py index 5f9d69c5..9d5660fe 100644 --- a/src/plone/app/imagecropping/browser/scaling.py +++ b/src/plone/app/imagecropping/browser/scaling.py @@ -6,20 +6,33 @@ class ScalingOverrides(object): - _rescale = True - - def need_rescale(self, fieldname, scale): + _allow_rescale = True + + def _need_rescale(self, fieldname, scale): + """if we've got a cropping annotation for the given fieldname and scale, + set self._rescale to False, to prevent plone.app.imaging traverser to + overwrite our cropped scale + + since the self.modified() method does not know about the currently + requested scale name, we need to use the _rescale property + """ cropped = IAnnotations(self.context).get(PAI_STORAGE_KEY) if cropped and '%s_%s' % (fieldname, scale) in cropped: - self._rescale = False + self._allow_rescale = False else: - self._rescale = True + self._allow_rescale = True class ImageScaling(ScalingOverrides, BaseImageScaling): def modified(self): - if self._rescale: + """we overwrite the default method that would return the modification + time of the context, + to return a way back modification time in case the currently requested + scale is a cropped scale. (so plone.scale does not create a new scale + w/o cropping information + """ + if self._allow_rescale: return super(ImageScaling, self).modified() else: return 1 @@ -30,7 +43,7 @@ def scale(self, height=None, width=None, **parameters): - self.need_rescale(fieldname, scale) + self._need_rescale(fieldname, scale) return super(ImageScaling, self).scale(fieldname, scale, height, width, **parameters) try: @@ -51,7 +64,13 @@ class NamedfileImageScaling(ScalingOverrides, NFImageScaling): """ def modified(self): - if self._rescale: + """we overwrite the default method that would return the modification + time of the context, + to return a way back modification time in case the currently requested + scale is a cropped scale. (so plone.scale does not create a new scale + w/o cropping information + """ + if self._allow_rescale: return super(NamedfileImageScaling, self).modified() else: return 1 @@ -63,7 +82,7 @@ def scale(self, width=None, direction='thumbnail', **parameters): - self.need_rescale(fieldname, scale) + self._need_rescale(fieldname, scale) return super(NamedfileImageScaling, self).scale( fieldname, scale, height, width, direction, **parameters) diff --git a/src/plone/app/imagecropping/utils.py b/src/plone/app/imagecropping/utils.py index 131282ae..a5b5bd45 100644 --- a/src/plone/app/imagecropping/utils.py +++ b/src/plone/app/imagecropping/utils.py @@ -215,6 +215,10 @@ def crop_factory(fieldname, **parameters): class ImageTraverser(BaseImageTraverser): + """extend the standard image traverser to remove our cropping annotations + (if present) in case the original image has been removed/replaced + (no blobScalesAttr) + """ adapts(IImageCropping, IRequest) From 110cead7bca4f99eb74d7472a7c8a8eb4fa46d84 Mon Sep 17 00:00:00 2001 From: Harald Friessnegger Date: Fri, 30 May 2014 15:35:03 +0200 Subject: [PATCH 37/37] remove redundant changelog --- CHANGES.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5e6da55c..5fdf1e3a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,6 @@ Changelog in the controlpanel if there are no croppable scales to show. (previously this caused a `SiteError`) [fRiSi] - -- Check for plone.namedfile not Dexterity. It can be used seperately - [tom_gross] - Rename ``imagecropping_keywords.txt`` to ``keywords.robot`` to allow simple reusage in ``plone.app.robotframework``.