diff --git a/src/pretix/base/templates/pretixbase/forms/widgets/thumbnailed_file_input.html b/src/pretix/base/templates/pretixbase/forms/widgets/thumbnailed_file_input.html index 73fa3003c1..a1674c57d9 100644 --- a/src/pretix/base/templates/pretixbase/forms/widgets/thumbnailed_file_input.html +++ b/src/pretix/base/templates/pretixbase/forms/widgets/thumbnailed_file_input.html @@ -4,3 +4,4 @@ {% endif %}{% if widget.value.is_img %}
{% endif %}
{{ widget.input_text }}:{% endif %} +{% if widget.cachedfile %}{% endif %} diff --git a/src/pretix/control/forms/__init__.py b/src/pretix/control/forms/__init__.py index 6bfef9e8b2..3caa21d079 100644 --- a/src/pretix/control/forms/__init__.py +++ b/src/pretix/control/forms/__init__.py @@ -7,6 +7,7 @@ from django.core.exceptions import ValidationError from django.core.files import File from django.core.files.uploadedfile import UploadedFile from django.forms.utils import from_current_timezone +from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from ...base.forms import I18nModelForm @@ -77,6 +78,8 @@ class ClearableBasenameFileInput(forms.ClearableFileInput): @property def name(self): + if hasattr(self.file, 'display_name'): + return self.file.display_name return self.file.name @property @@ -84,6 +87,8 @@ class ClearableBasenameFileInput(forms.ClearableFileInput): return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif')) def __str__(self): + if hasattr(self.file, 'display_name'): + return self.file.display_name return os.path.basename(self.file.name).split('.', 1)[-1] @property @@ -93,6 +98,48 @@ class ClearableBasenameFileInput(forms.ClearableFileInput): def get_context(self, name, value, attrs): ctx = super().get_context(name, value, attrs) ctx['widget']['value'] = self.FakeFile(value) + ctx['widget']['cachedfile'] = None + return ctx + + +class CachedFileInput(forms.ClearableFileInput): + template_name = 'pretixbase/forms/widgets/thumbnailed_file_input.html' + + class FakeFile(File): + def __init__(self, file): + self.file = file + + @property + def name(self): + return self.file.filename + + @property + def is_img(self): + return any(self.file.filename.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif')) + + def __str__(self): + return self.file.filename + + @property + def url(self): + return self.file.file.url + + def value_from_datadict(self, data, files, name): + from ...base.models import CachedFile + v = super().value_from_datadict(data, files, name) + if v is None and data.get(name + '-cachedfile'): # An explicit "[x] clear" would be False, not None + return CachedFile.objects.filter(id=data[name + '-cachedfile']).first() + return v + + def get_context(self, name, value, attrs): + from ...base.models import CachedFile + if isinstance(value, CachedFile): + value = self.FakeFile(value) + + ctx = super().get_context(name, value, attrs) + ctx['widget']['value'] = value + ctx['widget']['cachedfile'] = value.file if isinstance(value, self.FakeFile) else None + ctx['widget']['hidden_name'] = name + '-cachedfile' return ctx @@ -129,7 +176,7 @@ class ExtFileField(SizeFileField): def clean(self, *args, **kwargs): data = super().clean(*args, **kwargs) - if data: + if isinstance(data, File): filename = data.name ext = os.path.splitext(filename)[1] ext = ext.lower() @@ -138,6 +185,49 @@ class ExtFileField(SizeFileField): return data +class CachedFileField(ExtFileField): + widget = CachedFileInput + + def to_python(self, data): + from ...base.models import CachedFile + + if isinstance(data, CachedFile): + return data + + return super().to_python(data) + + def bound_data(self, data, initial): + from ...base.models import CachedFile + + if isinstance(data, File): + cf = CachedFile.objects.create( + expires=now() + datetime.timedelta(days=1), + date=now(), + filename=data.name, + type=data.content_type, + ) + cf.file.save(data.name, data.file) + cf.save() + return cf + return super().bound_data(data, initial) + + def clean(self, *args, **kwargs): + from ...base.models import CachedFile + + data = super().clean(*args, **kwargs) + if isinstance(data, File): + cf = CachedFile.objects.create( + expires=now() + datetime.timedelta(days=1), + date=now(), + filename=data.name, + type=data.content_type, + ) + cf.file.save(data.name, data.file) + cf.save() + return cf + return data + + class SlugWidget(forms.TextInput): template_name = 'pretixcontrol/slug_widget.html' prefix = '' diff --git a/src/pretix/plugins/sendmail/forms.py b/src/pretix/plugins/sendmail/forms.py index 46ad799d9e..b0e1f1858f 100644 --- a/src/pretix/plugins/sendmail/forms.py +++ b/src/pretix/plugins/sendmail/forms.py @@ -9,7 +9,7 @@ from pretix.base.email import get_available_placeholders from pretix.base.forms import PlaceholderValidator from pretix.base.forms.widgets import SplitDateTimePickerWidget from pretix.base.models import CheckinList, Item, Order, SubEvent -from pretix.control.forms import ExtFileField +from pretix.control.forms import CachedFileField from pretix.control.forms.widgets import Select2, Select2Multiple @@ -23,7 +23,7 @@ class MailForm(forms.Form): sendto = forms.MultipleChoiceField() # overridden later subject = forms.CharField(label=_("Subject")) message = forms.CharField(label=_("Message")) - attachment = ExtFileField( + attachment = CachedFileField( label=_("Attachment"), required=False, ext_whitelist=( diff --git a/src/pretix/plugins/sendmail/views.py b/src/pretix/plugins/sendmail/views.py index 6cef19cb51..6138b1fff2 100644 --- a/src/pretix/plugins/sendmail/views.py +++ b/src/pretix/plugins/sendmail/views.py @@ -1,5 +1,4 @@ import logging -from datetime import timedelta import bleach import dateutil @@ -13,7 +12,7 @@ from django.views.generic import FormView, ListView from pretix.base.email import get_available_placeholders from pretix.base.i18n import LazyI18nString, language -from pretix.base.models import CachedFile, LogEntry, Order, OrderPosition +from pretix.base.models import LogEntry, Order, OrderPosition from pretix.base.models.event import SubEvent from pretix.base.services.mail import TolerantDict from pretix.base.templatetags.rich_text import markdown_compile_email @@ -124,7 +123,6 @@ class SenderView(EventPermissionRequiredMixin, FormView): if self.request.POST.get("action") == "preview": for l in self.request.event.settings.locales: - with language(l): context_dict = TolerantDict() for k, v in get_available_placeholders(self.request.event, ['event', 'order', @@ -146,17 +144,6 @@ class SenderView(EventPermissionRequiredMixin, FormView): return self.get(self.request, *self.args, **self.kwargs) - attachment = None - if 'attachment' in self.request.FILES: - attachment = self.request.FILES['attachment'] - cf = CachedFile.objects.create( - expires=now() + timedelta(days=1), - date=now(), - filename=attachment.name, - type=attachment.content_type, - ) - cf.file.save(attachment.name, attachment.file) - cf.save() kwargs = { 'recipients': form.cleaned_data['recipients'], 'event': self.request.event.pk, @@ -169,8 +156,8 @@ class SenderView(EventPermissionRequiredMixin, FormView): 'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')], 'filter_checkins': form.cleaned_data.get('filter_checkins'), } - if attachment is not None: - kwargs['attachments'] = [cf.id] + if form.cleaned_data.get('attachment') is not None: + kwargs['attachments'] = [form.cleaned_data['attachment'].id] send_mails.apply_async( kwargs=kwargs