mirror of
https://github.com/pretix/pretix.git
synced 2026-02-22 09:22:27 +00:00
Compare commits
1 Commits
fix-instan
...
add-accept
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c25c03f398 |
@@ -365,10 +365,9 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
def _send_invite(self, instance):
|
||||
mail(
|
||||
instance.email,
|
||||
_('Account invitation'),
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'organizer': self.context['organizer'].name,
|
||||
'team': instance.team.name,
|
||||
|
||||
@@ -651,7 +651,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
pgettext('address', 'State'),
|
||||
_('Voucher'),
|
||||
_('Voucher budget usage'),
|
||||
_('Voucher tag'),
|
||||
_('Pseudonymization ID'),
|
||||
_('Ticket secret'),
|
||||
_('Seat ID'),
|
||||
@@ -770,7 +769,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
op.state_for_address or '',
|
||||
op.voucher.code if op.voucher else '',
|
||||
op.voucher_budget_use if op.voucher_budget_use else '',
|
||||
op.voucher.tag if op.voucher else '',
|
||||
op.pseudonymization_id,
|
||||
op.secret,
|
||||
]
|
||||
|
||||
@@ -346,8 +346,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
{
|
||||
'user': self,
|
||||
'messages': msg,
|
||||
'url': build_absolute_uri('control:user.settings'),
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
@@ -392,7 +391,6 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
'user': self,
|
||||
'reason': msg,
|
||||
'code': code,
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
@@ -432,7 +430,6 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
mail(
|
||||
self.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'url': (build_absolute_uri('control:auth.forgot.recover')
|
||||
+ '?id=%d&token=%s' % (self.id, default_token_generator.make_token(self)))
|
||||
|
||||
@@ -176,7 +176,6 @@ def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, lo
|
||||
_('Data shredding completed'),
|
||||
'pretixbase/email/shred_completed.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': user,
|
||||
'organizer': event.organizer.name,
|
||||
'event': str(event.name),
|
||||
|
||||
@@ -13,5 +13,5 @@ Start time: {{ start_time }} (new data added after this time might not have been
|
||||
|
||||
Best regards,
|
||||
|
||||
Your {{ instance }} team
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -9,5 +9,5 @@ Please do never give this code to another person. Our support team will never as
|
||||
If this code was not requested by you, please contact us immediately.
|
||||
|
||||
Best regards,
|
||||
Your {{ instance }} team
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -5,5 +5,5 @@ you requested a new password. Please go to the following page to reset your pass
|
||||
{{ url }}
|
||||
|
||||
Best regards,
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
you have been invited to a team on {{ instance }}, a platform to perform event
|
||||
you have been invited to a team on pretix, a platform to perform event
|
||||
ticket sales.
|
||||
|
||||
Organizer: {{ organizer }}
|
||||
@@ -13,5 +13,5 @@ If you do not want to join, you can safely ignore or delete this email.
|
||||
|
||||
Best regards,
|
||||
|
||||
Your {{ instance }} team
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello,
|
||||
|
||||
this is to inform you that the account information of your {{ instance }} account has been
|
||||
this is to inform you that the account information of your pretix account has been
|
||||
changed. In particular, the following changes have been performed:
|
||||
|
||||
{{ messages }}
|
||||
@@ -12,5 +12,5 @@ You can review and change your account settings here:
|
||||
{{ url }}
|
||||
|
||||
Best regards,
|
||||
Your {{ instance }} team
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -144,23 +144,14 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
If you lose access to your devices, you can use one of your emergency tokens to log in.
|
||||
We recommend to store them in a safe place, e.g. printed out or in a password manager.
|
||||
Every token can be used at most once.
|
||||
{% endblocktrans %}
|
||||
{% trans "If you lose access to your devices, you can use one of the following keys to log in. We recommend to store them in a safe place, e.g. printed out or in a password manager. Every token can be used at most once." %}
|
||||
</p>
|
||||
{% if static_tokens_device %}
|
||||
<p>
|
||||
{% blocktrans trimmed with generation_date_time=static_tokens_device.created_at %}
|
||||
You generated your emergency tokens on {{ generation_date_time }}.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{% trans "You don't have any emergency tokens yet." %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>{% trans "Unused tokens:" %}</p>
|
||||
<ul>
|
||||
{% for t in static_tokens %}
|
||||
<li><code>{{ t.token }}</code></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<a href="{% url "control:user.settings.2fa.regenemergency" %}" class="btn btn-default">
|
||||
<span class="fa fa-refresh"></span>
|
||||
{% trans "Generate new emergency tokens" %}
|
||||
|
||||
@@ -1039,7 +1039,7 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
def _send_invite(self, instance):
|
||||
mail(
|
||||
instance.email,
|
||||
_('Account invitation'),
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
|
||||
@@ -49,14 +49,12 @@ from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import format_html
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.generic import FormView, ListView, TemplateView, UpdateView
|
||||
from django_otp.plugins.otp_static.models import StaticDevice
|
||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
@@ -87,9 +85,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RecentAuthenticationRequiredMixin:
|
||||
max_time = 900
|
||||
max_time = 3600
|
||||
|
||||
@method_decorator(never_cache)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
tdelta = time.time() - request.session.get('pretix_auth_login_time', 0)
|
||||
if tdelta > self.max_time:
|
||||
@@ -292,13 +289,16 @@ class User2FAMainView(RecentAuthenticationRequiredMixin, TemplateView):
|
||||
ctx = super().get_context_data()
|
||||
|
||||
try:
|
||||
ctx['static_tokens_device'] = StaticDevice.objects.get(user=self.request.user, name='emergency')
|
||||
ctx['static_tokens'] = StaticDevice.objects.get(user=self.request.user, name='emergency').token_set.all()
|
||||
except StaticDevice.MultipleObjectsReturned:
|
||||
ctx['static_tokens_device'] = StaticDevice.objects.filter(
|
||||
ctx['static_tokens'] = StaticDevice.objects.filter(
|
||||
user=self.request.user, name='emergency'
|
||||
).first()
|
||||
).first().token_set.all()
|
||||
except StaticDevice.DoesNotExist:
|
||||
ctx['static_tokens_device'] = None
|
||||
d = StaticDevice.objects.create(user=self.request.user, name='emergency')
|
||||
for i in range(10):
|
||||
d.token_set.create(token=get_random_string(length=12, allowed_chars='1234567890'))
|
||||
ctx['static_tokens'] = d.token_set.all()
|
||||
|
||||
ctx['devices'] = []
|
||||
for dt in REAL_DEVICE_TYPES:
|
||||
@@ -631,8 +631,7 @@ class User2FARegenerateEmergencyView(RecentAuthenticationRequiredMixin, Template
|
||||
self.request.user.update_session_token()
|
||||
update_session_auth_hash(self.request, self.request.user)
|
||||
messages.success(request, _('Your emergency codes have been newly generated. Remember to store them in a safe '
|
||||
'place in case you lose access to your devices. You will not be able to view them '
|
||||
'again here.\n\nYour emergency codes:\n- ' + '\n- '.join(t.token for t in d.token_set.all())))
|
||||
'place in case you lose access to your devices.'))
|
||||
return redirect(reverse('control:user.settings.2fa'))
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,10 @@ def set_cookie_without_samesite(request, response, key, *args, **kwargs):
|
||||
if not is_secure:
|
||||
# https://www.chromestatus.com/feature/5633521622188032
|
||||
return
|
||||
if should_send_same_site_none(request.headers.get('User-Agent', '')):
|
||||
|
||||
useragent = request.headers.get('User-Agent', '')
|
||||
|
||||
if should_send_same_site_none(useragent):
|
||||
# Chromium is rolling out SameSite=Lax as a default
|
||||
# https://www.chromestatus.com/feature/5088147346030592
|
||||
# This however breaks all pretix-in-an-iframe things, such as the pretix Widget.
|
||||
@@ -44,8 +47,29 @@ def set_cookie_without_samesite(request, response, key, *args, **kwargs):
|
||||
# This will only work on secure cookies as well
|
||||
# https://www.chromestatus.com/feature/5633521622188032
|
||||
response.cookies[key]['secure'] = is_secure
|
||||
# CHIPS
|
||||
response.cookies[key]['Partitioned'] = True
|
||||
|
||||
if can_send_partitioned_cookie(useragent):
|
||||
# CHIPS
|
||||
response.cookies[key]['Partitioned'] = True
|
||||
|
||||
|
||||
def can_send_partitioned_cookie(useragent):
|
||||
# Safari currently exhibits a bug where Partitioned cookies (CHIPS) are not
|
||||
# sent back to the originating site after multi-hop cross-site redirects,
|
||||
# breaking SSO login flows in pretix.
|
||||
#
|
||||
# Partitioned cookies were initially introduced in Safari 18.4, removed
|
||||
# again in 18.5 due to a bug, and reintroduced in Safari 26.2, where the
|
||||
# current issue is present.
|
||||
#
|
||||
# Once the Safari issue is fixed, this check should be refined to be
|
||||
# conditional on the affected versions only.
|
||||
#
|
||||
# WebKit issues:
|
||||
#
|
||||
# - https://bugs.webkit.org/show_bug.cgi?id=292975
|
||||
# - https://bugs.webkit.org/show_bug.cgi?id=306194
|
||||
return not is_safari(useragent)
|
||||
|
||||
|
||||
# Based on https://www.chromium.org/updates/same-site/incompatible-clients
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-20 12:29+0000\n"
|
||||
"POT-Creation-Date: 2026-01-26 13:20+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-26 09:10+0000\n"
|
||||
"PO-Revision-Date: 2026-02-19 22:00+0000\n"
|
||||
"PO-Revision-Date: 2026-02-05 23:00+0000\n"
|
||||
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"nl/>\n"
|
||||
@@ -16,7 +16,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
"X-Generator: Weblate 5.15.2\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -674,7 +674,7 @@ msgstr "Zoekopdracht"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:461
|
||||
msgid "All"
|
||||
msgstr "Allemaal"
|
||||
msgstr "Alle"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:462
|
||||
msgid "None"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -786,29 +786,23 @@ class PaypalMethod(BasePaymentProvider):
|
||||
else:
|
||||
pp_captured_order = response.result
|
||||
|
||||
for purchaseunit in pp_captured_order.purchase_units:
|
||||
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 != 'COMPLETED':
|
||||
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as '
|
||||
'soon as the payment completed.'))
|
||||
payment.info = json.dumps(pp_captured_order.dict())
|
||||
payment.state = OrderPayment.PAYMENT_STATE_PENDING
|
||||
payment.save()
|
||||
return
|
||||
|
||||
payment.refresh_from_db()
|
||||
|
||||
any_captures = False
|
||||
all_captures_completed = True
|
||||
for purchaseunit in pp_captured_order.purchase_units:
|
||||
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 != 'COMPLETED':
|
||||
all_captures_completed = False
|
||||
else:
|
||||
any_captures = True
|
||||
if not (any_captures and all_captures_completed):
|
||||
messages.warning(request, _('PayPal has not yet approved the payment. We will inform you as '
|
||||
'soon as the payment completed.'))
|
||||
payment.info = json.dumps(pp_captured_order.dict())
|
||||
payment.state = OrderPayment.PAYMENT_STATE_PENDING
|
||||
payment.save()
|
||||
return
|
||||
|
||||
if pp_captured_order.status != 'COMPLETED':
|
||||
payment.fail(info=pp_captured_order.dict())
|
||||
logger.error('Invalid state: %s' % repr(pp_captured_order.dict()))
|
||||
|
||||
@@ -118,7 +118,6 @@ logger = logging.getLogger('pretix.plugins.stripe')
|
||||
# - UPI: ✗
|
||||
# - Netbanking: ✗
|
||||
# - TWINT: ✓
|
||||
# - Wero: ✓ (No settings UI yet)
|
||||
#
|
||||
# Bank transfers
|
||||
# - ACH Bank Transfer: ✗
|
||||
@@ -510,15 +509,6 @@ class StripeSettingsHolder(BasePaymentProvider):
|
||||
'before they work properly.'),
|
||||
required=False,
|
||||
)),
|
||||
# Disabled for now, since still in closed Beta and only available to dedicated boarded accounts.
|
||||
# ('method_wero',
|
||||
# forms.BooleanField(
|
||||
# label=_('Wero'),
|
||||
# disabled=self.event.currency not in 'EUR',
|
||||
# help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
|
||||
# 'before they work properly.'),
|
||||
# required=False,
|
||||
# )),
|
||||
] + extra_fields + list(super().settings_form_fields.items()) + moto_settings
|
||||
)
|
||||
if not self.settings.connect_client_id or self.settings.secret_key:
|
||||
@@ -1956,15 +1946,3 @@ class StripeMobilePay(StripeRedirectMethod):
|
||||
"type": "mobilepay",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class StripeWero(StripeRedirectMethod):
|
||||
identifier = 'stripe_wero'
|
||||
verbose_name = _('WERO via Stripe')
|
||||
public_name = 'WERO'
|
||||
method = 'wero'
|
||||
confirmation_method = 'automatic'
|
||||
explanation = _(
|
||||
'This payment method is available to European online banking users, whose banking institutions support WERO '
|
||||
'either through their native banking apps or through the WERO wallet app. Please have you app ready.'
|
||||
)
|
||||
|
||||
@@ -49,14 +49,14 @@ def register_payment_provider(sender, **kwargs):
|
||||
StripeMultibanco, StripePayByBank, StripePayPal, StripePromptPay,
|
||||
StripePrzelewy24, StripeRevolutPay, StripeSEPADirectDebit,
|
||||
StripeSettingsHolder, StripeSofort, StripeSwish, StripeTwint,
|
||||
StripeWeChatPay, StripeWero,
|
||||
StripeWeChatPay,
|
||||
)
|
||||
|
||||
return [
|
||||
StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
|
||||
StripeSofort, StripeEPS, StripeMultibanco, StripePayByBank, StripePrzelewy24, StripePromptPay, StripeRevolutPay,
|
||||
StripeWeChatPay, StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish,
|
||||
StripeTwint, StripeMobilePay, StripeWero
|
||||
StripeTwint, StripeMobilePay
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -339,17 +339,13 @@ class UserSettings2FATest(SoupTest):
|
||||
|
||||
def test_gen_emergency(self):
|
||||
self.client.get('/control/settings/2fa/')
|
||||
assert not StaticDevice.objects.filter(user=self.user, name='emergency').exists()
|
||||
|
||||
self.client.post('/control/settings/2fa/regenemergency')
|
||||
d = StaticDevice.objects.get(user=self.user, name='emergency')
|
||||
assert d.token_set.count() == 10
|
||||
old_tokens = set(t.token for t in d.token_set.all())
|
||||
|
||||
self.client.post('/control/settings/2fa/regenemergency')
|
||||
new_tokens = set(t.token for t in d.token_set.all())
|
||||
d = StaticDevice.objects.get(user=self.user, name='emergency')
|
||||
assert d.token_set.count() == 10
|
||||
new_tokens = set(t.token for t in d.token_set.all())
|
||||
assert old_tokens != new_tokens
|
||||
|
||||
def test_delete_u2f(self):
|
||||
|
||||
Reference in New Issue
Block a user