mirror of
https://github.com/pretix/pretix.git
synced 2026-08-22 12:42:00 +00:00
Display answers in confirm step, fix file upload/download
This commit is contained in:
@@ -157,16 +157,12 @@ class TimePickerWidget(forms.TimeInput):
|
|||||||
|
|
||||||
class UploadedFileWidget(forms.ClearableFileInput):
|
class UploadedFileWidget(forms.ClearableFileInput):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.position = kwargs.pop('position')
|
|
||||||
self.event = kwargs.pop('event')
|
|
||||||
self.answer = kwargs.pop('answer')
|
self.answer = kwargs.pop('answer')
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
class FakeFile:
|
class FakeFile:
|
||||||
def __init__(self, file, container, event, answer):
|
def __init__(self, file, answer):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.container = container
|
|
||||||
self.event = event
|
|
||||||
self.answer = answer
|
self.answer = answer
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -174,25 +170,7 @@ class UploadedFileWidget(forms.ClearableFileInput):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
from pretix.base.models import Order, OrderPosition
|
return self.answer.frontend_file_url
|
||||||
from pretix.multidomain.urlreverse import eventreverse
|
|
||||||
|
|
||||||
order = None
|
|
||||||
if isinstance(self.container, OrderPosition):
|
|
||||||
order = self.container.order
|
|
||||||
elif isinstance(self.container, Order):
|
|
||||||
order = self.container
|
|
||||||
|
|
||||||
if order:
|
|
||||||
return eventreverse(self.event, 'presale:event.order.download.answer', kwargs={
|
|
||||||
'order': order.code,
|
|
||||||
'secret': order.secret,
|
|
||||||
'answer': self.answer.pk,
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return eventreverse(self.event, 'presale:event.cart.download.answer', kwargs={
|
|
||||||
'answer': self.answer.pk,
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_context(self, name, value, attrs):
|
def get_context(self, name, value, attrs):
|
||||||
# Browsers can't recognize that the server already has a file uploaded
|
# Browsers can't recognize that the server already has a file uploaded
|
||||||
@@ -205,7 +183,7 @@ class UploadedFileWidget(forms.ClearableFileInput):
|
|||||||
|
|
||||||
def format_value(self, value):
|
def format_value(self, value):
|
||||||
if self.is_initial(value):
|
if self.is_initial(value):
|
||||||
return self.FakeFile(value, self.position, self.event, self.answer)
|
return self.FakeFile(value, self.answer)
|
||||||
|
|
||||||
|
|
||||||
class SplitDateTimePickerWidget(forms.SplitDateTimeWidget):
|
class SplitDateTimePickerWidget(forms.SplitDateTimeWidget):
|
||||||
|
|||||||
@@ -1608,7 +1608,7 @@ class Question(LoggedModel):
|
|||||||
"""
|
"""
|
||||||
class ContainerType(models.TextChoices):
|
class ContainerType(models.TextChoices):
|
||||||
ORDER = "O", _("Order")
|
ORDER = "O", _("Order")
|
||||||
ORDERPOSITION = "P", _("Order position")
|
ORDERPOSITION = "P", _("Order position")
|
||||||
TYPE_NUMBER = "N"
|
TYPE_NUMBER = "N"
|
||||||
TYPE_STRING = "S"
|
TYPE_STRING = "S"
|
||||||
TYPE_TEXT = "T"
|
TYPE_TEXT = "T"
|
||||||
|
|||||||
@@ -1304,10 +1304,9 @@ class Order(LockModel, LoggedModel):
|
|||||||
|
|
||||||
def answerfile_name(instance, filename: str) -> str:
|
def answerfile_name(instance, filename: str) -> str:
|
||||||
secret = get_random_string(length=32, allowed_chars=string.ascii_letters + string.digits)
|
secret = get_random_string(length=32, allowed_chars=string.ascii_letters + string.digits)
|
||||||
event = (instance.cartposition if instance.cartposition else instance.orderposition.order).event
|
|
||||||
return 'cachedfiles/answers/{org}/{ev}/{secret}.{filename}'.format(
|
return 'cachedfiles/answers/{org}/{ev}/{secret}.{filename}'.format(
|
||||||
org=event.organizer.slug,
|
org=instance.event.organizer.slug,
|
||||||
ev=event.slug,
|
ev=instance.event.slug,
|
||||||
secret=secret,
|
secret=secret,
|
||||||
filename=escape_uri_path(filename),
|
filename=escape_uri_path(filename),
|
||||||
)
|
)
|
||||||
@@ -1369,11 +1368,11 @@ class QuestionAnswer(models.Model):
|
|||||||
@property
|
@property
|
||||||
def backend_file_url(self):
|
def backend_file_url(self):
|
||||||
if self.file:
|
if self.file:
|
||||||
if self.orderposition:
|
if self.associated_order:
|
||||||
return reverse('control:event.order.download.answer', kwargs={
|
return reverse('control:event.order.download.answer', kwargs={
|
||||||
'code': self.orderposition.order.code,
|
'code': self.associated_order.code,
|
||||||
'event': self.orderposition.order.event.slug,
|
'event': self.associated_order.event.slug,
|
||||||
'organizer': self.orderposition.order.event.organizer.slug,
|
'organizer': self.associated_order.event.organizer.slug,
|
||||||
'answer': self.pk,
|
'answer': self.pk,
|
||||||
})
|
})
|
||||||
return ""
|
return ""
|
||||||
@@ -1383,14 +1382,14 @@ class QuestionAnswer(models.Model):
|
|||||||
from pretix.multidomain.urlreverse import eventreverse
|
from pretix.multidomain.urlreverse import eventreverse
|
||||||
|
|
||||||
if self.file:
|
if self.file:
|
||||||
if self.orderposition:
|
if self.associated_order:
|
||||||
url = eventreverse(self.orderposition.order.event, 'presale:event.order.download.answer', kwargs={
|
url = eventreverse(self.associated_order.event, 'presale:event.order.download.answer', kwargs={
|
||||||
'order': self.orderposition.order.code,
|
'order': self.associated_order.code,
|
||||||
'secret': self.orderposition.order.secret,
|
'secret': self.associated_order.secret,
|
||||||
'answer': self.pk,
|
'answer': self.pk,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
url = eventreverse(self.cartposition.event, 'presale:event.cart.download.answer', kwargs={
|
url = eventreverse(self.event, 'presale:event.cart.download.answer', kwargs={
|
||||||
'answer': self.pk,
|
'answer': self.pk,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1405,6 +1404,24 @@ class QuestionAnswer(models.Model):
|
|||||||
def file_name(self):
|
def file_name(self):
|
||||||
return self.file.name.split('.', 1)[-1]
|
return self.file.name.split('.', 1)[-1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def associated_order(self):
|
||||||
|
if self.orderposition:
|
||||||
|
return self.orderposition.order
|
||||||
|
elif self.order:
|
||||||
|
return self.order
|
||||||
|
|
||||||
|
@property
|
||||||
|
def event(self):
|
||||||
|
if self.orderposition:
|
||||||
|
return self.orderposition.order.event
|
||||||
|
elif self.cartposition:
|
||||||
|
return self.cartposition.event
|
||||||
|
elif self.order:
|
||||||
|
return self.order.event
|
||||||
|
elif self.checkoutsession:
|
||||||
|
return self.checkoutsession.event
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.to_string(use_cached=True)
|
return self.to_string(use_cached=True)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{% load safelink %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load rich_text %}
|
||||||
|
{% if answer %}
|
||||||
|
{% if answer.file %}
|
||||||
|
<span class="fa fa-file"></span>
|
||||||
|
<a href="{{ answer.backend_file_url }}?token={% answer_token request answer %}">
|
||||||
|
{{ answer.file_name }}
|
||||||
|
</a>
|
||||||
|
<span class="label label-danger" data-toggle="tooltip"
|
||||||
|
title="{% trans "This file has been uploaded by a user and could contain viruses or other malicious content." %}">
|
||||||
|
{% trans "UNSAFE" %}
|
||||||
|
</span>
|
||||||
|
{% if answer.is_image %}
|
||||||
|
<br>
|
||||||
|
<a href="{{ answer.backend_file_url }}?token={% answer_token request answer %}" data-lightbox="order"
|
||||||
|
class="answer-thumb">
|
||||||
|
<img src="{{ answer.backend_file_url }}?token={% answer_token request answer %}">
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% elif question.type == "M" %}
|
||||||
|
{{ answer.to_string_i18n|rich_text_snippet }}
|
||||||
|
{% else %}
|
||||||
|
{{ answer.to_string_i18n|linebreaksbr }}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<em>{% trans "not answered" %}</em>
|
||||||
|
{% endif %}
|
||||||
@@ -433,7 +433,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% for ans in order.answers.all %}
|
{% for ans in order.answers.all %}
|
||||||
<dt>{{ ans.question.internal_name|default:ans.question.question }}</dt>
|
<dt>{{ ans.question.internal_name|default:ans.question.question }}</dt>
|
||||||
<dd>{{ ans.answer }}</dd>
|
<dd>{% include "pretixcontrol/order/fragment_question_answer.html" with request=request question=ans.question answer=ans %}</dd>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
@@ -672,31 +672,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{% if q.answer %}
|
{% include "pretixcontrol/order/fragment_question_answer.html" with request=request question=q answer=q.answer %}
|
||||||
{% if q.answer.file %}
|
|
||||||
<span class="fa fa-file"></span>
|
|
||||||
<a href="{{ q.answer.backend_file_url }}?token={% answer_token request q.answer %}">
|
|
||||||
{{ q.answer.file_name }}
|
|
||||||
</a>
|
|
||||||
<span class="label label-danger" data-toggle="tooltip"
|
|
||||||
title="{% trans "This file has been uploaded by a user and could contain viruses or other malicious content." %}">
|
|
||||||
{% trans "UNSAFE" %}
|
|
||||||
</span>
|
|
||||||
{% if q.answer.is_image %}
|
|
||||||
<br>
|
|
||||||
<a href="{{ q.answer.backend_file_url }}?token={% answer_token request q.answer %}" data-lightbox="order"
|
|
||||||
class="answer-thumb">
|
|
||||||
<img src="{{ q.answer.backend_file_url }}?token={% answer_token request q.answer %}">
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% elif q.type == "M" %}
|
|
||||||
{{ q.answer.to_string_i18n|rich_text_snippet }}
|
|
||||||
{% else %}
|
|
||||||
{{ q.answer.to_string_i18n|linebreaksbr }}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<em>{% trans "not answered" %}</em>
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
</dd>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for q in line.additional_fields %}
|
{% for q in line.additional_fields %}
|
||||||
|
|||||||
@@ -2591,7 +2591,11 @@ class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
|||||||
answid = kwargs.get('answer')
|
answid = kwargs.get('answer')
|
||||||
token = request.GET.get('token', '')
|
token = request.GET.get('token', '')
|
||||||
|
|
||||||
answer = get_object_or_404(QuestionAnswer, orderposition__order=self.order, id=answid)
|
answer = get_object_or_404(
|
||||||
|
QuestionAnswer,
|
||||||
|
Q(orderposition__order=self.order) | Q(order=self.order),
|
||||||
|
id=answid
|
||||||
|
)
|
||||||
if not answer.file:
|
if not answer.file:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
if not check_token(request, answer, token):
|
if not check_token(request, answer, token):
|
||||||
@@ -2601,7 +2605,7 @@ class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
|||||||
resp = FileResponse(answer.file, content_type=ftype or 'application/binary')
|
resp = FileResponse(answer.file, content_type=ftype or 'application/binary')
|
||||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}"'.format(
|
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}"'.format(
|
||||||
self.request.event.slug.upper(), self.order.code,
|
self.request.event.slug.upper(), self.order.code,
|
||||||
answer.orderposition.positionid,
|
answer.orderposition.positionid if answer.orderposition else '',
|
||||||
os.path.basename(answer.file.name).split('.', 1)[1]
|
os.path.basename(answer.file.name).split('.', 1)[1]
|
||||||
)
|
)
|
||||||
return resp
|
return resp
|
||||||
|
|||||||
@@ -1564,6 +1564,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
|||||||
ctx['addr'] = self.invoice_address
|
ctx['addr'] = self.invoice_address
|
||||||
ctx['confirm_messages'] = self.confirm_messages
|
ctx['confirm_messages'] = self.confirm_messages
|
||||||
ctx['cart_session'] = self.cart_session
|
ctx['cart_session'] = self.cart_session
|
||||||
|
ctx['checkout_session'] = self.checkout_session
|
||||||
ctx['invoice_address_asked'] = self.address_asked
|
ctx['invoice_address_asked'] = self.address_asked
|
||||||
ctx['customer'] = self.cart_customer
|
ctx['customer'] = self.cart_customer
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,26 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if checkout_session.answers.all %}
|
||||||
|
<div class="panel panel-default panel-contact">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">
|
||||||
|
{% trans "Additional order information" %}
|
||||||
|
<a href="{% eventurl request.event "presale:event.checkout" step="questions" cart_namespace=cart_namespace|default_if_none:"" %}" aria-label="{% trans "Modify contact information" %}" class="h6">
|
||||||
|
<span class="fa fa-edit" aria-hidden="true"></span>{% trans "Modify" %}
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
{% for ans in checkout_session.answers.all %}
|
||||||
|
<dt>{{ ans.question.question }}</dt>
|
||||||
|
<dd>{% include "pretixpresale/event/fragment_question_answer.html" with request=request question=ans.question answer=ans %}</dd>
|
||||||
|
{% endfor %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if confirm_messages %}
|
{% if confirm_messages %}
|
||||||
|
|||||||
@@ -206,27 +206,7 @@
|
|||||||
{% for q in line.questions %}
|
{% for q in line.questions %}
|
||||||
<dt>{{ q.question }}</dt>
|
<dt>{{ q.question }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{% if q.answer %}
|
{% include "pretixpresale/event/fragment_question_answer.html" with request=request question=q answer=q.answer %}
|
||||||
{% if q.answer.file %}
|
|
||||||
<span class="fa fa-file" aria-hidden="true"></span>
|
|
||||||
<a href="{{ q.answer.frontend_file_url }}?token={% answer_token request q.answer %}">
|
|
||||||
{{ q.answer.file_name }}
|
|
||||||
</a>
|
|
||||||
{% if q.answer.is_image %}
|
|
||||||
<br>
|
|
||||||
<a href="{{ q.answer.frontend_file_url }}?token={% answer_token request q.answer %}" data-lightbox="order"
|
|
||||||
class="answer-thumb">
|
|
||||||
<img src="{{ q.answer.frontend_file_url }}?token={% answer_token request q.answer %}" alt="{% trans "The image you previously uploaded" %}">
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% elif q.type == "M" %}
|
|
||||||
{{ q.answer|rich_text_snippet }}
|
|
||||||
{% else %}
|
|
||||||
{{ q.answer|linebreaksbr }}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<em class="text-muted">{% trans "not answered" %}</em>
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
</dd>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for q in line.additional_answers %}
|
{% for q in line.additional_answers %}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{% load safelink %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load rich_text %}
|
||||||
|
{% if answer %}
|
||||||
|
{% if answer.file %}
|
||||||
|
<span class="fa fa-file" aria-hidden="true"></span>
|
||||||
|
<a href="{{ answer.frontend_file_url }}?token={% answer_token request answer %}">
|
||||||
|
{{ answer.file_name }}
|
||||||
|
</a>
|
||||||
|
{% if answer.is_image %}
|
||||||
|
<br>
|
||||||
|
<a href="{{ answer.frontend_file_url }}?token={% answer_token request answer %}" data-lightbox="order"
|
||||||
|
class="answer-thumb">
|
||||||
|
<img src="{{ answer.frontend_file_url }}?token={% answer_token request answer %}" alt="{% trans "The image you previously uploaded" %}">
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% elif question.type == "M" %}
|
||||||
|
{{ answer|rich_text_snippet }}
|
||||||
|
{% else %}
|
||||||
|
{{ answer|linebreaksbr }}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<em class="text-muted">{% trans "not answered" %}</em>
|
||||||
|
{% endif %}
|
||||||
@@ -388,6 +388,7 @@ def get_or_create_cart_id(request, create=True):
|
|||||||
if 'carts' in request.session:
|
if 'carts' in request.session:
|
||||||
request.session['carts'][current_id] = {}
|
request.session['carts'][current_id] = {}
|
||||||
else:
|
else:
|
||||||
|
# We found a valid, existing cart.
|
||||||
return current_id
|
return current_id
|
||||||
|
|
||||||
cart_data = {}
|
cart_data = {}
|
||||||
@@ -398,6 +399,7 @@ def get_or_create_cart_id(request, create=True):
|
|||||||
cart_data['widget_data'] = cached_widget_data
|
cart_data['widget_data'] = cached_widget_data
|
||||||
else:
|
else:
|
||||||
if not create:
|
if not create:
|
||||||
|
# There is no existing cart for this request and we're not supposed to create a new one.
|
||||||
return None
|
return None
|
||||||
new_id = generate_cart_id(request, prefix=prefix)
|
new_id = generate_cart_id(request, prefix=prefix)
|
||||||
|
|
||||||
@@ -846,9 +848,10 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
|||||||
class AnswerDownload(EventViewMixin, View):
|
class AnswerDownload(EventViewMixin, View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
answid = kwargs.get('answer')
|
answid = kwargs.get('answer')
|
||||||
|
cart_id = get_or_create_cart_id(self.request)
|
||||||
answer = get_object_or_404(
|
answer = get_object_or_404(
|
||||||
QuestionAnswer,
|
QuestionAnswer,
|
||||||
cartposition__cart_id=get_or_create_cart_id(self.request),
|
Q(cartposition__cart_id=cart_id) | Q(checkoutsession__cart_id=cart_id),
|
||||||
id=answid
|
id=answid
|
||||||
)
|
)
|
||||||
if not answer.file:
|
if not answer.file:
|
||||||
|
|||||||
Reference in New Issue
Block a user