Compare commits

...

15 Commits

Author SHA1 Message Date
Raphael Michel
3b62737d4c Widget: Support for inline seating plan 2022-12-22 13:24:42 +01:00
Raphael Michel
d3698b3e2f Widget: Annotate parts of widget source code 2022-12-22 11:36:22 +01:00
Fazenda Dengo
ff828ecc92 Translations: Update Portuguese (Portugal)
Currently translated at 85.6% (4228 of 4934 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt_PT/

powered by weblate
2022-12-22 10:47:53 +01:00
Fazenda Dengo
d0236572f0 Translations: Update Portuguese (Brazil)
Currently translated at 12.9% (637 of 4934 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt_BR/

powered by weblate
2022-12-22 10:47:53 +01:00
Raphael Michel
8ea6f3bc7d Fix #2499 -- Incorrect type detection order in RelativeDateWrapper 2022-12-21 15:34:31 +01:00
Raphael Michel
5587aebcd8 Fix failing widget tests 2022-12-21 15:02:36 +01:00
Raphael Michel
0b708067de Widget: Support for seated waiting list 2022-12-21 12:17:32 +01:00
Raphael Michel
e75dc74661 Allow consecutive password resets 2022-12-21 10:01:25 +01:00
Julian Rother
f0c5e54e34 PPv2: Fix continue button behaviour (#2821)
Co-authored-by: Richard Schreiber <wiffbi@gmail.com>
2022-12-20 16:09:42 +01:00
Raphael Michel
eeb6e11934 PPv2: Revert format change of log entries 2022-12-20 16:01:44 +01:00
Raphael Michel
1238165e7a PPv2: Handle types correctly in webhook 2022-12-20 15:29:37 +01:00
Raphael Michel
bcf65603e4 Enable database health checks for when we use Django 4.1
4.1 is a while in the future but I really don't want to forget this then
as it is so useful!
2022-12-20 14:54:31 +01:00
Raphael Michel
c4aed04a18 PPv2: Fix incomplete validation of capture status 2022-12-20 14:54:31 +01:00
Raphael Michel
8be09ee937 Bump dnspython to 2.2.* 2022-12-20 14:54:31 +01:00
Richard Schreiber
a1ec45daf6 Fix addon typo (#2987) 2022-12-20 09:15:38 +01:00
17 changed files with 270 additions and 66 deletions

View File

@@ -87,6 +87,18 @@ website. If you confident to have a good reason for not using SSL, you can overr
<pretix-widget event="https://pretix.eu/demo/democon/" skip-ssl-check></pretix-widget> <pretix-widget event="https://pretix.eu/demo/democon/" skip-ssl-check></pretix-widget>
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::
<pretix-widget event="https://pretix.eu/demo/democon/" seating-embedded></pretix-widget>
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 Always open a new tab
--------------------- ---------------------

View File

@@ -59,10 +59,10 @@ class RelativeDateWrapper:
def date(self, event) -> datetime.date: def date(self, event) -> datetime.date:
from .models import SubEvent from .models import SubEvent
if isinstance(self.data, datetime.date): if isinstance(self.data, datetime.datetime):
return self.data
elif isinstance(self.data, datetime.datetime):
return self.data.date() return self.data.date()
elif isinstance(self.data, datetime.date):
return self.data
else: else:
if self.data.minutes_before is not None: if self.data.minutes_before is not None:
raise ValueError('A minute-based relative datetime can not be used as a date') raise ValueError('A minute-based relative datetime can not be used as a date')

View File

@@ -140,7 +140,7 @@ error_messages = {
'addon_min_count': _('You need to select at least %(min)s add-ons from the category %(cat)s for the ' 'addon_min_count': _('You need to select at least %(min)s add-ons from the category %(cat)s for the '
'product %(base)s.'), 'product %(base)s.'),
'addon_no_multi': _('You can select every add-ons from the category %(cat)s for the product %(base)s at most once.'), 'addon_no_multi': _('You can select every add-ons from the category %(cat)s for the product %(base)s at most once.'),
'addon_only': _('One of the products you selected can only be bought as an add-on to another project.'), 'addon_only': _('One of the products you selected can only be bought as an add-on to another product.'),
'bundled_only': _('One of the products you selected can only be bought part of a bundle.'), 'bundled_only': _('One of the products you selected can only be bought part of a bundle.'),
'seat_required': _('You need to select a specific seat.'), 'seat_required': _('You need to select a specific seat.'),
'seat_invalid': _('Please select a valid seat.'), 'seat_invalid': _('Please select a valid seat.'),

View File

@@ -353,6 +353,12 @@ class Recover(TemplateView):
user.save() user.save()
messages.success(request, _('You can now login using your new password.')) messages.success(request, _('You can now login using your new password.'))
user.log_action('pretix.control.auth.user.forgot_password.recovered') user.log_action('pretix.control.auth.user.forgot_password.recovered')
has_redis = settings.HAS_REDIS
if has_redis:
from django_redis import get_redis_connection
rc = get_redis_connection("redis")
rc.delete('pretix_pwreset_%s' % user.id)
return redirect('control:auth.login') return redirect('control:auth.login')
else: else:
return self.get(request, *args, **kwargs) return self.get(request, *args, **kwargs)

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-14 13:09+0000\n" "POT-Creation-Date: 2022-12-14 13:09+0000\n"
"PO-Revision-Date: 2021-09-27 06:00+0000\n" "PO-Revision-Date: 2022-12-21 20:00+0000\n"
"Last-Translator: Diego Rodrigo <diegorodrigo90@gmail.com>\n" "Last-Translator: Fazenda Dengo <fazendadengo@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/" "Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
"pretix/pretix/pt_BR/>\n" "pretix/pretix/pt_BR/>\n"
"Language: pt_BR\n" "Language: pt_BR\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n" "Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.8\n" "X-Generator: Weblate 4.15\n"
#: pretix/api/auth/devicesecurity.py:28 #: pretix/api/auth/devicesecurity.py:28
msgid "" msgid ""
@@ -9268,10 +9268,8 @@ msgstr "Nome do evento"
#: pretix/base/settings.py:3031 pretix/base/settings.py:3045 #: pretix/base/settings.py:3031 pretix/base/settings.py:3045
#: pretix/base/settings.py:3096 pretix/base/settings.py:3114 #: pretix/base/settings.py:3096 pretix/base/settings.py:3114
#: pretix/base/settings.py:3133 #: pretix/base/settings.py:3133
#, fuzzy
#| msgid "Full name"
msgid "Family name" msgid "Family name"
msgstr "Nome completo" msgstr "Sobrenome"
#: pretix/base/settings.py:2943 pretix/base/settings.py:2959 #: pretix/base/settings.py:2943 pretix/base/settings.py:2959
#: pretix/base/settings.py:2975 pretix/base/settings.py:2990 #: pretix/base/settings.py:2975 pretix/base/settings.py:2990

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-14 13:09+0000\n" "POT-Creation-Date: 2022-12-14 13:09+0000\n"
"PO-Revision-Date: 2022-11-28 19:03+0000\n" "PO-Revision-Date: 2022-12-21 20:00+0000\n"
"Last-Translator: Vasco Baleia <vb2003.12@gmail.com>\n" "Last-Translator: Fazenda Dengo <fazendadengo@gmail.com>\n"
"Language-Team: Portuguese (Portugal) <https://translate.pretix.eu/projects/" "Language-Team: Portuguese (Portugal) <https://translate.pretix.eu/projects/"
"pretix/pretix/pt_PT/>\n" "pretix/pretix/pt_PT/>\n"
"Language: pt_PT\n" "Language: pt_PT\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n" "Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.14.1\n" "X-Generator: Weblate 4.15\n"
#: pretix/api/auth/devicesecurity.py:28 #: pretix/api/auth/devicesecurity.py:28
msgid "" msgid ""
@@ -9560,7 +9560,7 @@ msgstr "Nome próprio"
#: pretix/base/settings.py:3096 pretix/base/settings.py:3114 #: pretix/base/settings.py:3096 pretix/base/settings.py:3114
#: pretix/base/settings.py:3133 #: pretix/base/settings.py:3133
msgid "Family name" msgid "Family name"
msgstr "Apelido" msgstr "Sobrenome"
#: pretix/base/settings.py:2943 pretix/base/settings.py:2959 #: pretix/base/settings.py:2943 pretix/base/settings.py:2959
#: pretix/base/settings.py:2975 pretix/base/settings.py:2990 #: pretix/base/settings.py:2975 pretix/base/settings.py:2990

View File

@@ -698,7 +698,7 @@ class PaypalMethod(BasePaymentProvider):
except ReferencedPayPalObject.MultipleObjectsReturned: except ReferencedPayPalObject.MultipleObjectsReturned:
pass pass
if capture.status == 'PENDING': if capture.status != 'COMPLETED':
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as ' messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as '
'soon as the payment completed.')) 'soon as the payment completed.'))
payment.info = json.dumps(pp_captured_order.dict()) payment.info = json.dumps(pp_captured_order.dict())

View File

@@ -73,12 +73,21 @@ var pretixpaypal = {
pretixpaypal.locale = this.guessLocale(); pretixpaypal.locale = this.guessLocale();
} }
// If no payment option is selected, and we're not on the paypage (which doesn't have a continue button), $("input[name=payment][value^='paypal']").change(function () {
// disable the continue button if (pretixpaypal.paypal !== null) {
if (!pretixpaypal.paypage) { pretixpaypal.renderButton($(this).val());
if (!pretixpaypal.continue_button[0].form.elements['payment'].value) { } else {
pretixpaypal.continue_button.prop("disabled", true); pretixpaypal.continue_button.prop("disabled", true);
} }
});
$("input[name=payment]").not("[value^='paypal']").change(function () {
pretixpaypal.restore();
});
// If paypal is pre-selected, we must disable the continue button and handle it after SDK is loaded
if ($("input[name=payment][value^='paypal']").is(':checked')) {
pretixpaypal.continue_button.prop("disabled", true);
} }
// We are setting the cogwheel already here, as the renderAPM() method might take some time to get loaded. // We are setting the cogwheel already here, as the renderAPM() method might take some time to get loaded.
@@ -139,14 +148,6 @@ var pretixpaypal = {
pretixpaypal.renderAPMs(); pretixpaypal.renderAPMs();
} }
$("input[name=payment][value^='paypal']").change(function () {
pretixpaypal.renderButton($(this).val());
});
$("input[name=payment]").not("[value^='paypal']").change(function () {
pretixpaypal.restore();
});
if ($("input[name=payment][value^='paypal']").is(':checked')) { if ($("input[name=payment][value^='paypal']").is(':checked')) {
pretixpaypal.renderButton($("input[name=payment][value^='paypal']:checked").val()); pretixpaypal.renderButton($("input[name=payment][value^='paypal']:checked").val());
} else if ($(".payment-redo-form").length) { } else if ($(".payment-redo-form").length) {
@@ -162,8 +163,8 @@ var pretixpaypal = {
$('#paypal-button-container').empty() $('#paypal-button-container').empty()
pretixpaypal.continue_button.text(gettext('Continue')); pretixpaypal.continue_button.text(gettext('Continue'));
pretixpaypal.continue_button.show(); pretixpaypal.continue_button.show();
pretixpaypal.continue_button.prop("disabled", false);
} }
pretixpaypal.continue_button.prop("disabled", false);
}, },
renderButton: function (method) { renderButton: function (method) {
@@ -321,6 +322,15 @@ var pretixpaypal = {
}; };
$(function () { $(function () {
// This script is always loaded if paypal is enabled as a payment method, regardless of
// whether it is available (it could e.g. be hidden or limited to certain countries).
// We do not want to unnecessarily load the sdk.
// If no paypal/paypal_apm payment option is present and we are not on
// the (APM) PayView, then we do not need the SDK.
if (!$("input[name=payment][value^='paypal']").length && !$('#paypal-button-container').data('paypage')) {
return
}
pretixpaypal.load(); pretixpaypal.load();
(async() => { (async() => {

View File

@@ -426,7 +426,10 @@ def webhook(request, *args, **kwargs):
if not payment: if not payment:
return HttpResponse('Payment not found', status=200) return HttpResponse('Payment not found', status=200)
payment.order.log_action('pretix.plugins.paypal.event', data=event_json) payment.order.log_action('pretix.plugins.paypal.event', data={
**event_json,
'_order_state': sale.dict(),
})
if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED and sale['status'] in ('PARTIALLY_REFUNDED', 'REFUNDED', 'COMPLETED'): if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED and sale['status'] in ('PARTIALLY_REFUNDED', 'REFUNDED', 'COMPLETED'):
if event_json['resource_type'] == 'refund': if event_json['resource_type'] == 'refund':
@@ -470,10 +473,27 @@ def webhook(request, *args, **kwargs):
elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED, elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED,
OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED) \ OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED) \
and sale['status'] == 'COMPLETED': and sale['status'] == 'COMPLETED':
try: any_captures = False
payment.confirm() all_captures_completed = True
except Quota.QuotaExceededException: for purchaseunit in sale['purchase_units']:
pass for capture in purchaseunit['payments']['captures']:
try:
ReferencedPayPalObject.objects.get_or_create(order=payment.order, payment=payment,
reference=capture['id'])
except ReferencedPayPalObject.MultipleObjectsReturned:
pass
if capture['status'] not in ('COMPLETED', 'REFUNDED', 'PARTIALLY_REFUNDED'):
all_captures_completed = False
else:
any_captures = True
if any_captures and all_captures_completed:
try:
payment.info = json.dumps(sale.dict())
payment.save(update_fields=['info'])
payment.confirm()
except Quota.QuotaExceededException:
pass
return HttpResponse(status=200) return HttpResponse(status=200)

View File

@@ -663,11 +663,15 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
return context return context
@method_decorator(allow_frame_if_namespaced, 'dispatch')
@method_decorator(iframe_entry_view_wrapper, 'dispatch') @method_decorator(iframe_entry_view_wrapper, 'dispatch')
class SeatingPlanView(EventViewMixin, TemplateView): class SeatingPlanView(EventViewMixin, TemplateView):
template_name = "pretixpresale/event/seatingplan.html" 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): def get(self, request, *args, **kwargs):
from pretix.presale.views.cart import get_or_create_cart_id from pretix.presale.views.cart import get_or_create_cart_id

View File

@@ -243,6 +243,7 @@ class WidgetAPIProductList(EventListMixin, View):
voucher=self.voucher, voucher=self.voucher,
channel=self.request.sales_channel.identifier, channel=self.request.sales_channel.identifier,
base_qs=qs, base_qs=qs,
require_seat=None,
memberships=( memberships=(
self.request.customer.usable_memberships( self.request.customer.usable_memberships(
for_event=self.subevent or self.request.event, for_event=self.subevent or self.request.event,
@@ -252,7 +253,7 @@ class WidgetAPIProductList(EventListMixin, View):
) )
grps = [] grps = []
for cat, g in item_group_by_category(items): for cat, g in item_group_by_category([i for i in items if not i.requires_seat]):
grps.append({ grps.append({
'id': cat.pk if cat else None, 'id': cat.pk if cat else None,
'name': str(cat.name) if cat else None, 'name': str(cat.name) if cat else None,
@@ -312,7 +313,7 @@ class WidgetAPIProductList(EventListMixin, View):
} for item in g } for item in g
] ]
}) })
return grps, display_add_to_cart, len(items) return grps, display_add_to_cart, len(items), items
def post_process(self, data): def post_process(self, data):
data['poweredby'] = get_powered_by(self.request, safelink=False) data['poweredby'] = get_powered_by(self.request, safelink=False)
@@ -711,14 +712,30 @@ class WidgetAPIProductList(EventListMixin, View):
fail = True fail = True
if not fail and (ev.presale_is_running or request.event.settings.show_items_outside_presale_period): if not fail and (ev.presale_is_running or request.event.settings.show_items_outside_presale_period):
data['items_by_category'], data['display_add_to_cart'], data['itemnum'] = self._get_items() data['items_by_category'], data['display_add_to_cart'], data['itemnum'], items = self._get_items()
data['display_add_to_cart'] = data['display_add_to_cart'] and ev.presale_is_running data['display_add_to_cart'] = data['display_add_to_cart'] and ev.presale_is_running
else: else:
items = []
data['items_by_category'] = [] data['items_by_category'] = []
data['display_add_to_cart'] = False data['display_add_to_cart'] = False
data['itemnum'] = 0 data['itemnum'] = 0
data['has_seating_plan'] = ev.seating_plan is not None data['has_seating_plan'] = ev.seating_plan is not None
data['has_seating_plan_waitinglist'] = False
if request.event.settings.waiting_list_enabled and ev.presale_is_running:
for i in items:
if not i.allow_waitinglist or not i.requires_seat:
continue
if i.has_variations:
for v in i.available_variations:
if v.cached_availability[0] != Quota.AVAILABILITY_OK:
data['has_seating_plan_waitinglist'] = True
break
else:
if i.cached_availability[0] != Quota.AVAILABILITY_OK:
data['has_seating_plan_waitinglist'] = True
break
vouchers_exist = self.request.event.get_cache().get('vouchers_exist') vouchers_exist = self.request.event.get_cache().get('vouchers_exist')
if vouchers_exist is None: if vouchers_exist is None:

View File

@@ -130,6 +130,7 @@ DATABASES = {
'HOST': config.get('database', 'host', fallback=''), 'HOST': config.get('database', 'host', fallback=''),
'PORT': config.get('database', 'port', fallback=''), 'PORT': config.get('database', 'port', fallback=''),
'CONN_MAX_AGE': 0 if db_backend == 'sqlite3' else 120, 'CONN_MAX_AGE': 0 if db_backend == 'sqlite3' else 120,
'CONN_HEALTH_CHECKS': db_backend != 'sqlite3', # Will only be used from Django 4.1 onwards
'OPTIONS': db_options, 'OPTIONS': db_options,
'TEST': { 'TEST': {
'CHARSET': 'utf8mb4', 'CHARSET': 'utf8mb4',

View File

@@ -53,6 +53,7 @@ var strings = {
'next_week': django.pgettext('widget', 'Next week'), 'next_week': django.pgettext('widget', 'Next week'),
'previous_week': django.pgettext('widget', 'Previous week'), 'previous_week': django.pgettext('widget', 'Previous week'),
'show_seating': django.pgettext('widget', 'Open seat selection'), 'show_seating': django.pgettext('widget', 'Open seat selection'),
'seating_plan_waiting_list': django.pgettext('widget', 'Some or all ticket categories are currently sold out. If you want, you can add yourself to the waiting list. We will then notify if seats are available again.'),
'load_more': django.pgettext('widget', 'Load more'), 'load_more': django.pgettext('widget', 'Load more'),
'days': { 'days': {
'MO': django.gettext('Mo'), 'MO': django.gettext('Mo'),
@@ -140,18 +141,21 @@ var api = {
}, },
'_postFormJSON': function (endpoint, form, callback, err_callback) { '_postFormJSON': function (endpoint, form, callback, err_callback) {
var params = [].filter.call(form.elements, function (el) { var params;
return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked; if (Array.isArray(form)) {
}) params = form
.filter(function (el) { } else {
return !!el.name && !!el.value; params = [].filter.call(form.elements, function (el) {
}) return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked;
.filter(function (el) { }).filter(function (el) {
return !!el.name && !!el.value;
}).filter(function (el) {
return !el.disabled; return !el.disabled;
}) })
.map(function (el) { }
return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value); params = params.map(function (el) {
}).join('&'); return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
}).join('&');
var xhr = api._getXHR(); var xhr = api._getXHR();
xhr.open("POST", endpoint, true); xhr.open("POST", endpoint, true);
@@ -360,6 +364,7 @@ Vue.component('variation', {
template: ('<div class="pretix-widget-variation">' template: ('<div class="pretix-widget-variation">'
+ '<div class="pretix-widget-item-row">' + '<div class="pretix-widget-item-row">'
// Variation description
+ '<div class="pretix-widget-item-info-col">' + '<div class="pretix-widget-item-info-col">'
+ '<div class="pretix-widget-item-title-and-description">' + '<div class="pretix-widget-item-title-and-description">'
+ '<strong class="pretix-widget-item-title">{{ variation.value }}</strong>' + '<strong class="pretix-widget-item-title">{{ variation.value }}</strong>'
@@ -371,12 +376,15 @@ Vue.component('variation', {
+ '</div>' + '</div>'
+ '</div>' + '</div>'
// Price
+ '<div class="pretix-widget-item-price-col">' + '<div class="pretix-widget-item-price-col">'
+ '<pricebox :price="variation.price" :free_price="item.free_price" :original_price="orig_price"' + '<pricebox :price="variation.price" :free_price="item.free_price" :original_price="orig_price"'
+ ' :field_name="\'price_\' + item.id + \'_\' + variation.id" v-if="$root.showPrices">' + ' :field_name="\'price_\' + item.id + \'_\' + variation.id" v-if="$root.showPrices">'
+ '</pricebox>' + '</pricebox>'
+ '<span v-if="!$root.showPrices">&nbsp;</span>' + '<span v-if="!$root.showPrices">&nbsp;</span>'
+ '</div>' + '</div>'
// Availability
+ '<div class="pretix-widget-item-availability-col">' + '<div class="pretix-widget-item-availability-col">'
+ '<availbox :item="item" :variation="variation"></availbox>' + '<availbox :item="item" :variation="variation"></availbox>'
+ '</div>' + '</div>'
@@ -404,6 +412,7 @@ Vue.component('item', {
template: ('<div v-bind:class="classObject">' template: ('<div v-bind:class="classObject">'
+ '<div class="pretix-widget-item-row pretix-widget-main-item-row">' + '<div class="pretix-widget-item-row pretix-widget-main-item-row">'
// Product description
+ '<div class="pretix-widget-item-info-col">' + '<div class="pretix-widget-item-info-col">'
+ '<img :src="item.picture" v-if="item.picture" class="pretix-widget-item-picture">' + '<img :src="item.picture" v-if="item.picture" class="pretix-widget-item-picture">'
+ '<div class="pretix-widget-item-title-and-description">' + '<div class="pretix-widget-item-title-and-description">'
@@ -423,6 +432,7 @@ Vue.component('item', {
+ '</div>' + '</div>'
+ '</div>' + '</div>'
// Price
+ '<div class="pretix-widget-item-price-col">' + '<div class="pretix-widget-item-price-col">'
+ '<pricebox :price="item.price" :free_price="item.free_price" v-if="!item.has_variations && $root.showPrices"' + '<pricebox :price="item.price" :free_price="item.free_price" v-if="!item.has_variations && $root.showPrices"'
+ ' :field_name="\'price_\' + item.id" :original_price="item.original_price">' + ' :field_name="\'price_\' + item.id" :original_price="item.original_price">'
@@ -430,6 +440,8 @@ Vue.component('item', {
+ '<div class="pretix-widget-pricebox" v-if="item.has_variations && $root.showPrices">{{ pricerange }}</div>' + '<div class="pretix-widget-pricebox" v-if="item.has_variations && $root.showPrices">{{ pricerange }}</div>'
+ '<span v-if="!$root.showPrices">&nbsp;</span>' + '<span v-if="!$root.showPrices">&nbsp;</span>'
+ '</div>' + '</div>'
// Availability
+ '<div class="pretix-widget-item-availability-col">' + '<div class="pretix-widget-item-availability-col">'
+ '<a v-if="show_toggle" href="#" @click.prevent.stop="expand">'+ strings.variations + '</a>' + '<a v-if="show_toggle" href="#" @click.prevent.stop="expand">'+ strings.variations + '</a>'
+ '<availbox v-if="!item.has_variations" :item="item"></availbox>' + '<availbox v-if="!item.has_variations" :item="item"></availbox>'
@@ -438,6 +450,7 @@ Vue.component('item', {
+ '<div class="pretix-widget-clear"></div>' + '<div class="pretix-widget-clear"></div>'
+ '</div>' + '</div>'
// Variations
+ '<div :class="varClasses" v-if="item.has_variations">' + '<div :class="varClasses" v-if="item.has_variations">'
+ '<variation v-for="variation in item.variations" :variation="variation" :item="item" :key="variation.id">' + '<variation v-for="variation in item.variations" :variation="variation" :item="item" :key="variation.id">'
+ '</variation>' + '</variation>'
@@ -512,7 +525,7 @@ Vue.component('category', {
}); });
var shared_methods = { var shared_methods = {
buy: function (event) { buy: function (event, data) {
if (this.$root.useIframe) { if (this.$root.useIframe) {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();
@@ -531,7 +544,7 @@ var shared_methods = {
this.$root.overlay.frame_loading = true; this.$root.overlay.frame_loading = true;
this.async_task_interval = 100; this.async_task_interval = 100;
var form = this.$refs.form; var form = data === undefined ? this.$refs.form : data;
if (form === undefined) { if (form === undefined) {
form = this.$refs.formcomp.$refs.form; form = this.$refs.formcomp.$refs.form;
} }
@@ -653,7 +666,7 @@ var shared_methods = {
} }
}, },
handleResize: function () { handleResize: function () {
this.mobile = this.$refs.wrapper.clientWidth <= 800; this.clientWidth = this.$refs.wrapper.clientWidth;
} }
}; };
@@ -664,7 +677,7 @@ var shared_widget_data = function () {
async_task_timeout: null, async_task_timeout: null,
async_task_interval: 100, async_task_interval: 100,
voucher: null, voucher: null,
mobile: false, clientWidth: 1000,
} }
}; };
@@ -760,6 +773,7 @@ Vue.component('pretix-overlay', {
Vue.component('pretix-widget-event-form', { Vue.component('pretix-widget-event-form', {
template: ('<div class="pretix-widget-event-form">' template: ('<div class="pretix-widget-event-form">'
// Back navigation
+ '<div class="pretix-widget-event-list-back" v-if="$root.events || $root.weeks || $root.days">' + '<div class="pretix-widget-event-list-back" v-if="$root.events || $root.weeks || $root.days">'
+ '<a href="#" @click.prevent.stop="back_to_list" v-if="!$root.subevent">&lsaquo; ' + '<a href="#" @click.prevent.stop="back_to_list" v-if="!$root.subevent">&lsaquo; '
+ strings['back_to_list'] + strings['back_to_list']
@@ -768,18 +782,28 @@ Vue.component('pretix-widget-event-form', {
+ strings['back_to_dates'] + strings['back_to_dates']
+ '</a>' + '</a>'
+ '</div>' + '</div>'
// Event name
+ '<div class="pretix-widget-event-header" v-if="$root.events || $root.weeks || $root.days">' + '<div class="pretix-widget-event-header" v-if="$root.events || $root.weeks || $root.days">'
+ '<strong>{{ $root.name }}</strong>' + '<strong>{{ $root.name }}</strong>'
+ '</div>' + '</div>'
// Date range
+ '<div class="pretix-widget-event-details" v-if="($root.events || $root.weeks || $root.days) && $root.date_range">' + '<div class="pretix-widget-event-details" v-if="($root.events || $root.weeks || $root.days) && $root.date_range">'
+ '{{ $root.date_range }}' + '{{ $root.date_range }}'
+ '</div>' + '</div>'
// Form start
+ '<div class="pretix-widget-event-description" v-if="($root.events || $root.weeks || $root.days) && $root.frontpage_text" v-html="$root.frontpage_text"></div>' + '<div class="pretix-widget-event-description" v-if="($root.events || $root.weeks || $root.days) && $root.frontpage_text" v-html="$root.frontpage_text"></div>'
+ '<form method="post" :action="$root.formAction" ref="form" :target="$root.formTarget">' + '<form method="post" :action="$root.formAction" ref="form" :target="$root.formTarget">'
+ '<input type="hidden" name="_voucher_code" :value="$root.voucher_code" v-if="$root.voucher_code">' + '<input type="hidden" name="_voucher_code" :value="$root.voucher_code" v-if="$root.voucher_code">'
+ '<input type="hidden" name="subevent" :value="$root.subevent" />' + '<input type="hidden" name="subevent" :value="$root.subevent" />'
+ '<input type="hidden" name="widget_data" :value="$root.widget_data_json" />' + '<input type="hidden" name="widget_data" :value="$root.widget_data_json" />'
// Error message
+ '<div class="pretix-widget-error-message" v-if="$root.error">{{ $root.error }}</div>' + '<div class="pretix-widget-error-message" v-if="$root.error">{{ $root.error }}</div>'
// Resume cart
+ '<div class="pretix-widget-info-message pretix-widget-clickable"' + '<div class="pretix-widget-info-message pretix-widget-clickable"'
+ ' v-if="$root.cart_exists">' + ' v-if="$root.cart_exists">'
+ '<button @click.prevent.stop="$parent.resume" class="pretix-widget-resume-button" type="button">' + '<button @click.prevent.stop="$parent.resume" class="pretix-widget-resume-button" type="button">'
@@ -788,16 +812,43 @@ Vue.component('pretix-widget-event-form', {
+ strings['cart_exists'] + strings['cart_exists']
+ '<div class="pretix-widget-clear"></div>' + '<div class="pretix-widget-clear"></div>'
+ '</div>' + '</div>'
+ '<div class="pretix-widget-seating-link-wrapper" v-if="this.$root.has_seating_plan">'
// Seating plan
+ '<div class="pretix-widget-seating-link-wrapper" v-if="$root.has_seating_plan && !show_seating_plan_inline">'
+ '<button class="pretix-widget-seating-link" @click.prevent.stop="$root.startseating">' + '<button class="pretix-widget-seating-link" @click.prevent.stop="$root.startseating">'
+ strings['show_seating'] + strings['show_seating']
+ '</button>' + '</button>'
+ '</div>' + '</div>'
+ '<div class="pretix-widget-seating-embed" v-else-if="$root.has_seating_plan && show_seating_plan_inline">'
+ '<iframe :key="\'seatingframe\' + $root.loadid" class="pretix-widget-seating-embed-iframe" ref="seatingframe"'
+ ' :src="seatingframe" frameborder="0" referrerpolicy="origin" allowtransparency="true">'
+ '</iframe>'
+ '</div>'
// Waiting list for seating plan
+ '<div class="pretix-widget-seating-waitinglist" v-if="this.$root.has_seating_plan && this.$root.has_seating_plan_waitinglist">'
+ '<div class="pretix-widget-seating-waitinglist-text">'
+ strings['seating_plan_waiting_list']
+ '</div>'
+ '<div class="pretix-widget-seating-waitinglist-button-wrap">'
+ '<button class="pretix-widget-seating-waitinglist-button" @click.prevent.stop="$root.startseating">'
+ strings['waiting_list']
+ '</button>'
+ '</div>'
+ '<div class="pretix-widget-clear"></div>'
+ '</div>'
// Actual product list
+ '<category v-for="category in this.$root.categories" :category="category" :key="category.id"></category>' + '<category v-for="category in this.$root.categories" :category="category" :key="category.id"></category>'
// Buy button
+ '<div class="pretix-widget-action" v-if="$root.display_add_to_cart">' + '<div class="pretix-widget-action" v-if="$root.display_add_to_cart">'
+ '<button @click="$parent.buy" type="submit" :disabled="buy_disabled">{{ this.buy_label }}</button>' + '<button @click="$parent.buy" type="submit" :disabled="buy_disabled">{{ this.buy_label }}</button>'
+ '</div>' + '</div>'
+ '</form>' + '</form>'
// Voucher form
+ '<form method="get" :action="$root.voucherFormTarget" target="_blank" ' + '<form method="get" :action="$root.voucherFormTarget" target="_blank" '
+ ' v-if="$root.vouchers_exist && !$root.disable_vouchers && !$root.voucher_code">' + ' v-if="$root.vouchers_exist && !$root.disable_vouchers && !$root.voucher_code">'
+ '<div class="pretix-widget-voucher">' + '<div class="pretix-widget-voucher">'
@@ -812,8 +863,10 @@ Vue.component('pretix-widget-event-form', {
+ '<div class="pretix-widget-voucher-button-wrap">' + '<div class="pretix-widget-voucher-button-wrap">'
+ '<button @click="$parent.redeem">' + strings.redeem + '</button>' + '<button @click="$parent.redeem">' + strings.redeem + '</button>'
+ '</div>' + '</div>'
+ '<div class="pretix-widget-clear"></div>'
+ '</div>' + '</div>'
+ '</form>' + '</form>'
+ '</div>' + '</div>'
), ),
data: function () { data: function () {
@@ -825,10 +878,14 @@ Vue.component('pretix-widget-event-form', {
this.$root.$on('amounts_changed', this.calculate_buy_disabled) this.$root.$on('amounts_changed', this.calculate_buy_disabled)
this.$root.$on('focus_voucher_field', this.focus_voucher_field) this.$root.$on('focus_voucher_field', this.focus_voucher_field)
this.calculate_buy_disabled() this.calculate_buy_disabled()
window.addEventListener('message', this.on_seat_select);
}, },
beforeDestroy: function() { beforeDestroy: function() {
this.$root.$off('amounts_changed', this.calculate_buy_disabled) this.$root.$off('amounts_changed', this.calculate_buy_disabled)
this.$root.$off('focus_voucher_field', this.focus_voucher_field) this.$root.$off('focus_voucher_field', this.focus_voucher_field)
window.addEventListener('message', this.on_seat_select);
}, },
computed: { computed: {
buy_label: function () { buy_label: function () {
@@ -858,9 +915,26 @@ Vue.component('pretix-widget-event-form', {
} else { } else {
return strings.buy; 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: { 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() { focus_voucher_field: function() {
this.$refs.voucherinput.scrollIntoView(false) this.$refs.voucherinput.scrollIntoView(false)
this.$refs.voucherinput.focus() this.$refs.voucherinput.focus()
@@ -1144,15 +1218,21 @@ Vue.component('pretix-widget-event-calendar-row', {
Vue.component('pretix-widget-event-calendar', { Vue.component('pretix-widget-event-calendar', {
template: ('<div class="pretix-widget-event-calendar" ref="calendar">' template: ('<div class="pretix-widget-event-calendar" ref="calendar">'
// Back navigation
+ '<div class="pretix-widget-back" v-if="$root.events !== undefined">' + '<div class="pretix-widget-back" v-if="$root.events !== undefined">'
+ '<a href="#" @click.prevent.stop="back_to_list">&lsaquo; ' + '<a href="#" @click.prevent.stop="back_to_list">&lsaquo; '
+ strings['back'] + strings['back']
+ '</a>' + '</a>'
+ '</div>' + '</div>'
// Headline
+ '<div class="pretix-widget-event-header" v-if="$root.parent_stack.length > 0">' + '<div class="pretix-widget-event-header" v-if="$root.parent_stack.length > 0">'
+ '<strong>{{ $root.name }}</strong>' + '<strong>{{ $root.name }}</strong>'
+ '</div>' + '</div>'
+ '<div class="pretix-widget-event-description" v-if="$root.parent_stack.length > 0 && $root.frontpage_text" v-html="$root.frontpage_text"></div>' + '<div class="pretix-widget-event-description" v-if="$root.parent_stack.length > 0 && $root.frontpage_text" v-html="$root.frontpage_text"></div>'
// Calendar navigation
+ '<div class="pretix-widget-event-calendar-head">' + '<div class="pretix-widget-event-calendar-head">'
+ '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevmonth">&laquo; ' + '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevmonth">&laquo; '
+ strings['previous_month'] + strings['previous_month']
@@ -1162,6 +1242,8 @@ Vue.component('pretix-widget-event-calendar', {
+ strings['next_month'] + strings['next_month']
+ ' &raquo;</a>' + ' &raquo;</a>'
+ '</div>' + '</div>'
// Calendar
+ '<table class="pretix-widget-event-calendar-table">' + '<table class="pretix-widget-event-calendar-table">'
+ '<thead>' + '<thead>'
+ '<tr>' + '<tr>'
@@ -1220,14 +1302,19 @@ Vue.component('pretix-widget-event-calendar', {
Vue.component('pretix-widget-event-week-calendar', { Vue.component('pretix-widget-event-week-calendar', {
template: ('<div class="pretix-widget-event-calendar pretix-widget-event-week-calendar" ref="weekcalendar">' template: ('<div class="pretix-widget-event-calendar pretix-widget-event-week-calendar" ref="weekcalendar">'
// Back navigation
+ '<div class="pretix-widget-back" v-if="$root.events !== undefined">' + '<div class="pretix-widget-back" v-if="$root.events !== undefined">'
+ '<a href="#" @click.prevent.stop="back_to_list">&lsaquo; ' + '<a href="#" @click.prevent.stop="back_to_list">&lsaquo; '
+ strings['back'] + strings['back']
+ '</a>' + '</a>'
+ '</div>' + '</div>'
// Event header
+ '<div class="pretix-widget-event-header" v-if="$root.parent_stack.length > 0">' + '<div class="pretix-widget-event-header" v-if="$root.parent_stack.length > 0">'
+ '<strong>{{ $root.name }}</strong>' + '<strong>{{ $root.name }}</strong>'
+ '</div>' + '</div>'
// Calendar navigation
+ '<div class="pretix-widget-event-description" v-if="$root.parent_stack.length > 0 && $root.frontpage_text" v-html="$root.frontpage_text"></div>' + '<div class="pretix-widget-event-description" v-if="$root.parent_stack.length > 0 && $root.frontpage_text" v-html="$root.frontpage_text"></div>'
+ '<div class="pretix-widget-event-calendar-head">' + '<div class="pretix-widget-event-calendar-head">'
+ '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevweek">&laquo; ' + '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevweek">&laquo; '
@@ -1238,12 +1325,15 @@ Vue.component('pretix-widget-event-week-calendar', {
+ strings['next_week'] + strings['next_week']
+ ' &raquo;</a>' + ' &raquo;</a>'
+ '</div>' + '</div>'
// Actual calendar
+ '<div class="pretix-widget-event-week-table">' + '<div class="pretix-widget-event-week-table">'
+ '<div class="pretix-widget-event-week-col" v-for="d in $root.days">' + '<div class="pretix-widget-event-week-col" v-for="d in $root.days">'
+ '<pretix-widget-event-week-cell :day="d">' + '<pretix-widget-event-week-cell :day="d">'
+ '</pretix-widget-event-week-cell>' + '</pretix-widget-event-week-cell>'
+ '</div>' + '</div>'
+ '</div>' + '</div>'
+ '</div>' + '</div>'
+ '</div>'), + '</div>'),
computed: { computed: {
@@ -1310,9 +1400,12 @@ Vue.component('pretix-widget', {
data: shared_widget_data, data: shared_widget_data,
methods: shared_methods, methods: shared_methods,
mounted: function () { mounted: function () {
this.mobile = this.$refs.wrapper.clientWidth <= 600; this.clientWidth = this.$refs.wrapper.clientWidth;
}, },
computed: { computed: {
mobile: function () {
return this.clientWidth <= 600;
},
classObject: function () { classObject: function () {
o = {'pretix-widget': true}; o = {'pretix-widget': true};
if (this.mobile) { if (this.mobile) {
@@ -1472,14 +1565,16 @@ var shared_root_methods = {
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;
root.has_seating_plan_waitinglist = data.has_seating_plan_waitinglist;
root.itemnum = data.itemnum; root.itemnum = data.itemnum;
} }
root.loadid++; // force-reload iframes
root.poweredby = data.poweredby; root.poweredby = data.poweredby;
if (root.loading > 0) { if (root.loading > 0) {
root.loading--; root.loading--;
root.trigger_load_callback(); root.trigger_load_callback();
} }
if (root.parent_stack.length > 0 && root.has_seating_plan && root.categories.length === 0 && !root.frame_dismissed && root.useIframe && !root.error) { if (root.parent_stack.length > 0 && root.has_seating_plan && root.categories.length === 0 && !root.frame_dismissed && root.useIframe && !root.error && !root.has_seating_plan_waitinglist) {
// If we're on desktop and someone selects a seating-only event in a calendar, let's open it right away, // If we're on desktop and someone selects a seating-only event in a calendar, let's open it right away,
// but only if the person didn't close it before. // but only if the person didn't close it before.
root.startseating() root.startseating()
@@ -1612,7 +1707,7 @@ var shared_root_computed = {
}, },
widget_data_json: function () { widget_data_json: function () {
return JSON.stringify(this.widget_data); return JSON.stringify(this.widget_data);
} },
}; };
var create_overlay = function (app) { var create_overlay = function (app) {
@@ -1654,7 +1749,7 @@ function get_ga_client_id(tracking_id) {
return null; return null;
} }
var create_widget = function (element) { var create_widget = function (element, widgetindex) {
var target_url = element.attributes.event.value; var target_url = element.attributes.event.value;
if (!target_url.match(/\/$/)) { if (!target_url.match(/\/$/)) {
target_url += "/"; target_url += "/";
@@ -1663,6 +1758,7 @@ var create_widget = function (element) {
var subevent = element.attributes.subevent ? element.attributes.subevent.value : null; var subevent = element.attributes.subevent ? element.attributes.subevent.value : null;
var style = element.attributes.style ? element.attributes.style.value : null; var style = element.attributes.style ? element.attributes.style.value : null;
var skip_ssl = element.attributes["skip-ssl-check"] ? true : false; 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_iframe = element.attributes["disable-iframe"] ? true : false;
var disable_vouchers = element.attributes["disable-vouchers"] ? true : false; var disable_vouchers = element.attributes["disable-vouchers"] ? true : false;
var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data)); var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data));
@@ -1685,6 +1781,7 @@ var create_widget = function (element) {
el: element, el: element,
data: function () { data: function () {
return { return {
widgetindex: widgetindex,
target_url: target_url, target_url: target_url,
parent_stack: [], parent_stack: [],
subevent: subevent, subevent: subevent,
@@ -1706,6 +1803,7 @@ var create_widget = function (element) {
voucher_explanation_text: null, voucher_explanation_text: null,
show_variations_expanded: !!variations, show_variations_expanded: !!variations,
skip_ssl: skip_ssl, skip_ssl: skip_ssl,
seating_embedded: seating_embedded,
disable_iframe: disable_iframe, disable_iframe: disable_iframe,
style: style, style: style,
connection_error: false, connection_error: false,
@@ -1720,6 +1818,7 @@ var create_widget = function (element) {
display_add_to_cart: false, display_add_to_cart: false,
widget_data: widget_data, widget_data: widget_data,
loading: 1, loading: 1,
loadid: 1,
widget_id: 'pretix-widget-' + widget_id, widget_id: 'pretix-widget-' + widget_id,
vouchers_exist: false, vouchers_exist: false,
disable_vouchers: disable_vouchers, disable_vouchers: disable_vouchers,
@@ -1727,7 +1826,8 @@ var create_widget = function (element) {
itemcount: 0, itemcount: 0,
overlay: null, overlay: null,
poweredby: "", poweredby: "",
has_seating_plan: false has_seating_plan: false,
has_seating_plan_waitinglist: false,
} }
}, },
created: function () { created: function () {
@@ -1819,7 +1919,7 @@ window.PretixWidget.buildWidgets = function () {
var wlength = widgets.length; var wlength = widgets.length;
for (var i = 0; i < wlength; i++) { for (var i = 0; i < wlength; i++) {
var widget = widgets[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"); var buttons = document.querySelectorAll("pretix-button, div.pretix-button-compat");

View File

@@ -331,6 +331,37 @@
width: 100%; 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;
}
.pretix-widget-seating-waitinglist-text {
padding: 0 15px;
width: 75%;
box-sizing: border-box;
float: left;
}
.pretix-widget-seating-waitinglist-button-wrap {
padding: 0 15px;
width: 25%;
box-sizing: border-box;
float: left;
}
.pretix-widget-seating-waitinglist-button {
width: 100%;
}
.pretix-widget-item-with-picture .pretix-widget-main-item-row .pretix-widget-item-title-and-description { .pretix-widget-item-with-picture .pretix-widget-main-item-row .pretix-widget-item-title-and-description {
margin-left: 70px; margin-left: 70px;
} }

View File

@@ -189,7 +189,7 @@ setup(
'django-scopes==1.2.*', 'django-scopes==1.2.*',
'django-statici18n==2.3.*', 'django-statici18n==2.3.*',
'djangorestframework==3.14.*', 'djangorestframework==3.14.*',
'dnspython<2.0', # do not upgrade, causes issues with eventlet / gunicorn 19 and we cannot upgrade gunicorn right now 'dnspython==2.2.*',
'drf_ujson2==1.7.*', 'drf_ujson2==1.7.*',
'isoweek', 'isoweek',
'jsonschema', 'jsonschema',

View File

@@ -26,6 +26,7 @@ from decimal import Decimal
import pytest import pytest
from django.utils.timezone import now from django.utils.timezone import now
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from paypalhttp.http_response import Result
from pretix.base.models import ( from pretix.base.models import (
Event, Order, OrderPayment, OrderRefund, Organizer, Team, User, Event, Order, OrderPayment, OrderRefund, Organizer, Team, User,
@@ -262,7 +263,7 @@ def init_api(self):
@pytest.mark.django_db @pytest.mark.django_db
def test_webhook_all_good(env, client, monkeypatch): def test_webhook_all_good(env, client, monkeypatch):
order = env[1] order = env[1]
pp_order = get_test_order() pp_order = Result(get_test_order())
monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order) monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order)
monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api) monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api)
@@ -406,7 +407,7 @@ def test_webhook_mark_paid(env, client, monkeypatch):
with scopes_disabled(): with scopes_disabled():
order.payments.update(state=OrderPayment.PAYMENT_STATE_PENDING) order.payments.update(state=OrderPayment.PAYMENT_STATE_PENDING)
pp_order = get_test_order() pp_order = Result(get_test_order())
monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order) monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order)
monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api) monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api)
with scopes_disabled(): with scopes_disabled():
@@ -503,8 +504,8 @@ def test_webhook_mark_paid(env, client, monkeypatch):
@pytest.mark.django_db @pytest.mark.django_db
def test_webhook_refund1(env, client, monkeypatch): def test_webhook_refund1(env, client, monkeypatch):
order = env[1] order = env[1]
pp_order = get_test_order() pp_order = Result(get_test_order())
pp_refund = get_test_refund() pp_refund = Result(get_test_refund())
monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order) monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order)
monkeypatch.setattr("paypalcheckoutsdk.payments.RefundsGetRequest", lambda *args: pp_refund) monkeypatch.setattr("paypalcheckoutsdk.payments.RefundsGetRequest", lambda *args: pp_refund)
@@ -597,8 +598,8 @@ def test_webhook_refund1(env, client, monkeypatch):
@pytest.mark.django_db @pytest.mark.django_db
def test_webhook_refund2(env, client, monkeypatch): def test_webhook_refund2(env, client, monkeypatch):
order = env[1] order = env[1]
pp_order = get_test_order() pp_order = Result(get_test_order())
pp_refund = get_test_refund() pp_refund = Result(get_test_refund())
monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order) monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order)
monkeypatch.setattr("paypalcheckoutsdk.payments.RefundsGetRequest", lambda *args: pp_refund) monkeypatch.setattr("paypalcheckoutsdk.payments.RefundsGetRequest", lambda *args: pp_refund)

View File

@@ -172,6 +172,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
"waiting_list_enabled": False, "waiting_list_enabled": False,
"error": None, "error": None,
"has_seating_plan": False, "has_seating_plan": False,
"has_seating_plan_waitinglist": False,
'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>', 'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
"items_by_category": [ "items_by_category": [
{ {
@@ -348,6 +349,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
"show_variations_expanded": False, "show_variations_expanded": False,
"display_net_prices": False, "display_net_prices": False,
"has_seating_plan": False, "has_seating_plan": False,
"has_seating_plan_waitinglist": False,
"vouchers_exist": True, "vouchers_exist": True,
"waiting_list_enabled": False, "waiting_list_enabled": False,
"error": None, "error": None,
@@ -401,6 +403,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
"display_net_prices": False, "display_net_prices": False,
"vouchers_exist": True, "vouchers_exist": True,
"has_seating_plan": False, "has_seating_plan": False,
"has_seating_plan_waitinglist": False,
"waiting_list_enabled": False, "waiting_list_enabled": False,
"error": None, "error": None,
'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>', 'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
@@ -469,6 +472,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
"show_variations_expanded": False, "show_variations_expanded": False,
"display_net_prices": False, "display_net_prices": False,
"has_seating_plan": False, "has_seating_plan": False,
"has_seating_plan_waitinglist": False,
"vouchers_exist": True, "vouchers_exist": True,
"waiting_list_enabled": False, "waiting_list_enabled": False,
"error": "This voucher is expired.", "error": "This voucher is expired.",