forked from CGM_Public/pretix_original
Add ticket downloads to the backend
This commit is contained in:
@@ -260,6 +260,22 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<span class="fa fa-calendar"></span> {{ line.subevent.name }} · {{ line.subevent.get_date_range_display }}
|
<span class="fa fa-calendar"></span> {{ line.subevent.name }} · {{ line.subevent.get_date_range_display }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="position-buttons">
|
||||||
|
{% if not line.addon_to or request.event.settings.ticket_download_addons %}
|
||||||
|
{% if line.item.admission or request.event.settings.ticket_download_nonadm %}
|
||||||
|
{% for b in download_buttons %}
|
||||||
|
<form action="{% url "control:event.order.download.ticket" code=order.code event=request.event.slug organizer=request.event.organizer.slug position=line.pk output=b.identifier %}"
|
||||||
|
method="post" data-asynctask data-asynctask-download
|
||||||
|
class="form-inline helper-display-inline">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit"
|
||||||
|
class="btn btn-xs btn-default">
|
||||||
|
<span class="fa {{ b.icon }}"></span> {{ b.text }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% eventsignal event "pretix.control.signals.order_position_buttons" order=order position=line request=request %}
|
{% eventsignal event "pretix.control.signals.order_position_buttons" order=order position=line request=request %}
|
||||||
</div>
|
</div>
|
||||||
{% if line.has_questions %}
|
{% if line.has_questions %}
|
||||||
|
|||||||
@@ -199,6 +199,9 @@ urlpatterns = [
|
|||||||
name='event.order.regeninvoice'),
|
name='event.order.regeninvoice'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/reissue$', orders.OrderInvoiceReissue.as_view(),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/reissue$', orders.OrderInvoiceReissue.as_view(),
|
||||||
name='event.order.reissueinvoice'),
|
name='event.order.reissueinvoice'),
|
||||||
|
url(r'^orders/(?P<code>[0-9A-Z]+)/download/(?P<position>\d+)/(?P<output>[^/]+)/$',
|
||||||
|
orders.OrderDownload.as_view(),
|
||||||
|
name='event.order.download.ticket'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/answer/(?P<answer>[^/]+)/$',
|
url(r'^orders/(?P<code>[0-9A-Z]+)/answer/(?P<answer>[^/]+)/$',
|
||||||
orders.AnswerDownload.as_view(),
|
orders.AnswerDownload.as_view(),
|
||||||
name='event.order.download.answer'),
|
name='event.order.download.answer'),
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ from django.contrib import messages
|
|||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.http import FileResponse, Http404, HttpResponseNotAllowed
|
from django.http import (
|
||||||
|
FileResponse, Http404, HttpResponseNotAllowed, JsonResponse,
|
||||||
|
)
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
@@ -34,7 +36,9 @@ from pretix.base.models import (
|
|||||||
generate_position_secret, generate_secret,
|
generate_position_secret, generate_secret,
|
||||||
)
|
)
|
||||||
from pretix.base.models.event import SubEvent
|
from pretix.base.models.event import SubEvent
|
||||||
from pretix.base.models.orders import OrderFee, OrderPayment, OrderRefund
|
from pretix.base.models.orders import (
|
||||||
|
OrderFee, OrderPayment, OrderPosition, OrderRefund,
|
||||||
|
)
|
||||||
from pretix.base.models.tax import EU_COUNTRIES
|
from pretix.base.models.tax import EU_COUNTRIES
|
||||||
from pretix.base.payment import PaymentException
|
from pretix.base.payment import PaymentException
|
||||||
from pretix.base.services.export import export
|
from pretix.base.services.export import export
|
||||||
@@ -49,7 +53,10 @@ from pretix.base.services.orders import (
|
|||||||
extend_order, mark_order_expired, mark_order_refunded,
|
extend_order, mark_order_expired, mark_order_refunded,
|
||||||
)
|
)
|
||||||
from pretix.base.services.stats import order_overview
|
from pretix.base.services.stats import order_overview
|
||||||
from pretix.base.signals import register_data_exporters
|
from pretix.base.services.tickets import generate
|
||||||
|
from pretix.base.signals import (
|
||||||
|
register_data_exporters, register_ticket_outputs,
|
||||||
|
)
|
||||||
from pretix.base.templatetags.money import money_filter
|
from pretix.base.templatetags.money import money_filter
|
||||||
from pretix.base.views.mixins import OrderQuestionsViewMixin
|
from pretix.base.views.mixins import OrderQuestionsViewMixin
|
||||||
from pretix.base.views.tasks import AsyncAction
|
from pretix.base.views.tasks import AsyncAction
|
||||||
@@ -162,8 +169,24 @@ class OrderDetail(OrderView):
|
|||||||
|
|
||||||
ctx['overpaid'] = self.order.pending_sum * -1
|
ctx['overpaid'] = self.order.pending_sum * -1
|
||||||
ctx['sales_channel'] = get_all_sales_channels().get(self.order.sales_channel)
|
ctx['sales_channel'] = get_all_sales_channels().get(self.order.sales_channel)
|
||||||
|
ctx['download_buttons'] = self.download_buttons
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def download_buttons(self):
|
||||||
|
buttons = []
|
||||||
|
|
||||||
|
responses = register_ticket_outputs.send(self.request.event)
|
||||||
|
for receiver, response in responses:
|
||||||
|
provider = response(self.request.event)
|
||||||
|
buttons.append({
|
||||||
|
'text': provider.download_button_text or 'Ticket',
|
||||||
|
'icon': provider.download_button_icon or 'fa-download',
|
||||||
|
'identifier': provider.identifier,
|
||||||
|
'multi': provider.multi_download_enabled
|
||||||
|
})
|
||||||
|
return buttons
|
||||||
|
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
queryset = self.object.positions.all()
|
queryset = self.object.positions.all()
|
||||||
|
|
||||||
@@ -210,6 +233,100 @@ class OrderDetail(OrderView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class OrderDownload(AsyncAction, OrderView):
|
||||||
|
task = generate
|
||||||
|
permission = 'can_view_orders'
|
||||||
|
|
||||||
|
def get_success_url(self, value):
|
||||||
|
return self.get_self_url()
|
||||||
|
|
||||||
|
def get_error_url(self):
|
||||||
|
return self.get_order_url()
|
||||||
|
|
||||||
|
def get_self_url(self):
|
||||||
|
return reverse('control:event.order.download.ticket', kwargs=self.kwargs)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def output(self):
|
||||||
|
responses = register_ticket_outputs.send(self.request.event)
|
||||||
|
for receiver, response in responses:
|
||||||
|
provider = response(self.request.event)
|
||||||
|
if provider.identifier == self.kwargs.get('output'):
|
||||||
|
return provider
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def order_position(self):
|
||||||
|
try:
|
||||||
|
return self.order.positions.get(pk=self.kwargs.get('position'))
|
||||||
|
except OrderPosition.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if 'async_id' in request.GET and settings.HAS_CELERY:
|
||||||
|
return self.get_result(request)
|
||||||
|
ct = self.get_last_ct()
|
||||||
|
if ct:
|
||||||
|
return self.success(ct)
|
||||||
|
return self.http_method_not_allowed(request)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
if not self.output:
|
||||||
|
return self.error(_('You requested an invalid ticket output type.'))
|
||||||
|
if not self.order_position:
|
||||||
|
raise Http404(_('Unknown order code or not authorized to access this order.'))
|
||||||
|
if 'position' in kwargs and (self.order_position.addon_to and not self.request.event.settings.ticket_download_addons):
|
||||||
|
return self.error(_('Ticket download is not enabled for add-on products.'))
|
||||||
|
if 'position' in kwargs and (not self.order_position.item.admission and not self.request.event.settings.ticket_download_nonadm):
|
||||||
|
return self.error(_('Ticket download is not enabled for non-admission products.'))
|
||||||
|
|
||||||
|
ct = self.get_last_ct()
|
||||||
|
if ct:
|
||||||
|
return self.success(ct)
|
||||||
|
return self.do('orderposition' if 'position' in kwargs else 'order',
|
||||||
|
self.order_position.pk if 'position' in kwargs else self.order.pk,
|
||||||
|
self.output.identifier)
|
||||||
|
|
||||||
|
def get_success_message(self, value):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def success(self, value):
|
||||||
|
if "ajax" in self.request.POST or "ajax" in self.request.GET:
|
||||||
|
return JsonResponse({
|
||||||
|
'ready': True,
|
||||||
|
'success': True,
|
||||||
|
'redirect': self.get_success_url(value),
|
||||||
|
'message': str(self.get_success_message(value))
|
||||||
|
})
|
||||||
|
if isinstance(value, CachedTicket):
|
||||||
|
resp = FileResponse(value.file.file, content_type=value.type)
|
||||||
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}{}"'.format(
|
||||||
|
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
||||||
|
self.output.identifier, value.extension
|
||||||
|
)
|
||||||
|
return resp
|
||||||
|
elif isinstance(value, CachedCombinedTicket):
|
||||||
|
resp = FileResponse(value.file.file, content_type=value.type)
|
||||||
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}{}"'.format(
|
||||||
|
self.request.event.slug.upper(), self.order.code, self.output.identifier, value.extension
|
||||||
|
)
|
||||||
|
return resp
|
||||||
|
else:
|
||||||
|
return redirect(self.get_self_url())
|
||||||
|
|
||||||
|
def get_last_ct(self):
|
||||||
|
if 'position' in self.kwargs:
|
||||||
|
ct = CachedTicket.objects.filter(
|
||||||
|
order_position=self.order_position, provider=self.output.identifier, file__isnull=False
|
||||||
|
).last()
|
||||||
|
else:
|
||||||
|
ct = CachedCombinedTicket.objects.filter(
|
||||||
|
order=self.order, provider=self.output.identifier, file__isnull=False
|
||||||
|
).last()
|
||||||
|
if not ct or not ct.file:
|
||||||
|
return None
|
||||||
|
return ct
|
||||||
|
|
||||||
|
|
||||||
class OrderComment(OrderView):
|
class OrderComment(OrderView):
|
||||||
permission = 'can_change_orders'
|
permission = 'can_change_orders'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user