forked from CGM_Public/pretix_original
Move PDF editor out of plugin and into core
This commit is contained in:
28
.gitattributes
vendored
28
.gitattributes
vendored
@@ -1,17 +1,17 @@
|
|||||||
src/static/fontawesome/* linguist-vendored
|
src/pretix/static/fontawesome/* linguist-vendored
|
||||||
src/static/lightbox/* linguist-vendored
|
src/pretix/static/lightbox/* linguist-vendored
|
||||||
src/static/typeahead/* linguist-vendored
|
src/pretix/static/typeahead/* linguist-vendored
|
||||||
src/static/moment/* linguist-vendored
|
src/pretix/static/moment/* linguist-vendored
|
||||||
src/static/datetimepicker/* linguist-vendored
|
src/pretix/static/datetimepicker/* linguist-vendored
|
||||||
src/static/colorpicker/* linguist-vendored
|
src/pretix/static/colorpicker/* linguist-vendored
|
||||||
src/static/fileupload/* linguist-vendored
|
src/pretix/static/fileupload/* linguist-vendored
|
||||||
src/static/vuejs/* linguist-vendored
|
src/pretix/static/vuejs/* linguist-vendored
|
||||||
src/static/select2/* linguist-vendored
|
src/pretix/static/select2/* linguist-vendored
|
||||||
src/static/charts/* linguist-vendored
|
src/pretix/static/charts/* linguist-vendored
|
||||||
src/static/rrule/* linguist-vendored
|
src/pretix/static/rrule/* linguist-vendored
|
||||||
src/static/iframeresizer/* linguist-vendored
|
src/pretix/static/iframeresizer/* linguist-vendored
|
||||||
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/fabric.* linguist-vendored
|
src/pretix/static/pdfjs/* linguist-vendored
|
||||||
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/pdf.* linguist-vendored
|
src/pretix/static/fabric/* linguist-vendored
|
||||||
|
|
||||||
# Denote all files that are truly binary and should not be modified.
|
# Denote all files that are truly binary and should not be modified.
|
||||||
*.eot binary
|
*.eot binary
|
||||||
|
|||||||
@@ -68,5 +68,5 @@ Dashboards
|
|||||||
Ticket designs
|
Ticket designs
|
||||||
""""""""""""""
|
""""""""""""""
|
||||||
|
|
||||||
.. automodule:: pretix.plugins.ticketoutputpdf.signals
|
.. automodule:: pretix.base.signals
|
||||||
:members: layout_text_variables
|
:members: layout_text_variables
|
||||||
|
|||||||
143
src/pretix/base/pdf.py
Normal file
143
src/pretix/base/pdf.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import copy
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from django.utils.formats import date_format
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from pytz import timezone
|
||||||
|
|
||||||
|
from pretix.base.signals import layout_text_variables
|
||||||
|
from pretix.base.templatetags.money import money_filter
|
||||||
|
|
||||||
|
DEFAULT_VARIABLES = OrderedDict((
|
||||||
|
("secret", {
|
||||||
|
"label": _("Ticket code (barcode content)"),
|
||||||
|
"editor_sample": "tdmruoekvkpbv1o2mv8xccvqcikvr58u",
|
||||||
|
"evaluate": lambda orderposition, order, event: orderposition.secret
|
||||||
|
}),
|
||||||
|
("order", {
|
||||||
|
"label": _("Order code"),
|
||||||
|
"editor_sample": "A1B2C",
|
||||||
|
"evaluate": lambda orderposition, order, event: orderposition.order.code
|
||||||
|
}),
|
||||||
|
("item", {
|
||||||
|
"label": _("Product name"),
|
||||||
|
"editor_sample": _("Sample product"),
|
||||||
|
"evaluate": lambda orderposition, order, event: str(orderposition.item)
|
||||||
|
}),
|
||||||
|
("variation", {
|
||||||
|
"label": _("Variation name"),
|
||||||
|
"editor_sample": _("Sample variation"),
|
||||||
|
"evaluate": lambda op, order, event: str(op.variation) if op.variation else ''
|
||||||
|
}),
|
||||||
|
("item_description", {
|
||||||
|
"label": _("Product description"),
|
||||||
|
"editor_sample": _("Sample product description"),
|
||||||
|
"evaluate": lambda orderposition, order, event: str(orderposition.item.description)
|
||||||
|
}),
|
||||||
|
("itemvar", {
|
||||||
|
"label": _("Product name and variation"),
|
||||||
|
"editor_sample": _("Sample product – sample variation"),
|
||||||
|
"evaluate": lambda orderposition, order, event: (
|
||||||
|
'{} - {}'.format(orderposition.item, orderposition.variation)
|
||||||
|
if orderposition.variation else str(orderposition.item)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
("item_category", {
|
||||||
|
"label": _("Product category"),
|
||||||
|
"editor_sample": _("Ticket category"),
|
||||||
|
"evaluate": lambda orderposition, order, event: (
|
||||||
|
str(orderposition.item.category.name) if orderposition.item.category else ""
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
("price", {
|
||||||
|
"label": _("Price"),
|
||||||
|
"editor_sample": _("123.45 EUR"),
|
||||||
|
"evaluate": lambda op, order, event: money_filter(op.price, event.currency)
|
||||||
|
}),
|
||||||
|
("attendee_name", {
|
||||||
|
"label": _("Attendee name"),
|
||||||
|
"editor_sample": _("John Doe"),
|
||||||
|
"evaluate": lambda op, order, ev: op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '')
|
||||||
|
}),
|
||||||
|
("event_name", {
|
||||||
|
"label": _("Event name"),
|
||||||
|
"editor_sample": _("Sample event name"),
|
||||||
|
"evaluate": lambda op, order, ev: str(ev.name)
|
||||||
|
}),
|
||||||
|
("event_date", {
|
||||||
|
"label": _("Event date"),
|
||||||
|
"editor_sample": _("May 31st, 2017"),
|
||||||
|
"evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=False)
|
||||||
|
}),
|
||||||
|
("event_date_range", {
|
||||||
|
"label": _("Event date range"),
|
||||||
|
"editor_sample": _("May 31st – June 4th, 2017"),
|
||||||
|
"evaluate": lambda op, order, ev: ev.get_date_range_display()
|
||||||
|
}),
|
||||||
|
("event_begin", {
|
||||||
|
"label": _("Event begin date and time"),
|
||||||
|
"editor_sample": _("2017-05-31 20:00"),
|
||||||
|
"evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=True)
|
||||||
|
}),
|
||||||
|
("event_begin_time", {
|
||||||
|
"label": _("Event begin time"),
|
||||||
|
"editor_sample": _("20:00"),
|
||||||
|
"evaluate": lambda op, order, ev: ev.get_time_from_display()
|
||||||
|
}),
|
||||||
|
("event_admission", {
|
||||||
|
"label": _("Event admission date and time"),
|
||||||
|
"editor_sample": _("2017-05-31 19:00"),
|
||||||
|
"evaluate": lambda op, order, ev: date_format(
|
||||||
|
ev.date_admission.astimezone(timezone(ev.settings.timezone)),
|
||||||
|
"SHORT_DATETIME_FORMAT"
|
||||||
|
) if ev.date_admission else ""
|
||||||
|
}),
|
||||||
|
("event_admission_time", {
|
||||||
|
"label": _("Event admission time"),
|
||||||
|
"editor_sample": _("19:00"),
|
||||||
|
"evaluate": lambda op, order, ev: date_format(
|
||||||
|
ev.date_admission.astimezone(timezone(ev.settings.timezone)),
|
||||||
|
"TIME_FORMAT"
|
||||||
|
) if ev.date_admission else ""
|
||||||
|
}),
|
||||||
|
("event_location", {
|
||||||
|
"label": _("Event location"),
|
||||||
|
"editor_sample": _("Random City"),
|
||||||
|
"evaluate": lambda op, order, ev: str(ev.location).replace("\n", "<br/>\n")
|
||||||
|
}),
|
||||||
|
("invoice_name", {
|
||||||
|
"label": _("Invoice address: name"),
|
||||||
|
"editor_sample": _("John Doe"),
|
||||||
|
"evaluate": lambda op, order, ev: order.invoice_address.name if getattr(order, 'invoice_address') else ''
|
||||||
|
}),
|
||||||
|
("invoice_company", {
|
||||||
|
"label": _("Invoice address: company"),
|
||||||
|
"editor_sample": _("Sample company"),
|
||||||
|
"evaluate": lambda op, order, ev: order.invoice_address.company if getattr(order, 'invoice_address') else ''
|
||||||
|
}),
|
||||||
|
("addons", {
|
||||||
|
"label": _("List of Add-Ons"),
|
||||||
|
"editor_sample": _("Addon 1\nAddon 2"),
|
||||||
|
"evaluate": lambda op, order, ev: "<br/>".join([
|
||||||
|
'{} - {}'.format(p.item, p.variation) if p.variation else str(p.item)
|
||||||
|
for p in op.addons.select_related('item', 'variation')
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
("organizer", {
|
||||||
|
"label": _("Organizer name"),
|
||||||
|
"editor_sample": _("Event organizer company"),
|
||||||
|
"evaluate": lambda op, order, ev: str(order.event.organizer.name)
|
||||||
|
}),
|
||||||
|
("organizer_info_text", {
|
||||||
|
"label": _("Organizer info text"),
|
||||||
|
"editor_sample": _("Event organizer info text"),
|
||||||
|
"evaluate": lambda op, order, ev: str(order.event.settings.organizer_info_text)
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def get_variables(event):
|
||||||
|
v = copy.copy(DEFAULT_VARIABLES)
|
||||||
|
for recv, res in layout_text_variables.send(sender=event):
|
||||||
|
v.update(res)
|
||||||
|
return v
|
||||||
@@ -338,3 +338,22 @@ The ``message`` argument will contain an ``EmailMultiAlternatives`` object.
|
|||||||
If the email is associated with a specific order, the ``order`` argument will be passed as well, otherwise
|
If the email is associated with a specific order, the ``order`` argument will be passed as well, otherwise
|
||||||
it will be ``None``.
|
it will be ``None``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
layout_text_variables = EventPluginSignal()
|
||||||
|
"""
|
||||||
|
This signal is sent out to collect variables that can be used to display text in ticket-related PDF layouts.
|
||||||
|
Receivers are expected to return a dictionary with globally unique identifiers as keys and more
|
||||||
|
dictionaries as values that contain keys like in the following example::
|
||||||
|
|
||||||
|
return {
|
||||||
|
"product": {
|
||||||
|
"label": _("Product name"),
|
||||||
|
"editor_sample": _("Sample product"),
|
||||||
|
"evaluate": lambda orderposition, order, event: str(orderposition.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The evaluate member will be called with the order position, order and event as arguments. The event might
|
||||||
|
also be a subevent, if applicable.
|
||||||
|
"""
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
{% extends "pretixcontrol/event/base.html" %}
|
{% extends "pretixcontrol/event/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
{% load compress %}
|
||||||
{% block title %}{% trans "PDF Ticket Editor" %}{% endblock %}
|
{% block title %}{% trans "PDF Ticket Editor" %}{% endblock %}
|
||||||
|
{% block custom_header %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% compress css %}
|
||||||
|
<link type="text/css" rel="stylesheet" href="{% static "pretixcontrol/scss/pdfeditor.css" %}">
|
||||||
|
{% endcompress %}
|
||||||
|
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" organizer=request.organizer.slug event=request.event.slug %}">
|
||||||
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% trans "PDF Ticket Editor" %}</h1>
|
<h1>
|
||||||
|
{% trans "PDF Editor" %}
|
||||||
|
{% if title %}
|
||||||
|
<small>{{ title }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</h1>
|
||||||
|
|
||||||
<script type="application/json" id="editor-data">
|
<script type="application/json" id="editor-data">
|
||||||
{{ layout|safe }}
|
{{ layout|safe }}
|
||||||
@@ -2,7 +2,7 @@ from django.conf.urls import include, url
|
|||||||
|
|
||||||
from pretix.control.views import (
|
from pretix.control.views import (
|
||||||
auth, checkin, dashboards, event, global_settings, item, main, orders,
|
auth, checkin, dashboards, event, global_settings, item, main, orders,
|
||||||
organizer, search, subevents, typeahead, user, users, vouchers,
|
organizer, pdf, search, subevents, typeahead, user, users, vouchers,
|
||||||
waitinglist,
|
waitinglist,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,6 +97,8 @@ urlpatterns = [
|
|||||||
url(r'^settings/tax/add$', event.TaxCreate.as_view(), name='event.settings.tax.add'),
|
url(r'^settings/tax/add$', event.TaxCreate.as_view(), name='event.settings.tax.add'),
|
||||||
url(r'^settings/tax/(?P<rule>\d+)/delete$', event.TaxDelete.as_view(), name='event.settings.tax.delete'),
|
url(r'^settings/tax/(?P<rule>\d+)/delete$', event.TaxDelete.as_view(), name='event.settings.tax.delete'),
|
||||||
url(r'^settings/widget$', event.WidgetSettings.as_view(), name='event.settings.widget'),
|
url(r'^settings/widget$', event.WidgetSettings.as_view(), name='event.settings.widget'),
|
||||||
|
url(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
|
||||||
|
url(r'^pdf/editor/(?P<filename>[^/]+).pdf$', pdf.PdfView.as_view(), name='pdf.background'),
|
||||||
url(r'^subevents/$', subevents.SubEventList.as_view(), name='event.subevents'),
|
url(r'^subevents/$', subevents.SubEventList.as_view(), name='event.subevents'),
|
||||||
url(r'^subevents/select2$', typeahead.subevent_select2, name='event.subevents.select2'),
|
url(r'^subevents/select2$', typeahead.subevent_select2, name='event.subevents.select2'),
|
||||||
url(r'^subevents/(?P<subevent>\d+)/$', subevents.SubEventUpdate.as_view(), name='event.subevent'),
|
url(r'^subevents/(?P<subevent>\d+)/$', subevents.SubEventUpdate.as_view(), name='event.subevent'),
|
||||||
|
|||||||
198
src/pretix/control/views/pdf.py
Normal file
198
src/pretix/control/views/pdf.py
Normal 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
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.template.loader import get_template
|
|
||||||
from django.urls import resolve
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.models import QuestionAnswer
|
from pretix.base.models import QuestionAnswer
|
||||||
from pretix.base.signals import (
|
from pretix.base.signals import ( # NOQA: legacy import
|
||||||
EventPluginSignal, event_copy_data, register_data_exporters,
|
event_copy_data, layout_text_variables, register_data_exporters,
|
||||||
register_ticket_outputs,
|
register_ticket_outputs,
|
||||||
)
|
)
|
||||||
from pretix.control.signals import html_head
|
|
||||||
from pretix.presale.style import ( # NOQA: legacy import
|
from pretix.presale.style import ( # NOQA: legacy import
|
||||||
get_fonts, register_fonts,
|
get_fonts, register_fonts,
|
||||||
)
|
)
|
||||||
@@ -28,37 +25,6 @@ def register_data(sender, **kwargs):
|
|||||||
return AllTicketsPDF
|
return AllTicketsPDF
|
||||||
|
|
||||||
|
|
||||||
@receiver(html_head, dispatch_uid="ticketoutputpdf_html_head")
|
|
||||||
def html_head_presale(sender, request=None, **kwargs):
|
|
||||||
url = resolve(request.path_info)
|
|
||||||
if url.namespace == 'plugins:ticketoutputpdf' and getattr(request, 'organizer', None):
|
|
||||||
template = get_template('pretixplugins/ticketoutputpdf/control_head.html')
|
|
||||||
return template.render({
|
|
||||||
'request': request
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
layout_text_variables = EventPluginSignal()
|
|
||||||
"""
|
|
||||||
This signal is sent out to collect variables that can be used to display text in PDF ticket layouts.
|
|
||||||
Receivers are expected to return a dictionary with globally unique identifiers as keys and more
|
|
||||||
dictionaries as values that contain keys like in the following example::
|
|
||||||
|
|
||||||
return {
|
|
||||||
"product": {
|
|
||||||
"label": _("Product name"),
|
|
||||||
"editor_sample": _("Sample product"),
|
|
||||||
"evaluate": lambda orderposition, order, event: str(orderposition.item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The evaluate member will be called with the order position, order and event as arguments. The event might
|
|
||||||
also be a subevent, if applicable.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def get_answer(op, order, event, question_id):
|
def get_answer(op, order, event, question_id):
|
||||||
try:
|
try:
|
||||||
a = op.answers.get(question_id=question_id)
|
a = op.answers.get(question_id=question_id)
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{% load staticfiles %}
|
|
||||||
{% load compress %}
|
|
||||||
|
|
||||||
{% compress css %}
|
|
||||||
<link type="text/css" rel="stylesheet" href="{% static "pretixplugins/ticketoutputpdf/editor.css" %}">
|
|
||||||
{% endcompress %}
|
|
||||||
<link type="text/css" rel="stylesheet" href="{% url "plugins:ticketoutputpdf:css" organizer=request.organizer.slug event=request.event.slug %}">
|
|
||||||
@@ -2,7 +2,6 @@ import copy
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from collections import OrderedDict
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
@@ -11,9 +10,7 @@ from django.core.files import File
|
|||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils.formats import date_format
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pytz import timezone
|
|
||||||
from reportlab.graphics import renderPDF
|
from reportlab.graphics import renderPDF
|
||||||
from reportlab.graphics.barcode.qr import QrCodeWidget
|
from reportlab.graphics.barcode.qr import QrCodeWidget
|
||||||
from reportlab.graphics.shapes import Drawing
|
from reportlab.graphics.shapes import Drawing
|
||||||
@@ -29,150 +26,13 @@ from reportlab.platypus import Paragraph
|
|||||||
|
|
||||||
from pretix.base.i18n import language
|
from pretix.base.i18n import language
|
||||||
from pretix.base.models import Order, OrderPosition
|
from pretix.base.models import Order, OrderPosition
|
||||||
from pretix.base.templatetags.money import money_filter
|
from pretix.base.pdf import get_variables
|
||||||
from pretix.base.ticketoutput import BaseTicketOutput
|
from pretix.base.ticketoutput import BaseTicketOutput
|
||||||
from pretix.plugins.ticketoutputpdf.signals import (
|
from pretix.plugins.ticketoutputpdf.signals import get_fonts
|
||||||
get_fonts, layout_text_variables,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.plugins.ticketoutputpdf')
|
logger = logging.getLogger('pretix.plugins.ticketoutputpdf')
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_VARIABLES = OrderedDict((
|
|
||||||
("secret", {
|
|
||||||
"label": _("Ticket code (barcode content)"),
|
|
||||||
"editor_sample": "tdmruoekvkpbv1o2mv8xccvqcikvr58u",
|
|
||||||
"evaluate": lambda orderposition, order, event: orderposition.secret
|
|
||||||
}),
|
|
||||||
("order", {
|
|
||||||
"label": _("Order code"),
|
|
||||||
"editor_sample": "A1B2C",
|
|
||||||
"evaluate": lambda orderposition, order, event: orderposition.order.code
|
|
||||||
}),
|
|
||||||
("item", {
|
|
||||||
"label": _("Product name"),
|
|
||||||
"editor_sample": _("Sample product"),
|
|
||||||
"evaluate": lambda orderposition, order, event: str(orderposition.item)
|
|
||||||
}),
|
|
||||||
("variation", {
|
|
||||||
"label": _("Variation name"),
|
|
||||||
"editor_sample": _("Sample variation"),
|
|
||||||
"evaluate": lambda op, order, event: str(op.variation) if op.variation else ''
|
|
||||||
}),
|
|
||||||
("item_description", {
|
|
||||||
"label": _("Product description"),
|
|
||||||
"editor_sample": _("Sample product description"),
|
|
||||||
"evaluate": lambda orderposition, order, event: str(orderposition.item.description)
|
|
||||||
}),
|
|
||||||
("itemvar", {
|
|
||||||
"label": _("Product name and variation"),
|
|
||||||
"editor_sample": _("Sample product – sample variation"),
|
|
||||||
"evaluate": lambda orderposition, order, event: (
|
|
||||||
'{} - {}'.format(orderposition.item, orderposition.variation)
|
|
||||||
if orderposition.variation else str(orderposition.item)
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
("item_category", {
|
|
||||||
"label": _("Product category"),
|
|
||||||
"editor_sample": _("Ticket category"),
|
|
||||||
"evaluate": lambda orderposition, order, event: (
|
|
||||||
str(orderposition.item.category.name) if orderposition.item.category else ""
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
("price", {
|
|
||||||
"label": _("Price"),
|
|
||||||
"editor_sample": _("123.45 EUR"),
|
|
||||||
"evaluate": lambda op, order, event: money_filter(op.price, event.currency)
|
|
||||||
}),
|
|
||||||
("attendee_name", {
|
|
||||||
"label": _("Attendee name"),
|
|
||||||
"editor_sample": _("John Doe"),
|
|
||||||
"evaluate": lambda op, order, ev: op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '')
|
|
||||||
}),
|
|
||||||
("event_name", {
|
|
||||||
"label": _("Event name"),
|
|
||||||
"editor_sample": _("Sample event name"),
|
|
||||||
"evaluate": lambda op, order, ev: str(ev.name)
|
|
||||||
}),
|
|
||||||
("event_date", {
|
|
||||||
"label": _("Event date"),
|
|
||||||
"editor_sample": _("May 31st, 2017"),
|
|
||||||
"evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=False)
|
|
||||||
}),
|
|
||||||
("event_date_range", {
|
|
||||||
"label": _("Event date range"),
|
|
||||||
"editor_sample": _("May 31st – June 4th, 2017"),
|
|
||||||
"evaluate": lambda op, order, ev: ev.get_date_range_display()
|
|
||||||
}),
|
|
||||||
("event_begin", {
|
|
||||||
"label": _("Event begin date and time"),
|
|
||||||
"editor_sample": _("2017-05-31 20:00"),
|
|
||||||
"evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=True)
|
|
||||||
}),
|
|
||||||
("event_begin_time", {
|
|
||||||
"label": _("Event begin time"),
|
|
||||||
"editor_sample": _("20:00"),
|
|
||||||
"evaluate": lambda op, order, ev: ev.get_time_from_display()
|
|
||||||
}),
|
|
||||||
("event_admission", {
|
|
||||||
"label": _("Event admission date and time"),
|
|
||||||
"editor_sample": _("2017-05-31 19:00"),
|
|
||||||
"evaluate": lambda op, order, ev: date_format(
|
|
||||||
ev.date_admission.astimezone(timezone(ev.settings.timezone)),
|
|
||||||
"SHORT_DATETIME_FORMAT"
|
|
||||||
) if ev.date_admission else ""
|
|
||||||
}),
|
|
||||||
("event_admission_time", {
|
|
||||||
"label": _("Event admission time"),
|
|
||||||
"editor_sample": _("19:00"),
|
|
||||||
"evaluate": lambda op, order, ev: date_format(
|
|
||||||
ev.date_admission.astimezone(timezone(ev.settings.timezone)),
|
|
||||||
"TIME_FORMAT"
|
|
||||||
) if ev.date_admission else ""
|
|
||||||
}),
|
|
||||||
("event_location", {
|
|
||||||
"label": _("Event location"),
|
|
||||||
"editor_sample": _("Random City"),
|
|
||||||
"evaluate": lambda op, order, ev: str(ev.location).replace("\n", "<br/>\n")
|
|
||||||
}),
|
|
||||||
("invoice_name", {
|
|
||||||
"label": _("Invoice address: name"),
|
|
||||||
"editor_sample": _("John Doe"),
|
|
||||||
"evaluate": lambda op, order, ev: order.invoice_address.name if getattr(order, 'invoice_address') else ''
|
|
||||||
}),
|
|
||||||
("invoice_company", {
|
|
||||||
"label": _("Invoice address: company"),
|
|
||||||
"editor_sample": _("Sample company"),
|
|
||||||
"evaluate": lambda op, order, ev: order.invoice_address.company if getattr(order, 'invoice_address') else ''
|
|
||||||
}),
|
|
||||||
("addons", {
|
|
||||||
"label": _("List of Add-Ons"),
|
|
||||||
"editor_sample": _("Addon 1\nAddon 2"),
|
|
||||||
"evaluate": lambda op, order, ev: "<br/>".join([
|
|
||||||
'{} - {}'.format(p.item, p.variation) if p.variation else str(p.item)
|
|
||||||
for p in op.addons.select_related('item', 'variation')
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
("organizer", {
|
|
||||||
"label": _("Organizer name"),
|
|
||||||
"editor_sample": _("Event organizer company"),
|
|
||||||
"evaluate": lambda op, order, ev: str(order.event.organizer.name)
|
|
||||||
}),
|
|
||||||
("organizer_info_text", {
|
|
||||||
"label": _("Organizer info text"),
|
|
||||||
"editor_sample": _("Event organizer info text"),
|
|
||||||
"evaluate": lambda op, order, ev: str(order.event.settings.organizer_info_text)
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
def get_variables(event):
|
|
||||||
v = copy.copy(DEFAULT_VARIABLES)
|
|
||||||
for recv, res in layout_text_variables.send(sender=event):
|
|
||||||
v.update(res)
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
class PdfTicketOutput(BaseTicketOutput):
|
class PdfTicketOutput(BaseTicketOutput):
|
||||||
identifier = 'pdf'
|
identifier = 'pdf'
|
||||||
verbose_name = _('PDF output')
|
verbose_name = _('PDF output')
|
||||||
|
|||||||
@@ -5,9 +5,4 @@ from . import views
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/pdfoutput/editor/$', views.EditorView.as_view(),
|
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/pdfoutput/editor/$', views.EditorView.as_view(),
|
||||||
name='editor'),
|
name='editor'),
|
||||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/pdfoutput/editor/webfonts.css',
|
|
||||||
views.FontsCSSView.as_view(),
|
|
||||||
name='css'),
|
|
||||||
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/pdfoutput/editor/(?P<filename>[^/]+).pdf$',
|
|
||||||
views.PdfView.as_view(), name='pdf'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,191 +1,52 @@
|
|||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import mimetypes
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
from django.templatetags.static import static
|
||||||
from django.core.files import File
|
from django.utils.translation import ugettext_lazy as _
|
||||||
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 (
|
from pretix.base.models import (
|
||||||
CachedCombinedTicket, CachedFile, CachedTicket, InvoiceAddress,
|
CachedCombinedTicket, CachedTicket, OrderPosition,
|
||||||
)
|
)
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.views.pdf import BaseEditorView
|
||||||
from pretix.helpers.database import rolledback_transaction
|
from pretix.plugins.ticketoutputpdf.ticketoutput import PdfTicketOutput
|
||||||
from pretix.plugins.ticketoutputpdf.signals import get_fonts
|
|
||||||
|
|
||||||
from .ticketoutput import PdfTicketOutput, get_variables
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EditorView(EventPermissionRequiredMixin, TemplateView):
|
class EditorView(BaseEditorView):
|
||||||
template_name = 'pretixplugins/ticketoutputpdf/index.html'
|
title = _('Default ticket layout')
|
||||||
permission = 'can_change_settings'
|
|
||||||
accepted_formats = (
|
|
||||||
'application/pdf',
|
|
||||||
)
|
|
||||||
maxfilesize = 1024 * 1024 * 10
|
|
||||||
minfilesize = 10
|
|
||||||
identifier = 'pdf'
|
|
||||||
|
|
||||||
def get_output(self, *args, **kwargs):
|
def get_output(self, *args, **kwargs):
|
||||||
return PdfTicketOutput(self.request.event, *args, **kwargs)
|
return PdfTicketOutput(self.request.event, *args, **kwargs)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def save_layout(self):
|
||||||
resp = super().get(request, *args, **kwargs)
|
super().save_layout()
|
||||||
resp._csp_ignore = True
|
CachedTicket.objects.filter(
|
||||||
return resp
|
order_position__order__event=self.request.event, provider='pdf'
|
||||||
|
).delete()
|
||||||
|
CachedCombinedTicket.objects.filter(
|
||||||
|
order__event=self.request.event, provider='pdf'
|
||||||
|
).delete()
|
||||||
|
|
||||||
def process_upload(self):
|
def get_layout_settings_key(self):
|
||||||
f = self.request.FILES.get('background')
|
return 'ticketoutput_pdf_layout'
|
||||||
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):
|
def get_background_settings_key(self):
|
||||||
item = self.request.event.items.create(name=_("Sample product"), default_price=42.23,
|
return 'ticketoutput_pdf_background'
|
||||||
description=_("Sample product description"))
|
|
||||||
item2 = self.request.event.items.create(name=_("Sample workshop"), default_price=23.40)
|
|
||||||
|
|
||||||
from pretix.base.models import Order
|
def get_default_background(self):
|
||||||
order = self.request.event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
|
return static('pretixpresale/pdf/ticket_default_a4.pdf')
|
||||||
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)
|
def generate(self, p: OrderPosition, override_layout=None, override_background=None):
|
||||||
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
|
prov = self.get_output(
|
||||||
order.positions.create(item=item2, attendee_name=_("John Doe"), price=item.default_price, addon_to=p)
|
override_layout=override_layout,
|
||||||
|
override_background=override_background
|
||||||
InvoiceAddress.objects.create(order=order, name=_("John Doe"), company=_("Sample company"))
|
|
||||||
return p
|
|
||||||
|
|
||||||
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('plugins:ticketoutputpdf:pdf', 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()
|
|
||||||
|
|
||||||
prov = self.get_output(
|
|
||||||
override_layout=(json.loads(request.POST.get("data"))
|
|
||||||
if request.POST.get("data") else None),
|
|
||||||
override_background=cf.file if cf else None
|
|
||||||
)
|
|
||||||
fname, mimet, data = prov.generate(p)
|
|
||||||
|
|
||||||
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:
|
|
||||||
fexisting = request.event.settings.get('ticketoutput_{}_layout'.format(self.identifier), 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, 'ticketoutput_{}_layout'.format(self.identifier), nonce, 'pdf'
|
|
||||||
)
|
|
||||||
newname = default_storage.save(fname, cf.file)
|
|
||||||
request.event.settings.set('ticketoutput_{}_background'.format(self.identifier), 'file://' + newname)
|
|
||||||
|
|
||||||
request.event.settings.set('ticketoutput_{}_layout'.format(self.identifier), request.POST.get("data"))
|
|
||||||
|
|
||||||
CachedTicket.objects.filter(
|
|
||||||
order_position__order__event=self.request.event, provider=self.identifier
|
|
||||||
).delete()
|
|
||||||
CachedCombinedTicket.objects.filter(
|
|
||||||
order__event=self.request.event, provider=self.identifier
|
|
||||||
).delete()
|
|
||||||
|
|
||||||
return JsonResponse({'status': 'ok'})
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
ctx = super().get_context_data(**kwargs)
|
|
||||||
prov = self.get_output()
|
|
||||||
ctx['fonts'] = get_fonts()
|
|
||||||
ctx['pdf'] = (
|
|
||||||
self.request.event.settings.get('ticketoutput_{}_background'.format(self.identifier)).url
|
|
||||||
if self.request.event.settings.get('ticketoutput_{}_background'.format(self.identifier))
|
|
||||||
else static('pretixpresale/pdf/ticket_default_a4.pdf')
|
|
||||||
)
|
)
|
||||||
ctx['variables'] = get_variables(self.request.event)
|
fname, mimet, data = prov.generate(p)
|
||||||
ctx['layout'] = json.dumps(
|
return fname, mimet, data
|
||||||
self.request.event.settings.get('ticketoutput_{}_layout'.format(self.identifier), as_type=list)
|
|
||||||
|
def get_current_layout(self):
|
||||||
|
prov = self.get_output()
|
||||||
|
return (
|
||||||
|
self.request.event.settings.get(self.get_layout_settings_key(), as_type=list)
|
||||||
or prov._default_layout()
|
or prov._default_layout()
|
||||||
)
|
)
|
||||||
return ctx
|
|
||||||
|
|
||||||
|
|
||||||
class FontsCSSView(TemplateView):
|
|
||||||
content_type = 'text/css'
|
|
||||||
template_name = 'pretixplugins/ticketoutputpdf/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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user