diff --git a/src/pretix/base/forms/__init__.py b/src/pretix/base/forms/__init__.py index 7ef7e8d9a..875671d28 100644 --- a/src/pretix/base/forms/__init__.py +++ b/src/pretix/base/forms/__init__.py @@ -1,6 +1,11 @@ import copy +import logging +import os from django import forms +from django.conf import settings +from django.core.files import File +from django.core.files.uploadedfile import UploadedFile from django.db import models from django.forms.models import BaseModelForm, ModelFormMetaclass from django.utils import six @@ -8,6 +13,9 @@ from django.utils.translation import ugettext_lazy as _ from versions.models import Versionable from pretix.base.i18n import I18nFormField +from pretix.base.models import Event + +logger = logging.getLogger('pretix.plugins.ticketoutputpdf') class BaseI18nModelForm(BaseModelForm): @@ -89,6 +97,28 @@ class SettingsForm(forms.Form): def save(self): for name, field in self.fields.items(): value = self.cleaned_data[name] + + if isinstance(value, UploadedFile): + if isinstance(self.obj, Event): + fname = '%s/%s/%s.%s' % ( + self.obj.organizer.slug, self.obj.slug, name, value.name.split('.')[-1] + ) + else: + fname = '%s/%s.%s' % (self.obj.slug, name, value.name.split('.')[-1]) + fpath = os.path.join(settings.MEDIA_ROOT, fname) + with open(fpath, 'wb+') as destination: + for chunk in value.chunks(): + destination.write(chunk) + value._name = fname + elif isinstance(field, forms.FileField): # value should be None + fname = self.obj.settings.get(name, as_type=File) + value = None + if fname: + try: + os.unlink(fname.name) + except OSError: + logger.error('Deleting file %s failed.' % fname.name) + if value is None: del self.obj.settings[name] elif self.obj.settings.get(name, as_type=type(value)) != value: diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index cb390bee9..f26c425ed 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -1,9 +1,12 @@ import decimal import json +import os from datetime import date, datetime, time +from urllib.parse import urljoin import dateutil.parser from django.conf import settings +from django.core.files import File from django.db.models import Model from versions.models import Versionable @@ -134,6 +137,14 @@ class SettingsProxy: return json.loads(value) elif as_type == bool or value in ('True', 'False'): return value == 'True' + elif as_type == File: + try: + f = open(os.path.join(settings.MEDIA_ROOT, value[7:]), 'r') + fi = File(f) + fi.url = urljoin(settings.MEDIA_URL, value[7:]) + return fi + except OSError: + return False elif as_type == datetime: return dateutil.parser.parse(value) elif as_type == date: @@ -160,6 +171,8 @@ class SettingsProxy: return value.identity elif isinstance(value, Model): return value.pk + elif isinstance(value, File): + return 'file://' + value.name raise TypeError('Unable to serialize %s into a setting.' % str(type(value))) @@ -173,14 +186,19 @@ class SettingsProxy: as_type = DEFAULTS[key]['type'] if key in self._cache(): - return self._unserialize(self._cache()[key].value, as_type) - value = None - if self._parent: - value = self._parent.settings.get(key) - if value is None and key in DEFAULTS: - return self._unserialize(DEFAULTS[key]['default'], as_type) - if value is None and default is not None: - return self._unserialize(default, as_type) + value = self._cache()[key].value + else: + value = None + if self._parent: + value = self._parent.settings.get(key) + if value is None and key in DEFAULTS: + value = DEFAULTS[key]['default'] + if value is None and default is not None: + value = default + + if as_type is None and value is not None and value.startswith('file://'): + as_type = File + return self._unserialize(value, as_type) def __getitem__(self, key): diff --git a/src/pretix/control/forms/__init__.py b/src/pretix/control/forms/__init__.py index 042d57268..06cbfb44a 100644 --- a/src/pretix/control/forms/__init__.py +++ b/src/pretix/control/forms/__init__.py @@ -1,3 +1,4 @@ +import os from functools import partial from itertools import product @@ -403,3 +404,21 @@ class VariationsField(forms.ModelMultipleChoiceField): return cleaned_value choices = property(_get_choices, forms.ChoiceField._set_choices) + + +class ExtFileField(forms.FileField): + + def __init__(self, *args, **kwargs): + ext_whitelist = kwargs.pop("ext_whitelist") + self.ext_whitelist = [i.lower() for i in ext_whitelist] + super().__init__(*args, **kwargs) + + def clean(self, *args, **kwargs): + data = super().clean(*args, **kwargs) + if data: + filename = data.name + ext = os.path.splitext(filename)[1] + ext = ext.lower() + if ext not in self.ext_whitelist: + raise forms.ValidationError(_("Filetype not allowed!")) + return data diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 5284a444a..bc3bbc65e 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -185,7 +185,6 @@ class ProviderForm(SettingsForm): for k, v in self.fields.items(): val = cleaned_data.get(k) if v._required and (val is None or val == ""): - print(enabled, k, v) self.add_error(k, _('This field is required.')) @@ -217,5 +216,4 @@ class TicketSettingsForm(SettingsForm): for k, v in self.fields.items(): val = cleaned_data.get(k) if v._required and (val is None or val == ""): - print(enabled, k, v) self.add_error(k, _('This field is required.')) diff --git a/src/pretix/control/templates/pretixcontrol/event/tickets.html b/src/pretix/control/templates/pretixcontrol/event/tickets.html index d2c978578..e6c0227a4 100644 --- a/src/pretix/control/templates/pretixcontrol/event/tickets.html +++ b/src/pretix/control/templates/pretixcontrol/event/tickets.html @@ -2,7 +2,7 @@ {% load i18n %} {% load bootstrap3 %} {% block inside %} -
+ {% csrf_token %}
{% trans "Ticket download" %} diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 78f6bac65..7fcd6616d 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -231,7 +231,8 @@ class TicketSettings(EventPermissionRequiredMixin, FormView): provider.form = ProviderForm( obj=self.request.event, settingspref='ticketoutput_%s_' % provider.identifier, - data=(self.request.POST if self.request.method == 'POST' else None) + data=(self.request.POST if self.request.method == 'POST' else None), + files=(self.request.FILES if self.request.method == 'POST' else None) ) provider.form.fields = OrderedDict( [ diff --git a/src/pretix/plugins/ticketoutputpdf/ticketoutput.py b/src/pretix/plugins/ticketoutputpdf/ticketoutput.py index 8b5fcfb52..e08e685df 100644 --- a/src/pretix/plugins/ticketoutputpdf/ticketoutput.py +++ b/src/pretix/plugins/ticketoutputpdf/ticketoutput.py @@ -8,6 +8,7 @@ from django.http import HttpResponse from django.utils.translation import ugettext_lazy as _ from pretix.base.ticketoutput import BaseTicketOutput +from pretix.control.forms import ExtFileField logger = logging.getLogger('pretix.plugins.ticketoutputpdf') @@ -112,5 +113,11 @@ class PdfTicketOutput(BaseTicketOutput): ), required=False )), + ('background', + ExtFileField( + label=_('Background PDF'), + ext_whitelist=(".pdf", ), + required=False + )), ] )