diff --git a/src/pretix/base/migrations/0051_auto_20161221_1720.py b/src/pretix/base/migrations/0051_auto_20161221_1720.py new file mode 100644 index 0000000000..395353ab48 --- /dev/null +++ b/src/pretix/base/migrations/0051_auto_20161221_1720.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-21 17:20 +from __future__ import unicode_literals + +from django.db import migrations, models + +import pretix.base.models.orders + + +def invalidate_ticket_cache(apps, schema_editor): + CachedTicket = apps.get_model('pretixbase', 'CachedTicket') + for ct in CachedTicket.objects.all(): + try: + if ct.cachedfile: + ct.cachedfile.delete() + if ct.cachedfile.file: + ct.cachedfile.file.delete(False) + except models.Model.DoesNotExist: + pass + ct.delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0050_orderposition_positionid'), + ] + + operations = [ + migrations.RunPython( + invalidate_ticket_cache, migrations.RunPython.noop + ), + migrations.RemoveField( + model_name='cachedticket', + name='cachedfile', + ), + migrations.AddField( + model_name='cachedticket', + name='extension', + field=models.CharField(default='', max_length=255), + preserve_default=False, + ), + migrations.AddField( + model_name='cachedticket', + name='file', + field=models.FileField(blank=True, null=True, upload_to=pretix.base.models.orders.cachedticket_name), + ), + migrations.AddField( + model_name='cachedticket', + name='type', + field=models.CharField(default='', max_length=255), + preserve_default=False, + ), + ] diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 9ca1b21faa..573093066e 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -15,7 +15,7 @@ from django.utils.timezone import make_aware, now from django.utils.translation import ugettext_lazy as _ from ..decimal import round_decimal -from .base import CachedFile, LoggedModel +from .base import LoggedModel from .event import Event from .items import Item, ItemVariation, Question, QuestionOption, Quota @@ -565,16 +565,28 @@ class InvoiceAddress(models.Model): vat_id = models.CharField(max_length=255, blank=True, verbose_name=_('VAT ID')) +def cachedticket_name(instance, filename: str) -> str: + secret = get_random_string(length=16, allowed_chars=string.ascii_letters + string.digits) + return 'tickets/{org}/{ev}/{code}-{no}-{prov}-{secret}.pdf'.format( + org=instance.order_position.order.event.organizer.slug, + ev=instance.order_position.order.event.slug, + prov=instance.provider, + no=instance.order_position.positionid, + code=instance.order_position.order.code, + secret=secret + ) + + class CachedTicket(models.Model): order_position = models.ForeignKey(OrderPosition, on_delete=models.CASCADE) - cachedfile = models.ForeignKey(CachedFile, on_delete=models.CASCADE, null=True) provider = models.CharField(max_length=255) + type = models.CharField(max_length=255) + extension = models.CharField(max_length=255) + file = models.FileField(null=True, blank=True, upload_to=cachedticket_name) @receiver(post_delete, sender=CachedTicket) -def cached_file_delete(sender, instance, **kwargs): - try: - if instance.cachedfile: - instance.cachedfile.delete() - except CachedFile.DoesNotExist: - pass +def cachedticket_delete(sender, instance, **kwargs): + if instance.file: + # Pass false so FileField doesn't save the model. + instance.file.delete(False) diff --git a/src/pretix/base/services/tickets.py b/src/pretix/base/services/tickets.py index a074381606..ab8d66f6d7 100644 --- a/src/pretix/base/services/tickets.py +++ b/src/pretix/base/services/tickets.py @@ -1,13 +1,11 @@ -from datetime import timedelta +import os from django.core.files.base import ContentFile from django.utils.timezone import now from django.utils.translation import ugettext as _ from pretix.base.i18n import language -from pretix.base.models import ( - CachedFile, CachedTicket, Event, Order, OrderPosition, cachedfile_name, -) +from pretix.base.models import CachedTicket, Event, Order, OrderPosition from pretix.base.services.async import ProfiledTask from pretix.base.signals import register_ticket_outputs from pretix.celery import app @@ -17,23 +15,21 @@ from pretix.helpers.database import rolledback_transaction @app.task(base=ProfiledTask) def generate(order_position: str, provider: str): order_position = OrderPosition.objects.select_related('order', 'order__event').get(id=order_position) - ct = CachedTicket.objects.get_or_create(order_position=order_position, provider=provider)[0] - if not ct.cachedfile: - cf = CachedFile() - cf.date = now() - cf.expires = order_position.order.event.date_from + timedelta(days=30) - cf.save() - ct.cachedfile = cf - ct.save() + try: + ct = CachedTicket.objects.get(order_position=order_position, provider=provider) + except CachedTicket.DoesNotExist: + ct = CachedTicket(order_position=order_position, provider=provider) with language(order_position.order.locale): responses = register_ticket_outputs.send(order_position.order.event) for receiver, response in responses: prov = response(order_position.order.event) if prov.identifier == provider: - ct.cachedfile.filename, ct.cachedfile.type, data = prov.generate(order_position) - ct.cachedfile.file.save(cachedfile_name(ct.cachedfile, ct.cachedfile.filename), ContentFile(data)) - ct.cachedfile.save() + filename, ct.type, data = prov.generate(order_position) + path, ext = os.path.splitext(filename) + ct.extension = ext + ct.save() + ct.file.save(filename, ContentFile(data)) class DummyRollbackException(Exception): diff --git a/src/pretix/base/ticketoutput.py b/src/pretix/base/ticketoutput.py index 758b9c7a35..ce8ab5f9d6 100644 --- a/src/pretix/base/ticketoutput.py +++ b/src/pretix/base/ticketoutput.py @@ -32,7 +32,8 @@ class BaseTicketOutput: def generate(self, order: OrderPosition) -> Tuple[str, str, str]: """ This method should generate the download file and return a tuple consisting of a - filename, a file type and file content. + filename, a file type and file content. The extension will be taken from the filename + which is otherwise ignored. """ raise NotImplementedError() diff --git a/src/pretix/base/views/cachedfiles.py b/src/pretix/base/views/cachedfiles.py index 8eb8384789..3d39ffdf73 100644 --- a/src/pretix/base/views/cachedfiles.py +++ b/src/pretix/base/views/cachedfiles.py @@ -1,5 +1,3 @@ -import os - from django.http import FileResponse, Http404, HttpRequest, HttpResponse from django.shortcuts import get_object_or_404 from django.utils.functional import cached_property @@ -23,8 +21,7 @@ class DownloadView(TemplateView): return HttpResponse('1' if self.object.file else '0') elif self.object.file: resp = FileResponse(self.object.file.file, content_type=self.object.type) - _, ext = os.path.splitext(self.object.filename) - resp['Content-Disposition'] = 'attachment; filename="{}{}"'.format(self.object.id, ext) + resp['Content-Disposition'] = 'attachment; filename="{}"'.format(self.object.filename) return resp else: return super().get(request, *args, **kwargs) diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index ea5b1595b8..5de945f011 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -159,6 +159,7 @@ {% for line in items.positions %}