mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Move PDF editor out of plugin and into core
This commit is contained in:
354
src/pretix/control/templates/pretixcontrol/pdf/index.html
Normal file
354
src/pretix/control/templates/pretixcontrol/pdf/index.html
Normal file
@@ -0,0 +1,354 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load compress %}
|
||||
{% 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 %}
|
||||
<h1>
|
||||
{% trans "PDF Editor" %}
|
||||
{% if title %}
|
||||
<small>{{ title }}</small>
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
<script type="application/json" id="editor-data">
|
||||
{{ layout|safe }}
|
||||
</script>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs" id="toolbox-source"
|
||||
title="{% trans "Code" %}">
|
||||
<span class="fa fa-code"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-xs" id="toolbox-paste"
|
||||
title="{% trans "Paste" %}">
|
||||
<span class="fa fa-paste"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-xs" id="toolbox-undo"
|
||||
title="{% trans "Undo" %}">
|
||||
<span class="fa fa-undo"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-xs" id="toolbox-redo"
|
||||
title="{% trans "Redo" %}">
|
||||
<span class="fa fa-repeat"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% trans "Editor" %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="editor-canvas-area">
|
||||
<canvas id="pdf-canvas"
|
||||
data-pdf-url="{{ pdf }}"
|
||||
data-worker-url="{% static "pretixplugins/ticketoutputpdf/pdf.worker.js" %}">
|
||||
|
||||
</canvas>
|
||||
<div id="fabric-container">
|
||||
<canvas id="fabric-canvas">
|
||||
</canvas>
|
||||
</div>
|
||||
<div id="source-container">
|
||||
<div class="alert alert-warning">
|
||||
<strong>
|
||||
{% blocktrans trimmed %}
|
||||
This feature is only intended for advanced users. We recommend to only use it
|
||||
to copy and share ticket designs, not to modify the design source code.
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
</div>
|
||||
<p>
|
||||
<textarea id="source-textarea" class="form-control"></textarea>
|
||||
</p>
|
||||
<p class="text-right">
|
||||
<button class="btn btn-default" id="source-close">
|
||||
{% trans "Cancel" %}
|
||||
</button>
|
||||
<button class="btn btn-default" id="source-save">
|
||||
{% trans "Apply" %}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div id="loading-container">
|
||||
<div id="loading-upload">
|
||||
<span class="fa fa-cog big-rotating-icon"></span>
|
||||
<p>
|
||||
{% trans "Uploading new PDF background…" %}
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: 0%;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading-initial">
|
||||
<h2>{% trans "Welcome to the PDF ticket editor!" %}</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
This editor allows you to create a design for the PDF tickets of your event.
|
||||
You can upload a background PDF and then use this tool to place texts and
|
||||
a QR code on the ticket.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p> </p>
|
||||
<p>
|
||||
<span class="fa fa-eye fa-2x"></span>
|
||||
</p>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Please note that the editor can only provide a rough preview. Some details,
|
||||
for example in text rendering, might look slightly different in the final
|
||||
tickets. You can use the "Preview" button on the right for a more precise
|
||||
preview.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p> </p>
|
||||
<p>
|
||||
<span class="fa fa-chrome fa-2x"></span>
|
||||
<span class="fa fa-firefox fa-2x"></span>
|
||||
<span class="fa fa-opera fa-2x"></span>
|
||||
</p>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The editor is tested with recent versions of Google Chrome, Mozilla Firefox
|
||||
and Opera. Other browsers, especially Internet Explorer or Microsoft Edge, might
|
||||
have problems displaying your background PDF or loading the correct fonts.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<noscript>
|
||||
<div class="alert alert-danger">
|
||||
{% blocktrans trimmed %}
|
||||
The editor requires JavaScript to work. Please enable JavaScript in your
|
||||
browser to continue.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</noscript>
|
||||
<p> </p>
|
||||
<p>
|
||||
<em id="editor-loading">
|
||||
<span class="fa fa-cog fa-spin"></span>
|
||||
{% trans "Loading…" %}
|
||||
</em>
|
||||
<button id="editor-start" class="btn btn-primary sr-only">
|
||||
{% trans "Start editing" %}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3" id="editor-toolbox-area">
|
||||
<div class="panel panel-default" id="toolbox">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right object-buttons">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs" id="toolbox-cut"
|
||||
title="{% trans "Cut" %}">
|
||||
<span class="fa fa-cut"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-xs" id="toolbox-copy"
|
||||
title="{% trans "Copy" %}">
|
||||
<span class="fa fa-copy"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger btn-xs" id="toolbox-delete"
|
||||
title="{% trans "Delete" %}">
|
||||
<span class="fa fa-trash"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span id="toolbox-heading">
|
||||
{% trans "Loading…" %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-body" id="toolbox-body">
|
||||
<div class="row control-group pdf-info">
|
||||
<div class="col-sm-6">
|
||||
<label>{% trans "Width (mm)" %}</label><br>
|
||||
<input type="number" id="pdf-info-width" class="input-block-level form-control" disabled>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label>{% trans "Height (mm)" %}</label><br>
|
||||
<input type="number" id="pdf-info-height" class="input-block-level form-control" disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group pdf-info">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Background PDF" %}</label><br>
|
||||
<span class="btn btn-default fileinput-button">
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>{% trans "Upload new background" %}</span>
|
||||
<input id="fileupload" type="file" name="background">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group position">
|
||||
<div class="col-sm-6">
|
||||
<label>{% trans "x (mm)" %}</label><br>
|
||||
<input type="number" value="13" class="input-block-level form-control" step="0.01"
|
||||
id="toolbox-position-x">
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label>{% trans "y (mm)" %}</label><br>
|
||||
<input type="number" value="13" class="input-block-level form-control" step="0.01"
|
||||
id="toolbox-position-y">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group squaresize">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Size (mm)" %}</label><br>
|
||||
<input type="number" value="13" class="input-block-level form-control" step="0.01"
|
||||
id="toolbox-squaresize">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group squaresize">
|
||||
<div class="col-sm-12">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The final QR code will be slightly smaller because some whitespace is required
|
||||
for proper scanning.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group text">
|
||||
<div class="col-sm-6">
|
||||
<label>{% trans "Font size (pt)" %}</label><br>
|
||||
<input type="number" value="13" class="input-block-level form-control" step="0.1"
|
||||
id="toolbox-fontsize">
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label> </label><br>
|
||||
<div class="btn-group btn-group-justified" role="group">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default toggling" data-action="bold">
|
||||
<span class="fa fa-bold"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default toggling" data-action="italic">
|
||||
<span class="fa fa-italic"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group text">
|
||||
<div class="col-sm-6">
|
||||
<label>{% trans "Text color" %}</label><br>
|
||||
<input type="text" value="#000000" class="input-block-level form-control colorpickerfield"
|
||||
id="toolbox-col">
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label> </label><br>
|
||||
<div class="btn-group btn-group-justified" id="toolbox-align">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default option toggling" data-action="left">
|
||||
<span class="fa fa-align-left"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default option toggling" data-action="center">
|
||||
<span class="fa fa-align-center"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default option toggling" data-action="right">
|
||||
<span class="fa fa-align-right"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group text">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Font" %}</label><br>
|
||||
<select class="input-block-level form-control" id="toolbox-fontfamily">
|
||||
<option>Open Sans</option>
|
||||
{% for family in fonts.keys %}
|
||||
<option>{{ family }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group text">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Width (mm)" %}</label><br>
|
||||
<input type="number" value="13" class="input-block-level form-control" step="0.01"
|
||||
id="toolbox-textwidth">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group text">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Text content" %}</label><br>
|
||||
<select class="input-block-level form-control" id="toolbox-content">
|
||||
{% for varname, var in variables.items %}
|
||||
<option data-sample="{{ var.editor_sample }}" value="{{ varname }}">{{ var.label }}</option>
|
||||
{% endfor %}
|
||||
{% for p in request.organizer.meta_properties.all %}
|
||||
<option value="meta:{{ p.name }}">
|
||||
{% trans "Event attribute:" %} {{ p.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
<option value="other">{% trans "Other…" %}</option>
|
||||
</select>
|
||||
<textarea type="text" value="" class="input-block-level form-control"
|
||||
id="toolbox-content-other"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-toolbox-text panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% trans "Add a new object" %}
|
||||
</div>
|
||||
<div class="panel-body add-buttons">
|
||||
<button class="btn btn-default btn-block" id="editor-add-text" disabled>
|
||||
<span class="fa fa-font"></span>
|
||||
{% trans "Text" %}
|
||||
</button>
|
||||
<button class="btn btn-default btn-block" id="editor-add-qrcode" disabled>
|
||||
<span class="fa fa-qrcode"></span>
|
||||
{% trans "QR code area" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" action="" id="preview-form" target="_blank">
|
||||
<div class="form-group submit-group">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" value="" name="data">
|
||||
<input type="hidden" value="" name="background">
|
||||
<input type="hidden" value="true" name="preview">
|
||||
<button type="submit" class="btn btn-default btn-lg" id="editor-preview">
|
||||
{% trans "Preview" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-save" id="editor-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="{% static "pretixplugins/ticketoutputpdf/pdf.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixplugins/ticketoutputpdf/fabric.min.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixplugins/ticketoutputpdf/editor.js" %}"></script>
|
||||
{% for family, styles in fonts.items %}
|
||||
{% for style, formats in styles.items %}
|
||||
<span class="preload-font" data-family="{{ family }}" data-style="{{ style }}">
|
||||
giItT1WQy@!-/#
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
36
src/pretix/control/templates/pretixcontrol/pdf/webfonts.css
Normal file
36
src/pretix/control/templates/pretixcontrol/pdf/webfonts.css
Normal file
@@ -0,0 +1,36 @@
|
||||
{% load staticfiles %}
|
||||
|
||||
{% for family, styles in fonts.items %}
|
||||
{% for style, formats in styles.items %}
|
||||
@font-face {
|
||||
font-family: '{{ family }}';
|
||||
{% if style == "italic" or style == "bolditalic" %}
|
||||
font-style: italic;
|
||||
{% else %}
|
||||
font-style: normal;
|
||||
{% endif %}
|
||||
{% if style == "bold" or style == "bolditalic" %}
|
||||
font-weight: bold;
|
||||
{% else %}
|
||||
font-weight: normal;
|
||||
{% endif %}
|
||||
src: {% if "woff2" in formats %}url('{% static formats.woff2 %}') format('woff2'),{% endif %}
|
||||
{% if "woff" in formats %}url('{% static formats.woff %}') format('woff'),{% endif %}
|
||||
{% if "truetype" in formats %}url('{% static formats.truetype %}') format('truetype'){% endif %};
|
||||
}
|
||||
.preload-font[data-family="{{family}}"][data-style="{{style}}"] {
|
||||
font-family: '{{ family }}';
|
||||
{% if style == "italic" or style == "bolditalic" %}
|
||||
font-style: italic;
|
||||
{% else %}
|
||||
font-style: normal;
|
||||
{% endif %}
|
||||
{% if style == "bold" or style == "bolditalic" %}
|
||||
font-weight: bold;
|
||||
{% else %}
|
||||
font-weight: normal;
|
||||
{% endif %}
|
||||
|
||||
}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -2,7 +2,7 @@ from django.conf.urls import include, url
|
||||
|
||||
from pretix.control.views import (
|
||||
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,
|
||||
)
|
||||
|
||||
@@ -97,6 +97,8 @@ urlpatterns = [
|
||||
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/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/select2$', typeahead.subevent_select2, name='event.subevents.select2'),
|
||||
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
|
||||
Reference in New Issue
Block a user