From d02e09e00e63f1ab03d74b1ae2f0336507c3585e Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Wed, 2 Oct 2024 21:35:15 +0100 Subject: [PATCH] Explicitly add image/heic to 'accept' attribute on image fields File upload dialogs (at least on Chrome / Mac) don't count heic as part of image/*, as it's not a web-safe format. --- wagtail/images/fields.py | 20 ++++++++++++++++++-- wagtail/images/tests/test_admin_views.py | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/wagtail/images/fields.py b/wagtail/images/fields.py index 0b6cb85200..78e2852da1 100644 --- a/wagtail/images/fields.py +++ b/wagtail/images/fields.py @@ -6,6 +6,7 @@ from django.conf import settings from django.core.exceptions import ValidationError from django.core.validators import FileExtensionValidator from django.forms.fields import FileField, ImageField +from django.forms.widgets import FileInput from django.template.defaultfilters import filesizeformat from django.utils.translation import gettext_lazy as _ @@ -32,6 +33,8 @@ class WagtailImageField(ImageField): default_validators = [ImageFileExtensionValidator] def __init__(self, *args, **kwargs): + self.allowed_image_extensions = get_allowed_image_extensions() + super().__init__(*args, **kwargs) # Get max upload size from settings @@ -43,8 +46,6 @@ class WagtailImageField(ImageField): ) self.max_upload_size_text = filesizeformat(self.max_upload_size) - self.allowed_image_extensions = get_allowed_image_extensions() - self.supported_formats_text = ", ".join(self.allowed_image_extensions).upper() # Help text @@ -181,3 +182,18 @@ class WagtailImageField(ImageField): self.check_image_pixel_size(f) return f + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + + if ( + isinstance(widget, FileInput) + and "accept" not in widget.attrs + and attrs.get("accept") == "image/*" + and "heic" in self.allowed_image_extensions + ): + # File upload dialogs (at least on Chrome / Mac) don't count heic as part of image/*, as it's not a + # web-safe format, so add it explicitly + attrs["accept"] = "image/*, image/heic" + + return attrs diff --git a/wagtail/images/tests/test_admin_views.py b/wagtail/images/tests/test_admin_views.py index 2614fc2fc8..ddc56aeda6 100644 --- a/wagtail/images/tests/test_admin_views.py +++ b/wagtail/images/tests/test_admin_views.py @@ -1621,6 +1621,10 @@ class TestImageChooserView(WagtailTestUtils, TestCase): # draftail should NOT be a standard JS include on this page self.assertNotIn("wagtailadmin/js/draftail.js", response_json["html"]) + # upload file field should have accept="image/*" + soup = self.get_soup(response_json["html"]) + self.assertEqual(soup.select_one('input[type="file"]').get("accept"), "image/*") + def test_simple_with_collection_nesting(self): root_collection = Collection.get_first_root_node() evil_plans = root_collection.add_child(name="Evil plans") @@ -1630,6 +1634,22 @@ class TestImageChooserView(WagtailTestUtils, TestCase): # "Eviler Plans" should be prefixed with ↳ (↳) and 4 non-breaking spaces. self.assertContains(response, "    ↳ Eviler plans") + @override_settings( + WAGTAILIMAGES_EXTENSIONS=["gif", "jpg", "jpeg", "png", "webp", "avif", "heic"] + ) + def test_upload_field_accepts_heic(self): + response = self.get() + self.assertEqual(response.status_code, 200) + response_json = json.loads(response.content.decode()) + self.assertEqual(response_json["step"], "choose") + self.assertTemplateUsed(response, "wagtailimages/chooser/chooser.html") + + # upload file field should have an explicit 'accept' case for image/heic + soup = self.get_soup(response_json["html"]) + self.assertEqual( + soup.select_one('input[type="file"]').get("accept"), "image/*, image/heic" + ) + def test_choose_permissions(self): # Create group with access to admin and Chooser permission on one Collection, but not another. bakers_group = Group.objects.create(name="Bakers")