From 3651c88289b81ec9ba96e1b1941469082db6523a Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Wed, 20 Mar 2024 09:19:02 +0100 Subject: [PATCH] Widget: pass utm-params from embedding page to presale --- src/pretix/presale/views/checkout.py | 5 +- src/pretix/presale/views/event.py | 27 ++++++--- .../static/pretixpresale/js/widget/widget.js | 60 ++++++++++++++++++- 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/pretix/presale/views/checkout.py b/src/pretix/presale/views/checkout.py index f61bb0c363..271464ae2b 100644 --- a/src/pretix/presale/views/checkout.py +++ b/src/pretix/presale/views/checkout.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from urllib.parse import quote +from urllib.parse import quote, urlencode from django.contrib import messages from django.http import Http404 @@ -75,7 +75,8 @@ class CheckoutView(View): return self.redirect(previous_step.get_step_url(request) if previous_step else self.get_index_url(request)) if 'step' not in kwargs: - return self.redirect(step.get_step_url(request)) + utm_params = {k: v for k, v in request.GET.items() if k.startswith("utm_")} + return self.redirect(step.get_step_url(request) + '?' + urlencode(utm_params)) is_selected = (step.identifier == kwargs.get('step', '')) if "async_id" not in request.GET and not is_selected and not step.is_completed(request, warn=not is_selected): return self.redirect(step.get_step_url(request)) diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py index 7af403ba55..775e0f31be 100644 --- a/src/pretix/presale/views/event.py +++ b/src/pretix/presale/views/event.py @@ -35,7 +35,6 @@ import calendar import hashlib import sys -import urllib.parse from collections import defaultdict from datetime import date, datetime, timedelta from decimal import Decimal @@ -483,17 +482,19 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView): from pretix.presale.views.cart import get_or_create_cart_id self.subevent = None + utm_params = {k: v for k, v in request.GET.items() if k.startswith("utm_")} if request.GET.get('src', '') == 'widget' and 'take_cart_id' in request.GET: # User has clicked "Open in a new tab" link in widget get_or_create_cart_id(request) - return redirect_to_url(eventreverse(request.event, 'presale:event.index', kwargs=kwargs)) + return redirect_to_url(eventreverse(request.event, 'presale:event.index', kwargs=kwargs) + '?' + urlencode(utm_params)) elif request.GET.get('iframe', '') == '1' and 'take_cart_id' in request.GET: # Widget just opened, a cart already exists. Let's to a stupid redirect to check if cookies are disabled get_or_create_cart_id(request) - return redirect_to_url(eventreverse(request.event, 'presale:event.index', kwargs=kwargs) + '?' + urllib.parse.urlencode({ + return redirect_to_url(eventreverse(request.event, 'presale:event.index', kwargs=kwargs) + '?' + urlencode({ 'require_cookie': 'true', 'cart_id': request.GET.get('take_cart_id'), **({"locale": request.GET.get('locale')} if request.GET.get('locale') else {}), + **utm_params, })) elif request.GET.get('iframe', '') == '1' and len(self.request.GET.get('widget_data', '{}')) > 3: # We've been passed data from a widget, we need to create a cart session to store it. @@ -504,10 +505,11 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView): r = render(request, 'pretixpresale/event/cookies.html', { 'url': eventreverse( request.event, "presale:event.index", kwargs={'cart_namespace': kwargs.get('cart_namespace') or ''} - ) + "?" + urllib.parse.urlencode({ + ) + "?" + urlencode({ "src": "widget", **({"locale": request.GET.get('locale')} if request.GET.get('locale') else {}), **({"take_cart_id": request.GET.get('cart_id')} if request.GET.get('cart_id') else {}), + **utm_params, }) }) r._csp_ignore = True @@ -526,7 +528,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView): return super().get(request, *args, **kwargs) else: if 'subevent' in kwargs: - return redirect_to_url(self.get_index_url()) + return redirect_to_url(self.get_index_url() + '?' + urlencode(utm_params)) else: return super().get(request, *args, **kwargs) @@ -802,16 +804,19 @@ class SeatingPlanView(EventViewMixin, TemplateView): from pretix.presale.views.cart import get_or_create_cart_id self.subevent = None + utm_params = {k: v for k, v in request.GET.items() if k.startswith("utm_")} if request.GET.get('src', '') == 'widget' and 'take_cart_id' in request.GET: # User has clicked "Open in a new tab" link in widget get_or_create_cart_id(request) - return redirect_to_url(eventreverse(request.event, 'presale:event.seatingplan', kwargs=kwargs)) + return redirect_to_url(eventreverse(request.event, 'presale:event.seatingplan', kwargs=kwargs) + '?' + urlencode(utm_params)) elif request.GET.get('iframe', '') == '1' and 'take_cart_id' in request.GET: # Widget just opened, a cart already exists. Let's to a stupid redirect to check if cookies are disabled get_or_create_cart_id(request) - return redirect_to_url(eventreverse(request.event, 'presale:event.seatingplan', kwargs=kwargs) + '?require_cookie=true&cart_id={}'.format( - request.GET.get('take_cart_id') - )) + return redirect_to_url(eventreverse(request.event, 'presale:event.seatingplan', kwargs=kwargs) + '?' + urlencode({ + **utm_params, + 'require_cookie': 'true', + 'cart_id': request.GET.get('take_cart_id'), + })) elif request.GET.get('iframe', '') == '1' and len(self.request.GET.get('widget_data', '{}')) > 3: # We've been passed data from a widget, we need to create a cart session to store it. get_or_create_cart_id(request) @@ -838,6 +843,10 @@ class SeatingPlanView(EventViewMixin, TemplateView): if context['cart_redirect'].startswith('https:'): context['cart_redirect'] = '/' + context['cart_redirect'].split('/', 3)[3] + utm_params = {k: v for k, v in self.request.GET.items() if k.startswith("utm_")} + if utm_params: + context['cart_redirect'] += '?' + urlencode(utm_params) + v = self.request.GET.get('voucher') if v: v = v.strip() diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js index b2dc71433d..cae4b7d166 100644 --- a/src/pretix/static/pretixpresale/js/widget/widget.js +++ b/src/pretix/static/pretixpresale/js/widget/widget.js @@ -689,6 +689,9 @@ var shared_methods = { } else { url = url + '?iframe=1&locale=' + lang + '&take_cart_id=' + this.$root.cart_id; } + if (this.$root.additionalURLParams) { + url += '&' + this.$root.additionalURLParams; + } if (data.success === false) { url = url.replace(/checkout\/start/g, ""); this.$root.overlay.error_message = data.message; @@ -715,12 +718,27 @@ var shared_methods = { if (this.$root.useIframe) { event.preventDefault(); } else { + if (this.$root.additionalURLParams) { + var params = new URLSearchParams(this.$root.additionalURLParams); + for (var [key, value] of params.entries()) { + if (!event.target.form.elements[key]) { + var input = document.createElement("input"); + input.type = "hidden"; + input.name = key; + input.value = value; + event.target.form.appendChild(input); + } + } + } return; } var redirect_url = this.$root.voucherFormTarget + '&voucher=' + encodeURIComponent(this.voucher) + '&subevent=' + this.$root.subevent; if (this.$root.widget_data) { redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json); } + if (this.$root.additionalURLParams) { + redirect_url += '&' + this.$root.additionalURLParams; + } var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; this.$root.overlay.frame_loading = true; iframe.src = redirect_url; @@ -731,6 +749,9 @@ var shared_methods = { if (this.$root.widget_data) { redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json); } + if (this.$root.additionalURLParams) { + redirect_url += '&' + this.$root.additionalURLParams; + } if (this.$root.useIframe) { var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; this.$root.overlay.frame_loading = true; @@ -753,6 +774,9 @@ var shared_methods = { if (this.$root.widget_data) { redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json); } + if (this.$root.additionalURLParams) { + redirect_url += '&' + this.$root.additionalURLParams; + } if (this.$root.useIframe) { var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; this.$root.overlay.frame_loading = true; @@ -1634,9 +1658,16 @@ Vue.component('pretix-button', { var shared_root_methods = { open_link_in_frame: function (event) { + var url = event.target.attributes.href.value; + if (this.$root.additionalURLParams) { + if (url.indexOf('?')) { + url += '&' + this.$root.additionalURLParams; + } else { + url += '?' + this.$root.additionalURLParams; + } + } if (this.$root.useIframe) { event.preventDefault(); - var url = event.target.attributes.href.value; if (url.indexOf('?')) { url += '&iframe=1'; } else { @@ -1645,6 +1676,7 @@ var shared_root_methods = { this.$root.overlay.$children[0].$refs['frame-container'].children[0].src = url; this.$root.overlay.frame_loading = true; } else { + event.target.href = url; return; } }, @@ -1801,6 +1833,9 @@ var shared_root_methods = { if (this.$root.subevent){ redirect_url += '&subevent=' + this.$root.subevent; } + if (this.$root.additionalURLParams) { + redirect_url += '&' + this.$root.additionalURLParams; + } if (this.$root.useIframe) { var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; this.$root.overlay.frame_loading = true; @@ -1824,6 +1859,9 @@ var shared_root_methods = { if (this.$root.widget_data) { redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json); } + if (this.$root.additionalURLParams) { + redirect_url += '&' + this.$root.additionalURLParams; + } if (this.$root.useIframe) { var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; this.$root.overlay.frame_loading = true; @@ -1868,6 +1906,9 @@ var shared_root_computed = { if (this.subevent) { form_target += "&subevent=" + this.subevent; } + if (this.$root.additionalURLParams) { + form_target += '&' + this.$root.additionalURLParams; + } return form_target; }, formMethod: function () { @@ -1888,6 +1929,9 @@ var shared_root_computed = { if (!this.$root.cart_exists) { checkout_url += "checkout/start"; } + if (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 cookie = getCookie(this.cookieName); if (cookie) { @@ -1924,7 +1968,19 @@ var shared_root_computed = { }, widget_data_json: function () { return JSON.stringify(this.widget_data); - } + }, + additionalURLParams: function () { + if (!window.location.search.indexOf('utm_')) { + return ''; + } + var params = new URLSearchParams(window.location.search); + for (var [key, value] of params.entries()) { + if (!key.startsWith('utm_')) { + params.delete(key); + } + } + return params.toString(); + }, }; var create_overlay = function (app) {