mirror of
https://github.com/pretix/pretix.git
synced 2026-04-18 22:32:28 +00:00
Compare commits
1 Commits
master
...
pajowu/pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61c965154a |
@@ -31,9 +31,7 @@ from pretix.api.serializers.order import OrderPositionSerializer
|
||||
from pretix.api.serializers.organizer import (
|
||||
CustomerSerializer, GiftCardSerializer,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
Device, Order, OrderPosition, ReusableMedium, TeamAPIToken,
|
||||
)
|
||||
from pretix.base.models import Order, OrderPosition, ReusableMedium
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -82,7 +80,8 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
)
|
||||
|
||||
if 'linked_orderposition' in self.context['request'].query_params.getlist('expand'):
|
||||
# Permission Check performed in to_representation
|
||||
# No additional permission check performed, documented limitation of the permission system
|
||||
# Would get to complex/unusable otherwise since the permission depends on the event
|
||||
self.fields['linked_orderposition'] = NestedOrderPositionSerializer(read_only=True)
|
||||
else:
|
||||
self.fields['linked_orderposition'] = serializers.PrimaryKeyRelatedField(
|
||||
@@ -118,27 +117,6 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
)
|
||||
return data
|
||||
|
||||
def to_representation(self, instance):
|
||||
r = super().to_representation(instance)
|
||||
request = self.context.get('request')
|
||||
# late permission evaluations for checks that depend on the actual linked events
|
||||
expand_nested = self.context['request'].query_params.getlist('expand')
|
||||
perm_holder = request.auth if isinstance(request.auth, (Device, TeamAPIToken)) else request.user
|
||||
if 'linked_orderposition' in expand_nested:
|
||||
if instance.linked_orderposition is not None:
|
||||
event = instance.linked_orderposition.order.event
|
||||
if not perm_holder.has_event_permission(event.organizer, event, 'event.orders:read', request):
|
||||
r['linked_orderposition'] = {'id': instance.linked_orderposition.id}
|
||||
|
||||
if 'linked_giftcard.owner_ticket' in expand_nested:
|
||||
gc = instance.linked_giftcard
|
||||
if gc is not None and gc.owner_ticket is not None:
|
||||
event = gc.owner_ticket.order.event
|
||||
if not perm_holder.has_event_permission(event.organizer, event, 'event.orders:read', request):
|
||||
r['linked_giftcard']['owner_ticket'] = {'id': instance.linked_giftcard.owner_ticket.id}
|
||||
|
||||
return r
|
||||
|
||||
class Meta:
|
||||
model = ReusableMedium
|
||||
fields = (
|
||||
|
||||
@@ -286,19 +286,6 @@ class GiftCardSerializer(I18nAwareModelSerializer):
|
||||
)
|
||||
return data
|
||||
|
||||
def to_representation(self, instance):
|
||||
r = super().to_representation(instance)
|
||||
request = self.context.get('request')
|
||||
# late permission evaluations for checks that depend on the actual linked events
|
||||
if 'owner_ticket' in self.context['request'].query_params.getlist('expand'):
|
||||
owner_ticket = instance.owner_ticket
|
||||
if owner_ticket:
|
||||
event = owner_ticket.order.event
|
||||
perm_holder = request.auth if isinstance(request.auth, (Device, TeamAPIToken)) else request.user
|
||||
if not perm_holder.has_event_permission(event.organizer, event, 'event.orders:read', request):
|
||||
r['owner_ticket'] = {'id': instance.owner_ticket.id}
|
||||
return r
|
||||
|
||||
class Meta:
|
||||
model = GiftCard
|
||||
fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode', 'expires', 'conditions', 'owner_ticket',
|
||||
|
||||
@@ -381,15 +381,12 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
|
||||
resp = HttpResponse(ct.file.file.read(), content_type='text/uri-list')
|
||||
return resp
|
||||
else:
|
||||
return FileResponse(
|
||||
ct.file.file,
|
||||
filename='{}-{}-{}{}'.format(
|
||||
self.request.event.slug.upper(), order.code,
|
||||
provider.identifier, ct.extension
|
||||
),
|
||||
as_attachment=True,
|
||||
content_type=ct.type
|
||||
resp = FileResponse(ct.file.file, content_type=ct.type)
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}{}"'.format(
|
||||
self.request.event.slug.upper(), order.code,
|
||||
provider.identifier, ct.extension
|
||||
)
|
||||
return resp
|
||||
|
||||
@action(detail=True, methods=['POST'])
|
||||
def mark_paid(self, request, **kwargs):
|
||||
@@ -1306,17 +1303,14 @@ class EventOrderPositionViewSet(OrderPositionViewSetMixin, viewsets.ModelViewSet
|
||||
raise NotFound()
|
||||
|
||||
ftype, ignored = mimetypes.guess_type(answer.file.name)
|
||||
return FileResponse(
|
||||
answer.file,
|
||||
filename='{}-{}-{}-{}'.format(
|
||||
self.request.event.slug.upper(),
|
||||
pos.order.code,
|
||||
pos.positionid,
|
||||
os.path.basename(answer.file.name).split('.', 1)[1]
|
||||
),
|
||||
as_attachment=True,
|
||||
content_type=ftype or 'application/binary'
|
||||
resp = FileResponse(answer.file, content_type=ftype or 'application/binary')
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}"'.format(
|
||||
self.request.event.slug.upper(),
|
||||
pos.order.code,
|
||||
pos.positionid,
|
||||
os.path.basename(answer.file.name).split('.', 1)[1]
|
||||
)
|
||||
return resp
|
||||
|
||||
@action(detail=True, url_name="printlog", url_path="printlog", methods=["POST"])
|
||||
def printlog(self, request, **kwargs):
|
||||
@@ -1371,18 +1365,15 @@ class EventOrderPositionViewSet(OrderPositionViewSetMixin, viewsets.ModelViewSet
|
||||
if hasattr(image_file, 'seek'):
|
||||
image_file.seek(0)
|
||||
|
||||
return FileResponse(
|
||||
image_file,
|
||||
filename='{}-{}-{}-{}.{}'.format(
|
||||
self.request.event.slug.upper(),
|
||||
pos.order.code,
|
||||
pos.positionid,
|
||||
key,
|
||||
extension,
|
||||
),
|
||||
as_attachment=True,
|
||||
content_type=ftype or 'application/binary'
|
||||
resp = FileResponse(image_file, content_type=ftype or 'application/binary')
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}.{}"'.format(
|
||||
self.request.event.slug.upper(),
|
||||
pos.order.code,
|
||||
pos.positionid,
|
||||
key,
|
||||
extension,
|
||||
)
|
||||
return resp
|
||||
|
||||
@action(detail=True, url_name='download', url_path='download/(?P<output>[^/]+)')
|
||||
def download(self, request, output, **kwargs):
|
||||
@@ -1408,15 +1399,12 @@ class EventOrderPositionViewSet(OrderPositionViewSetMixin, viewsets.ModelViewSet
|
||||
resp = HttpResponse(ct.file.file.read(), content_type='text/uri-list')
|
||||
return resp
|
||||
else:
|
||||
return FileResponse(
|
||||
ct.file.file,
|
||||
filename='{}-{}-{}-{}{}'.format(
|
||||
self.request.event.slug.upper(), pos.order.code, pos.positionid,
|
||||
provider.identifier, ct.extension
|
||||
),
|
||||
as_attachment=True,
|
||||
content_type=ct.type
|
||||
resp = FileResponse(ct.file.file, content_type=ct.type)
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}{}"'.format(
|
||||
self.request.event.slug.upper(), pos.order.code, pos.positionid,
|
||||
provider.identifier, ct.extension
|
||||
)
|
||||
return resp
|
||||
|
||||
@action(detail=True, methods=['POST'])
|
||||
def regenerate_secrets(self, request, **kwargs):
|
||||
@@ -1998,12 +1986,9 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
if not invoice.file:
|
||||
raise RetryException()
|
||||
|
||||
return FileResponse(
|
||||
invoice.file.file,
|
||||
filename='{}.pdf'.format(invoice.number),
|
||||
as_attachment=True,
|
||||
content_type='application/pdf'
|
||||
)
|
||||
resp = FileResponse(invoice.file.file, content_type='application/pdf')
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(invoice.number)
|
||||
return resp
|
||||
|
||||
@action(detail=True, methods=['POST'])
|
||||
def transmit(self, request, **kwargs):
|
||||
|
||||
@@ -58,7 +58,6 @@ from pretix.base.invoicing.transmission import (
|
||||
from pretix.base.models import (
|
||||
ExchangeRate, Invoice, InvoiceAddress, InvoiceLine, Order, OrderFee,
|
||||
)
|
||||
from pretix.base.models.orders import OrderPayment
|
||||
from pretix.base.models.tax import EU_CURRENCIES
|
||||
from pretix.base.services.tasks import (
|
||||
TransactionAwareProfiledEventTask, TransactionAwareTask,
|
||||
@@ -103,7 +102,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
introductory = invoice.event.settings.get('invoice_introductory_text', as_type=LazyI18nString)
|
||||
additional = invoice.event.settings.get('invoice_additional_text', as_type=LazyI18nString)
|
||||
footer = invoice.event.settings.get('invoice_footer_text', as_type=LazyI18nString)
|
||||
if lp and lp.payment_provider and lp.state not in (OrderPayment.PAYMENT_STATE_FAILED, OrderPayment.PAYMENT_STATE_CANCELED):
|
||||
if lp and lp.payment_provider:
|
||||
if 'payment' in inspect.signature(lp.payment_provider.render_invoice_text).parameters:
|
||||
payment = str(lp.payment_provider.render_invoice_text(invoice.order, lp))
|
||||
else:
|
||||
|
||||
@@ -763,7 +763,12 @@ 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)
|
||||
resp['Content-Disposition'] = 'inline; filename="{}"'.format(fname)
|
||||
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)
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
@@ -300,4 +300,5 @@ class SysReportView(AdministratorPermissionRequiredMixin, TemplateView):
|
||||
resp = HttpResponse(data)
|
||||
resp['Content-Type'] = mime
|
||||
resp['Content-Disposition'] = 'inline; filename="{}"'.format(name)
|
||||
resp._csp_ignore = True
|
||||
return resp
|
||||
|
||||
@@ -710,26 +710,22 @@ class OrderDownload(AsyncAction, OrderView):
|
||||
resp = HttpResponseRedirect(value.file.file.read())
|
||||
return resp
|
||||
else:
|
||||
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
|
||||
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):
|
||||
if value.type == 'text/uri-list':
|
||||
resp = HttpResponseRedirect(value.file.file.read())
|
||||
return resp
|
||||
else:
|
||||
return FileResponse(
|
||||
value.file.file,
|
||||
filename='{}-{}-{}{}'.format(
|
||||
self.request.event.slug.upper(), self.order.code, self.output.identifier, value.extension
|
||||
),
|
||||
content_type=value.type
|
||||
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())
|
||||
|
||||
@@ -1835,15 +1831,15 @@ class InvoiceDownload(EventPermissionRequiredMixin, View):
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
try:
|
||||
return FileResponse(
|
||||
self.invoice.file.file,
|
||||
filename='{}.pdf'.format(re.sub("[^a-zA-Z0-9-_.]+", "_", self.invoice.number)),
|
||||
content_type='application/pdf'
|
||||
)
|
||||
resp = FileResponse(self.invoice.file.file, 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'
|
||||
|
||||
@@ -263,7 +263,12 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
resp = HttpResponse(data, content_type=mimet)
|
||||
ftype = fname.split(".")[-1]
|
||||
resp['Content-Disposition'] = 'inline; filename="ticket-preview.{}"'.format(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="ticket-preview.{}"'.format(ftype)
|
||||
resp._csp_ignore = True
|
||||
else:
|
||||
resp['Content-Disposition'] = 'attachment; filename="ticket-preview.{}"'.format(ftype)
|
||||
return resp
|
||||
elif "data" in request.POST:
|
||||
if cf:
|
||||
@@ -304,5 +309,6 @@ 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, filename=cf.filename, content_type='application/pdf')
|
||||
resp = FileResponse(cf.file, content_type='application/pdf')
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}"'.format(cf.filename)
|
||||
return resp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -216,7 +216,7 @@ class PayView(PaypalOrderView, TemplateView):
|
||||
|
||||
|
||||
@scopes_disabled()
|
||||
@event_permission_required('event.settings.payment:write')
|
||||
@event_permission_required('event.settings.general:write')
|
||||
def isu_return(request, *args, **kwargs):
|
||||
getparams = ['merchantId', 'merchantIdInPayPal', 'permissionsGranted', 'accountStatus', 'consentStatus', 'productIntentID', 'isEmailConfirmed']
|
||||
sessionparams = ['payment_paypal_isu_event', 'payment_paypal_isu_tracking_id']
|
||||
@@ -526,7 +526,7 @@ def webhook(request, *args, **kwargs):
|
||||
return HttpResponse(status=200)
|
||||
|
||||
|
||||
@event_permission_required('event.settings.payment:write')
|
||||
@event_permission_required('event.settings.general:write')
|
||||
@require_POST
|
||||
def isu_disconnect(request, **kwargs):
|
||||
del request.event.settings.payment_paypal_connect_refresh_token
|
||||
|
||||
@@ -91,9 +91,6 @@ event_patterns = [
|
||||
re_path(r'w/(?P<cart_namespace>[a-zA-Z0-9]{16})/cart/add',
|
||||
csrf_exempt(pretix.presale.views.cart.CartAdd.as_view()),
|
||||
name='event.cart.add'),
|
||||
re_path(r'w/(?P<cart_namespace>[a-zA-Z0-9]{16})/cart/create',
|
||||
csrf_exempt(pretix.presale.views.cart.CartCreate.as_view()),
|
||||
name='event.cart.create'),
|
||||
|
||||
re_path(r'unlock/(?P<hash>[a-z0-9]{64})/$', pretix.presale.views.user.UnlockHashView.as_view(),
|
||||
name='event.payment.unlock'),
|
||||
|
||||
@@ -114,8 +114,8 @@ class CartMixin:
|
||||
return cached_invoice_address(self.request)
|
||||
|
||||
def get_cart(self, answers=False, queryset=None, order=None, downloads=False, payments=None):
|
||||
if not self.request.session.session_key and not order:
|
||||
# The user has not even a session ID yet, so they can't have a cart and we can save a lot of work
|
||||
if not cart_exists(self.request) and not order:
|
||||
# The user has no cart, so we can save a lot of work
|
||||
return {
|
||||
'positions': [],
|
||||
# Other keys are not used on non-checkout pages
|
||||
@@ -377,9 +377,13 @@ def cart_exists(request):
|
||||
from pretix.presale.views.cart import get_or_create_cart_id
|
||||
|
||||
if not hasattr(request, '_cart_cache'):
|
||||
return CartPosition.objects.filter(
|
||||
cart_id=get_or_create_cart_id(request), event=request.event
|
||||
).exists()
|
||||
cid = get_or_create_cart_id(request, create=False)
|
||||
if cid:
|
||||
return CartPosition.objects.filter(
|
||||
cart_id=cid, event=request.event
|
||||
).exists()
|
||||
else:
|
||||
return False
|
||||
return bool(request._cart_cache)
|
||||
|
||||
|
||||
|
||||
@@ -555,18 +555,6 @@ class CartClear(EventViewMixin, CartActionMixin, AsyncAction, View):
|
||||
request.sales_channel.identifier, time_machine_now(default=None))
|
||||
|
||||
|
||||
@method_decorator(allow_cors_if_namespaced, 'dispatch')
|
||||
class CartCreate(EventViewMixin, CartActionMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'ajax' in self.request.GET:
|
||||
cart_id = get_or_create_cart_id(self.request, create=True)
|
||||
return JsonResponse({
|
||||
'cart_id': cart_id,
|
||||
})
|
||||
else:
|
||||
return redirect_to_url(self.get_success_url())
|
||||
|
||||
|
||||
@method_decorator(allow_frame_if_namespaced, 'dispatch')
|
||||
class CartExtendReservation(EventViewMixin, CartActionMixin, AsyncAction, View):
|
||||
task = extend_cart_reservation
|
||||
@@ -855,13 +843,9 @@ class AnswerDownload(EventViewMixin, View):
|
||||
return Http404()
|
||||
|
||||
ftype, _ = mimetypes.guess_type(answer.file.name)
|
||||
filename = '{}-cart-{}'.format(
|
||||
resp = FileResponse(answer.file, content_type=ftype or 'application/binary')
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-cart-{}"'.format(
|
||||
self.request.event.slug.upper(),
|
||||
os.path.basename(answer.file.name).split('.', 1)[1]
|
||||
)
|
||||
resp = FileResponse(
|
||||
answer.file,
|
||||
filename=filename,
|
||||
content_type=ftype or 'application/binary'
|
||||
)
|
||||
).encode("ascii", "ignore")
|
||||
return resp
|
||||
|
||||
@@ -1220,26 +1220,30 @@ class OrderDownloadMixin:
|
||||
resp = HttpResponseRedirect(value.file.file.read())
|
||||
return resp
|
||||
else:
|
||||
name_parts = (
|
||||
self.request.event.slug.upper(),
|
||||
self.order.code,
|
||||
str(self.order_position.positionid),
|
||||
self.order_position.subevent.date_from.strftime('%Y_%m_%d') if self.order_position.subevent else None,
|
||||
self.output.identifier
|
||||
)
|
||||
filename = "-".join(filter(None, name_parts)) + value.extension
|
||||
return FileResponse(value.file.file, filename=filename, content_type=value.type)
|
||||
resp = FileResponse(value.file.file, content_type=value.type)
|
||||
if self.order_position.subevent:
|
||||
# Subevent date in filename improves accessibility e.g. for screen reader users
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}-{}{}"'.format(
|
||||
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
||||
self.order_position.subevent.date_from.strftime('%Y_%m_%d'),
|
||||
self.output.identifier, value.extension
|
||||
)
|
||||
else:
|
||||
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):
|
||||
if value.type == 'text/uri-list':
|
||||
resp = HttpResponseRedirect(value.file.file.read())
|
||||
return resp
|
||||
else:
|
||||
return FileResponse(
|
||||
value.file.file,
|
||||
filename="{}-{}-{}{}".format(
|
||||
self.request.event.slug.upper(), self.order.code, self.output.identifier, value.extension),
|
||||
content_type=value.type
|
||||
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())
|
||||
|
||||
@@ -1379,14 +1383,13 @@ class InvoiceDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
try:
|
||||
return FileResponse(
|
||||
invoice.file.file,
|
||||
filename='{}.pdf'.format(re.sub("[^a-zA-Z0-9-_.]+", "_", invoice.number)),
|
||||
content_type='application/pdf'
|
||||
)
|
||||
resp = FileResponse(invoice.file.file, content_type='application/pdf')
|
||||
except FileNotFoundError:
|
||||
invoice_pdf_task.apply(args=(invoice.pk,))
|
||||
return self.get(request, *args, **kwargs)
|
||||
resp['Content-Disposition'] = 'inline; filename="{}.pdf"'.format(re.sub("[^a-zA-Z0-9-_.]+", "_", invoice.number))
|
||||
resp._csp_ignore = True # Some browser's PDF readers do not work with CSP
|
||||
return resp
|
||||
|
||||
|
||||
class OrderChangeMixin:
|
||||
|
||||
@@ -110,10 +110,6 @@ var setCookie = function (cname, cvalue, exdays) {
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
||||
var expires = "expires=" + d.toUTCString();
|
||||
if (!cvalue) {
|
||||
var expires = "expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
cvalue = "";
|
||||
}
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||
};
|
||||
var getCookie = function (name) {
|
||||
@@ -730,16 +726,17 @@ var shared_methods = {
|
||||
buy_callback: function (data) {
|
||||
if (data.redirect) {
|
||||
if (data.cart_id) {
|
||||
this.$root.set_cart_id(data.cart_id);
|
||||
this.$root.cart_id = data.cart_id;
|
||||
setCookie(this.$root.cookieName, data.cart_id, 30);
|
||||
}
|
||||
if (data.redirect.substr(0, 1) === '/') {
|
||||
data.redirect = this.$root.target_url.replace(/^([^\/]+:\/\/[^\/]+)\/.*$/, "$1") + data.redirect;
|
||||
}
|
||||
var url = data.redirect;
|
||||
if (url.indexOf('?')) {
|
||||
url = url + '&iframe=1&locale=' + lang + '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
|
||||
url = url + '&iframe=1&locale=' + lang + '&take_cart_id=' + this.$root.cart_id;
|
||||
} else {
|
||||
url = url + '?iframe=1&locale=' + lang + '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
|
||||
url = url + '?iframe=1&locale=' + lang + '&take_cart_id=' + this.$root.cart_id;
|
||||
}
|
||||
url += this.$root.consent_parameter;
|
||||
if (this.$root.additionalURLParams) {
|
||||
@@ -782,24 +779,15 @@ var shared_methods = {
|
||||
}
|
||||
},
|
||||
resume: function () {
|
||||
if (!this.$root.get_cart_id() && this.$root.keep_cart) {
|
||||
// create an empty cart whose id we can persist
|
||||
this.$root.create_cart(this.resume)
|
||||
return;
|
||||
}
|
||||
var redirect_url;
|
||||
redirect_url = this.$root.target_url + 'w/' + widget_id + '/';
|
||||
if (this.$root.subevent && this.$root.is_button && this.$root.items.length === 0) {
|
||||
if (this.$root.subevent && !this.$root.cart_id) {
|
||||
// button with subevent but no items
|
||||
redirect_url += this.$root.subevent + '/';
|
||||
}
|
||||
redirect_url += '?iframe=1&locale=' + lang;
|
||||
if (this.$root.get_cart_id()) {
|
||||
redirect_url += '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
|
||||
if (this.$root.keep_cart) {
|
||||
// make sure the cart-id is used, even if the cart is currently empty
|
||||
redirect_url += '&ajax=1'
|
||||
}
|
||||
if (this.$root.cart_id) {
|
||||
redirect_url += '&take_cart_id=' + this.$root.cart_id;
|
||||
}
|
||||
if (this.$root.widget_data) {
|
||||
redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json);
|
||||
@@ -1876,11 +1864,12 @@ var shared_root_methods = {
|
||||
if (this.$root.variation_filter) {
|
||||
url += '&variations=' + encodeURIComponent(this.$root.variation_filter);
|
||||
}
|
||||
var cart_id = getCookie(this.cookieName);
|
||||
if (this.$root.voucher_code) {
|
||||
url += '&voucher=' + encodeURIComponent(this.$root.voucher_code);
|
||||
}
|
||||
if (this.$root.get_cart_id()) {
|
||||
url += "&cart_id=" + encodeURIComponent(this.$root.get_cart_id());
|
||||
if (cart_id) {
|
||||
url += "&cart_id=" + encodeURIComponent(cart_id);
|
||||
}
|
||||
if (this.$root.date !== null) {
|
||||
url += "&date=" + this.$root.date.substr(0, 7);
|
||||
@@ -1950,6 +1939,7 @@ var shared_root_methods = {
|
||||
root.display_add_to_cart = data.display_add_to_cart;
|
||||
root.waiting_list_enabled = data.waiting_list_enabled;
|
||||
root.show_variations_expanded = data.show_variations_expanded || !!root.variation_filter;
|
||||
root.cart_id = cart_id;
|
||||
root.cart_exists = data.cart_exists;
|
||||
root.vouchers_exist = data.vouchers_exist;
|
||||
root.has_seating_plan = data.has_seating_plan;
|
||||
@@ -2014,8 +2004,8 @@ var shared_root_methods = {
|
||||
if (this.$root.voucher_code) {
|
||||
redirect_url += '&voucher=' + encodeURIComponent(this.$root.voucher_code);
|
||||
}
|
||||
if (this.$root.get_cart_id()) {
|
||||
redirect_url += '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
|
||||
if (this.$root.cart_id) {
|
||||
redirect_url += '&take_cart_id=' + this.$root.cart_id;
|
||||
}
|
||||
if (this.$root.widget_data) {
|
||||
redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json);
|
||||
@@ -2037,28 +2027,7 @@ var shared_root_methods = {
|
||||
this.$root.subevent = event.subevent;
|
||||
this.$root.loading++;
|
||||
this.$root.reload();
|
||||
},
|
||||
create_cart: function(callback) {
|
||||
var url = this.$root.target_url + 'w/' + widget_id + '/cart/create?ajax=1';
|
||||
|
||||
this.$root.overlay.frame_loading = true;
|
||||
api._getJSON(url, (data) => {
|
||||
this.$root.set_cart_id(data.cart_id);
|
||||
this.$root.overlay.frame_loading = false;
|
||||
callback()
|
||||
}, () => {
|
||||
this.$root.overlay.error_message = strings['cart_error'];
|
||||
this.$root.overlay.frame_loading = false;
|
||||
})
|
||||
},
|
||||
get_cart_id: function() {
|
||||
if (this.$root.keep_cart) {
|
||||
return getCookie(this.$root.cookieName);
|
||||
}
|
||||
},
|
||||
set_cart_id: function(newValue) {
|
||||
setCookie(this.$root.cookieName, newValue, 30);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var shared_root_computed = {
|
||||
@@ -2080,8 +2049,9 @@ var shared_root_computed = {
|
||||
},
|
||||
voucherFormTarget: function () {
|
||||
var form_target = this.target_url + 'w/' + widget_id + '/redeem?iframe=1&locale=' + lang;
|
||||
if (this.get_cart_id()) {
|
||||
form_target += "&take_cart_id=" + encodeURIComponent(this.get_cart_id());
|
||||
var cookie = getCookie(this.cookieName);
|
||||
if (cookie) {
|
||||
form_target += "&take_cart_id=" + cookie;
|
||||
}
|
||||
if (this.subevent) {
|
||||
form_target += "&subevent=" + this.subevent;
|
||||
@@ -2121,8 +2091,9 @@ var shared_root_computed = {
|
||||
checkout_url += '?' + this.$root.additionalURLParams;
|
||||
}
|
||||
var form_target = this.target_url + 'w/' + widget_id + '/cart/add?iframe=1&next=' + encodeURIComponent(checkout_url);
|
||||
if (this.get_cart_id()) {
|
||||
form_target += "&take_cart_id=" + encodeURIComponent(this.get_cart_id());
|
||||
var cookie = getCookie(this.cookieName);
|
||||
if (cookie) {
|
||||
form_target += "&take_cart_id=" + cookie;
|
||||
}
|
||||
form_target += this.$root.consent_parameter
|
||||
return form_target
|
||||
@@ -2358,7 +2329,6 @@ var create_widget = function (element, html_id=null) {
|
||||
has_seating_plan: false,
|
||||
has_seating_plan_waitinglist: false,
|
||||
meta_filter_fields: [],
|
||||
keep_cart: true,
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
@@ -2396,7 +2366,6 @@ var create_button = function (element, html_id=null) {
|
||||
var raw_items = element.attributes.items ? element.attributes.items.value : "";
|
||||
var skip_ssl = element.attributes["skip-ssl-check"] ? true : false;
|
||||
var disable_iframe = element.attributes["disable-iframe"] ? true : false;
|
||||
var keep_cart = element.attributes["keep-cart"] ? true : false;
|
||||
var button_text = element.innerHTML;
|
||||
var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data));
|
||||
for (var i = 0; i < element.attributes.length; i++) {
|
||||
@@ -2448,8 +2417,7 @@ var create_button = function (element, html_id=null) {
|
||||
widget_data: widget_data,
|
||||
widget_id: 'pretix-widget-' + widget_id,
|
||||
html_id: html_id,
|
||||
button_text: button_text,
|
||||
keep_cart: keep_cart || items.length > 0,
|
||||
button_text: button_text
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
@@ -2458,7 +2426,7 @@ var create_button = function (element, html_id=null) {
|
||||
observer.observe(this.$el, observerOptions);
|
||||
},
|
||||
computed: shared_root_computed,
|
||||
methods: shared_root_methods,
|
||||
methods: shared_root_methods
|
||||
});
|
||||
create_overlay(app);
|
||||
return app;
|
||||
@@ -2524,14 +2492,13 @@ window.PretixWidget.open = function (target_url, voucher, subevent, items, widge
|
||||
frame_dismissed: false,
|
||||
widget_data: all_widget_data,
|
||||
widget_id: 'pretix-widget-' + widget_id,
|
||||
button_text: "",
|
||||
keep_cart: true
|
||||
button_text: ""
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
},
|
||||
computed: shared_root_computed,
|
||||
methods: shared_root_methods,
|
||||
methods: shared_root_methods
|
||||
});
|
||||
create_overlay(app);
|
||||
app.$nextTick(function () {
|
||||
|
||||
@@ -171,35 +171,6 @@ def test_giftcard_detail_expand(token_client, organizer, event, giftcard):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_detail_expand_without_permissions(team, token_client, organizer, event, giftcard):
|
||||
with scopes_disabled():
|
||||
o = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING, datetime=now(), expires=now() + timedelta(days=10),
|
||||
sales_channel=event.organizer.sales_channels.get(identifier="web"),
|
||||
total=14, locale='en'
|
||||
)
|
||||
ticket = event.items.create(name='Early-bird ticket', category=None, default_price=23, admission=True,
|
||||
personalized=True)
|
||||
op = o.positions.create(item=ticket, price=Decimal("14"))
|
||||
giftcard.owner_ticket = op
|
||||
giftcard.save()
|
||||
|
||||
team.all_event_permissions = False
|
||||
team.save()
|
||||
|
||||
res = dict(TEST_GC_RES)
|
||||
res["id"] = giftcard.pk
|
||||
res["issuance"] = giftcard.issuance.isoformat().replace('+00:00', 'Z')
|
||||
resp = token_client.get('/api/v1/organizers/{}/giftcards/{}/?expand=owner_ticket'.format(organizer.slug, giftcard.pk))
|
||||
assert resp.status_code == 200
|
||||
|
||||
assert resp.data["owner_ticket"] == {
|
||||
"id": op.pk,
|
||||
}
|
||||
|
||||
|
||||
TEST_GIFTCARD_CREATE_PAYLOAD = {
|
||||
"secret": "DEFABC",
|
||||
"value": "12.00",
|
||||
|
||||
@@ -252,76 +252,6 @@ def test_medium_detail(token_client, organizer, event, medium, giftcard, custome
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_medium_detail_event_permission_missing(token_client, organizer, event, medium, giftcard, customer, team):
|
||||
team.all_organizer_permissions = False
|
||||
team.limit_organizer_permissions = {
|
||||
"organizer.reusablemedia:read": True,
|
||||
"organizer.customers:read": True,
|
||||
"organizer.giftcards:read": True,
|
||||
}
|
||||
team.all_event_permissions = False
|
||||
team.save()
|
||||
|
||||
with scopes_disabled():
|
||||
o = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING, datetime=now(), expires=now() + timedelta(days=10),
|
||||
sales_channel=event.organizer.sales_channels.get(identifier="web"),
|
||||
total=14, locale='en'
|
||||
)
|
||||
ticket = event.items.create(name='Early-bird ticket', category=None, default_price=23, admission=True,
|
||||
personalized=True)
|
||||
op = o.positions.create(item=ticket, price=Decimal("14"))
|
||||
medium.linked_orderposition = op
|
||||
medium.linked_giftcard = giftcard
|
||||
medium.customer = customer
|
||||
medium.save()
|
||||
giftcard.owner_ticket = op
|
||||
giftcard.save()
|
||||
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/reusablemedia/{}/?expand=linked_giftcard&expand='
|
||||
'linked_giftcard.owner_ticket&expand=linked_orderposition&expand=customer'.format(
|
||||
organizer.slug, medium.pk
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
assert resp.data["linked_orderposition"] == {
|
||||
"id": op.pk,
|
||||
}
|
||||
|
||||
assert resp.data["linked_giftcard"] == {
|
||||
"id": giftcard.pk,
|
||||
"secret": "ABCDEF",
|
||||
"issuance": giftcard.issuance.isoformat().replace("+00:00", "Z"),
|
||||
"value": "23.00",
|
||||
"currency": "EUR",
|
||||
"testmode": False,
|
||||
"expires": None,
|
||||
"conditions": None,
|
||||
"owner_ticket": {"id": op.pk},
|
||||
"issuer": "dummy",
|
||||
}
|
||||
|
||||
assert resp.data["customer"] == {
|
||||
"identifier": customer.identifier,
|
||||
"external_identifier": None,
|
||||
"email": "foo@example.org",
|
||||
"phone": None,
|
||||
"name": "Foo",
|
||||
"name_parts": {"_legacy": "Foo"},
|
||||
"is_active": True,
|
||||
"is_verified": False,
|
||||
"last_login": None,
|
||||
"date_joined": customer.date_joined.isoformat().replace("+00:00", "Z"),
|
||||
"locale": "en",
|
||||
"last_modified": customer.last_modified.isoformat().replace("+00:00", "Z"),
|
||||
"notes": None
|
||||
}
|
||||
|
||||
|
||||
TEST_MEDIUM_CREATE_PAYLOAD = {
|
||||
"type": "barcode",
|
||||
"identifier": "FOOBAR",
|
||||
|
||||
@@ -33,7 +33,7 @@ from django.conf import settings
|
||||
from django.core import mail as djmail
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.signing import dumps
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.test import Client, TestCase, TransactionTestCase
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
@@ -4413,6 +4413,18 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
||||
assert len(djmail.outbox) == 1
|
||||
assert any(["Invoice_" in a[0] for a in djmail.outbox[0].attachments])
|
||||
|
||||
def test_checkout_empty_session_valid_cart(self):
|
||||
client = Client()
|
||||
with scopes_disabled():
|
||||
api_cid = "{}@api".format(get_random_string(48))
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=api_cid, item=self.ticket,
|
||||
price=23, expires=now() + timedelta(minutes=10)
|
||||
)
|
||||
|
||||
response = client.get('/%s/%s/w/1234567890abcdef/checkout/questions/' % (self.orga.slug, self.event.slug), query_params={"take_cart_id": api_cid})
|
||||
assert '€23.00' in response.content.decode()
|
||||
|
||||
|
||||
class CheckoutTransactionTestCase(BaseCheckoutTestCase, TransactionTestCase):
|
||||
def test_order_confirmation_mail_invoice_sent_somewhere_else(self):
|
||||
|
||||
Reference in New Issue
Block a user