diff --git a/doc/user/events/widget.rst b/doc/user/events/widget.rst
index 432840fde..e101f5c61 100644
--- a/doc/user/events/widget.rst
+++ b/doc/user/events/widget.rst
@@ -87,6 +87,18 @@ website. If you confident to have a good reason for not using SSL, you can overr
+Seating plans
+-------------
+
+By default, events with seating plans just show a button that opens the seating plan. You can also have the seating
+plan embedded into the widget directly by using::
+
+
+
+Note that the seating plan will only be embedded if the widget has enough space (currently min. 992 pixels width, may change
+in the future) and that the seating plan part of the widget can unfortunately *not* be styled with CSS like the rest of
+the widget.
+
Always open a new tab
---------------------
diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py
index 18b0658ce..9b6b1ddc5 100644
--- a/src/pretix/presale/views/event.py
+++ b/src/pretix/presale/views/event.py
@@ -663,11 +663,15 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
return context
-@method_decorator(allow_frame_if_namespaced, 'dispatch')
@method_decorator(iframe_entry_view_wrapper, 'dispatch')
class SeatingPlanView(EventViewMixin, TemplateView):
template_name = "pretixpresale/event/seatingplan.html"
+ def dispatch(self, request, *args, **kwargs):
+ r = super().dispatch(request, *args, **kwargs)
+ r.xframe_options_exempt = True
+ return r
+
def get(self, request, *args, **kwargs):
from pretix.presale.views.cart import get_or_create_cart_id
diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js
index 247f09c0c..5a64bc209 100644
--- a/src/pretix/static/pretixpresale/js/widget/widget.js
+++ b/src/pretix/static/pretixpresale/js/widget/widget.js
@@ -141,18 +141,21 @@ var api = {
},
'_postFormJSON': function (endpoint, form, callback, err_callback) {
- var params = [].filter.call(form.elements, function (el) {
- return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked;
- })
- .filter(function (el) {
- return !!el.name && !!el.value;
- })
- .filter(function (el) {
+ var params;
+ if (Array.isArray(form)) {
+ params = form
+ } else {
+ params = [].filter.call(form.elements, function (el) {
+ return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked;
+ }).filter(function (el) {
+ return !!el.name && !!el.value;
+ }).filter(function (el) {
return !el.disabled;
})
- .map(function (el) {
- return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
- }).join('&');
+ }
+ params = params.map(function (el) {
+ return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
+ }).join('&');
var xhr = api._getXHR();
xhr.open("POST", endpoint, true);
@@ -522,7 +525,7 @@ Vue.component('category', {
});
var shared_methods = {
- buy: function (event) {
+ buy: function (event, data) {
if (this.$root.useIframe) {
if (event) {
event.preventDefault();
@@ -541,7 +544,7 @@ var shared_methods = {
this.$root.overlay.frame_loading = true;
this.async_task_interval = 100;
- var form = this.$refs.form;
+ var form = data === undefined ? this.$refs.form : data;
if (form === undefined) {
form = this.$refs.formcomp.$refs.form;
}
@@ -663,7 +666,7 @@ var shared_methods = {
}
},
handleResize: function () {
- this.mobile = this.$refs.wrapper.clientWidth <= 800;
+ this.clientWidth = this.$refs.wrapper.clientWidth;
}
};
@@ -674,7 +677,7 @@ var shared_widget_data = function () {
async_task_timeout: null,
async_task_interval: 100,
voucher: null,
- mobile: false,
+ clientWidth: 1000,
}
};
@@ -811,11 +814,16 @@ Vue.component('pretix-widget-event-form', {
+ ''
// Seating plan
- + '
'
+ + '
'
+ ''
+ '
'
+ + '
'
+ + ''
+ + '
'
// Waiting list for seating plan
+ '
'
@@ -870,10 +878,14 @@ Vue.component('pretix-widget-event-form', {
this.$root.$on('amounts_changed', this.calculate_buy_disabled)
this.$root.$on('focus_voucher_field', this.focus_voucher_field)
this.calculate_buy_disabled()
+
+ window.addEventListener('message', this.on_seat_select);
},
beforeDestroy: function() {
this.$root.$off('amounts_changed', this.calculate_buy_disabled)
this.$root.$off('focus_voucher_field', this.focus_voucher_field)
+
+ window.addEventListener('message', this.on_seat_select);
},
computed: {
buy_label: function () {
@@ -903,9 +915,26 @@ Vue.component('pretix-widget-event-form', {
} else {
return strings.buy;
}
- }
+ },
+ show_seating_plan_inline: function () {
+ return this.$root.seating_embedded && this.$parent.clientWidth > 992;
+ },
+ seatingframe: function () {
+ var seatingframe_url = this.$root.target_url;
+ if (this.$root.subevent){
+ seatingframe_url += '/' + this.$root.subevent;
+ }
+ seatingframe_url += '/seatingframe/?inline=1&locale=' + lang + '&widget_id=' + this.$root.widgetindex;
+ return seatingframe_url;
+ },
},
methods: {
+ on_seat_select: function (ev) {
+ if (ev.data.source !== "pretix_widget_seating") return;
+ if (parseInt(ev.data.widget_id) !== this.$root.widgetindex) return; // In case multiple widgets are on this page
+ if (ev.data.action !== "buy") return;
+ this.$parent.buy(null, ev.data.data);
+ },
focus_voucher_field: function() {
this.$refs.voucherinput.scrollIntoView(false)
this.$refs.voucherinput.focus()
@@ -1371,9 +1400,12 @@ Vue.component('pretix-widget', {
data: shared_widget_data,
methods: shared_methods,
mounted: function () {
- this.mobile = this.$refs.wrapper.clientWidth <= 600;
+ this.clientWidth = this.$refs.wrapper.clientWidth;
},
computed: {
+ mobile: function () {
+ return this.clientWidth <= 600;
+ },
classObject: function () {
o = {'pretix-widget': true};
if (this.mobile) {
@@ -1536,6 +1568,7 @@ var shared_root_methods = {
root.has_seating_plan_waitinglist = data.has_seating_plan_waitinglist;
root.itemnum = data.itemnum;
}
+ root.loadid++; // force-reload iframes
root.poweredby = data.poweredby;
if (root.loading > 0) {
root.loading--;
@@ -1674,7 +1707,7 @@ var shared_root_computed = {
},
widget_data_json: function () {
return JSON.stringify(this.widget_data);
- }
+ },
};
var create_overlay = function (app) {
@@ -1716,7 +1749,7 @@ function get_ga_client_id(tracking_id) {
return null;
}
-var create_widget = function (element) {
+var create_widget = function (element, widgetindex) {
var target_url = element.attributes.event.value;
if (!target_url.match(/\/$/)) {
target_url += "/";
@@ -1725,6 +1758,7 @@ var create_widget = function (element) {
var subevent = element.attributes.subevent ? element.attributes.subevent.value : null;
var style = element.attributes.style ? element.attributes.style.value : null;
var skip_ssl = element.attributes["skip-ssl-check"] ? true : false;
+ var seating_embedded = element.attributes["seating-embedded"] ? true : false;
var disable_iframe = element.attributes["disable-iframe"] ? true : false;
var disable_vouchers = element.attributes["disable-vouchers"] ? true : false;
var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data));
@@ -1747,6 +1781,7 @@ var create_widget = function (element) {
el: element,
data: function () {
return {
+ widgetindex: widgetindex,
target_url: target_url,
parent_stack: [],
subevent: subevent,
@@ -1768,6 +1803,7 @@ var create_widget = function (element) {
voucher_explanation_text: null,
show_variations_expanded: !!variations,
skip_ssl: skip_ssl,
+ seating_embedded: seating_embedded,
disable_iframe: disable_iframe,
style: style,
connection_error: false,
@@ -1782,6 +1818,7 @@ var create_widget = function (element) {
display_add_to_cart: false,
widget_data: widget_data,
loading: 1,
+ loadid: 1,
widget_id: 'pretix-widget-' + widget_id,
vouchers_exist: false,
disable_vouchers: disable_vouchers,
@@ -1882,7 +1919,7 @@ window.PretixWidget.buildWidgets = function () {
var wlength = widgets.length;
for (var i = 0; i < wlength; i++) {
var widget = widgets[i];
- widgetlist.push(create_widget(widget));
+ widgetlist.push(create_widget(widget, i + 1));
}
var buttons = document.querySelectorAll("pretix-button, div.pretix-button-compat");
diff --git a/src/pretix/static/pretixpresale/scss/widget.scss b/src/pretix/static/pretixpresale/scss/widget.scss
index 95d2296dd..7068306cf 100644
--- a/src/pretix/static/pretixpresale/scss/widget.scss
+++ b/src/pretix/static/pretixpresale/scss/widget.scss
@@ -331,6 +331,15 @@
width: 100%;
}
+ .pretix-widget-seating-embed {
+ margin: 0 -10px;
+ }
+ .pretix-widget-seating-embed-iframe {
+ width: 100%;
+ aspect-ratio: 2/1;
+ max-height: 60vh;
+ }
+
.pretix-widget-seating-waitinglist {
margin: 15px 0;
}