diff --git a/src/pretix/plugins/ticketoutputpdf/signals.py b/src/pretix/plugins/ticketoutputpdf/signals.py index 9d05460da..b5f467ef3 100644 --- a/src/pretix/plugins/ticketoutputpdf/signals.py +++ b/src/pretix/plugins/ticketoutputpdf/signals.py @@ -23,17 +23,19 @@ import copy import json from django.dispatch import receiver +from django.template.loader import get_template from django.urls import reverse from django.utils.html import escape from django.utils.translation import gettext_lazy as _ from pretix.base.channels import get_all_sales_channels +from pretix.base.models import Event from pretix.base.signals import ( # NOQA: legacy import EventPluginSignal, event_copy_data, item_copy_data, layout_text_variables, logentry_display, logentry_object_link, register_data_exporters, register_multievent_data_exporters, register_ticket_outputs, ) -from pretix.control.signals import item_forms +from pretix.control.signals import item_forms, order_position_buttons from pretix.plugins.ticketoutputpdf.forms import TicketLayoutItemForm from pretix.plugins.ticketoutputpdf.models import ( TicketLayout, TicketLayoutItem, @@ -160,6 +162,53 @@ def pdf_logentry_object_link(sender, logentry, **kwargs): return a_text.format_map(a_map) +def _ticket_layouts_for_item(request, item): + if not hasattr(request, '_ticket_layouts_for_item'): + request._ticket_layouts_for_item = {} + if item.pk not in request._ticket_layouts_for_item: + request._ticket_layouts_for_item[item.pk] = { + tli.sales_channel: tli.layout + for tli in item.ticketlayout_assignments.select_related('layout') + } + if request._ticket_layouts_for_item[item.pk] and 'web' not in request._ticket_layouts_for_item[item.pk]: + request._ticket_layouts_for_item[item.pk]['web'] = request.event.ticket_layouts.get(default=True) + + return request._ticket_layouts_for_item[item.pk] + + +@receiver(order_position_buttons, dispatch_uid="pretix_ticketoutputpdf_control_order_buttons") +def control_order_position_info(sender: Event, position, request, order, **kwargs): + if not position.generate_ticket: + return '' + + layouts = [] + seen = set() + lm = _ticket_layouts_for_item(request, position.item) + if order.sales_channel in lm: + seen.add(lm[order.sales_channel]) + for k, l in lm.items(): + if k == order.sales_channel or l in seen: + continue + layouts.append({ + 'label': str(l.name), + 'channel': k, + }) + seen.add(l) + + if not layouts: + return '' + + template = get_template('pretixplugins/ticketoutputpdf/control_order_position_buttons.html') + ctx = { + 'order': position.order, + 'request': request, + 'event': sender, + 'position': position, + 'layouts': layouts, + } + return template.render(ctx, request=request).strip() + + override_layout = EventPluginSignal() """ Arguments: ``layout``, ``orderposition`` diff --git a/src/pretix/plugins/ticketoutputpdf/ticketoutput.py b/src/pretix/plugins/ticketoutputpdf/ticketoutput.py index dd99310e7..ce3b9926c 100644 --- a/src/pretix/plugins/ticketoutputpdf/ticketoutput.py +++ b/src/pretix/plugins/ticketoutputpdf/ticketoutput.py @@ -65,9 +65,10 @@ class PdfTicketOutput(BaseTicketOutput): multi_download_button_text = _('Download tickets (PDF)') long_download_button_text = _('Download ticket (PDF)') - def __init__(self, event, override_layout=None, override_background=None): + def __init__(self, event, override_layout=None, override_background=None, override_channel=None): self.override_layout = override_layout self.override_background = override_background + self.override_channel = override_channel super().__init__(event) @cached_property @@ -117,7 +118,7 @@ class PdfTicketOutput(BaseTicketOutput): for op in order.positions_with_tickets: layout = override_layout.send_chained( order.event, 'layout', orderposition=op, layout=self.layout_map.get( - (op.item_id, order.sales_channel), + (op.item_id, self.override_channel or order.sales_channel), self.layout_map.get( (op.item_id, 'web'), self.default_layout @@ -138,7 +139,7 @@ class PdfTicketOutput(BaseTicketOutput): layout = override_layout.send_chained( order.event, 'layout', orderposition=op, layout=self.layout_map.get( - (op.item_id, order.sales_channel), + (op.item_id, self.override_channel or order.sales_channel), self.layout_map.get( (op.item_id, 'web'), self.default_layout diff --git a/src/pretix/plugins/ticketoutputpdf/urls.py b/src/pretix/plugins/ticketoutputpdf/urls.py index 863772b46..1180ce0f4 100644 --- a/src/pretix/plugins/ticketoutputpdf/urls.py +++ b/src/pretix/plugins/ticketoutputpdf/urls.py @@ -27,10 +27,12 @@ from pretix.plugins.ticketoutputpdf.api import ( ) from pretix.plugins.ticketoutputpdf.views import ( LayoutCreate, LayoutDelete, LayoutEditorView, LayoutGetDefault, - LayoutListView, LayoutSetDefault, + LayoutListView, LayoutSetDefault, OrderPrintDo, ) urlpatterns = [ + re_path(r'^control/event/(?P[^/]+)/(?P[^/]+)/badges/print$', + OrderPrintDo.as_view(), name='print'), re_path(r'^control/event/(?P[^/]+)/(?P[^/]+)/pdfoutput/$', LayoutListView.as_view(), name='index'), re_path(r'^control/event/(?P[^/]+)/(?P[^/]+)/pdfoutput/add$', diff --git a/src/pretix/plugins/ticketoutputpdf/views.py b/src/pretix/plugins/ticketoutputpdf/views.py index 0a1e38375..6bc4bf6bc 100644 --- a/src/pretix/plugins/ticketoutputpdf/views.py +++ b/src/pretix/plugins/ticketoutputpdf/views.py @@ -21,6 +21,7 @@ # import json import logging +from datetime import timedelta from io import BytesIO from django.contrib import messages @@ -29,10 +30,11 @@ from django.core.files import File from django.core.files.storage import default_storage from django.db import transaction from django.http import Http404 -from django.shortcuts import redirect +from django.shortcuts import get_object_or_404, redirect from django.templatetags.static import static from django.urls import reverse from django.utils.functional import cached_property +from django.utils.timezone import now from django.utils.translation import gettext, gettext_lazy as _ from django.views import View from django.views.generic import CreateView, DeleteView, DetailView, ListView @@ -48,7 +50,9 @@ from pretix.helpers.models import modelcopy from pretix.plugins.ticketoutputpdf.forms import TicketLayoutForm from pretix.plugins.ticketoutputpdf.ticketoutput import PdfTicketOutput +from ...base.views.tasks import AsyncAction from .models import TicketLayout +from .tasks import tickets_create_pdf logger = logging.getLogger(__name__) @@ -286,3 +290,42 @@ class LayoutEditorView(BaseEditorView): self.layout.background.delete() self.layout.background.save('background.pdf', f.file) invalidate_cache.apply_async(kwargs={'event': self.request.event.pk, 'provider': 'pdf'}) + + +class OrderPrintDo(EventPermissionRequiredMixin, AsyncAction, View): + task = tickets_create_pdf + permission = 'can_view_orders' + known_errortypes = ['OrderError', 'ExportError'] + + def get_success_message(self, value): + return None + + def get_success_url(self, value): + return reverse('cachedfile.download', kwargs={'id': str(value)}) + + def get_error_url(self): + return reverse('control:event.index', kwargs={ + 'organizer': self.request.organizer.slug, + 'event': self.request.event.slug, + }) + + def get_error_message(self, exception): + if isinstance(exception, str): + return exception + return super().get_error_message(exception) + + def post(self, request, *args, **kwargs): + order = get_object_or_404(self.request.event.orders, code=request.GET.get("code")) + cf = CachedFile(web_download=True, session_key=self.request.session.session_key) + cf.date = now() + cf.type = 'application/pdf' + cf.expires = now() + timedelta(days=3) + position = get_object_or_404(order.positions, pk=request.GET.get('position')) + cf.filename = f'tickets_{self.request.event.slug}_{order.code}-{position.positionid}.pdf' + cf.save() + return self.do( + self.request.event.pk, + str(cf.id), + position.pk, + request.GET.get('channel'), + )