import json import logging import mimetypes from datetime import timedelta from django.core.files import File from django.core.files.storage import default_storage from django.http import ( FileResponse, HttpResponse, HttpResponseBadRequest, JsonResponse, ) from django.shortcuts import get_object_or_404 from django.urls import reverse from django.utils.crypto import get_random_string from django.utils.timezone import now from django.utils.translation import ugettext as _ from django.views.generic import TemplateView from pretix.base.i18n import language from pretix.base.models import CachedFile, InvoiceAddress, OrderPosition from pretix.base.pdf import get_variables from pretix.base.settings import PERSON_NAME_SCHEMES from pretix.control.permissions import EventPermissionRequiredMixin from pretix.helpers.database import rolledback_transaction from pretix.presale.style import get_fonts logger = logging.getLogger(__name__) class BaseEditorView(EventPermissionRequiredMixin, TemplateView): template_name = 'pretixcontrol/pdf/index.html' permission = 'can_change_settings' accepted_formats = ( 'application/pdf', ) maxfilesize = 1024 * 1024 * 10 minfilesize = 10 title = None def get(self, request, *args, **kwargs): resp = super().get(request, *args, **kwargs) resp._csp_ignore = True return resp def process_upload(self): f = self.request.FILES.get('background') error = False if f.size > self.maxfilesize: error = _('The uploaded PDF file is to large.') if f.size < self.minfilesize: error = _('The uploaded PDF file is to small.') if mimetypes.guess_type(f.name)[0] not in self.accepted_formats: error = _('Please only upload PDF files.') # if there was an error, add error message to response_data and return if error: return error, None return None, f def _get_preview_position(self): item = self.request.event.items.create(name=_("Sample product"), default_price=42.23, description=_("Sample product description")) item2 = self.request.event.items.create(name=_("Sample workshop"), default_price=23.40) from pretix.base.models import Order order = self.request.event.orders.create(status=Order.STATUS_PENDING, datetime=now(), email='sample@pretix.eu', locale=self.request.event.settings.locale, expires=now(), code="PREVIEW1234", total=119) scheme = PERSON_NAME_SCHEMES[self.request.event.settings.name_scheme] sample = {k: str(v) for k, v in scheme['sample'].items()} p = order.positions.create(item=item, attendee_name_parts=sample, price=item.default_price) order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=p) order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=p) InvoiceAddress.objects.create(order=order, name_parts=sample, company=_("Sample company")) return p def generate(self, p: OrderPosition, override_layout=None, override_background=None): raise NotImplementedError() def get_layout_settings_key(self): raise NotImplementedError() def get_background_settings_key(self): raise NotImplementedError() def get_default_background(self): raise NotImplementedError() def get_current_background(self): return ( self.request.event.settings.get(self.get_background_settings_key()).url if self.request.event.settings.get(self.get_background_settings_key()) else self.get_default_background() ) def get_current_layout(self): return self.request.event.settings.get(self.get_layout_settings_key(), as_type=list) def save_layout(self): self.request.event.settings.set(self.get_layout_settings_key(), self.request.POST.get("data")) def save_background(self, f: CachedFile): fexisting = self.request.event.settings.get(self.get_background_settings_key(), as_type=File) if fexisting: try: default_storage.delete(fexisting.name) except OSError: # pragma: no cover logger.error('Deleting file %s failed.' % fexisting.name) # Create new file nonce = get_random_string(length=8) fname = 'pub/%s-%s/%s/%s.%s.%s' % ( 'event', 'settings', self.request.event.pk, self.get_layout_settings_key(), nonce, 'pdf' ) newname = default_storage.save(fname, f.file) self.request.event.settings.set(self.get_background_settings_key(), 'file://' + newname) def post(self, request, *args, **kwargs): if "background" in request.FILES: error, fileobj = self.process_upload() if error: return JsonResponse({ "status": "error", "error": error }) c = CachedFile() c.expires = now() + timedelta(days=7) c.date = now() c.filename = 'background_preview.pdf' c.type = 'application/pdf' c.file = fileobj c.save() c.refresh_from_db() return JsonResponse({ "status": "ok", "id": c.id, "url": reverse('control:pdf.background', kwargs={ 'event': request.event.slug, 'organizer': request.organizer.slug, 'filename': str(c.id) }) }) cf = None if request.POST.get("background", "").strip(): try: cf = CachedFile.objects.get(id=request.POST.get("background")) except CachedFile.DoesNotExist: pass if "preview" in request.POST: with rolledback_transaction(), language(request.event.settings.locale): p = self._get_preview_position() fname, mimet, data = self.generate( p, override_layout=(json.loads(self.request.POST.get("data")) if self.request.POST.get("data") else None), override_background=cf.file if cf else None ) resp = HttpResponse(data, content_type=mimet) ftype = fname.split(".")[-1] resp['Content-Disposition'] = 'attachment; filename="ticket-preview.{}"'.format(ftype) return resp elif "data" in request.POST: if cf: self.save_background(cf) self.save_layout() return JsonResponse({'status': 'ok'}) return HttpResponseBadRequest() def get_variables(self): return get_variables(self.request.event) def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['fonts'] = get_fonts() ctx['pdf'] = self.get_current_background() ctx['variables'] = self.get_variables() ctx['layout'] = json.dumps(self.get_current_layout()) ctx['title'] = self.title return ctx class FontsCSSView(TemplateView): content_type = 'text/css' template_name = 'pretixcontrol/pdf/webfonts.css' def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['fonts'] = get_fonts() return ctx class PdfView(TemplateView): def get(self, request, *args, **kwargs): cf = get_object_or_404(CachedFile, id=kwargs.get("filename"), filename="background_preview.pdf") resp = FileResponse(cf.file, content_type='application/pdf') resp['Content-Disposition'] = 'attachment; filename="{}"'.format(cf.filename) return resp