Improve error messages for name-parts inputs (Z#23234440)

This commit is contained in:
Richard Schreiber
2026-05-18 13:02:25 +02:00
parent 857791445f
commit 58eb3c2b2e

View File

@@ -35,6 +35,7 @@
import copy import copy
import json import json
import logging import logging
import re
from datetime import timedelta from datetime import timedelta
from decimal import Decimal from decimal import Decimal
from io import BytesIO from io import BytesIO
@@ -48,7 +49,7 @@ from django.contrib import messages
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.validators import ( from django.core.validators import (
MaxValueValidator, MinValueValidator, RegexValidator, MaxValueValidator, MinValueValidator,
) )
from django.db.models import QuerySet from django.db.models import QuerySet
from django.forms import Select, widgets from django.forms import Select, widgets
@@ -220,20 +221,6 @@ class NamePartsFormField(forms.MultiValueField):
defaults = { defaults = {
'widget': self.widget, 'widget': self.widget,
'max_length': kwargs.pop('max_length', None), '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.max_length = defaults['max_length']
self.scheme_name = kwargs.pop('scheme') self.scheme_name = kwargs.pop('scheme')
@@ -255,7 +242,6 @@ class NamePartsFormField(forms.MultiValueField):
if fname == 'title' and self.scheme_titles: if fname == 'title' and self.scheme_titles:
d = dict(defaults) d = dict(defaults)
d.pop('max_length', None) d.pop('max_length', None)
d.pop('validators', None)
field = forms.ChoiceField( field = forms.ChoiceField(
**d, **d,
choices=[('', '')] + [(d, d) for d in self.scheme_titles[1]] choices=[('', '')] + [(d, d) for d in self.scheme_titles[1]]
@@ -264,7 +250,6 @@ class NamePartsFormField(forms.MultiValueField):
elif fname == 'salutation': elif fname == 'salutation':
d = dict(defaults) d = dict(defaults)
d.pop('max_length', None) d.pop('max_length', None)
d.pop('validators', None)
field = forms.ChoiceField( field = forms.ChoiceField(
**d, **d,
choices=[ 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): 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') 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": if value.get("salutation") == "empty":
value["salutation"] = "" value["salutation"] = ""