forked from CGM_Public/pretix_original
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48c110b32b |
@@ -87,18 +87,6 @@ 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
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|||||||
@@ -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.datetime):
|
if isinstance(self.data, datetime.date):
|
||||||
return self.data.date()
|
|
||||||
elif isinstance(self.data, datetime.date):
|
|
||||||
return self.data
|
return self.data
|
||||||
|
elif isinstance(self.data, datetime.datetime):
|
||||||
|
return self.data.date()
|
||||||
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')
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ from pretix.base.i18n import (
|
|||||||
LazyLocaleException, get_language_without_region, language,
|
LazyLocaleException, get_language_without_region, language,
|
||||||
)
|
)
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
CartPosition, Device, Event, GiftCard, Item, ItemVariation, Membership,
|
CartPosition, Customer, Device, Event, GiftCard, Item, ItemVariation,
|
||||||
Order, OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User,
|
Membership, Order, OrderPayment, OrderPosition, Quota, Seat,
|
||||||
Voucher,
|
SeatCategoryMapping, User, Voucher,
|
||||||
)
|
)
|
||||||
from pretix.base.models.event import SubEvent
|
from pretix.base.models.event import SubEvent
|
||||||
from pretix.base.models.orders import (
|
from pretix.base.models.orders import (
|
||||||
@@ -849,7 +849,7 @@ def _get_fees(positions: List[CartPosition], payment_requests: List[dict], addre
|
|||||||
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
|
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
|
||||||
payment_requests: List[dict], locale: str=None, address: InvoiceAddress=None,
|
payment_requests: List[dict], locale: str=None, address: InvoiceAddress=None,
|
||||||
meta_info: dict=None, sales_channel: str='web', shown_total=None,
|
meta_info: dict=None, sales_channel: str='web', shown_total=None,
|
||||||
customer=None):
|
customer=None, customer_attached=False):
|
||||||
payments = []
|
payments = []
|
||||||
sales_channel = get_all_sales_channels()[sales_channel]
|
sales_channel = get_all_sales_channels()[sales_channel]
|
||||||
|
|
||||||
@@ -931,6 +931,8 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
|||||||
if meta_info:
|
if meta_info:
|
||||||
for msg in meta_info.get('confirm_messages', []):
|
for msg in meta_info.get('confirm_messages', []):
|
||||||
order.log_action('pretix.event.order.consent', data={'msg': msg})
|
order.log_action('pretix.event.order.consent', data={'msg': msg})
|
||||||
|
if customer and customer_attached:
|
||||||
|
customer.log_action('pretix.customer.order.attached', data={'order': order.code})
|
||||||
|
|
||||||
order_placed.send(event, order=order)
|
order_placed.send(event, order=order)
|
||||||
return order, payments
|
return order, payments
|
||||||
@@ -981,9 +983,6 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
|||||||
if not p['pprov']:
|
if not p['pprov']:
|
||||||
raise OrderError(error_messages['internal'])
|
raise OrderError(error_messages['internal'])
|
||||||
|
|
||||||
if customer:
|
|
||||||
customer = event.organizer.customers.get(pk=customer)
|
|
||||||
|
|
||||||
if email == settings.PRETIX_EMAIL_NONE_VALUE:
|
if email == settings.PRETIX_EMAIL_NONE_VALUE:
|
||||||
email = None
|
email = None
|
||||||
|
|
||||||
@@ -995,6 +994,30 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
|||||||
except InvoiceAddress.DoesNotExist:
|
except InvoiceAddress.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
customer_attached = False
|
||||||
|
if customer:
|
||||||
|
customer = event.organizer.customers.get(pk=customer)
|
||||||
|
elif event.settings.customer_accounts_link_by_email in ('attach', 'create') and email:
|
||||||
|
try:
|
||||||
|
customer = event.organizer.customers.get(email__iexact=email)
|
||||||
|
customer_attached = True
|
||||||
|
except Customer.MultipleObjectsReturned:
|
||||||
|
logger.warning(f'Multiple customer accounts found for {email}, not attaching.')
|
||||||
|
except Customer.DoesNotExist:
|
||||||
|
if event.settings.customer_accounts_link_by_email == 'create':
|
||||||
|
customer = event.organizer.customers.create(
|
||||||
|
email=email,
|
||||||
|
name_parts=addr.name_parts if addr else None,
|
||||||
|
phone=(meta_info or {}).get('contact_form_data', {}).get('phone'),
|
||||||
|
is_active=True,
|
||||||
|
is_verified=False,
|
||||||
|
locale=locale,
|
||||||
|
)
|
||||||
|
customer.set_unusable_password()
|
||||||
|
customer.save()
|
||||||
|
customer.log_action('pretix.customer.created.auto', {})
|
||||||
|
customer_attached = True
|
||||||
|
|
||||||
requires_seat = Exists(
|
requires_seat = Exists(
|
||||||
SeatCategoryMapping.objects.filter(
|
SeatCategoryMapping.objects.filter(
|
||||||
Q(product=OuterRef('item'))
|
Q(product=OuterRef('item'))
|
||||||
@@ -1018,7 +1041,7 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
|||||||
locale=locale,
|
locale=locale,
|
||||||
invoice_address=addr,
|
invoice_address=addr,
|
||||||
meta_info=meta_info,
|
meta_info=meta_info,
|
||||||
customer=customer,
|
customer=customer if not customer_attached else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
lockfn = NoLockManager
|
lockfn = NoLockManager
|
||||||
@@ -1041,10 +1064,11 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
|||||||
raise OrderError(error_messages['empty'])
|
raise OrderError(error_messages['empty'])
|
||||||
if len(position_ids) != len(positions):
|
if len(position_ids) != len(positions):
|
||||||
raise OrderError(error_messages['internal'])
|
raise OrderError(error_messages['internal'])
|
||||||
_check_positions(event, now_dt, positions, address=addr, sales_channel=sales_channel, customer=customer)
|
_check_positions(event, now_dt, positions, address=addr, sales_channel=sales_channel,
|
||||||
|
customer=customer if not customer_attached else None)
|
||||||
order, payment_objs = _create_order(event, email, positions, now_dt, payment_requests,
|
order, payment_objs = _create_order(event, email, positions, now_dt, payment_requests,
|
||||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel,
|
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel,
|
||||||
shown_total=shown_total, customer=customer)
|
shown_total=shown_total, customer=customer, customer_attached=customer_attached)
|
||||||
try:
|
try:
|
||||||
for p in payment_objs:
|
for p in payment_objs:
|
||||||
if p.provider == 'free':
|
if p.provider == 'free':
|
||||||
|
|||||||
@@ -159,13 +159,37 @@ DEFAULTS = {
|
|||||||
},
|
},
|
||||||
'customer_accounts_link_by_email': {
|
'customer_accounts_link_by_email': {
|
||||||
'default': 'False',
|
'default': 'False',
|
||||||
'type': bool,
|
'type': str,
|
||||||
'form_class': forms.BooleanField,
|
'form_class': forms.ChoiceField,
|
||||||
'serializer_class': serializers.BooleanField,
|
'serializer_class': serializers.ChoiceField,
|
||||||
|
'serializer_kwargs': dict(
|
||||||
|
choices=(
|
||||||
|
('False', _('Keep guest orders separate and do not link them to customer accounts.')),
|
||||||
|
('True', _('Do not attach guest orders to customer accounts, but show them in customer '
|
||||||
|
'accounts with a verified matching email address.')),
|
||||||
|
('attach', _('Attach guest orders to existing customer accounts with a matching email address if such '
|
||||||
|
'an account exists.')),
|
||||||
|
('create', _('Attach guest orders to existing customer accounts with a matching email and '
|
||||||
|
'automatically create a customer account for all others.')),
|
||||||
|
('forbidden', _('Do not allow guest orders.')),
|
||||||
|
),
|
||||||
|
),
|
||||||
'form_kwargs': dict(
|
'form_kwargs': dict(
|
||||||
label=_("Match orders based on email address"),
|
label=_("Guest order handling"),
|
||||||
help_text=_("This will allow registered customers to access orders made with the same email address, even if the customer "
|
widget=forms.RadioSelect,
|
||||||
"was not logged in during the purchase.")
|
choices=(
|
||||||
|
('False', _('Keep guest orders separate and do not link them to customer accounts.')),
|
||||||
|
('True', _('Do not attach guest orders to customer accounts, but show them in customer '
|
||||||
|
'accounts with a verified matching email address.')),
|
||||||
|
('attach', _('Attach guest orders to existing customer accounts with a matching email address if '
|
||||||
|
'such an account exists.')),
|
||||||
|
('create', _('Attach guest orders to existing customer accounts with a matching email address and '
|
||||||
|
'automatically create a customer account for all others.')),
|
||||||
|
('forbidden', _('Do not allow guest orders.')),
|
||||||
|
),
|
||||||
|
help_text=_('Please be aware that creating customer accounts without a users consent might not be a '
|
||||||
|
'good idea for privacy reasons in some jurisdictions or situations. We always recommend to '
|
||||||
|
'let the user choose if they want an account.'),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
'max_items_per_order': {
|
'max_items_per_order': {
|
||||||
|
|||||||
@@ -331,7 +331,10 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
|||||||
'pretix.membershiptype.changed': _('The membership type has been changed.'),
|
'pretix.membershiptype.changed': _('The membership type has been changed.'),
|
||||||
'pretix.membershiptype.deleted': _('The membership type has been deleted.'),
|
'pretix.membershiptype.deleted': _('The membership type has been deleted.'),
|
||||||
'pretix.customer.created': _('The account has been created.'),
|
'pretix.customer.created': _('The account has been created.'),
|
||||||
|
'pretix.customer.created.auto': _('The account has been automatically created during an order.'),
|
||||||
|
'pretix.customer.claimed': _('The automatically created account has been claimed by the customer.'),
|
||||||
'pretix.customer.changed': _('The account has been changed.'),
|
'pretix.customer.changed': _('The account has been changed.'),
|
||||||
|
'pretix.customer.order.attached': _('An order was automatically attached to the customer.'),
|
||||||
'pretix.customer.membership.created': _('A membership for this account has been added.'),
|
'pretix.customer.membership.created': _('A membership for this account has been added.'),
|
||||||
'pretix.customer.membership.changed': _('A membership of this account has been changed.'),
|
'pretix.customer.membership.changed': _('A membership of this account has been changed.'),
|
||||||
'pretix.customer.membership.deleted': _('A membership of this account has been deleted.'),
|
'pretix.customer.membership.deleted': _('A membership of this account has been deleted.'),
|
||||||
|
|||||||
@@ -79,9 +79,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if not c.is_verified %}<strike>{% endif %}
|
{% if not c.is_verified %}<span class="text-muted">{% endif %}
|
||||||
{{ c.email|default_if_none:"" }}
|
{{ c.email|default_if_none:"" }}
|
||||||
{% if not c.is_verified %}</strike>{% endif %}
|
{% if not c.is_verified %}</span>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ c.name }}</td>
|
<td>{{ c.name }}</td>
|
||||||
<td>{% if c.external_identifier %}{{ c.external_identifier }}{% endif %}</td>
|
<td>{% if c.external_identifier %}{{ c.external_identifier }}{% endif %}</td>
|
||||||
|
|||||||
@@ -2215,9 +2215,9 @@ class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
q = Q(customer=self.customer)
|
q = Q(customer=self.customer)
|
||||||
if self.request.organizer.settings.customer_accounts_link_by_email and self.customer.email:
|
if self.request.organizer.settings.customer_accounts_link_by_email == 'True' and self.customer.email:
|
||||||
# This is safe because we only let customers with verified emails log in
|
# This is safe because we only let customers with verified emails log in
|
||||||
q |= Q(email__iexact=self.customer.email)
|
q |= Q(customer__isnull=True, email__iexact=self.customer.email)
|
||||||
qs = Order.objects.filter(
|
qs = Order.objects.filter(
|
||||||
q
|
q
|
||||||
).select_related('event').order_by('-datetime')
|
).select_related('event').order_by('-datetime')
|
||||||
|
|||||||
@@ -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-12-21 20:00+0000\n"
|
"PO-Revision-Date: 2021-09-27 06:00+0000\n"
|
||||||
"Last-Translator: Fazenda Dengo <fazendadengo@gmail.com>\n"
|
"Last-Translator: Diego Rodrigo <diegorodrigo90@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.15\n"
|
"X-Generator: Weblate 4.8\n"
|
||||||
|
|
||||||
#: pretix/api/auth/devicesecurity.py:28
|
#: pretix/api/auth/devicesecurity.py:28
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -9268,8 +9268,10 @@ 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 "Sobrenome"
|
msgstr "Nome completo"
|
||||||
|
|
||||||
#: 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
|
||||||
|
|||||||
@@ -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-12-21 20:00+0000\n"
|
"PO-Revision-Date: 2022-11-28 19:03+0000\n"
|
||||||
"Last-Translator: Fazenda Dengo <fazendadengo@gmail.com>\n"
|
"Last-Translator: Vasco Baleia <vb2003.12@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.15\n"
|
"X-Generator: Weblate 4.14.1\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 "Sobrenome"
|
msgstr "Apelido"
|
||||||
|
|
||||||
#: 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
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ class CustomerStep(CartMixin, TemplateFlowStep):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def guest_allowed(self):
|
def guest_allowed(self):
|
||||||
return not any(
|
return self.request.event.settings.customer_accounts_link_by_email != 'forbidden' and not any(
|
||||||
p.item.require_membership or
|
p.item.require_membership or
|
||||||
(p.variation and p.variation.require_membership) or
|
(p.variation and p.variation.require_membership) or
|
||||||
p.item.grant_membership_type_id
|
p.item.grant_membership_type_id
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ class RegistrationForm(forms.Form):
|
|||||||
self.standalone = kwargs.pop('standalone')
|
self.standalone = kwargs.pop('standalone')
|
||||||
self.signer = signing.TimestampSigner(salt=f'customer-registration-captcha-{get_client_ip(request)}')
|
self.signer = signing.TimestampSigner(salt=f'customer-registration-captcha-{get_client_ip(request)}')
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.instance = None
|
||||||
|
|
||||||
event = getattr(request, "event", None)
|
event = getattr(request, "event", None)
|
||||||
if event and event.settings.order_phone_asked:
|
if event and event.settings.order_phone_asked:
|
||||||
@@ -214,14 +215,17 @@ class RegistrationForm(forms.Form):
|
|||||||
|
|
||||||
if email is not None:
|
if email is not None:
|
||||||
try:
|
try:
|
||||||
self.request.organizer.customers.get(email=email.lower())
|
existing = self.request.organizer.customers.get(email=email.lower())
|
||||||
except Customer.DoesNotExist:
|
except Customer.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise forms.ValidationError(
|
if not existing.is_active or existing.is_verified or existing.has_usable_password():
|
||||||
{'email': self.error_messages['duplicate']},
|
raise forms.ValidationError(
|
||||||
code='duplicate',
|
{'email': self.error_messages['duplicate']},
|
||||||
)
|
code='duplicate',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.instance = existing
|
||||||
|
|
||||||
if self.standalone:
|
if self.standalone:
|
||||||
expect = -1
|
expect = -1
|
||||||
@@ -256,19 +260,29 @@ class RegistrationForm(forms.Form):
|
|||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
customer = self.request.organizer.customers.create(
|
if self.instance:
|
||||||
email=self.cleaned_data['email'],
|
self.instance.name_parts = self.cleaned_data['name_parts']
|
||||||
name_parts=self.cleaned_data['name_parts'],
|
if self.cleaned_data.get('phone'):
|
||||||
phone=self.cleaned_data.get('phone'),
|
self.instance.phone = self.cleaned_data.get('phone')
|
||||||
is_active=True,
|
self.instance.locale = get_language_without_region()
|
||||||
is_verified=False,
|
self.instance.save()
|
||||||
locale=get_language_without_region(),
|
self.instance.log_action('pretix.customer.claimed', {})
|
||||||
)
|
self.instance.send_activation_mail()
|
||||||
customer.set_unusable_password()
|
return self.instance
|
||||||
customer.save()
|
else:
|
||||||
customer.log_action('pretix.customer.created', {})
|
customer = self.request.organizer.customers.create(
|
||||||
customer.send_activation_mail()
|
email=self.cleaned_data['email'],
|
||||||
return customer
|
name_parts=self.cleaned_data['name_parts'],
|
||||||
|
phone=self.cleaned_data.get('phone'),
|
||||||
|
is_active=True,
|
||||||
|
is_verified=False,
|
||||||
|
locale=get_language_without_region(),
|
||||||
|
)
|
||||||
|
customer.set_unusable_password()
|
||||||
|
customer.save()
|
||||||
|
customer.log_action('pretix.customer.created', {})
|
||||||
|
customer.send_activation_mail()
|
||||||
|
return customer
|
||||||
|
|
||||||
|
|
||||||
class SetPasswordForm(forms.Form):
|
class SetPasswordForm(forms.Form):
|
||||||
|
|||||||
@@ -352,9 +352,9 @@ class ProfileView(CustomerRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
q = Q(customer=self.request.customer)
|
q = Q(customer=self.request.customer)
|
||||||
if self.request.organizer.settings.customer_accounts_link_by_email and self.request.customer.email:
|
if self.request.organizer.settings.customer_accounts_link_by_email == 'True' and self.request.customer.email:
|
||||||
# This is safe because we only let customers with verified emails log in
|
# This is safe because we only let customers with verified emails log in
|
||||||
q |= Q(email__iexact=self.request.customer.email)
|
q |= Q(customer__isnull=True, email__iexact=self.request.customer.email)
|
||||||
qs = Order.objects.filter(
|
qs = Order.objects.filter(
|
||||||
q
|
q
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
|
|||||||
@@ -663,15 +663,11 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -141,21 +141,18 @@ var api = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
'_postFormJSON': function (endpoint, form, callback, err_callback) {
|
'_postFormJSON': function (endpoint, form, callback, err_callback) {
|
||||||
var params;
|
var params = [].filter.call(form.elements, function (el) {
|
||||||
if (Array.isArray(form)) {
|
return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked;
|
||||||
params = form
|
})
|
||||||
} else {
|
.filter(function (el) {
|
||||||
params = [].filter.call(form.elements, function (el) {
|
return !!el.name && !!el.value;
|
||||||
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) {
|
||||||
params = params.map(function (el) {
|
return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
|
||||||
return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
|
}).join('&');
|
||||||
}).join('&');
|
|
||||||
|
|
||||||
var xhr = api._getXHR();
|
var xhr = api._getXHR();
|
||||||
xhr.open("POST", endpoint, true);
|
xhr.open("POST", endpoint, true);
|
||||||
@@ -364,7 +361,6 @@ 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>'
|
||||||
@@ -376,15 +372,12 @@ 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"> </span>'
|
+ '<span v-if="!$root.showPrices"> </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>'
|
||||||
@@ -412,7 +405,6 @@ 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">'
|
||||||
@@ -432,7 +424,6 @@ 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">'
|
||||||
@@ -440,8 +431,6 @@ 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"> </span>'
|
+ '<span v-if="!$root.showPrices"> </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>'
|
||||||
@@ -450,7 +439,6 @@ 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>'
|
||||||
@@ -525,7 +513,7 @@ Vue.component('category', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var shared_methods = {
|
var shared_methods = {
|
||||||
buy: function (event, data) {
|
buy: function (event) {
|
||||||
if (this.$root.useIframe) {
|
if (this.$root.useIframe) {
|
||||||
if (event) {
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -544,7 +532,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 = data === undefined ? this.$refs.form : data;
|
var form = this.$refs.form;
|
||||||
if (form === undefined) {
|
if (form === undefined) {
|
||||||
form = this.$refs.formcomp.$refs.form;
|
form = this.$refs.formcomp.$refs.form;
|
||||||
}
|
}
|
||||||
@@ -666,7 +654,7 @@ var shared_methods = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleResize: function () {
|
handleResize: function () {
|
||||||
this.clientWidth = this.$refs.wrapper.clientWidth;
|
this.mobile = this.$refs.wrapper.clientWidth <= 800;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -677,7 +665,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,
|
||||||
clientWidth: 1000,
|
mobile: false,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -773,7 +761,6 @@ 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">‹ '
|
+ '<a href="#" @click.prevent.stop="back_to_list" v-if="!$root.subevent">‹ '
|
||||||
+ strings['back_to_list']
|
+ strings['back_to_list']
|
||||||
@@ -782,28 +769,18 @@ 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">'
|
||||||
@@ -812,20 +789,11 @@ 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" v-if="this.$root.has_seating_plan && this.$root.has_seating_plan_waitinglist">'
|
||||||
+ '<div class="pretix-widget-seating-waitinglist-text">'
|
+ '<div class="pretix-widget-seating-waitinglist-text">'
|
||||||
+ strings['seating_plan_waiting_list']
|
+ strings['seating_plan_waiting_list']
|
||||||
@@ -837,18 +805,11 @@ Vue.component('pretix-widget-event-form', {
|
|||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="pretix-widget-clear"></div>'
|
+ '<div class="pretix-widget-clear"></div>'
|
||||||
+ '</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">'
|
||||||
@@ -866,7 +827,6 @@ Vue.component('pretix-widget-event-form', {
|
|||||||
+ '<div class="pretix-widget-clear"></div>'
|
+ '<div class="pretix-widget-clear"></div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</form>'
|
+ '</form>'
|
||||||
|
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
),
|
),
|
||||||
data: function () {
|
data: function () {
|
||||||
@@ -878,14 +838,10 @@ 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 () {
|
||||||
@@ -915,26 +871,9 @@ 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()
|
||||||
@@ -1218,21 +1157,15 @@ 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">‹ '
|
+ '<a href="#" @click.prevent.stop="back_to_list">‹ '
|
||||||
+ 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">« '
|
+ '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevmonth">« '
|
||||||
+ strings['previous_month']
|
+ strings['previous_month']
|
||||||
@@ -1242,8 +1175,6 @@ Vue.component('pretix-widget-event-calendar', {
|
|||||||
+ strings['next_month']
|
+ strings['next_month']
|
||||||
+ ' »</a>'
|
+ ' »</a>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
|
|
||||||
// Calendar
|
|
||||||
+ '<table class="pretix-widget-event-calendar-table">'
|
+ '<table class="pretix-widget-event-calendar-table">'
|
||||||
+ '<thead>'
|
+ '<thead>'
|
||||||
+ '<tr>'
|
+ '<tr>'
|
||||||
@@ -1302,19 +1233,14 @@ 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">‹ '
|
+ '<a href="#" @click.prevent.stop="back_to_list">‹ '
|
||||||
+ 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">« '
|
+ '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevweek">« '
|
||||||
@@ -1325,15 +1251,12 @@ Vue.component('pretix-widget-event-week-calendar', {
|
|||||||
+ strings['next_week']
|
+ strings['next_week']
|
||||||
+ ' »</a>'
|
+ ' »</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: {
|
||||||
@@ -1400,12 +1323,9 @@ Vue.component('pretix-widget', {
|
|||||||
data: shared_widget_data,
|
data: shared_widget_data,
|
||||||
methods: shared_methods,
|
methods: shared_methods,
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
this.clientWidth = this.$refs.wrapper.clientWidth;
|
this.mobile = this.$refs.wrapper.clientWidth <= 600;
|
||||||
},
|
},
|
||||||
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) {
|
||||||
@@ -1568,7 +1488,6 @@ var shared_root_methods = {
|
|||||||
root.has_seating_plan_waitinglist = data.has_seating_plan_waitinglist;
|
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--;
|
||||||
@@ -1707,7 +1626,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) {
|
||||||
@@ -1749,7 +1668,7 @@ function get_ga_client_id(tracking_id) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var create_widget = function (element, widgetindex) {
|
var create_widget = function (element) {
|
||||||
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 += "/";
|
||||||
@@ -1758,7 +1677,6 @@ var create_widget = function (element, widgetindex) {
|
|||||||
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));
|
||||||
@@ -1781,7 +1699,6 @@ var create_widget = function (element, widgetindex) {
|
|||||||
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,
|
||||||
@@ -1803,7 +1720,6 @@ var create_widget = function (element, widgetindex) {
|
|||||||
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,
|
||||||
@@ -1818,7 +1734,6 @@ var create_widget = function (element, widgetindex) {
|
|||||||
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,
|
||||||
@@ -1919,7 +1834,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, i + 1));
|
widgetlist.push(create_widget(widget));
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttons = document.querySelectorAll("pretix-button, div.pretix-button-compat");
|
var buttons = document.querySelectorAll("pretix-button, div.pretix-button-compat");
|
||||||
|
|||||||
@@ -331,15 +331,6 @@
|
|||||||
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 {
|
.pretix-widget-seating-waitinglist {
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4315,6 +4315,56 @@ class CustomerCheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
|||||||
assert order.email == 'admin@localhost'
|
assert order.email == 'admin@localhost'
|
||||||
assert not order.customer
|
assert not order.customer
|
||||||
|
|
||||||
|
def test_guest_not_allowed(self):
|
||||||
|
self.orga.settings.customer_accounts_link_by_email = 'forbidden'
|
||||||
|
response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
|
||||||
|
target_status_code=200)
|
||||||
|
|
||||||
|
response = self.client.post('/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'customer_mode': 'guest'
|
||||||
|
}, follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_guest_attach(self):
|
||||||
|
self.orga.settings.customer_accounts_link_by_email = 'attach'
|
||||||
|
response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
|
||||||
|
target_status_code=200)
|
||||||
|
|
||||||
|
self.client.post('/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'customer_mode': 'guest'
|
||||||
|
}, follow=True)
|
||||||
|
self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'email': 'john@example.org'
|
||||||
|
}, follow=True)
|
||||||
|
|
||||||
|
order = self._finish()
|
||||||
|
assert order.email == 'john@example.org'
|
||||||
|
assert order.customer == self.customer
|
||||||
|
|
||||||
|
def test_guest_create(self):
|
||||||
|
self.orga.settings.customer_accounts_link_by_email = 'create'
|
||||||
|
response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
|
||||||
|
target_status_code=200)
|
||||||
|
|
||||||
|
self.client.post('/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'customer_mode': 'guest'
|
||||||
|
}, follow=True)
|
||||||
|
self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'email': 'jack@example.org'
|
||||||
|
}, follow=True)
|
||||||
|
|
||||||
|
order = self._finish()
|
||||||
|
assert order.email == 'jack@example.org'
|
||||||
|
assert order.customer
|
||||||
|
c = order.customer
|
||||||
|
assert c.email == order.email
|
||||||
|
assert not c.has_usable_password()
|
||||||
|
assert c.is_active
|
||||||
|
assert not c.is_verified
|
||||||
|
|
||||||
def test_guest_even_if_logged_in(self):
|
def test_guest_even_if_logged_in(self):
|
||||||
self.client.post('/%s/account/login' % self.orga.slug, {
|
self.client.post('/%s/account/login' % self.orga.slug, {
|
||||||
'email': 'john@example.org',
|
'email': 'john@example.org',
|
||||||
@@ -4410,6 +4460,24 @@ class CustomerCheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'alert-danger' in response.content
|
assert b'alert-danger' in response.content
|
||||||
|
|
||||||
|
def test_register_valid_account_previously_autocrated(self):
|
||||||
|
with scopes_disabled():
|
||||||
|
c = self.orga.customers.create(email='foo@example.com', is_active=True, is_verified=False)
|
||||||
|
c.set_unusable_password()
|
||||||
|
c.save()
|
||||||
|
response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
|
||||||
|
target_status_code=200)
|
||||||
|
|
||||||
|
response = self.client.post('/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'customer_mode': 'register',
|
||||||
|
'register-email': 'foo@example.com',
|
||||||
|
'register-name_parts_0': 'John Doe',
|
||||||
|
}, follow=False)
|
||||||
|
self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug),
|
||||||
|
target_status_code=200)
|
||||||
|
assert len(djmail.outbox) == 1
|
||||||
|
|
||||||
def test_register_valid(self):
|
def test_register_valid(self):
|
||||||
response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
|
response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
|
||||||
|
|||||||
@@ -124,16 +124,40 @@ def test_org_register(env, client):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_org_register_duplicate_email(env, client):
|
def test_org_register_duplicate_email(env, client):
|
||||||
|
signer = signing.TimestampSigner(salt='customer-registration-captcha-127.0.0.1')
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
env[0].customers.create(email='john@example.org')
|
env[0].customers.create(email='john@example.org')
|
||||||
r = client.post('/bigevents/account/register', {
|
r = client.post('/bigevents/account/register', {
|
||||||
'email': 'john@example.org',
|
'email': 'john@example.org',
|
||||||
'name_parts_0': 'John Doe',
|
'name_parts_0': 'John Doe',
|
||||||
})
|
'challenge': signer.sign('1+2'),
|
||||||
|
'response': '3',
|
||||||
|
}, REMOTE_ADDR='127.0.0.1')
|
||||||
assert b'already registered' in r.content
|
assert b'already registered' in r.content
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_org_register_duplicate_email_ignored_if_previously_autocreated(env, client):
|
||||||
|
signer = signing.TimestampSigner(salt='customer-registration-captcha-127.0.0.1')
|
||||||
|
with scopes_disabled():
|
||||||
|
c = env[0].customers.create(email='john@example.org', is_active=True, is_verified=False)
|
||||||
|
c.set_unusable_password()
|
||||||
|
c.save()
|
||||||
|
r = client.post('/bigevents/account/register', {
|
||||||
|
'email': 'john@example.org',
|
||||||
|
'name_parts_0': 'John Doe',
|
||||||
|
'challenge': signer.sign('1+2'),
|
||||||
|
'response': '3',
|
||||||
|
}, REMOTE_ADDR='127.0.0.1')
|
||||||
|
assert r.status_code == 302
|
||||||
|
assert len(djmail.outbox) == 1
|
||||||
|
with scopes_disabled():
|
||||||
|
customer = env[0].customers.get(email='john@example.org')
|
||||||
|
assert not customer.is_verified
|
||||||
|
assert customer.is_active
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_org_resetpw(env, client):
|
def test_org_resetpw(env, client):
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
@@ -479,7 +503,7 @@ def test_org_order_list(env, client):
|
|||||||
assert o2.code not in content
|
assert o2.code not in content
|
||||||
assert o3.code in content
|
assert o3.code in content
|
||||||
|
|
||||||
env[0].settings.customer_accounts_link_by_email = True
|
env[0].settings.customer_accounts_link_by_email = 'True'
|
||||||
|
|
||||||
r = client.get('/bigevents/account/')
|
r = client.get('/bigevents/account/')
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|||||||
Reference in New Issue
Block a user