Compare commits

...
Author SHA1 Message Date
Kara Engelhardt 387948b841 Fix crash in mail_send_task for nonexistant mails 2026-03-30 14:34:54 +02:00
Raphael MichelandRaphael Michel 4eade5070e Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (6287 of 6287 strings)

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

powered by weblate
2026-03-30 14:01:13 +02:00
Raphael MichelandRaphael Michel 32b1997208 Translations: Update German
Currently translated at 100.0% (6287 of 6287 strings)

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

powered by weblate
2026-03-30 14:01:13 +02:00
Raphael Michel eaf4a310f6 Translations: Update wordlist 2026-03-30 13:59:37 +02:00
Raphael Michel 8dc0f7c1b2 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2026-03-30 13:26:02 +02:00
CVZ-esandRaphael Michel dd3e6c4692 Translations: Update Spanish
Currently translated at 100.0% (256 of 256 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/es/

powered by weblate
2026-03-30 13:21:45 +02:00
Kara Engelhardtandpajowu c7437336b4 Add length help text to customer password forms
Also cleans up dead code, as `validate_password` always returns None or raises a ValidationError.
2026-03-30 11:25:14 +02:00
62 changed files with 17036 additions and 15588 deletions
+1 -2
View File
@@ -196,8 +196,7 @@ class RegistrationForm(forms.Form):
def clean_password(self):
password1 = self.cleaned_data.get('password', '')
user = User(email=self.cleaned_data.get('email'))
if validate_password(password1, user=user) is not None:
raise forms.ValidationError(_(password_validators_help_texts()), code='pw_invalid')
validate_password(password1, user=user)
return password1
def clean_email(self):
+1 -1
View File
@@ -411,7 +411,7 @@ def mail_send_task(self, **kwargs) -> bool:
try:
outgoing_mail = OutgoingMail.objects.select_for_update(of=OF_SELF).get(pk=outgoing_mail)
except OutgoingMail.DoesNotExist:
logger.info(f"Ignoring job for non existing email {outgoing_mail.guid}")
logger.info(f"Ignoring job for non existing email {outgoing_mail}")
return False
if outgoing_mail.status == OutgoingMail.STATUS_INFLIGHT:
logger.info(f"Ignoring job for inflight email {outgoing_mail.guid}")
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
+4
View File
@@ -32,6 +32,7 @@ ausgecheckt
ausgeklappt
auswahl
Authentication
Authenticator
Authenticator-App
Autorisierungscode
Autorisierungs-Endpunktes
@@ -130,6 +131,7 @@ Eingangsscan
Einlassbuchung
Einlassdatum
Einlasskontrolle
Einmalpasswörter
einzuchecken
email
E-Mail-Renderer
@@ -163,6 +165,7 @@ Explorer
FA
Favicon
F-Droid
freeOTP
Footer
Footer-Link
Footer-Text
@@ -557,6 +560,7 @@ Zahlungs-ID
Zahlungspflichtig
Zehnerkarten
Zeitbasiert
zeitbasierte
Zeitslotbuchung
Zimpler
ZIP-Datei
File diff suppressed because it is too large Load Diff
@@ -32,6 +32,7 @@ ausgecheckt
ausgeklappt
auswahl
Authentication
Authenticator
Authenticator-App
Autorisierungscode
Autorisierungs-Endpunktes
@@ -130,6 +131,7 @@ Eingangsscan
Einlassbuchung
Einlassdatum
Einlasskontrolle
Einmalpasswörter
einzuchecken
email
E-Mail-Renderer
@@ -163,6 +165,7 @@ Explorer
FA
Favicon
F-Droid
freeOTP
Footer
Footer-Link
Footer-Text
@@ -557,6 +560,7 @@ Zahlungs-ID
Zahlungspflichtig
Zehnerkarten
Zeitbasiert
zeitbasierte
Zeitslotbuchung
Zimpler
ZIP-Datei
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-17 14:06+0000\n"
"POT-Creation-Date: 2026-03-30 11:25+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
+2 -2
View File
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-17 14:06+0000\n"
"PO-Revision-Date: 2026-03-18 12:23+0000\n"
"PO-Revision-Date: 2026-03-30 03:00+0000\n"
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
"js/es/>\n"
@@ -329,7 +329,7 @@ msgstr "Pedido no aprobado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
msgid "Checked-in Tickets"
msgstr "Registro de código QR"
msgstr "Billetes registrados"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
msgid "Valid Tickets"
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
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
+3
View File
@@ -12,6 +12,7 @@ anonymized
Auth
authentification
authenticator
Authenticator
automatical
availabilities
backend
@@ -22,6 +23,7 @@ barcodes
Bcc
BCC
BezahlCode
biometric
BLIK
blocklist
BN
@@ -56,6 +58,7 @@ EPS
eps
favicon
filetype
freeOTP
frontend
frontpage
Galician
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+23 -10
View File
@@ -28,7 +28,7 @@ from django import forms
from django.conf import settings
from django.contrib.auth.hashers import check_password
from django.contrib.auth.password_validation import (
get_password_validators, password_validators_help_texts, validate_password,
MinimumLengthValidator, get_password_validators, validate_password,
)
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core import signing
@@ -300,13 +300,12 @@ class SetPasswordForm(forms.Form):
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput(attrs={'minlength': '8', 'autocomplete': 'new-password'}),
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
max_length=4096,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput(attrs={'minlength': '8', 'autocomplete': 'new-password'}),
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
max_length=4096,
)
@@ -316,6 +315,14 @@ class SetPasswordForm(forms.Form):
kwargs['initial']['email'] = self.customer.email
super().__init__(*args, **kwargs)
pw_min_len_validators = [v for v in get_customer_password_validators() if isinstance(v, MinimumLengthValidator)]
if pw_min_len_validators:
self.fields['password'].widget.attrs['minlength'] = max(v.min_length for v in pw_min_len_validators)
self.fields['password_repeat'].widget.attrs['minlength'] = max(v.min_length for v in pw_min_len_validators)
if 'password' not in self.data:
self.fields['password'].help_text = ' '.join(v.get_help_text() for v in pw_min_len_validators)
def clean(self):
password1 = self.cleaned_data.get('password', '')
password2 = self.cleaned_data.get('password_repeat')
@@ -329,8 +336,7 @@ class SetPasswordForm(forms.Form):
def clean_password(self):
password1 = self.cleaned_data.get('password', '')
if validate_password(password1, user=self.customer, password_validators=get_customer_password_validators()) is not None:
raise forms.ValidationError(_(password_validators_help_texts()), code='pw_invalid')
validate_password(password1, user=self.customer, password_validators=get_customer_password_validators())
return password1
@@ -395,13 +401,13 @@ class ChangePasswordForm(forms.Form):
)
password = forms.CharField(
label=_('New password'),
widget=forms.PasswordInput(attrs={'minlength': '8', 'autocomplete': 'new-password'}),
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
max_length=4096,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput(attrs={'minlength': '8', 'autocomplete': 'new-password'}),
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
max_length=4096,
)
@@ -411,6 +417,14 @@ class ChangePasswordForm(forms.Form):
kwargs['initial']['email'] = self.customer.email
super().__init__(*args, **kwargs)
pw_min_len_validators = [v for v in get_customer_password_validators() if isinstance(v, MinimumLengthValidator)]
if pw_min_len_validators:
self.fields['password'].widget.attrs['minlength'] = max(v.min_length for v in pw_min_len_validators)
self.fields['password_repeat'].widget.attrs['minlength'] = max(v.min_length for v in pw_min_len_validators)
if 'password' not in self.data:
self.fields['password'].help_text = ' '.join(v.get_help_text() for v in pw_min_len_validators)
def clean(self):
password1 = self.cleaned_data.get('password', '')
password2 = self.cleaned_data.get('password_repeat')
@@ -424,8 +438,7 @@ class ChangePasswordForm(forms.Form):
def clean_password(self):
password1 = self.cleaned_data.get('password', '')
if validate_password(password1, user=self.customer, password_validators=get_customer_password_validators()) is not None:
raise forms.ValidationError(_(password_validators_help_texts()), code='pw_invalid')
validate_password(password1, user=self.customer, password_validators=get_customer_password_validators())
return password1
def clean_password_current(self):