diff --git a/src/wagtailmedia/forms.py b/src/wagtailmedia/forms.py index 320aeaee..8df03cec 100644 --- a/src/wagtailmedia/forms.py +++ b/src/wagtailmedia/forms.py @@ -10,7 +10,7 @@ ) from wagtail.models import Collection -from wagtailmedia.models import Media +from wagtailmedia.models import Media, MediaType from wagtailmedia.permissions import permission_policy as media_permission_policy from wagtailmedia.settings import wagtailmedia_settings @@ -29,6 +29,14 @@ def formfield_for_dbfield(db_field, **kwargs): return db_field.formfield(**kwargs) +def format_extensions_for_accept_value(allowed_extensions: list[str]) -> str: + """Returns the specified extensions in a format usable in the `accept=""` attribute of a `FileInput` widget. + This assumes the list of extensions are the bare extensions e.g., `["mp4", "webm"]` and prefixes each extension with + a ".". + """ + return ",".join([f".{e}" for e in allowed_extensions]) + + class BaseMediaForm(BaseCollectionMemberForm): class Meta: widgets = { @@ -42,12 +50,41 @@ class Meta: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + if file_accept_value := self.get_file_accept_value(self.instance.type): + self.fields["file"].widget.attrs["accept"] = file_accept_value + if self.instance.type == "audio": for name in ("width", "height"): # these fields might be editable=False so verify before accessing if name in self.fields: del self.fields[name] + @staticmethod + def get_file_accept_value(media_type: MediaType) -> str | None: + """Dynamically set the `accept` attribute on the file input based on the media type. If allowed extensions have + been configured in settings, this will restrict the file input to only those extensions. Otherwise, it will + fall back to allowing all video or audio file types. + See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/file#unique_file_type_specifiers + for more information on the `accept` attribute. + """ + + if media_type == MediaType.VIDEO: + return ( + format_extensions_for_accept_value( + wagtailmedia_settings.VIDEO_EXTENSIONS + ) + or "video/*" + ) + elif media_type == MediaType.AUDIO: + return ( + format_extensions_for_accept_value( + wagtailmedia_settings.AUDIO_EXTENSIONS + ) + or "audio/*" + ) + + return None + def get_media_base_form(): base_form_override = wagtailmedia_settings.MEDIA_FORM_BASE diff --git a/tests/test_form.py b/tests/test_form.py new file mode 100644 index 00000000..ded7a396 --- /dev/null +++ b/tests/test_form.py @@ -0,0 +1,44 @@ +from django.test import TestCase, override_settings + +from wagtailmedia.forms import format_extensions_for_accept_value, get_media_form +from wagtailmedia.models import MediaType, get_media_model +from wagtailmedia.settings import wagtailmedia_settings + + +class TestForm(TestCase): + def test_file_input_accept_attribute(self): + """Tests that the file input widget only accepts the default file extensions per media type.""" + Media = get_media_model() + MediaForm = get_media_form(Media) + media_type_to_extensions = { + MediaType.VIDEO: wagtailmedia_settings.VIDEO_EXTENSIONS, + MediaType.AUDIO: wagtailmedia_settings.AUDIO_EXTENSIONS, + } + for media_type, extensions in media_type_to_extensions.items(): + media = Media(type=media_type) + form = MediaForm(instance=media) + + self.assertIn( + f'accept="{format_extensions_for_accept_value(extensions)}"', + form["file"].as_widget(), + ) + + @override_settings(WAGTAILMEDIA={"VIDEO_EXTENSIONS": [], "AUDIO_EXTENSIONS": []}) + def test_file_input_accept_attribute_all_extensions_allowed(self): + """Tests that if `VIDEO_EXTENSIONS` and `AUDIO_EXTENSIONS` are set to empty lists, that the accept attribute + allows all file extensions for the given media type. + """ + Media = get_media_model() + MediaForm = get_media_form(Media) + media_type_to_extensions = { + MediaType.VIDEO: "video/*", + MediaType.AUDIO: "audio/*", + } + for media_type, accept_value in media_type_to_extensions.items(): + media = Media(type=media_type) + form = MediaForm(instance=media) + + self.assertIn( + f'accept="{accept_value}"', + form["file"].as_widget(), + )