Allow buttons to reuse cart (Z#23226853) (#6047)

* Allow buttons to reuse cart (Z#23226853)

* Always keep cart of buttons with items set
This commit is contained in:
pajowu
2026-04-13 19:32:33 +02:00
committed by GitHub
parent f46fc7fa69
commit 059ff6c99b
3 changed files with 72 additions and 24 deletions

View File

@@ -91,6 +91,9 @@ event_patterns = [
re_path(r'w/(?P<cart_namespace>[a-zA-Z0-9]{16})/cart/add', re_path(r'w/(?P<cart_namespace>[a-zA-Z0-9]{16})/cart/add',
csrf_exempt(pretix.presale.views.cart.CartAdd.as_view()), csrf_exempt(pretix.presale.views.cart.CartAdd.as_view()),
name='event.cart.add'), 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(), re_path(r'unlock/(?P<hash>[a-z0-9]{64})/$', pretix.presale.views.user.UnlockHashView.as_view(),
name='event.payment.unlock'), name='event.payment.unlock'),

View File

@@ -555,6 +555,18 @@ class CartClear(EventViewMixin, CartActionMixin, AsyncAction, View):
request.sales_channel.identifier, time_machine_now(default=None)) 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') @method_decorator(allow_frame_if_namespaced, 'dispatch')
class CartExtendReservation(EventViewMixin, CartActionMixin, AsyncAction, View): class CartExtendReservation(EventViewMixin, CartActionMixin, AsyncAction, View):
task = extend_cart_reservation task = extend_cart_reservation

View File

@@ -110,6 +110,10 @@ var setCookie = function (cname, cvalue, exdays) {
var d = new Date(); var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toUTCString(); 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=/"; document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}; };
var getCookie = function (name) { var getCookie = function (name) {
@@ -726,17 +730,16 @@ var shared_methods = {
buy_callback: function (data) { buy_callback: function (data) {
if (data.redirect) { if (data.redirect) {
if (data.cart_id) { if (data.cart_id) {
this.$root.cart_id = data.cart_id; this.$root.set_cart_id(data.cart_id);
setCookie(this.$root.cookieName, data.cart_id, 30);
} }
if (data.redirect.substr(0, 1) === '/') { if (data.redirect.substr(0, 1) === '/') {
data.redirect = this.$root.target_url.replace(/^([^\/]+:\/\/[^\/]+)\/.*$/, "$1") + data.redirect; data.redirect = this.$root.target_url.replace(/^([^\/]+:\/\/[^\/]+)\/.*$/, "$1") + data.redirect;
} }
var url = data.redirect; var url = data.redirect;
if (url.indexOf('?')) { if (url.indexOf('?')) {
url = url + '&iframe=1&locale=' + lang + '&take_cart_id=' + this.$root.cart_id; url = url + '&iframe=1&locale=' + lang + '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
} else { } else {
url = url + '?iframe=1&locale=' + lang + '&take_cart_id=' + this.$root.cart_id; url = url + '?iframe=1&locale=' + lang + '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
} }
url += this.$root.consent_parameter; url += this.$root.consent_parameter;
if (this.$root.additionalURLParams) { if (this.$root.additionalURLParams) {
@@ -779,15 +782,24 @@ var shared_methods = {
} }
}, },
resume: function () { 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; var redirect_url;
redirect_url = this.$root.target_url + 'w/' + widget_id + '/'; redirect_url = this.$root.target_url + 'w/' + widget_id + '/';
if (this.$root.subevent && !this.$root.cart_id) { if (this.$root.subevent && this.$root.is_button && this.$root.items.length === 0) {
// button with subevent but no items // button with subevent but no items
redirect_url += this.$root.subevent + '/'; redirect_url += this.$root.subevent + '/';
} }
redirect_url += '?iframe=1&locale=' + lang; redirect_url += '?iframe=1&locale=' + lang;
if (this.$root.cart_id) { if (this.$root.get_cart_id()) {
redirect_url += '&take_cart_id=' + this.$root.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.widget_data) { if (this.$root.widget_data) {
redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json); redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json);
@@ -1864,12 +1876,11 @@ var shared_root_methods = {
if (this.$root.variation_filter) { if (this.$root.variation_filter) {
url += '&variations=' + encodeURIComponent(this.$root.variation_filter); url += '&variations=' + encodeURIComponent(this.$root.variation_filter);
} }
var cart_id = getCookie(this.cookieName);
if (this.$root.voucher_code) { if (this.$root.voucher_code) {
url += '&voucher=' + encodeURIComponent(this.$root.voucher_code); url += '&voucher=' + encodeURIComponent(this.$root.voucher_code);
} }
if (cart_id) { if (this.$root.get_cart_id()) {
url += "&cart_id=" + encodeURIComponent(cart_id); url += "&cart_id=" + encodeURIComponent(this.$root.get_cart_id());
} }
if (this.$root.date !== null) { if (this.$root.date !== null) {
url += "&date=" + this.$root.date.substr(0, 7); url += "&date=" + this.$root.date.substr(0, 7);
@@ -1939,7 +1950,6 @@ var shared_root_methods = {
root.display_add_to_cart = data.display_add_to_cart; root.display_add_to_cart = data.display_add_to_cart;
root.waiting_list_enabled = data.waiting_list_enabled; root.waiting_list_enabled = data.waiting_list_enabled;
root.show_variations_expanded = data.show_variations_expanded || !!root.variation_filter; root.show_variations_expanded = data.show_variations_expanded || !!root.variation_filter;
root.cart_id = cart_id;
root.cart_exists = data.cart_exists; root.cart_exists = data.cart_exists;
root.vouchers_exist = data.vouchers_exist; root.vouchers_exist = data.vouchers_exist;
root.has_seating_plan = data.has_seating_plan; root.has_seating_plan = data.has_seating_plan;
@@ -2004,8 +2014,8 @@ var shared_root_methods = {
if (this.$root.voucher_code) { if (this.$root.voucher_code) {
redirect_url += '&voucher=' + encodeURIComponent(this.$root.voucher_code); redirect_url += '&voucher=' + encodeURIComponent(this.$root.voucher_code);
} }
if (this.$root.cart_id) { if (this.$root.get_cart_id()) {
redirect_url += '&take_cart_id=' + this.$root.cart_id; redirect_url += '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id());
} }
if (this.$root.widget_data) { if (this.$root.widget_data) {
redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json); redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json);
@@ -2027,7 +2037,28 @@ var shared_root_methods = {
this.$root.subevent = event.subevent; this.$root.subevent = event.subevent;
this.$root.loading++; this.$root.loading++;
this.$root.reload(); 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 = { var shared_root_computed = {
@@ -2049,9 +2080,8 @@ var shared_root_computed = {
}, },
voucherFormTarget: function () { voucherFormTarget: function () {
var form_target = this.target_url + 'w/' + widget_id + '/redeem?iframe=1&locale=' + lang; var form_target = this.target_url + 'w/' + widget_id + '/redeem?iframe=1&locale=' + lang;
var cookie = getCookie(this.cookieName); if (this.get_cart_id()) {
if (cookie) { form_target += "&take_cart_id=" + encodeURIComponent(this.get_cart_id());
form_target += "&take_cart_id=" + cookie;
} }
if (this.subevent) { if (this.subevent) {
form_target += "&subevent=" + this.subevent; form_target += "&subevent=" + this.subevent;
@@ -2091,9 +2121,8 @@ var shared_root_computed = {
checkout_url += '?' + this.$root.additionalURLParams; checkout_url += '?' + this.$root.additionalURLParams;
} }
var form_target = this.target_url + 'w/' + widget_id + '/cart/add?iframe=1&next=' + encodeURIComponent(checkout_url); var form_target = this.target_url + 'w/' + widget_id + '/cart/add?iframe=1&next=' + encodeURIComponent(checkout_url);
var cookie = getCookie(this.cookieName); if (this.get_cart_id()) {
if (cookie) { form_target += "&take_cart_id=" + encodeURIComponent(this.get_cart_id());
form_target += "&take_cart_id=" + cookie;
} }
form_target += this.$root.consent_parameter form_target += this.$root.consent_parameter
return form_target return form_target
@@ -2329,6 +2358,7 @@ var create_widget = function (element, html_id=null) {
has_seating_plan: false, has_seating_plan: false,
has_seating_plan_waitinglist: false, has_seating_plan_waitinglist: false,
meta_filter_fields: [], meta_filter_fields: [],
keep_cart: true,
} }
}, },
created: function () { created: function () {
@@ -2366,6 +2396,7 @@ var create_button = function (element, html_id=null) {
var raw_items = element.attributes.items ? element.attributes.items.value : ""; var raw_items = element.attributes.items ? element.attributes.items.value : "";
var skip_ssl = element.attributes["skip-ssl-check"] ? true : false; var skip_ssl = element.attributes["skip-ssl-check"] ? true : false;
var disable_iframe = element.attributes["disable-iframe"] ? 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 button_text = element.innerHTML;
var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data)); var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data));
for (var i = 0; i < element.attributes.length; i++) { for (var i = 0; i < element.attributes.length; i++) {
@@ -2417,7 +2448,8 @@ var create_button = function (element, html_id=null) {
widget_data: widget_data, widget_data: widget_data,
widget_id: 'pretix-widget-' + widget_id, widget_id: 'pretix-widget-' + widget_id,
html_id: html_id, html_id: html_id,
button_text: button_text button_text: button_text,
keep_cart: keep_cart || items.length > 0,
} }
}, },
created: function () { created: function () {
@@ -2426,7 +2458,7 @@ var create_button = function (element, html_id=null) {
observer.observe(this.$el, observerOptions); observer.observe(this.$el, observerOptions);
}, },
computed: shared_root_computed, computed: shared_root_computed,
methods: shared_root_methods methods: shared_root_methods,
}); });
create_overlay(app); create_overlay(app);
return app; return app;
@@ -2492,13 +2524,14 @@ window.PretixWidget.open = function (target_url, voucher, subevent, items, widge
frame_dismissed: false, frame_dismissed: false,
widget_data: all_widget_data, widget_data: all_widget_data,
widget_id: 'pretix-widget-' + widget_id, widget_id: 'pretix-widget-' + widget_id,
button_text: "" button_text: "",
keep_cart: true
} }
}, },
created: function () { created: function () {
}, },
computed: shared_root_computed, computed: shared_root_computed,
methods: shared_root_methods methods: shared_root_methods,
}); });
create_overlay(app); create_overlay(app);
app.$nextTick(function () { app.$nextTick(function () {