diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 39f5759295..12b77dfddd 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -11,6 +11,7 @@ Changelog * Highlight broken links to pages and missing documents in rich text (Brady Moe) * Preserve links when copy-pasting rich text content from Wagtail to other tools (Thibaud Colas) * Rich text to contentstate conversion now prioritises more specific rules, to accommodate `

` and `
` elements with attributes (Matt Westcott) + * Added limit image upload size by number of pixels (Thomas Elliott) * Fix: Set `SERVER_PORT` to 443 in `Page.dummy_request()` for HTTPS sites (Sergey Fedoseev) * Fix: Include port number in `Host` header of `Page.dummy_request()` (Sergey Fedoseev) * Fix: Validation error messages in `InlinePanel` no longer count towards `max_num` when disabling the 'add' button (Todd Dembrey, Thibaud Colas) diff --git a/docs/advanced_topics/settings.rst b/docs/advanced_topics/settings.rst index 7a97cea1e9..b4752ef18a 100644 --- a/docs/advanced_topics/settings.rst +++ b/docs/advanced_topics/settings.rst @@ -255,6 +255,12 @@ This setting lets you provide your own image model for use in Wagtail, which mig This setting lets you override the maximum upload size for images (in bytes). If omitted, Wagtail will fall back to using its 10MB default value. +.. code-block:: python + + WAGTAILIMAGES_MAX_IMAGE_PIXELS = 128000000 # i.e. 128 megapixels + +This setting lets you override the maximum number of pixels an image can have. If omitted, Wagtail will fall back to using its 128 megapixels default value. + .. code-block:: python WAGTAILIMAGES_FEATURE_DETECTION_ENABLED = True diff --git a/wagtail/images/fields.py b/wagtail/images/fields.py index 857f0522a0..1873e9d85e 100644 --- a/wagtail/images/fields.py +++ b/wagtail/images/fields.py @@ -2,6 +2,7 @@ import os from django.conf import settings from django.core.exceptions import ValidationError +from django.core.files.images import get_image_dimensions from django.forms.fields import ImageField from django.template.defaultfilters import filesizeformat from django.utils.translation import ugettext_lazy as _ @@ -16,6 +17,7 @@ class WagtailImageField(ImageField): # Get max upload size from settings self.max_upload_size = getattr(settings, 'WAGTAILIMAGES_MAX_UPLOAD_SIZE', 10 * 1024 * 1024) + self.max_image_pixels = getattr(settings, 'WAGTAILIMAGES_MAX_IMAGE_PIXELS', 128 * 1000000) max_upload_size_text = filesizeformat(self.max_upload_size) # Help text @@ -46,6 +48,10 @@ class WagtailImageField(ImageField): "This file is too big (%%s). Maximum filesize %s." ) % max_upload_size_text + self.error_messages['file_too_many_pixels'] = _( + "This file has too many pixels (%%s). Maximum pixels %s." + ) % self.max_image_pixels + self.error_messages['file_too_large_unknown_size'] = _( "This file is too big. Maximum filesize %s." ) % max_upload_size_text @@ -83,11 +89,28 @@ class WagtailImageField(ImageField): filesizeformat(f.size), ), code='file_too_large') + def check_image_pixel_size(self, f): + # Upload pixel size checking can be disabled by setting max upload pixel to None + if self.max_image_pixels is None: + return + + # Check the pixel size + dimensions = get_image_dimensions(f) + if dimensions == (None, None): + return + + pixel_size = dimensions[0] * dimensions[1] + if pixel_size > self.max_image_pixels: + raise ValidationError(self.error_messages['file_too_many_pixels'] % ( + pixel_size + ), code='file_too_many_pixels') + def to_python(self, data): f = super().to_python(data) if f is not None: self.check_image_file_size(f) self.check_image_file_format(f) + self.check_image_pixel_size(f) return f diff --git a/wagtail/images/tests/test_admin_views.py b/wagtail/images/tests/test_admin_views.py index 460c7e39a9..22c32a0a94 100644 --- a/wagtail/images/tests/test_admin_views.py +++ b/wagtail/images/tests/test_admin_views.py @@ -192,6 +192,25 @@ class TestImageAddView(TestCase, WagtailTestUtils): ) ) + @override_settings(WAGTAILIMAGES_MAX_IMAGE_PIXELS=1) + def test_add_too_many_pixels(self): + file_content = get_test_image_file().file.getvalue() + + response = self.post({ + 'title': "Test image", + 'file': SimpleUploadedFile('test.png', file_content), + }) + + # Shouldn't redirect anywhere + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'wagtailimages/images/add.html') + + # The form should have an error + self.assertFormError( + response, 'form', 'file', + 'This file has too many pixels (307200). Maximum pixels 1.' + ) + def test_add_with_collections(self): root_collection = Collection.get_first_root_node() evil_plans_collection = root_collection.add_child(name="Evil plans")