Skip to content

Commit ddb5255

Browse files
marbrufsbraun
andauthored
feat: Image plugin refactored for simpler size control (#316)
Co-authored-by: Fabian Braun <[email protected]>
1 parent ec1da18 commit ddb5255

File tree

4 files changed

+80
-33
lines changed

4 files changed

+80
-33
lines changed

djangocms_frontend/contrib/image/cms_plugins.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,16 @@ class ImagePlugin(
6565
"classes": ("collapse",),
6666
"fields": (
6767
"use_responsive_image",
68-
("width", "height"),
6968
"alignment",
7069
),
7170
},
7271
),
7372
(
74-
_("Cropping"),
73+
_("Sizing"),
7574
{
7675
"classes": ("collapse",),
7776
"fields": (
78-
("use_automatic_scaling", "use_no_cropping"),
77+
("width", "height"),
7978
("use_crop", "use_upscale"),
8079
"thumbnail_options",
8180
),

djangocms_frontend/contrib/image/forms.py

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ class Meta:
7474
"height",
7575
"alignment",
7676
"link_attributes",
77-
"use_automatic_scaling",
7877
"use_crop",
79-
"use_no_cropping",
8078
"use_upscale",
8179
"use_responsive_image",
8280
"thumbnail_options",
@@ -120,13 +118,19 @@ class Meta:
120118
label=_("Width"),
121119
required=False,
122120
min_value=1,
123-
help_text=_('The image width as number in pixels. Example: "720" and not "720px".'),
121+
help_text=_(
122+
'The image width as number in pixels (eg, "720" and not "720px"). '
123+
),
124124
)
125125
height = forms.IntegerField(
126126
label=_("Height"),
127127
required=False,
128128
min_value=1,
129-
help_text=_('The image height as number in pixels. Example: "720" and not "720px".'),
129+
help_text=_(
130+
'The image height as number in pixels (eg, "720" and not "720px"). '
131+
"Note: if width is set, height will be calculated automatically to preserve aspect ratio. "
132+
"In case of cropping, then both width and height are applied as given."
133+
),
130134
)
131135
alignment = forms.ChoiceField(
132136
label=_("Alignment"),
@@ -140,31 +144,17 @@ class Meta:
140144
help_text=_("Attributes apply to the <b>link</b>."),
141145
)
142146

143-
# cropping models
144-
# active per default
145-
use_automatic_scaling = forms.BooleanField(
146-
label=_("Automatic scaling"),
147-
required=False,
148-
help_text=_("Uses the placeholder dimensions to automatically calculate the size."),
149-
)
150-
# ignores all other cropping options
151-
# throws validation error if other cropping options are selected
152-
use_no_cropping = forms.BooleanField(
153-
label=_("Use original image"),
154-
required=False,
155-
help_text=_("Outputs the raw image without cropping."),
156-
)
157147
# upscale and crop work together
158148
# throws validation error if other cropping options are selected
159149
use_crop = forms.BooleanField(
160150
label=_("Crop image"),
161151
required=False,
162-
help_text=_("Crops the image according to the thumbnail settings provided in the template."),
152+
help_text=_("Crops the image rather than resizing"),
163153
)
164154
use_upscale = forms.BooleanField(
165155
label=_("Upscale image"),
166156
required=False,
167-
help_text=_("Upscales the image to the size of the thumbnail settings in the template."),
157+
help_text=_("Allows the image to be upscaled beyond its original size."),
168158
)
169159
use_responsive_image = forms.ChoiceField(
170160
label=_("Use responsive image"),
@@ -215,11 +205,6 @@ def clean(self):
215205
# certain cropping options do not work together, the following
216206
# list defines the disallowed options used in the ``clean`` method
217207
invalid_option_pairs = [
218-
("use_automatic_scaling", "use_no_cropping"),
219-
("use_automatic_scaling", "thumbnail_options"),
220-
("use_no_cropping", "use_crop"),
221-
("use_no_cropping", "use_upscale"),
222-
("use_no_cropping", "thumbnail_options"),
223208
("thumbnail_options", "use_crop"),
224209
("thumbnail_options", "use_upscale"),
225210
]

djangocms_frontend/contrib/image/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def get_size(self, width=None, height=None):
2424
height = thumbnail_options.height
2525
crop = thumbnail_options.crop
2626
upscale = thumbnail_options.upscale
27-
elif not getattr(self, "use_automatic_scaling", None):
27+
else:
2828
width = getattr(self, "width", None)
2929
height = getattr(self, "height", None)
3030

@@ -120,8 +120,9 @@ def img_src(self):
120120
# in this case we want to return an empty string to avoid #69
121121
elif not self.picture:
122122
return ""
123-
# return the original, unmodified image
124-
elif self.use_no_cropping:
123+
# skip image processing when there's no width or height defined,
124+
# or when legacy use_no_cropping flag is present
125+
elif getattr(self, 'use_no_cropping', None) or not (self.width or self.height):
125126
return self.rel_image.url if self.rel_image else ""
126127

127128
picture_options = self.get_size(

tests/image/test_plugins.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,67 @@ def test_plugin(self):
6464
f'class="img-thumbnail rounded" not found in {response.content.decode("utf-8")}',
6565
)
6666

67+
def test_img_processing(self):
68+
# Image gets resized if user width and height are provided
69+
plugin = add_plugin(
70+
placeholder=self.placeholder,
71+
plugin_type=ImagePlugin.__name__,
72+
language=self.language,
73+
config={
74+
"picture": {"pk": self.image.id, "model": "filer.Image"},
75+
"width": 50,
76+
"height": 100,
77+
},
78+
)
79+
plugin.initialize_from_form(ImageForm)
80+
plugin.save()
81+
self.publish(self.page, self.language)
82+
83+
with self.login_user_context(self.superuser):
84+
response = self.client.get(self.request_url)
85+
self.assertEqual(response.status_code, 200)
86+
self.assertContains(response, '/test_file.jpg__50x100_q85_subsampling-2.jpg"')
87+
88+
# Original image is used if NEITHER width nor height are provided
89+
plugin = add_plugin(
90+
placeholder=self.placeholder,
91+
plugin_type=ImagePlugin.__name__,
92+
language=self.language,
93+
config={
94+
"picture": {"pk": self.image.id, "model": "filer.Image"},
95+
},
96+
)
97+
plugin.initialize_from_form(ImageForm)
98+
plugin.save()
99+
self.publish(self.page, self.language)
100+
101+
with self.login_user_context(self.superuser):
102+
response = self.client.get(self.request_url)
103+
self.assertEqual(response.status_code, 200)
104+
self.assertContains(response, '/test_file.jpg"')
105+
106+
# Original image also used if legacy use_no_cropping flag is present,
107+
# even when there is widht and height
108+
plugin = add_plugin(
109+
placeholder=self.placeholder,
110+
plugin_type=ImagePlugin.__name__,
111+
language=self.language,
112+
config={
113+
"picture": {"pk": self.image.id, "model": "filer.Image"},
114+
"width": 50,
115+
"height": 100,
116+
"use_no_cropping": True,
117+
},
118+
)
119+
plugin.initialize_from_form(ImageForm)
120+
plugin.save()
121+
self.publish(self.page, self.language)
122+
123+
with self.login_user_context(self.superuser):
124+
response = self.client.get(self.request_url)
125+
self.assertEqual(response.status_code, 200)
126+
self.assertContains(response, '/test_file.jpg"')
127+
67128
def test_image_form(self):
68129
request = HttpRequest()
69130
request.POST = {
@@ -77,10 +138,11 @@ def test_image_form(self):
77138
self.assertTrue(form.is_valid(), f"{form.__class__.__name__}:form errors: {form.errors}")
78139
self.assertEqual(form.cleaned_data["config"]["use_responsive_image"], "yes")
79140

141+
# Test invalid option pair
80142
request.POST.update(
81143
{
82-
"use_automatic_scaling": True,
83-
"use_no_cropping": True,
144+
"thumbnail_options": True,
145+
"use_crop": True,
84146
}
85147
)
86148
form = ImageForm(request.POST)

0 commit comments

Comments
 (0)