Sendmail: Keep uploaded attachment when preview is used

This commit is contained in:
Raphael Michel
2020-10-29 18:46:57 +01:00
parent 1fe4d1a8ca
commit 8759155357
4 changed files with 97 additions and 19 deletions

View File

@@ -4,3 +4,4 @@
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}{% if widget.value.is_img %}<br><a href="{{ widget.value.url }}" data-lightbox="{{ widget.value.name }}"><img src="{{ widget.value|thumb:"200x100" }}" /></a>{% endif %}<br> <label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}{% if widget.value.is_img %}<br><a href="{{ widget.value.url }}" data-lightbox="{{ widget.value.name }}"><img src="{{ widget.value|thumb:"200x100" }}" /></a>{% endif %}<br>
{{ widget.input_text }}:{% endif %} {{ widget.input_text }}:{% endif %}
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}> <input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
{% if widget.cachedfile %}<input type="hidden" name="{{ widget.hidden_name }}" value="{{ widget.cachedfile.id }}">{% endif %}

View File

@@ -7,6 +7,7 @@ from django.core.exceptions import ValidationError
from django.core.files import File from django.core.files import File
from django.core.files.uploadedfile import UploadedFile from django.core.files.uploadedfile import UploadedFile
from django.forms.utils import from_current_timezone from django.forms.utils import from_current_timezone
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from ...base.forms import I18nModelForm from ...base.forms import I18nModelForm
@@ -77,6 +78,8 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
@property @property
def name(self): def name(self):
if hasattr(self.file, 'display_name'):
return self.file.display_name
return self.file.name return self.file.name
@property @property
@@ -84,6 +87,8 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif')) return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif'))
def __str__(self): def __str__(self):
if hasattr(self.file, 'display_name'):
return self.file.display_name
return os.path.basename(self.file.name).split('.', 1)[-1] return os.path.basename(self.file.name).split('.', 1)[-1]
@property @property
@@ -93,6 +98,48 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
def get_context(self, name, value, attrs): def get_context(self, name, value, attrs):
ctx = super().get_context(name, value, attrs) ctx = super().get_context(name, value, attrs)
ctx['widget']['value'] = self.FakeFile(value) 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 return ctx
@@ -129,7 +176,7 @@ class ExtFileField(SizeFileField):
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
data = super().clean(*args, **kwargs) data = super().clean(*args, **kwargs)
if data: if isinstance(data, File):
filename = data.name filename = data.name
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
ext = ext.lower() ext = ext.lower()
@@ -138,6 +185,49 @@ class ExtFileField(SizeFileField):
return data 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): class SlugWidget(forms.TextInput):
template_name = 'pretixcontrol/slug_widget.html' template_name = 'pretixcontrol/slug_widget.html'
prefix = '' prefix = ''

View File

@@ -9,7 +9,7 @@ from pretix.base.email import get_available_placeholders
from pretix.base.forms import PlaceholderValidator from pretix.base.forms import PlaceholderValidator
from pretix.base.forms.widgets import SplitDateTimePickerWidget from pretix.base.forms.widgets import SplitDateTimePickerWidget
from pretix.base.models import CheckinList, Item, Order, SubEvent 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 from pretix.control.forms.widgets import Select2, Select2Multiple
@@ -23,7 +23,7 @@ class MailForm(forms.Form):
sendto = forms.MultipleChoiceField() # overridden later sendto = forms.MultipleChoiceField() # overridden later
subject = forms.CharField(label=_("Subject")) subject = forms.CharField(label=_("Subject"))
message = forms.CharField(label=_("Message")) message = forms.CharField(label=_("Message"))
attachment = ExtFileField( attachment = CachedFileField(
label=_("Attachment"), label=_("Attachment"),
required=False, required=False,
ext_whitelist=( ext_whitelist=(

View File

@@ -1,5 +1,4 @@
import logging import logging
from datetime import timedelta
import bleach import bleach
import dateutil import dateutil
@@ -13,7 +12,7 @@ from django.views.generic import FormView, ListView
from pretix.base.email import get_available_placeholders from pretix.base.email import get_available_placeholders
from pretix.base.i18n import LazyI18nString, language 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.models.event import SubEvent
from pretix.base.services.mail import TolerantDict from pretix.base.services.mail import TolerantDict
from pretix.base.templatetags.rich_text import markdown_compile_email 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": if self.request.POST.get("action") == "preview":
for l in self.request.event.settings.locales: for l in self.request.event.settings.locales:
with language(l): with language(l):
context_dict = TolerantDict() context_dict = TolerantDict()
for k, v in get_available_placeholders(self.request.event, ['event', 'order', 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) 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 = { kwargs = {
'recipients': form.cleaned_data['recipients'], 'recipients': form.cleaned_data['recipients'],
'event': self.request.event.pk, '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')], 'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')],
'filter_checkins': form.cleaned_data.get('filter_checkins'), 'filter_checkins': form.cleaned_data.get('filter_checkins'),
} }
if attachment is not None: if form.cleaned_data.get('attachment') is not None:
kwargs['attachments'] = [cf.id] kwargs['attachments'] = [form.cleaned_data['attachment'].id]
send_mails.apply_async( send_mails.apply_async(
kwargs=kwargs kwargs=kwargs