diff --git a/opentech/apply/stream_forms/blocks.py b/opentech/apply/stream_forms/blocks.py index fd7dcdfaf08550809dc5a5451691061ac584fcba..1f0db044b550db62f3ff6804f3ef32e16602fb2c 100644 --- a/opentech/apply/stream_forms/blocks.py +++ b/opentech/apply/stream_forms/blocks.py @@ -3,7 +3,10 @@ import bleach from django_bleach.templatetags.bleach_tags import bleach_value from django import forms +from django.conf import settings +from django.core.validators import FileExtensionValidator from django.db.models import BLANK_CHOICE_DASH +from django.forms.widgets import ClearableFileInput from django.utils.dateparse import parse_datetime from django.utils.encoding import force_text from django.utils.text import slugify @@ -315,11 +318,19 @@ class UploadableMediaBlock(OptionalFormFieldBlock): class ImageFieldBlock(UploadableMediaBlock): field_class = forms.ImageField + widget = ClearableFileInput class Meta: label = _('Image field') icon = 'image' + def get_field_kwargs(self, struct_value): + kwargs = super().get_field_kwargs(struct_value) + # We do not need this when we are on Django 2.1 + # https://docs.djangoproject.com/en/2.1/releases/2.1/#forms + kwargs['widget'] = self.get_widget(struct_value)(attrs={'accept': 'image/*'}) + return kwargs + class FileFieldBlock(UploadableMediaBlock): """This doesn't know how to save the uploaded files @@ -327,11 +338,20 @@ class FileFieldBlock(UploadableMediaBlock): You must implement this if you want to reuse it. """ field_class = forms.FileField + widget = ClearableFileInput class Meta: label = _('File field') icon = 'download' + def get_field_kwargs(self, struct_value): + kwargs = super().get_field_kwargs(struct_value) + kwargs['validators'] = [ + FileExtensionValidator(allowed_extensions=settings.FILE_ALLOWED_EXTENSIONS) + ] + kwargs['widget'] = self.get_widget(struct_value)(attrs={'accept': settings.FILE_ACCEPT_ATTR_VALUE}) + return kwargs + class MultiFileFieldBlock(UploadableMediaBlock): field_class = MultiFileField diff --git a/opentech/apply/stream_forms/fields.py b/opentech/apply/stream_forms/fields.py index c45d4f1337f7f482c984b52976827e52c3e92baa..c7a8deed5921ec957558420f721f73f737fdcbef 100644 --- a/opentech/apply/stream_forms/fields.py +++ b/opentech/apply/stream_forms/fields.py @@ -1,3 +1,5 @@ +from django.conf import settings +from django.core.validators import FileExtensionValidator from django.forms import ClearableFileInput, FileField, CheckboxInput @@ -63,7 +65,11 @@ class MultiFileField(FileField): cleared = value['cleared'] if not files and not cleared: return initial - new = [FileField().clean(file, initial) for file in files] + new = [ + FileField(validators=[ + FileExtensionValidator(allowed_extensions=settings.FILE_ALLOWED_EXTENSIONS) + ]).clean(file, initial) for file in files + ] if initial: old = [file for i, file in enumerate(initial) if i not in cleared] @@ -71,3 +77,9 @@ class MultiFileField(FileField): old = [] return old + new + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + if isinstance(widget, MultiFileInput) and 'accept' not in widget.attrs: + attrs.setdefault('accept', settings.FILE_ACCEPT_ATTR_VALUE) + return attrs diff --git a/opentech/apply/stream_forms/testing/factories.py b/opentech/apply/stream_forms/testing/factories.py index 902ba4ce90239e439023ae722f12e5ca5079e6d1..f3421670610081f668ae6adeb5b2929cb521468d 100644 --- a/opentech/apply/stream_forms/testing/factories.py +++ b/opentech/apply/stream_forms/testing/factories.py @@ -143,6 +143,8 @@ class UploadableMediaFactory(FormFieldBlockFactory): def make_answer(cls, params=dict()): params = params.copy() params.setdefault('data', b'this is some content') + if params.get('filename') is None: + params['filename'] = 'example.pdf' file_name, file = cls.default_value()._make_content(params) return SimpleUploadedFile(file_name, file.read()) diff --git a/opentech/apply/stream_forms/tests.py b/opentech/apply/stream_forms/tests.py index 5bdf4b189f2951344bc06e496c9bf9a4f9a2283a..a8d37d02de09a7a503431f897d4c7299db54e86a 100644 --- a/opentech/apply/stream_forms/tests.py +++ b/opentech/apply/stream_forms/tests.py @@ -10,7 +10,7 @@ fake = Faker() def make_files(number): - file_names = [f'{fake.word()}_{i}' for i in range(3)] + file_names = [f'{fake.word()}_{i}.pdf' for i in range(number)] files = [ StreamFieldFile(SimpleUploadedFile(name, b'Some File Content'), filename=name) for name in file_names diff --git a/opentech/settings/base.py b/opentech/settings/base.py index 72f065276b6b29c03f650879a4413c36c293f502..e2157aecbcae27912724a158ffa0f1e0ef98c85d 100644 --- a/opentech/settings/base.py +++ b/opentech/settings/base.py @@ -438,6 +438,12 @@ BLEACH_STRIP_TAGS = True BLEACH_STRIP_COMMENTS = True +# File Field settings +FILE_ALLOWED_EXTENSIONS = ['doc', 'docx', 'odp', 'ods', 'odt', 'pdf', 'ppt', 'pptx', 'rtf', 'txt', 'xls', 'xlsx'] + +# Accept attribute in input tag of type file needs filename extensions, starting with a period (".") character. +FILE_ACCEPT_ATTR_VALUE = ", ".join(['.' + ext for ext in FILE_ALLOWED_EXTENSIONS]) + # Hijack Settings HIJACK_LOGIN_REDIRECT_URL = '/dashboard/' HIJACK_LOGOUT_REDIRECT_URL = '/account/'