Do not force PDFs to be downloaded (Z#23225892) (#5994)

* Display invoice and tickets inline in browser (Z#23225892)

* Use FileResponse filename for AnswerDownload

* Use inline for PDF-view in pretix-control editor

* use as_attachment for API FileResponses

* do not ignore csp even for disposition=inline

* use as_attachment for file responses in control

* remove unused code

* improve code style

* Invoice preview inline

* do not force download on tickets in backend

* do not force download on AnswerDownload

* imrpove code style

* improve code style

* fix missing int str conversion

* Apply suggestions from code review

Co-authored-by: luelista <mira@teamwiki.de>

---------

Co-authored-by: luelista <mira@teamwiki.de>
This commit is contained in:
Richard Schreiber
2026-04-14 09:12:09 +02:00
committed by GitHub
parent 059ff6c99b
commit 5682d3ed56
7 changed files with 90 additions and 82 deletions

View File

@@ -763,12 +763,7 @@ class InvoicePreview(EventPermissionRequiredMixin, View):
def get(self, request, *args, **kwargs):
fname, ftype, fcontent = build_preview_invoice_pdf(request.event)
resp = HttpResponse(fcontent, content_type=ftype)
if settings.DEBUG:
# attachment is more secure as we're dealing with user-generated stuff here, but inline is much more convenient during debugging
resp['Content-Disposition'] = 'inline; filename="{}"'.format(fname)
resp._csp_ignore = True
else:
resp['Content-Disposition'] = 'attachment; filename="{}"'.format(fname)
resp['Content-Disposition'] = 'inline; filename="{}"'.format(fname)
return resp

View File

@@ -300,5 +300,4 @@ class SysReportView(AdministratorPermissionRequiredMixin, TemplateView):
resp = HttpResponse(data)
resp['Content-Type'] = mime
resp['Content-Disposition'] = 'inline; filename="{}"'.format(name)
resp._csp_ignore = True
return resp

View File

@@ -710,22 +710,26 @@ class OrderDownload(AsyncAction, OrderView):
resp = HttpResponseRedirect(value.file.file.read())
return resp
else:
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 FileResponse(
value.file.file,
filename='{}-{}-{}-{}{}'.format(
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
self.output.identifier, value.extension
),
content_type=value.type
)
return resp
elif isinstance(value, CachedCombinedTicket):
if value.type == 'text/uri-list':
resp = HttpResponseRedirect(value.file.file.read())
return resp
else:
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 FileResponse(
value.file.file,
filename='{}-{}-{}{}'.format(
self.request.event.slug.upper(), self.order.code, self.output.identifier, value.extension
),
content_type=value.type
)
return resp
else:
return redirect(self.get_self_url())
@@ -1831,15 +1835,15 @@ class InvoiceDownload(EventPermissionRequiredMixin, View):
return redirect(self.get_order_url())
try:
resp = FileResponse(self.invoice.file.file, content_type='application/pdf')
return FileResponse(
self.invoice.file.file,
filename='{}.pdf'.format(re.sub("[^a-zA-Z0-9-_.]+", "_", self.invoice.number)),
content_type='application/pdf'
)
except FileNotFoundError:
invoice_pdf_task.apply(args=(self.invoice.pk,))
return self.get(request, *args, **kwargs)
resp['Content-Disposition'] = 'inline; filename="{}.pdf"'.format(re.sub("[^a-zA-Z0-9-_.]+", "_", self.invoice.number))
resp._csp_ignore = True # Some browser's PDF readers do not work with CSP
return resp
class OrderExtend(OrderView):
permission = 'event.orders:write'

View File

@@ -263,12 +263,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
resp = HttpResponse(data, content_type=mimet)
ftype = fname.split(".")[-1]
if settings.DEBUG:
# attachment is more secure as we're dealing with user-generated stuff here, but inline is much more convenient during debugging
resp['Content-Disposition'] = 'inline; filename="ticket-preview.{}"'.format(ftype)
resp._csp_ignore = True
else:
resp['Content-Disposition'] = 'attachment; filename="ticket-preview.{}"'.format(ftype)
resp['Content-Disposition'] = 'inline; filename="ticket-preview.{}"'.format(ftype)
return resp
elif "data" in request.POST:
if cf:
@@ -309,6 +304,5 @@ class FontsCSSView(TemplateView):
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)
resp = FileResponse(cf.file, filename=cf.filename, content_type='application/pdf')
return resp