From 89d0e9c9481d610afe2e0aaa4b99d950091129c4 Mon Sep 17 00:00:00 2001 From: Mar Date: Fri, 17 Oct 2025 11:20:41 +0200 Subject: [PATCH 01/14] Remove "Automatic scaling" (use_automatic_scaling) option from image plugin, and purge ocurrences --- djangocms_frontend/contrib/image/cms_plugins.py | 2 +- djangocms_frontend/contrib/image/forms.py | 10 ---------- djangocms_frontend/contrib/image/models.py | 2 +- djangocms_frontend/management/bootstrap4_migration.py | 2 +- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/djangocms_frontend/contrib/image/cms_plugins.py b/djangocms_frontend/contrib/image/cms_plugins.py index dec1d97a..a87b6ad0 100644 --- a/djangocms_frontend/contrib/image/cms_plugins.py +++ b/djangocms_frontend/contrib/image/cms_plugins.py @@ -75,7 +75,7 @@ class ImagePlugin( { "classes": ("collapse",), "fields": ( - ("use_automatic_scaling", "use_no_cropping"), + ("use_no_cropping"), ("use_crop", "use_upscale"), "thumbnail_options", ), diff --git a/djangocms_frontend/contrib/image/forms.py b/djangocms_frontend/contrib/image/forms.py index 55e6df29..491fb1ea 100644 --- a/djangocms_frontend/contrib/image/forms.py +++ b/djangocms_frontend/contrib/image/forms.py @@ -74,7 +74,6 @@ class Meta: "height", "alignment", "link_attributes", - "use_automatic_scaling", "use_crop", "use_no_cropping", "use_upscale", @@ -140,13 +139,6 @@ class Meta: help_text=_("Attributes apply to the link."), ) - # cropping models - # active per default - use_automatic_scaling = forms.BooleanField( - label=_("Automatic scaling"), - required=False, - help_text=_("Uses the placeholder dimensions to automatically calculate the size."), - ) # ignores all other cropping options # throws validation error if other cropping options are selected use_no_cropping = forms.BooleanField( @@ -215,8 +207,6 @@ def clean(self): # certain cropping options do not work together, the following # list defines the disallowed options used in the ``clean`` method invalid_option_pairs = [ - ("use_automatic_scaling", "use_no_cropping"), - ("use_automatic_scaling", "thumbnail_options"), ("use_no_cropping", "use_crop"), ("use_no_cropping", "use_upscale"), ("use_no_cropping", "thumbnail_options"), diff --git a/djangocms_frontend/contrib/image/models.py b/djangocms_frontend/contrib/image/models.py index c09966ea..ee159f62 100644 --- a/djangocms_frontend/contrib/image/models.py +++ b/djangocms_frontend/contrib/image/models.py @@ -24,7 +24,7 @@ def get_size(self, width=None, height=None): height = thumbnail_options.height crop = thumbnail_options.crop upscale = thumbnail_options.upscale - elif not getattr(self, "use_automatic_scaling", None): + else: width = getattr(self, "width", None) height = getattr(self, "height", None) diff --git a/djangocms_frontend/management/bootstrap4_migration.py b/djangocms_frontend/management/bootstrap4_migration.py index dc594a6b..713747ee 100644 --- a/djangocms_frontend/management/bootstrap4_migration.py +++ b/djangocms_frontend/management/bootstrap4_migration.py @@ -219,7 +219,7 @@ def breakpoints(props): "link_url -> external_link", "link_target", "link_attributes", - "use_automatic_scaling", + "use_automatic_scaling", # ask "use_no_cropping", "use_crop", "use_upscale", From d2fec4e2ab76ef68966bfbf3d38b9ab77f086343 Mon Sep 17 00:00:00 2001 From: Mar Date: Fri, 17 Oct 2025 11:24:00 +0200 Subject: [PATCH 02/14] Remove "Use original image" (use_no_cropping) option from image plugin, and purge ocurrences --- djangocms_frontend/contrib/image/cms_plugins.py | 1 - djangocms_frontend/contrib/image/forms.py | 11 ----------- djangocms_frontend/contrib/image/image_save.py | 2 +- djangocms_frontend/contrib/image/models.py | 3 --- 4 files changed, 1 insertion(+), 16 deletions(-) diff --git a/djangocms_frontend/contrib/image/cms_plugins.py b/djangocms_frontend/contrib/image/cms_plugins.py index a87b6ad0..e3daeb7e 100644 --- a/djangocms_frontend/contrib/image/cms_plugins.py +++ b/djangocms_frontend/contrib/image/cms_plugins.py @@ -75,7 +75,6 @@ class ImagePlugin( { "classes": ("collapse",), "fields": ( - ("use_no_cropping"), ("use_crop", "use_upscale"), "thumbnail_options", ), diff --git a/djangocms_frontend/contrib/image/forms.py b/djangocms_frontend/contrib/image/forms.py index 491fb1ea..dd4127f5 100644 --- a/djangocms_frontend/contrib/image/forms.py +++ b/djangocms_frontend/contrib/image/forms.py @@ -75,7 +75,6 @@ class Meta: "alignment", "link_attributes", "use_crop", - "use_no_cropping", "use_upscale", "use_responsive_image", "thumbnail_options", @@ -139,13 +138,6 @@ class Meta: help_text=_("Attributes apply to the link."), ) - # ignores all other cropping options - # throws validation error if other cropping options are selected - use_no_cropping = forms.BooleanField( - label=_("Use original image"), - required=False, - help_text=_("Outputs the raw image without cropping."), - ) # upscale and crop work together # throws validation error if other cropping options are selected use_crop = forms.BooleanField( @@ -207,9 +199,6 @@ def clean(self): # certain cropping options do not work together, the following # list defines the disallowed options used in the ``clean`` method invalid_option_pairs = [ - ("use_no_cropping", "use_crop"), - ("use_no_cropping", "use_upscale"), - ("use_no_cropping", "thumbnail_options"), ("thumbnail_options", "use_crop"), ("thumbnail_options", "use_upscale"), ] diff --git a/djangocms_frontend/contrib/image/image_save.py b/djangocms_frontend/contrib/image/image_save.py index e3659e47..e761f0ae 100644 --- a/djangocms_frontend/contrib/image/image_save.py +++ b/djangocms_frontend/contrib/image/image_save.py @@ -29,7 +29,7 @@ def create_image_plugin(filename, file, parent_plugin, **kwargs): img.config.update( { "picture": {"pk": image_obj.pk, "model": "filer.image"}, - "use_no_cropping": True, + "use_no_cropping": True, # ask } ) add_plugin(parent_plugin.placeholder, img) diff --git a/djangocms_frontend/contrib/image/models.py b/djangocms_frontend/contrib/image/models.py index ee159f62..9e1dd45a 100644 --- a/djangocms_frontend/contrib/image/models.py +++ b/djangocms_frontend/contrib/image/models.py @@ -120,9 +120,6 @@ def img_src(self): # in this case we want to return an empty string to avoid #69 elif not self.picture: return "" - # return the original, unmodified image - elif self.use_no_cropping: - return self.rel_image.url if self.rel_image else "" picture_options = self.get_size( width=self.width or 0, From ff78be4be5f17b067d20de20c01cba987be27b59 Mon Sep 17 00:00:00 2001 From: Mar Date: Fri, 17 Oct 2025 11:36:32 +0200 Subject: [PATCH 03/14] Move over widht and height attributes to the cropping tab, and rename to Dimensions --- djangocms_frontend/contrib/image/cms_plugins.py | 4 ++-- djangocms_frontend/contrib/image/forms.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/djangocms_frontend/contrib/image/cms_plugins.py b/djangocms_frontend/contrib/image/cms_plugins.py index e3daeb7e..d0228e22 100644 --- a/djangocms_frontend/contrib/image/cms_plugins.py +++ b/djangocms_frontend/contrib/image/cms_plugins.py @@ -65,16 +65,16 @@ class ImagePlugin( "classes": ("collapse",), "fields": ( "use_responsive_image", - ("width", "height"), "alignment", ), }, ), ( - _("Cropping"), + _("Dimensions"), { "classes": ("collapse",), "fields": ( + ("width", "height"), ("use_crop", "use_upscale"), "thumbnail_options", ), diff --git a/djangocms_frontend/contrib/image/forms.py b/djangocms_frontend/contrib/image/forms.py index dd4127f5..a8b01dc3 100644 --- a/djangocms_frontend/contrib/image/forms.py +++ b/djangocms_frontend/contrib/image/forms.py @@ -118,13 +118,19 @@ class Meta: label=_("Width"), required=False, min_value=1, - help_text=_('The image width as number in pixels. Example: "720" and not "720px".'), + help_text=_( + 'The image width as number in pixels (eg, "720" and not "720px"). ' + ), ) height = forms.IntegerField( label=_("Height"), required=False, min_value=1, - help_text=_('The image height as number in pixels. Example: "720" and not "720px".'), + help_text=_( + 'The image height as number in pixels (eg, "720" and not "720px"). ' + "Note: if width is set, height will be calculated automatically to preserve aspect ratio. " + "In case of cropping, then both width and height are applied as given." + ), ) alignment = forms.ChoiceField( label=_("Alignment"), From 515277a8b49f0f3a28706a43d0bf160460d8f961 Mon Sep 17 00:00:00 2001 From: Mar Date: Fri, 17 Oct 2025 12:20:05 +0200 Subject: [PATCH 04/14] Better help text for cropping and upscaling --- djangocms_frontend/contrib/image/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djangocms_frontend/contrib/image/forms.py b/djangocms_frontend/contrib/image/forms.py index a8b01dc3..f8255a80 100644 --- a/djangocms_frontend/contrib/image/forms.py +++ b/djangocms_frontend/contrib/image/forms.py @@ -149,12 +149,12 @@ class Meta: use_crop = forms.BooleanField( label=_("Crop image"), required=False, - help_text=_("Crops the image according to the thumbnail settings provided in the template."), + help_text=_("Crops the image rather than resizing"), ) use_upscale = forms.BooleanField( label=_("Upscale image"), required=False, - help_text=_("Upscales the image to the size of the thumbnail settings in the template."), + help_text=_("Allows the image to be upscaled beyond its original size."), ) use_responsive_image = forms.ChoiceField( label=_("Use responsive image"), From f8f699d2f1094ec8137ba5ddfb7f19e1628ca682 Mon Sep 17 00:00:00 2001 From: Mar Date: Mon, 20 Oct 2025 10:52:19 +0200 Subject: [PATCH 05/14] Update test to use new invalid option pair --- tests/image/test_plugins.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/image/test_plugins.py b/tests/image/test_plugins.py index 01697e5d..3ccf33d9 100644 --- a/tests/image/test_plugins.py +++ b/tests/image/test_plugins.py @@ -77,10 +77,11 @@ def test_image_form(self): self.assertTrue(form.is_valid(), f"{form.__class__.__name__}:form errors: {form.errors}") self.assertEqual(form.cleaned_data["config"]["use_responsive_image"], "yes") + # Test invalid option pair request.POST.update( { - "use_automatic_scaling": True, - "use_no_cropping": True, + "thumbnail_options": True, + "use_crop": True, } ) form = ImageForm(request.POST) From 1eeebcab6aeccd65992d68c038a4fc76dad421b8 Mon Sep 17 00:00:00 2001 From: Mar Date: Tue, 21 Oct 2025 10:08:30 +0200 Subject: [PATCH 06/14] Dummy commit to retrigger CI --- tests/image/test_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/image/test_plugins.py b/tests/image/test_plugins.py index 3ccf33d9..8ebf6f94 100644 --- a/tests/image/test_plugins.py +++ b/tests/image/test_plugins.py @@ -77,7 +77,7 @@ def test_image_form(self): self.assertTrue(form.is_valid(), f"{form.__class__.__name__}:form errors: {form.errors}") self.assertEqual(form.cleaned_data["config"]["use_responsive_image"], "yes") - # Test invalid option pair + # Test invalid option pair request.POST.update( { "thumbnail_options": True, From 9952680846d1f5ace1d75ab58ab7b19d05160a74 Mon Sep 17 00:00:00 2001 From: Mar Date: Wed, 22 Oct 2025 13:14:20 +0200 Subject: [PATCH 07/14] skip image processing when there's no width or height defined --- djangocms_frontend/contrib/image/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/djangocms_frontend/contrib/image/models.py b/djangocms_frontend/contrib/image/models.py index 9e1dd45a..b9b8b52c 100644 --- a/djangocms_frontend/contrib/image/models.py +++ b/djangocms_frontend/contrib/image/models.py @@ -120,6 +120,9 @@ def img_src(self): # in this case we want to return an empty string to avoid #69 elif not self.picture: return "" + # skip image processing when there's no width or height defined + elif not self.width and not self.height: + return self.rel_image.url if self.rel_image else "" picture_options = self.get_size( width=self.width or 0, From c838929ceb2d6aa7b401149e7ed7e748a182778c Mon Sep 17 00:00:00 2001 From: Mar Date: Wed, 22 Oct 2025 13:16:11 +0200 Subject: [PATCH 08/14] Remove #ask --- djangocms_frontend/management/bootstrap4_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_frontend/management/bootstrap4_migration.py b/djangocms_frontend/management/bootstrap4_migration.py index 713747ee..dc594a6b 100644 --- a/djangocms_frontend/management/bootstrap4_migration.py +++ b/djangocms_frontend/management/bootstrap4_migration.py @@ -219,7 +219,7 @@ def breakpoints(props): "link_url -> external_link", "link_target", "link_attributes", - "use_automatic_scaling", # ask + "use_automatic_scaling", "use_no_cropping", "use_crop", "use_upscale", From d7a936373fe832cd3d6fd4d7cc74c53b02faae52 Mon Sep 17 00:00:00 2001 From: Mar Date: Wed, 22 Oct 2025 13:19:21 +0200 Subject: [PATCH 09/14] Rename Dimensions tab title to Sizing --- djangocms_frontend/contrib/image/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_frontend/contrib/image/cms_plugins.py b/djangocms_frontend/contrib/image/cms_plugins.py index d0228e22..55d68ebe 100644 --- a/djangocms_frontend/contrib/image/cms_plugins.py +++ b/djangocms_frontend/contrib/image/cms_plugins.py @@ -70,7 +70,7 @@ class ImagePlugin( }, ), ( - _("Dimensions"), + _("Sizing"), { "classes": ("collapse",), "fields": ( From fba83f9e0076fbb38bfd4374d58d410fe819b815 Mon Sep 17 00:00:00 2001 From: Mar Date: Thu, 23 Oct 2025 09:54:28 +0200 Subject: [PATCH 10/14] Remove #ask --- djangocms_frontend/contrib/image/image_save.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_frontend/contrib/image/image_save.py b/djangocms_frontend/contrib/image/image_save.py index e761f0ae..e3659e47 100644 --- a/djangocms_frontend/contrib/image/image_save.py +++ b/djangocms_frontend/contrib/image/image_save.py @@ -29,7 +29,7 @@ def create_image_plugin(filename, file, parent_plugin, **kwargs): img.config.update( { "picture": {"pk": image_obj.pk, "model": "filer.image"}, - "use_no_cropping": True, # ask + "use_no_cropping": True, } ) add_plugin(parent_plugin.placeholder, img) From 32eb54295783f7468e305bff2aa5b48ea788fdbe Mon Sep 17 00:00:00 2001 From: Mar Date: Thu, 23 Oct 2025 10:54:58 +0200 Subject: [PATCH 11/14] Add test --- tests/image/test_plugins.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/image/test_plugins.py b/tests/image/test_plugins.py index 8ebf6f94..b2cba970 100644 --- a/tests/image/test_plugins.py +++ b/tests/image/test_plugins.py @@ -64,6 +64,50 @@ def test_plugin(self): f'class="img-thumbnail rounded" not found in {response.content.decode("utf-8")}', ) + def test_img_processing(self): + # Image gets resized if user width and height are provided + plugin = add_plugin( + placeholder=self.placeholder, + plugin_type=ImagePlugin.__name__, + language=self.language, + config={ + "picture": {"pk": self.image.id, "model": "filer.Image"}, + "width": 50, + "height": 100, + }, + ) + plugin.initialize_from_form(ImageForm) + plugin.save() + self.publish(self.page, self.language) + + with self.login_user_context(self.superuser): + response = self.client.get(self.request_url) + self.assertEqual(response.status_code, 200) + + self.assertTrue('/test_file.jpg__50x100_q85_subsampling-2.jpg"' in res) + + # Original image is used if NEITHER width nor height are provided + plugin = add_plugin( + placeholder=self.placeholder, + plugin_type=ImagePlugin.__name__, + language=self.language, + config={ + "picture": {"pk": self.image.id, "model": "filer.Image"}, + }, + ) + plugin.initialize_from_form(ImageForm) + plugin.save() + self.publish(self.page, self.language) + + with self.login_user_context(self.superuser): + response = self.client.get(self.request_url) + self.assertEqual(response.status_code, 200) + from pprint import pprint as pp + res = response.content.decode("utf-8") + + self.assertTrue('/test_file.jpg"' in res) + + def test_image_form(self): request = HttpRequest() request.POST = { From afd327d1cf2b06e1b6eba9c78f7611da15745ff7 Mon Sep 17 00:00:00 2001 From: Mar Date: Thu, 23 Oct 2025 11:02:47 +0200 Subject: [PATCH 12/14] Leftover dev litter --- tests/image/test_plugins.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/image/test_plugins.py b/tests/image/test_plugins.py index b2cba970..ac801757 100644 --- a/tests/image/test_plugins.py +++ b/tests/image/test_plugins.py @@ -102,12 +102,10 @@ def test_img_processing(self): with self.login_user_context(self.superuser): response = self.client.get(self.request_url) self.assertEqual(response.status_code, 200) - from pprint import pprint as pp - res = response.content.decode("utf-8") + res = response.content.decode("utf-8") self.assertTrue('/test_file.jpg"' in res) - def test_image_form(self): request = HttpRequest() request.POST = { From 70840784575a80f4ffdd1c2b02e0a924d5a77655 Mon Sep 17 00:00:00 2001 From: Mar Date: Fri, 24 Oct 2025 11:03:39 +0200 Subject: [PATCH 13/14] Fix test asserts --- tests/image/test_plugins.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/image/test_plugins.py b/tests/image/test_plugins.py index ac801757..f202d800 100644 --- a/tests/image/test_plugins.py +++ b/tests/image/test_plugins.py @@ -83,8 +83,7 @@ def test_img_processing(self): with self.login_user_context(self.superuser): response = self.client.get(self.request_url) self.assertEqual(response.status_code, 200) - - self.assertTrue('/test_file.jpg__50x100_q85_subsampling-2.jpg"' in res) + self.assertContains(response, '/test_file.jpg__50x100_q85_subsampling-2.jpg"') # Original image is used if NEITHER width nor height are provided plugin = add_plugin( @@ -102,9 +101,7 @@ def test_img_processing(self): with self.login_user_context(self.superuser): response = self.client.get(self.request_url) self.assertEqual(response.status_code, 200) - - res = response.content.decode("utf-8") - self.assertTrue('/test_file.jpg"' in res) + self.assertContains(response, '/test_file.jpg"') def test_image_form(self): request = HttpRequest() From 31a3e6604e2f3e1eced853fc6da42d8e75b380d3 Mon Sep 17 00:00:00 2001 From: Mar Date: Fri, 24 Oct 2025 11:26:55 +0200 Subject: [PATCH 14/14] Keep checking for use_no_cropping flag, and test case to cover --- djangocms_frontend/contrib/image/models.py | 5 +++-- tests/image/test_plugins.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/djangocms_frontend/contrib/image/models.py b/djangocms_frontend/contrib/image/models.py index b9b8b52c..1213d403 100644 --- a/djangocms_frontend/contrib/image/models.py +++ b/djangocms_frontend/contrib/image/models.py @@ -120,8 +120,9 @@ def img_src(self): # in this case we want to return an empty string to avoid #69 elif not self.picture: return "" - # skip image processing when there's no width or height defined - elif not self.width and not self.height: + # skip image processing when there's no width or height defined, + # or when legacy use_no_cropping flag is present + elif getattr(self, 'use_no_cropping', None) or not (self.width or self.height): return self.rel_image.url if self.rel_image else "" picture_options = self.get_size( diff --git a/tests/image/test_plugins.py b/tests/image/test_plugins.py index f202d800..537a3503 100644 --- a/tests/image/test_plugins.py +++ b/tests/image/test_plugins.py @@ -103,6 +103,28 @@ def test_img_processing(self): self.assertEqual(response.status_code, 200) self.assertContains(response, '/test_file.jpg"') + # Original image also used if legacy use_no_cropping flag is present, + # even when there is widht and height + plugin = add_plugin( + placeholder=self.placeholder, + plugin_type=ImagePlugin.__name__, + language=self.language, + config={ + "picture": {"pk": self.image.id, "model": "filer.Image"}, + "width": 50, + "height": 100, + "use_no_cropping": True, + }, + ) + plugin.initialize_from_form(ImageForm) + plugin.save() + self.publish(self.page, self.language) + + with self.login_user_context(self.superuser): + response = self.client.get(self.request_url) + self.assertEqual(response.status_code, 200) + self.assertContains(response, '/test_file.jpg"') + def test_image_form(self): request = HttpRequest() request.POST = {