mirror of
https://github.com/pretix/pretix.git
synced 2026-06-20 02:46:14 +00:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 12ba49e263 | |||
| e37b032d4e | |||
| 3c3b5529bf | |||
| 87b3e0c417 | |||
| d3fd031639 | |||
| 9253327334 | |||
| 080b9cacaf | |||
| 9c2cc02df1 | |||
| fceae0a2fe | |||
| 9fc3fdf751 | |||
| 04f79b7014 | |||
| 9d0b9387e6 | |||
| b25e6f598d | |||
| e8e2648f7e | |||
| e0fac42225 | |||
| 3e9bc7675b | |||
| 1541033467 | |||
| 6b8c3ef15c | |||
| 135e07c183 | |||
| fe97915b36 | |||
| 233281cea4 | |||
| 0300a44634 | |||
| 449d930565 | |||
| 49f49bd8a6 | |||
| e896704fe0 | |||
| cfee402a27 | |||
| f8878e53a3 | |||
| fd6a342bc6 | |||
| 865433276e | |||
| f616f64f47 | |||
| 26550887b7 | |||
| 0f3de911b8 | |||
| b648390dbf | |||
| 50fec0b31c | |||
| e44af04e43 | |||
| 276c3177f5 | |||
| 27ac004a0b | |||
| 6d517d4e8d | |||
| d9c3deda8a | |||
| fe6add618a | |||
| 3615a52cc4 | |||
| e3ae3b08bd | |||
| 959e926a67 | |||
| 876ddf1321 | |||
| 005b1d54d3 | |||
| 2066471086 | |||
| a25bca7471 | |||
| da43984ad2 | |||
| 7cce1c9219 | |||
| cb9c4466f9 | |||
| 3398cda74b | |||
| e5c8f19984 | |||
| 5027f6dd59 | |||
| 787db18d72 | |||
| aadce7be00 | |||
| 26f296bc11 | |||
| 6ae80cdd4b | |||
| cb3956c994 | |||
| b9f350bf3a | |||
| ab447bb85f | |||
| bf33a42ae8 | |||
| 081f975ff9 | |||
| eab7d81a51 | |||
| b2dce51a24 | |||
| 5bd660a913 | |||
| 8e9cdd7548 | |||
| d6592cbb93 | |||
| 0e3ccae5d4 | |||
| 034b46d218 | |||
| a3f120198d | |||
| fa5f3bb15a | |||
| 5120b312b6 | |||
| 09064844b2 | |||
| 1a60b3a712 | |||
| 6216f0d7df | |||
| 380b55e699 | |||
| 6e67bb5045 | |||
| 1463ee9227 | |||
| 3b49e77722 | |||
| ceed07af94 | |||
| 802c03f8f3 | |||
| 9962d8a3be | |||
| 028a41f3e4 | |||
| 6d8a9854f9 | |||
| 861e14bb16 | |||
| 7a080c0820 | |||
| 2dbdb91066 | |||
| b8efb8f61d | |||
| 5f0cc4cc59 | |||
| d3bb1f3190 | |||
| 69a215feff | |||
| 435dd5ebaf | |||
| 015d74f7ae | |||
| 5c9a069d77 | |||
| 5866cf94ee | |||
| fa15ba4435 | |||
| e982f04d59 | |||
| ced00266dc | |||
| b534c125db | |||
| 769e1312d4 |
+1
-1
@@ -113,7 +113,7 @@ dev = [
|
||||
"fakeredis==2.34.*",
|
||||
"flake8==7.3.*",
|
||||
"freezegun",
|
||||
"isort==7.0.*",
|
||||
"isort==8.0.*",
|
||||
"pep8-naming==0.15.*",
|
||||
"potypo",
|
||||
"pytest-asyncio>=0.24",
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2026.2.0.dev0"
|
||||
__version__ = "2026.3.0.dev0"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections import Counter, defaultdict
|
||||
@@ -1215,6 +1216,18 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
raise ValidationError('The given payment provider is not known.')
|
||||
return pp
|
||||
|
||||
def validate_payment_info(self, info):
|
||||
if info:
|
||||
try:
|
||||
obj = json.loads(info)
|
||||
except ValueError:
|
||||
raise ValidationError('payment_info must be valid JSON.')
|
||||
|
||||
if not isinstance(obj, dict):
|
||||
# only objects are allowed
|
||||
raise ValidationError('payment_info must be a JSON object.')
|
||||
return info
|
||||
|
||||
def validate_expires(self, expires):
|
||||
if expires < now():
|
||||
raise ValidationError('Expiration date must be in the future.')
|
||||
|
||||
@@ -365,9 +365,10 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
def _send_invite(self, instance):
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
_('Account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'organizer': self.context['organizer'].name,
|
||||
'team': instance.team.name,
|
||||
|
||||
@@ -183,6 +183,7 @@ class ParametrizedGiftcardWebhookEvent(ParametrizedWebhookEvent):
|
||||
return {
|
||||
'notification_id': logentry.pk,
|
||||
'issuer_id': logentry.organizer_id,
|
||||
'issuer_slug': logentry.organizer.slug,
|
||||
'giftcard': giftcard.pk,
|
||||
'action': logentry.action_type,
|
||||
}
|
||||
@@ -197,6 +198,7 @@ class ParametrizedGiftcardTransactionWebhookEvent(ParametrizedWebhookEvent):
|
||||
return {
|
||||
'notification_id': logentry.pk,
|
||||
'issuer_id': logentry.organizer_id,
|
||||
'issuer_slug': logentry.organizer.slug,
|
||||
'acceptor_id': logentry.parsed_data.get('acceptor_id'),
|
||||
'acceptor_slug': logentry.parsed_data.get('acceptor_slug'),
|
||||
'giftcard': giftcard.pk,
|
||||
@@ -473,7 +475,7 @@ def register_default_webhook_events(sender, **kwargs):
|
||||
),
|
||||
ParametrizedGiftcardTransactionWebhookEvent(
|
||||
'pretix.giftcards.transaction.*',
|
||||
_('Gift card used in transcation'),
|
||||
_('Gift card used in transaction'),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -216,7 +216,10 @@ class OutboundSyncProvider:
|
||||
|
||||
try:
|
||||
mapped_objects = self.sync_order(sq.order)
|
||||
if not all(all(not res or res.sync_info.get("action", "") == "nothing_to_do" for res in res_list) for res_list in mapped_objects.values()):
|
||||
actions_taken = [res and res.sync_info.get("action", "") for res_list in mapped_objects.values() for res in res_list]
|
||||
should_write_logentry = any(action not in (None, "nothing_to_do") for action in actions_taken)
|
||||
logger.info('Synced order %s to %s, actions: %r, log: %r', sq.order.code, sq.sync_provider, actions_taken, should_write_logentry)
|
||||
if should_write_logentry:
|
||||
sq.order.log_action("pretix.event.order.data_sync.success", {
|
||||
"provider": self.identifier,
|
||||
"objects": {
|
||||
@@ -237,7 +240,7 @@ class OutboundSyncProvider:
|
||||
sq.set_sync_error("exceeded", e.messages, e.full_message)
|
||||
else:
|
||||
logger.info(
|
||||
f"Could not sync order {sq.order.code} to {type(self).__name__} "
|
||||
f"Could not sync order {sq.order.code} to {sq.sync_provider} "
|
||||
f"(transient error, attempt #{sq.failed_attempts}, next {sq.not_before})",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
@@ -42,6 +42,8 @@ from django.utils.html import escape
|
||||
from django.utils.timezone import get_current_timezone, now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.helpers.format import PlainHtmlAlternativeString
|
||||
|
||||
|
||||
def replace_arabic_numbers(inp):
|
||||
if not isinstance(inp, str):
|
||||
@@ -61,11 +63,18 @@ def replace_arabic_numbers(inp):
|
||||
return inp.translate(table)
|
||||
|
||||
|
||||
def format_placeholder_help_text(placeholder_name, sample_value):
|
||||
if isinstance(sample_value, PlainHtmlAlternativeString):
|
||||
sample_value = sample_value.plain
|
||||
title = (_("Sample: %s") % sample_value) if sample_value else ""
|
||||
return ('<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(title), escape(placeholder_name)))
|
||||
|
||||
|
||||
def format_placeholders_help_text(placeholders, event=None):
|
||||
placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()]
|
||||
placeholders.sort(key=lambda x: x[0])
|
||||
phs = [
|
||||
'<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(_("Sample: %s") % v) if v else "", escape(k))
|
||||
format_placeholder_help_text(k, v)
|
||||
for k, v in placeholders
|
||||
]
|
||||
return _('Available placeholders: {list}').format(
|
||||
|
||||
@@ -346,7 +346,8 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
{
|
||||
'user': self,
|
||||
'messages': msg,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
'url': build_absolute_uri('control:user.settings'),
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
@@ -391,6 +392,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
'user': self,
|
||||
'reason': msg,
|
||||
'code': code,
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
@@ -430,6 +432,7 @@ 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)))
|
||||
|
||||
@@ -86,7 +86,7 @@ class OrderSyncQueue(models.Model):
|
||||
|
||||
def set_sync_error(self, failure_mode, messages, full_message):
|
||||
logger.exception(
|
||||
f"Could not sync order {self.order.code} to {type(self).__name__} ({failure_mode})"
|
||||
f"Could not sync order {self.order.code} to {self.sync_provider} ({failure_mode})"
|
||||
)
|
||||
self.order.log_action(f"pretix.event.order.data_sync.failed.{failure_mode}", {
|
||||
"provider": self.sync_provider,
|
||||
|
||||
@@ -334,7 +334,8 @@ def _check_position_constraints(
|
||||
raise CartPositionError(error_messages['voucher_invalid_subevent'])
|
||||
|
||||
# Voucher expired
|
||||
if voucher and voucher.valid_until and voucher.valid_until < time_machine_now_dt:
|
||||
# (checked using real_now_dt as vouchers influence quota calculations)
|
||||
if voucher and voucher.valid_until and voucher.valid_until < real_now_dt:
|
||||
raise CartPositionError(error_messages['voucher_expired'])
|
||||
|
||||
# Subevent has been disabled
|
||||
|
||||
@@ -389,7 +389,7 @@ def mail_send_task(self, **kwargs) -> bool:
|
||||
# mail_send_task(self, *, outgoing_mail)
|
||||
with scopes_disabled():
|
||||
mail_send(**kwargs)
|
||||
return
|
||||
return False
|
||||
else:
|
||||
raise ValueError("Unknown arguments")
|
||||
|
||||
@@ -443,15 +443,24 @@ def mail_send_task(self, **kwargs) -> bool:
|
||||
content = ct.file.read()
|
||||
args.append((name, content, ct.type))
|
||||
attach_size += len(content)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
# This sometimes fails e.g. with FileNotFoundError. We haven't been able to figure out
|
||||
# why (probably some race condition with ticket cache invalidation?), so retry later.
|
||||
try:
|
||||
self.retry(max_retries=5, countdown=60)
|
||||
logger.exception(f'Could not attach tickets to email {outgoing_mail.guid}, will retry')
|
||||
retry_after = 60
|
||||
outgoing_mail.error = "Tickets not ready"
|
||||
outgoing_mail.error_detail = str(e)
|
||||
outgoing_mail.sent = now()
|
||||
outgoing_mail.status = OutgoingMail.STATUS_AWAITING_RETRY
|
||||
outgoing_mail.retry_after = now() + timedelta(seconds=retry_after)
|
||||
outgoing_mail.save(update_fields=["status", "error", "error_detail", "sent", "retry_after",
|
||||
"actual_attachments"])
|
||||
self.retry(max_retries=5, countdown=retry_after)
|
||||
except MaxRetriesExceededError:
|
||||
# Well then, something is really wrong, let's send it without attachment before we
|
||||
# don't send at all
|
||||
logger.exception(f'Could not attach tickets to email {outgoing_mail.guid}')
|
||||
logger.exception(f'Too many retries attaching tickets to email {outgoing_mail.guid}, skip attachment')
|
||||
pass
|
||||
|
||||
if attach_size * 1.37 < settings.FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT - 1024 * 1024:
|
||||
|
||||
@@ -176,6 +176,7 @@ 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 pretix team
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django import template
|
||||
from django.utils.html import mark_safe
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter("anon_email")
|
||||
def anon_email(value):
|
||||
"""Replaces @ with [at] and . with [dot] for anonymization."""
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
value = value.replace("@", "[at]").replace(".", "[dot]")
|
||||
return mark_safe(''.join(['&#{0};'.format(ord(char)) for char in value]))
|
||||
@@ -19,17 +19,44 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django import forms
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_scopes.forms import SafeModelChoiceField
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
|
||||
from pretix.base.forms import I18nModelForm
|
||||
from pretix.base.forms.questions import (
|
||||
NamePartsFormField, WrappedPhoneNumberPrefixWidget,
|
||||
)
|
||||
from pretix.base.models import WaitingListEntry
|
||||
from pretix.control.forms.widgets import Select2
|
||||
|
||||
|
||||
class WaitingListEntryTransferForm(I18nModelForm):
|
||||
class WaitingListEntryEditForm(I18nModelForm):
|
||||
itemvar = forms.ChoiceField(
|
||||
error_messages={
|
||||
'invalid_choice': _("Select a valid choice.")
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.instance = kwargs.get('instance', None)
|
||||
initial = kwargs.get('initial', {})
|
||||
|
||||
choices = []
|
||||
if self.instance and self.instance.pk and 'itemvar' not in initial:
|
||||
if self.instance.variation is not None:
|
||||
initial['itemvar'] = f'{self.instance.item.pk}-{self.instance.variation.pk}'
|
||||
if self.instance.variation.active is False:
|
||||
choices.append((initial['itemvar'], str(self.instance.variation)))
|
||||
else:
|
||||
initial['itemvar'] = self.instance.item.pk
|
||||
if self.instance.item.active is False:
|
||||
choices.append((initial['itemvar'], str(self.instance)))
|
||||
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if self.event.has_subevents:
|
||||
@@ -45,12 +72,73 @@ class WaitingListEntryTransferForm(I18nModelForm):
|
||||
}
|
||||
)
|
||||
self.fields['subevent'].widget.choices = self.fields['subevent'].choices
|
||||
else:
|
||||
del self.fields['subevent']
|
||||
|
||||
if self.event.settings.waiting_list_names_asked:
|
||||
self.fields['name_parts'] = NamePartsFormField(
|
||||
max_length=255,
|
||||
required=self.event.settings.waiting_list_names_required,
|
||||
scheme=self.event.organizer.settings.name_scheme,
|
||||
titles=self.event.organizer.settings.name_scheme_titles,
|
||||
label=_('Name'),
|
||||
)
|
||||
else:
|
||||
del self.fields['name_parts']
|
||||
|
||||
if not self.event.settings.waiting_list_phones_asked:
|
||||
del self.fields['phone']
|
||||
|
||||
items = self.event.items.filter(active=True).prefetch_related(
|
||||
'variations'
|
||||
)
|
||||
|
||||
for item in items:
|
||||
if len(item.variations.all()) > 0:
|
||||
for variation in item.variations.all():
|
||||
if variation.active:
|
||||
choices.append(
|
||||
('{}-{}'.format(item.pk, variation.pk), '{} - {}'.format(str(item), str(variation)))
|
||||
)
|
||||
else:
|
||||
choices.append(('{}'.format(item.pk), str(item)))
|
||||
|
||||
self.fields['itemvar'].label = _("Product")
|
||||
self.fields['itemvar'].help_text = _("Only includes active products.")
|
||||
self.fields['itemvar'].required = True
|
||||
self.fields['itemvar'].choices = choices
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if self.instance.voucher is not None:
|
||||
raise forms.ValidationError(_('A voucher for this waiting list entry was already sent out.'))
|
||||
|
||||
itemvar = cleaned_data.get('itemvar')
|
||||
if itemvar:
|
||||
self.instance.item = self.event.items.get(pk=itemvar.split('-')[0])
|
||||
if '-' in itemvar:
|
||||
self.instance.variation = self.instance.item.variations.get(pk=itemvar.split('-')[1])
|
||||
|
||||
if ((self.instance.item and not self.instance.item.active) or
|
||||
(self.instance.variation and not self.instance.variation.active)):
|
||||
self.add_error('itemvar', _('The selected product is not active.'))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = WaitingListEntry
|
||||
fields = [
|
||||
'email',
|
||||
'name_parts',
|
||||
'phone',
|
||||
'subevent',
|
||||
]
|
||||
field_classes = {
|
||||
'subevent': SafeModelChoiceField,
|
||||
'email': forms.EmailField,
|
||||
'phone': PhoneNumberField,
|
||||
}
|
||||
widgets = {
|
||||
'phone': WrappedPhoneNumberPrefixWidget,
|
||||
}
|
||||
|
||||
@@ -518,6 +518,7 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
|
||||
'The order requires approval before it can continue to be processed.'),
|
||||
'pretix.event.order.approved': _('The order has been approved.'),
|
||||
'pretix.event.order.denied': _('The order has been denied (comment: "{comment}").'),
|
||||
'pretix.event.order.vatid.validated': _('The customer VAT ID has been verified.'),
|
||||
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
|
||||
'to "{new_email}".'),
|
||||
'pretix.event.order.contact.confirmed': _(
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
</ul>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% if possible_cookie_problem %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
It looks like your browser is not accepting our cookie and you need to log in repeatedly. Please
|
||||
check if your browser is set to block cookies, or delete all existing cookies and retry.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
<div class="form-group buttons">
|
||||
|
||||
@@ -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 pretix team
|
||||
Your {{ instance }} 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 pretix team
|
||||
{% endblocktrans %}
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
you have been invited to a team on pretix, a platform to perform event
|
||||
you have been invited to a team on {{ instance }}, 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 pretix team
|
||||
Your {{ instance }} 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 pretix account has been
|
||||
this is to inform you that the account information of your {{ instance }} 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 pretix team
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Edit entry" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{% trans "Edit entry" %}</h1>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
|
||||
{% if form.subevent %}
|
||||
{% bootstrap_field form.subevent layout="control" %}
|
||||
{% endif %}
|
||||
|
||||
{% bootstrap_field form.email layout="control" %}
|
||||
|
||||
{% if form.name_parts %}
|
||||
{% bootstrap_field form.name_parts layout="control" %}
|
||||
{% endif %}
|
||||
|
||||
{% if form.phone %}
|
||||
{% bootstrap_field form.phone layout="control" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.itemvar layout="control" %}
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:event.orders.waitinglist" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -124,6 +124,7 @@
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input name="search" type="text" placeholder="{% trans "Search" %}" class="form-control" value="{{ request.GET.search }}">
|
||||
{% if request.event.has_subevents %}
|
||||
<select name="subevent" class="form-control">
|
||||
<option value="">{% trans "All dates" context "subevent" %}</option>
|
||||
@@ -267,13 +268,13 @@
|
||||
data-toggle="tooltip" title="{% trans "Move to the end of the list" %}">
|
||||
<span class="fa fa-thumbs-down"></span>
|
||||
</button>
|
||||
{% if request.event.has_subevents %}
|
||||
<a href="{% url "control:event.orders.waitinglist.transfer" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}"
|
||||
class="btn btn-default btn-sm" title="{% trans "Transfer to other date" context "subevent" %}"
|
||||
data-toggle="tooltip">
|
||||
<i class="fa fa-calendar" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url "control:event.orders.waitinglist.edit" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}"
|
||||
class="btn btn-default btn-sm" title="{% trans "Edit entry" %}"
|
||||
data-toggle="tooltip">
|
||||
<i class="fa fa-edit" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
||||
<a href="{% url "control:event.orders.waitinglist.delete" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}?next={{ request.get_full_path|urlencode }}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% else %}
|
||||
<button class="btn btn-default btn-sm disabled">
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Transfer entry" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{% trans "Transfer entry" %}</h1>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<p>{% blocktrans trimmed context "subevent" %}
|
||||
Please select the date to which the following waiting list entry should be
|
||||
transferred: <strong>{{ entry }}</strong>?
|
||||
{% endblocktrans %}</p>
|
||||
{% bootstrap_field form.subevent layout="control" %}
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:event.orders.waitinglist" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Transfer" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -480,8 +480,8 @@ urlpatterns = [
|
||||
re_path(r'^waitinglist/auto_assign$', waitinglist.AutoAssign.as_view(), name='event.orders.waitinglist.auto'),
|
||||
re_path(r'^waitinglist/(?P<entry>\d+)/delete$', waitinglist.EntryDelete.as_view(),
|
||||
name='event.orders.waitinglist.delete'),
|
||||
re_path(r'^waitinglist/(?P<entry>\d+)/transfer$', waitinglist.EntryTransfer.as_view(),
|
||||
name='event.orders.waitinglist.transfer'),
|
||||
re_path(r'^waitinglist/(?P<entry>\d+)/edit$', waitinglist.EntryEdit.as_view(),
|
||||
name='event.orders.waitinglist.edit'),
|
||||
re_path(r'^checkins/$', checkin.CheckinListView.as_view(), name='event.orders.checkins'),
|
||||
re_path(r'^checkinlists/$', checkin.CheckinListList.as_view(), name='event.orders.checkinlists'),
|
||||
re_path(r'^checkinlists/add$', checkin.CheckinListCreate.as_view(), name='event.orders.checkinlists.add'),
|
||||
|
||||
@@ -149,6 +149,8 @@ def login(request):
|
||||
return process_login(request, form.user_cache, form.cleaned_data.get('keep_logged_in', False))
|
||||
else:
|
||||
form = LoginForm(backend=backend, request=request)
|
||||
# Detect redirection loop (usually means cookie not accepted)
|
||||
ctx['possible_cookie_problem'] = request.path in request.headers.get("Referer", "")
|
||||
ctx['form'] = form
|
||||
ctx['can_register'] = settings.PRETIX_REGISTRATION
|
||||
ctx['can_reset'] = settings.PRETIX_PASSWORD_RESET
|
||||
|
||||
@@ -870,11 +870,15 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
msgs[self.supported_locale[idx]] = '<div class="alert alert-danger">{}</div>'.format(
|
||||
PlaceholderValidator.error_message)
|
||||
msgs[self.supported_locale[idx]] = format_html(
|
||||
'<div class="alert alert-danger">{}</div>',
|
||||
PlaceholderValidator.error_message
|
||||
)
|
||||
except KeyError as e:
|
||||
msgs[self.supported_locale[idx]] = '<div class="alert alert-danger">{}</div>'.format(
|
||||
_('Invalid placeholder: {%(value)s}') % {'value': e.args[0]})
|
||||
msgs[self.supported_locale[idx]] = format_html(
|
||||
'<div class="alert alert-danger">{}</div>',
|
||||
_('Invalid placeholder: {%(value)s}') % {'value': e.args[0]}
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
'item': preview_item,
|
||||
|
||||
@@ -1641,9 +1641,17 @@ class OrderCheckVATID(OrderView):
|
||||
|
||||
try:
|
||||
normalized_id = validate_vat_id(ia.vat_id, str(ia.country))
|
||||
ia.vat_id_validated = True
|
||||
ia.vat_id = normalized_id
|
||||
ia.save()
|
||||
with transaction.atomic():
|
||||
ia.vat_id_validated = True
|
||||
ia.vat_id = normalized_id
|
||||
ia.save()
|
||||
self.order.log_action(
|
||||
'pretix.event.order.vatid.validated',
|
||||
data={
|
||||
'vat_id': normalized_id,
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
except VATIDFinalError as e:
|
||||
messages.error(self.request, e.message)
|
||||
except VATIDTemporaryError:
|
||||
|
||||
@@ -1039,9 +1039,10 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
def _send_invite(self, instance):
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
_('Account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'organizer': self.request.organizer.name,
|
||||
'team': instance.team.name,
|
||||
|
||||
@@ -630,9 +630,14 @@ 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())))
|
||||
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{tokens}').format(
|
||||
tokens='- ' + '\n- '.join(t.token for t in d.token_set.all())
|
||||
)
|
||||
)
|
||||
return redirect(reverse('control:user.settings.2fa'))
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ from pretix.base.models import Item, LogEntry, Quota, WaitingListEntry
|
||||
from pretix.base.models.waitinglist import WaitingListException
|
||||
from pretix.base.services.waitinglist import assign_automatically
|
||||
from pretix.base.views.tasks import AsyncAction
|
||||
from pretix.control.forms.waitinglist import WaitingListEntryTransferForm
|
||||
from pretix.control.forms.waitinglist import WaitingListEntryEditForm
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.control.views import PaginationMixin
|
||||
|
||||
@@ -138,6 +138,17 @@ class WaitingListQuerySetMixin:
|
||||
elif force_filtered and '__ALL' not in self.request_data:
|
||||
qs = qs.none()
|
||||
|
||||
if self.request_data.get("search", "") != "":
|
||||
s = self.request_data.get("search", "")
|
||||
search_q = Q(email__icontains=s)
|
||||
|
||||
if self.request.event.settings.waiting_list_names_asked:
|
||||
search_q = search_q | Q(name_cached__icontains=s)
|
||||
if self.request.event.settings.waiting_list_phones_asked:
|
||||
search_q = search_q | Q(phone__icontains=s)
|
||||
|
||||
qs = qs.filter(search_q)
|
||||
|
||||
return qs
|
||||
|
||||
|
||||
@@ -238,7 +249,7 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['items'] = Item.objects.filter(event=self.request.event)
|
||||
ctx['filtered'] = ("status" in self.request.GET or "item" in self.request.GET)
|
||||
ctx['filtered'] = any(param in self.request.GET for param in ("status", "item", "search"))
|
||||
|
||||
itemvar_cache = {}
|
||||
quota_cache = {}
|
||||
@@ -390,25 +401,20 @@ class EntryDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
})
|
||||
|
||||
|
||||
class EntryTransfer(EventPermissionRequiredMixin, UpdateView):
|
||||
class EntryEdit(EventPermissionRequiredMixin, UpdateView):
|
||||
model = WaitingListEntry
|
||||
template_name = 'pretixcontrol/waitinglist/transfer.html'
|
||||
template_name = 'pretixcontrol/waitinglist/edit.html'
|
||||
permission = 'can_change_orders'
|
||||
form_class = WaitingListEntryTransferForm
|
||||
form_class = WaitingListEntryEditForm
|
||||
context_object_name = 'entry'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not self.request.event.has_subevents:
|
||||
raise Http404(_("This is not an event series."))
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_object(self, queryset=None) -> WaitingListEntry:
|
||||
return get_object_or_404(WaitingListEntry, pk=self.kwargs['entry'], event=self.request.event, voucher__isnull=True)
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
messages.success(self.request, _('The waitinglist entry has been transferred.'))
|
||||
if form.has_changed():
|
||||
messages.success(self.request, _('The waitinglist entry has been changed.'))
|
||||
self.object.log_action(
|
||||
'pretix.event.orders.waitinglist.changed', user=self.request.user, data={
|
||||
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||
|
||||
@@ -34,10 +34,7 @@ def set_cookie_without_samesite(request, response, key, *args, **kwargs):
|
||||
if not is_secure:
|
||||
# https://www.chromestatus.com/feature/5633521622188032
|
||||
return
|
||||
|
||||
useragent = request.headers.get('User-Agent', '')
|
||||
|
||||
if should_send_same_site_none(useragent):
|
||||
if should_send_same_site_none(request.headers.get('User-Agent', '')):
|
||||
# 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.
|
||||
@@ -47,29 +44,8 @@ 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
|
||||
|
||||
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)
|
||||
# CHIPS
|
||||
response.cookies[key]['Partitioned'] = True
|
||||
|
||||
|
||||
# Based on https://www.chromium.org/updates/same-site/incompatible-clients
|
||||
|
||||
+2561
-2267
File diff suppressed because it is too large
Load Diff
+2735
-2270
File diff suppressed because it is too large
Load Diff
+2561
-2267
File diff suppressed because it is too large
Load Diff
+2661
-2277
File diff suppressed because it is too large
Load Diff
+2684
-2260
File diff suppressed because it is too large
Load Diff
+2581
-2267
File diff suppressed because it is too large
Load Diff
+2667
-2279
File diff suppressed because it is too large
Load Diff
+2661
-2268
File diff suppressed because it is too large
Load Diff
@@ -265,6 +265,7 @@ Objekt-IDs
|
||||
Offline-Scan
|
||||
OK
|
||||
Online-Banking
|
||||
Online-Banking-Nutzer
|
||||
Onlinebanking
|
||||
Onlinebanking-Zugangsdaten
|
||||
Open
|
||||
@@ -539,6 +540,8 @@ WeChat-Zahlung
|
||||
Weiterleitungs-URIs
|
||||
Weiterleitungs-URL
|
||||
Weiterleitungs-URLs
|
||||
WERO
|
||||
WERO-App
|
||||
WhatsApp
|
||||
Widget
|
||||
Widget-Code
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -265,6 +265,7 @@ Objekt-IDs
|
||||
Offline-Scan
|
||||
OK
|
||||
Online-Banking
|
||||
Online-Banking-Nutzer
|
||||
Onlinebanking
|
||||
Onlinebanking-Zugangsdaten
|
||||
Open
|
||||
@@ -539,6 +540,8 @@ WeChat-Zahlung
|
||||
Weiterleitungs-URIs
|
||||
Weiterleitungs-URL
|
||||
Weiterleitungs-URLs
|
||||
WERO
|
||||
WERO-App
|
||||
WhatsApp
|
||||
Widget
|
||||
Widget-Code
|
||||
|
||||
+2561
-2267
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-01-26 13:20+0000\n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+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"
|
||||
|
||||
+2708
-2288
File diff suppressed because it is too large
Load Diff
+2561
-2267
File diff suppressed because it is too large
Load Diff
+2687
-2292
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2563
-2267
File diff suppressed because it is too large
Load Diff
+2643
-2256
File diff suppressed because it is too large
Load Diff
+2684
-2258
File diff suppressed because it is too large
Load Diff
+2578
-2269
File diff suppressed because it is too large
Load Diff
+2685
-2285
File diff suppressed because it is too large
Load Diff
+2881
-2467
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-26 09:10+0000\n"
|
||||
"PO-Revision-Date: 2025-12-03 23:00+0000\n"
|
||||
"Last-Translator: sandra r <sandrarial@gestiontickets.online>\n"
|
||||
"PO-Revision-Date: 2026-03-02 21:00+0000\n"
|
||||
"Last-Translator: Sandra Rial Pérez <sandrarial@gestiontickets.online>\n"
|
||||
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/gl/>\n"
|
||||
"Language: gl\n"
|
||||
@@ -17,7 +17,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.14.3\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -162,12 +162,12 @@ msgstr "Pedidos pagados"
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Attendees (ordered)"
|
||||
msgstr ""
|
||||
msgstr "Asistentes (ordenados)"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Attendees (paid)"
|
||||
msgstr ""
|
||||
msgstr "Asistentes (de pago)"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:51
|
||||
msgid "Total revenue"
|
||||
@@ -732,8 +732,8 @@ msgid ""
|
||||
"The items in your cart are no longer reserved for you. You can still "
|
||||
"complete your order as long as they’re available."
|
||||
msgstr ""
|
||||
"Os artigos da túa cesta xa non están reservados para ti. Aínda podes "
|
||||
"completar o teu pedido mentres estean dispoñibles."
|
||||
"Os artigos do teu carro xa non están reservados para ti. Podes completar o "
|
||||
"teu pedido sempre que estean dispoñibles."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:49
|
||||
msgid "Cart expired"
|
||||
|
||||
+2721
-2256
File diff suppressed because it is too large
Load Diff
+2662
-2254
File diff suppressed because it is too large
Load Diff
+2654
-2286
File diff suppressed because it is too large
Load Diff
+2716
-2259
File diff suppressed because it is too large
Load Diff
+2630
-2258
File diff suppressed because it is too large
Load Diff
+2882
-2482
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-26 09:10+0000\n"
|
||||
"PO-Revision-Date: 2026-02-12 20:00+0000\n"
|
||||
"Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n"
|
||||
"PO-Revision-Date: 2026-02-23 10:00+0000\n"
|
||||
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/ja/>\n"
|
||||
"Language: ja\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.15.2\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -256,7 +256,7 @@ msgstr "承認保留中"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
|
||||
msgid "Redeemed"
|
||||
msgstr "使用済"
|
||||
msgstr "引き換え済み"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:49
|
||||
msgid "Cancel"
|
||||
|
||||
+2639
-2257
File diff suppressed because it is too large
Load Diff
+2648
-2276
File diff suppressed because it is too large
Load Diff
+2563
-2267
File diff suppressed because it is too large
Load Diff
+2648
-2260
File diff suppressed because it is too large
Load Diff
+2561
-2267
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2705
-2308
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-05 23:00+0000\n"
|
||||
"PO-Revision-Date: 2026-02-19 22: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.15.2\n"
|
||||
"X-Generator: Weblate 5.16\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 "Alle"
|
||||
msgstr "Allemaal"
|
||||
|
||||
#: 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
+2727
-2261
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2580
-2247
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
+2732
-2273
File diff suppressed because it is too large
Load Diff
+2648
-2265
File diff suppressed because it is too large
Load Diff
+2551
-2244
File diff suppressed because it is too large
Load Diff
+2704
-2258
File diff suppressed because it is too large
Load Diff
+2623
-2262
File diff suppressed because it is too large
Load Diff
+2575
-2271
File diff suppressed because it is too large
Load Diff
+2723
-2261
File diff suppressed because it is too large
Load Diff
+3235
-2932
File diff suppressed because it is too large
Load Diff
+2700
-2285
File diff suppressed because it is too large
Load Diff
+2704
-2261
File diff suppressed because it is too large
Load Diff
+2688
-2256
File diff suppressed because it is too large
Load Diff
+2561
-2267
File diff suppressed because it is too large
Load Diff
@@ -187,6 +187,7 @@ webhooks
|
||||
webserver
|
||||
Wechat
|
||||
WeChat
|
||||
WERO
|
||||
WhatsApp
|
||||
whitespace
|
||||
xlsx
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -786,7 +786,12 @@ class PaypalMethod(BasePaymentProvider):
|
||||
else:
|
||||
pp_captured_order = response.result
|
||||
|
||||
for purchaseunit in pp_captured_order.purchase_units:
|
||||
payment.refresh_from_db()
|
||||
|
||||
any_captures = False
|
||||
all_captures_completed = True
|
||||
for purchaseunit in pp_captured_order.purchase_units:
|
||||
if hasattr(purchaseunit, 'payments'):
|
||||
for capture in purchaseunit.payments.captures:
|
||||
try:
|
||||
ReferencedPayPalObject.objects.get_or_create(order=payment.order, payment=payment, reference=capture.id)
|
||||
@@ -794,32 +799,40 @@ class PaypalMethod(BasePaymentProvider):
|
||||
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
|
||||
all_captures_completed = False
|
||||
else:
|
||||
any_captures = True
|
||||
|
||||
payment.refresh_from_db()
|
||||
|
||||
if pp_captured_order.status != 'COMPLETED':
|
||||
payment.fail(info=pp_captured_order.dict())
|
||||
logger.error('Invalid state: %s' % repr(pp_captured_order.dict()))
|
||||
raise PaymentException(
|
||||
_('We were unable to process your payment. See below for details on how to proceed.')
|
||||
)
|
||||
|
||||
if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED:
|
||||
logger.warning('PayPal success event even though order is already marked as paid')
|
||||
return
|
||||
|
||||
try:
|
||||
# Payment has at least one capture, but it is not yet completed
|
||||
if any_captures and not 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.save(update_fields=['info'])
|
||||
payment.confirm()
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise PaymentException(str(e))
|
||||
payment.state = OrderPayment.PAYMENT_STATE_PENDING
|
||||
payment.save()
|
||||
return
|
||||
# Payment has at least one capture and all captures are completed
|
||||
elif any_captures and all_captures_completed:
|
||||
if pp_captured_order.status != 'COMPLETED':
|
||||
payment.fail(info=pp_captured_order.dict())
|
||||
logger.error('Invalid state: %s' % repr(pp_captured_order.dict()))
|
||||
raise PaymentException(
|
||||
_('We were unable to process your payment. See below for details on how to proceed.')
|
||||
)
|
||||
|
||||
if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED:
|
||||
logger.warning('PayPal success event even though order is already marked as paid')
|
||||
return
|
||||
|
||||
try:
|
||||
payment.info = json.dumps(pp_captured_order.dict())
|
||||
payment.save(update_fields=['info'])
|
||||
payment.confirm()
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise PaymentException(str(e))
|
||||
# Payment has not any captures yet - so it's probably in created status
|
||||
else:
|
||||
return
|
||||
finally:
|
||||
if 'payment_paypal_oid' in request.session:
|
||||
del request.session['payment_paypal_oid']
|
||||
@@ -829,7 +842,7 @@ class PaypalMethod(BasePaymentProvider):
|
||||
try:
|
||||
if (
|
||||
payment.info
|
||||
and payment.info_data['purchase_units'][0]['payments']['captures'][0]['status'] == 'pending'
|
||||
and payment.info_data['purchase_units'][0]['payments']['captures'][0]['status'] == 'PENDING'
|
||||
):
|
||||
retry = False
|
||||
except (KeyError, IndexError):
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<script type="text/plain" id="stripe_payment_intent_action_type">{{ payment_intent_action_type }}</script>
|
||||
<script type="text/plain" id="stripe_payment_intent_client_secret">{{ payment_intent_client_secret }}</script>
|
||||
{% if payment_intent_next_action_redirect_url %}
|
||||
<script type="text/plain" id="stripe_payment_intent_next_action_redirect_url">{{ payment_intent_next_action_redirect_url }}</script>
|
||||
<script type="text/plain" id="stripe_payment_intent_next_action_redirect_url">{{ payment_intent_next_action_redirect_url|safe }}</script>
|
||||
{% endif %}
|
||||
{% if payment_intent_redirect_action_handling %}
|
||||
<script type="text/plain" id="stripe_payment_intent_redirect_action_handling">{{ payment_intent_redirect_action_handling }}</script>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
{% load eventurl %}
|
||||
{% load safelink %}
|
||||
{% load rich_text %}
|
||||
{% load anonymize_email %}
|
||||
{% block thetitle %}
|
||||
{% if messages %}
|
||||
{{ messages|join:" " }} ::
|
||||
@@ -219,7 +220,7 @@
|
||||
{% endblock %}
|
||||
{% block footernav %}
|
||||
{% if request.event.settings.contact_mail %}
|
||||
<li><a href="mailto:{{ request.event.settings.contact_mail }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
<li><a href="{{ 'mailto:'|add:request.event.settings.contact_mail|anon_email }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
{% endif %}
|
||||
{% if request.event.settings.privacy_url %}
|
||||
<li><a href="{% safelink request.event.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
{% bootstrap_form_errors timemachine_form "all" %}
|
||||
|
||||
<p>{% trans "Test your shop as if it were a different date and time." %}</p>
|
||||
<p>{% trans "Please note that the changed time is not taken into account for aspects of the shop that affect quotas, such as the validity period of carts and vouchers." %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@@ -44,4 +45,4 @@
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -21,4 +21,5 @@
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/deanonymize_email.js" %}"></script>
|
||||
{% endcompress %}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
{% load thumb %}
|
||||
{% load eventurl %}
|
||||
{% load safelink %}
|
||||
{% load anonymize_email %}
|
||||
{% block thetitle %}
|
||||
{% block title %}{% endblock %}{% if url_name != "organizer.index" %} :: {% endif %}{{ organizer.name }}
|
||||
{% endblock %}
|
||||
@@ -97,7 +98,7 @@
|
||||
{% endblock %}
|
||||
{% block footernav %}
|
||||
{% if not request.event and request.organizer.settings.contact_mail %}
|
||||
<li><a href="mailto:{{ request.organizer.settings.contact_mail }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
<li><a href="{{ 'mailto:'|add:request.organizer.settings.contact_mail|anon_email }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
{% endif %}
|
||||
{% if not request.event and request.organizer.settings.privacy_url %}
|
||||
<li><a href="{% safelink request.organizer.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user