Move PDF editor out of plugin and into core

This commit is contained in:
Raphael Michel
2018-04-09 09:40:18 +02:00
parent f1d4a686b1
commit 87c54f07c6
19 changed files with 428 additions and 378 deletions

View File

@@ -0,0 +1,198 @@
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.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)
p = order.positions.create(item=item, attendee_name=_("John Doe"), price=item.default_price)
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
InvoiceAddress.objects.create(order=order, name=_("John Doe"), company=_("Sample company"))
return p
def generate(self, p: OrderPosition, override_layout=None, override_background=None):
raise NotImplemented
def get_layout_settings_key(self):
raise NotImplemented
def get_background_settings_key(self):
raise NotImplemented
def get_default_background(self):
raise NotImplemented
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