From 58eb3c2b2e41ee62ae965b4b2cfeb8d71c8a67b3 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Mon, 18 May 2026 13:02:25 +0200 Subject: [PATCH] Improve error messages for name-parts inputs (Z#23234440) --- src/pretix/base/forms/questions.py | 49 +++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index 488e684b7..f0670e062 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -35,6 +35,7 @@ import copy import json import logging +import re from datetime import timedelta from decimal import Decimal from io import BytesIO @@ -48,7 +49,7 @@ from django.contrib import messages from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile from django.core.validators import ( - MaxValueValidator, MinValueValidator, RegexValidator, + MaxValueValidator, MinValueValidator, ) from django.db.models import QuerySet from django.forms import Select, widgets @@ -220,20 +221,6 @@ class NamePartsFormField(forms.MultiValueField): defaults = { 'widget': self.widget, 'max_length': kwargs.pop('max_length', None), - 'validators': [ - RegexValidator( - # The following characters should never appear in a name anywhere of - # the world. However, they commonly appear in inputs generated by spam - # bots. - r'^[^$€/%§{}<>~]*$', - message=_('Please do not use special characters in names.') - ), - RegexValidator( - URL_RE, - inverse_match=True, - message=_('Please do not use special characters in names.') - ) - ] } self.max_length = defaults['max_length'] self.scheme_name = kwargs.pop('scheme') @@ -255,7 +242,6 @@ class NamePartsFormField(forms.MultiValueField): if fname == 'title' and self.scheme_titles: d = dict(defaults) d.pop('max_length', None) - d.pop('validators', None) field = forms.ChoiceField( **d, choices=[('', '')] + [(d, d) for d in self.scheme_titles[1]] @@ -264,7 +250,6 @@ class NamePartsFormField(forms.MultiValueField): elif fname == 'salutation': d = dict(defaults) d.pop('max_length', None) - d.pop('validators', None) field = forms.ChoiceField( **d, choices=[ @@ -296,6 +281,36 @@ class NamePartsFormField(forms.MultiValueField): if sum(len(v) for v in value.values() if v) > (self.max_length or 250): raise forms.ValidationError(_('Please enter a shorter name.'), code='max_length') + for fname, label, size in self.scheme['fields']: + if fname == 'salutation' or (fname == 'title' and self.scheme_titles): + continue + v = value.get(fname) + if not v: + continue + special_chars = re.findall('[$€/%§{}<>~]', v) + if special_chars: + raise forms.ValidationError( + _('The field "%(label)s" may not contain special characters such as "%(chars)s".'), + code='name_special_chars', + params={ + "label": label, + "chars": "".join(special_chars), + }, + ) + # URL_RE checks for valid domain names, including one special TLD med, which can be part of a title + # correct spelling for URL-matching, but keep original spelling in user-provided value + v = v.replace(".med", ". med") + url_matched = URL_RE.match(v) + if url_matched: + raise forms.ValidationError( + _('The field "%(label)s" may not contain an URL (%(url)s).'), + code='url_in_title', + params={ + "label": label, + "url": url_matched.group(0), + } + ) + if value.get("salutation") == "empty": value["salutation"] = ""