Allow to use a selection for name titles

This commit is contained in:
Raphael Michel
2019-07-16 10:23:43 +02:00
parent 44ffc0685e
commit dc42dbb837
6 changed files with 95 additions and 26 deletions

View File

@@ -12,6 +12,7 @@ from django import forms
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.db.models import QuerySet
from django.forms import Select
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import get_language, ugettext_lazy as _
@@ -24,7 +25,7 @@ from pretix.base.forms.widgets import (
)
from pretix.base.models import InvoiceAddress, Question, QuestionOption
from pretix.base.models.tax import EU_COUNTRIES
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.settings import PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS
from pretix.base.templatetags.rich_text import rich_text
from pretix.control.forms import SplitDateTimeField
from pretix.helpers.escapejson import escapejson_attr
@@ -37,14 +38,18 @@ logger = logging.getLogger(__name__)
class NamePartsWidget(forms.MultiWidget):
widget = forms.TextInput
def __init__(self, scheme: dict, field: forms.Field, attrs=None):
def __init__(self, scheme: dict, field: forms.Field, attrs=None, titles: list=None):
widgets = []
self.scheme = scheme
self.field = field
self.titles = titles
for fname, label, size in self.scheme['fields']:
a = copy.copy(attrs) or {}
a['data-fname'] = fname
widgets.append(self.widget(attrs=a))
if fname == 'title' and self.titles:
widgets.append(Select(attrs=a, choices=[('', '')] + [(d, d) for d in self.titles[1]]))
else:
widgets.append(self.widget(attrs=a))
super().__init__(widgets, attrs)
def decompress(self, value):
@@ -103,19 +108,34 @@ class NamePartsFormField(forms.MultiValueField):
'max_length': kwargs.pop('max_length', None),
}
self.scheme_name = kwargs.pop('scheme')
self.titles = kwargs.pop('titles')
self.scheme = PERSON_NAME_SCHEMES.get(self.scheme_name)
if self.titles:
self.scheme_titles = PERSON_NAME_TITLE_GROUPS.get(self.titles)
else:
self.scheme_titles = None
self.one_required = kwargs.get('required', True)
require_all_fields = kwargs.pop('require_all_fields', False)
kwargs['required'] = False
kwargs['widget'] = (kwargs.get('widget') or self.widget)(
scheme=self.scheme, field=self, **kwargs.pop('widget_kwargs', {})
scheme=self.scheme, titles=self.scheme_titles, field=self, **kwargs.pop('widget_kwargs', {})
)
defaults.update(**kwargs)
for fname, label, size in self.scheme['fields']:
defaults['label'] = label
field = forms.CharField(**defaults)
field.part_name = fname
fields.append(field)
if fname == 'title' and self.scheme_titles:
d = dict(defaults)
d.pop('max_length', None)
field = forms.ChoiceField(
**d,
choices=[('', '')] + [(d, d) for d in self.scheme_titles[1]]
)
field.part_name = fname
fields.append(field)
else:
field = forms.CharField(**defaults)
field.part_name = fname
fields.append(field)
super().__init__(
fields=fields, require_all_fields=False, *args, **kwargs
)
@@ -160,6 +180,7 @@ class BaseQuestionsForm(forms.Form):
max_length=255,
required=event.settings.attendee_names_required,
scheme=event.settings.name_scheme,
titles=event.settings.name_scheme_titles,
label=_('Attendee name'),
initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts),
)
@@ -400,6 +421,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
max_length=255,
required=event.settings.invoice_name_required and not self.all_optional,
scheme=event.settings.name_scheme,
titles=event.settings.name_scheme_titles,
label=_('Name'),
initial=(self.instance.name_parts if self.instance else self.instance.name_parts),
)

View File

@@ -705,6 +705,23 @@ Your {event} team"""))
'type': str
}
}
PERSON_NAME_TITLE_GROUPS = OrderedDict([
('english_common', (_('Most common English titles'), (
'Mr',
'Ms',
'Mrs',
'Miss',
'Mx',
'Dr',
'Professor',
'Sir'
))),
('german_common', (_('Most common German titles'), (
'Dr.',
'Prof.',
'Prof. Dr.',
)))
])
PERSON_NAME_SCHEMES = OrderedDict([
('given_family', {
'fields': (
@@ -734,6 +751,22 @@ PERSON_NAME_SCHEMES = OrderedDict([
'_scheme': 'title_given_family',
},
}),
('title_given_family', {
'fields': (
('title', pgettext_lazy('person_name', 'Title'), 1),
('given_name', _('Given name'), 2),
('family_name', _('Family name'), 2),
),
'concatenation': lambda d: ' '.join(
str(p) for p in [d.get('title', ''), d.get('given_name', ''), d.get('family_name', '')] if p
),
'sample': {
'title': pgettext_lazy('person_name_sample', 'Dr'),
'given_name': pgettext_lazy('person_name_sample', 'John'),
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
'_scheme': 'title_given_family',
},
}),
('given_middle_family', {
'fields': (
('given_name', _('First name'), 2),

View File

@@ -24,7 +24,7 @@ from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
from pretix.base.models import Event, Organizer, TaxRule
from pretix.base.models.event import EventMetaValue, SubEvent
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.settings import PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS
from pretix.control.forms import (
ExtFileField, FontSelect, MultipleLanguagesWidget, SingleLanguageWidget,
SlugWidget, SplitDateTimeField, SplitDateTimePickerWidget,
@@ -383,6 +383,12 @@ class EventSettingsForm(SettingsForm):
"orders might lead to unexpected behaviour when sorting or changing names."),
required=True,
)
name_scheme_titles = forms.ChoiceField(
label=_("Allowed titles"),
help_text=_("If the naming scheme you defined above allows users to input a title, you can use this to "
"restrict the set of selectable titles."),
required=False,
)
attendee_emails_asked = forms.BooleanField(
label=_("Ask for email addresses per ticket"),
help_text=_("Normally, pretix asks for one email address per order and the order confirmation will be sent "
@@ -466,6 +472,13 @@ class EventSettingsForm(SettingsForm):
))
for k, v in PERSON_NAME_SCHEMES.items()
)
self.fields['name_scheme_titles'].choices = [('', _('Free text input'))] + [
(k, '{scheme}: {samples}'.format(
scheme=v[0],
samples=', '.join(v[1])
))
for k, v in PERSON_NAME_TITLE_GROUPS.items()
]
class CancelSettingsForm(SettingsForm):

View File

@@ -65,6 +65,7 @@
{% bootstrap_field sform.attendee_names_asked layout="control" %}
{% bootstrap_field sform.attendee_names_required layout="control" %}
{% bootstrap_field sform.name_scheme layout="control" %}
{% bootstrap_field sform.name_scheme_titles layout="control" %}
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
{% bootstrap_field sform.attendee_emails_asked layout="control" %}
{% bootstrap_field sform.attendee_emails_required layout="control" %}

View File

@@ -416,11 +416,11 @@ table td > .checkbox input[type="checkbox"] {
@media(max-width: $screen-xs-max) {
.nameparts-form-group {
display: block;
input:not(:first-child) {
input:not(:first-child), select:not(:first-child) {
border-top-right-radius: 0;
border-top-left-radius: 0;
}
input:not(:last-child) {
input:not(:last-child), select:not(:last-child) {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
@@ -430,32 +430,32 @@ table td > .checkbox input[type="checkbox"] {
.nameparts-form-group {
display: flex;
flex-direction: row;
input {
input, select {
width: auto;
flex-basis: 0;
flex-grow: 1;
}
input:not(:first-child) {
input:not(:first-child), select:not(:first-child) {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
input:not(:last-child) {
input:not(:last-child), select:not(:last-child) {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
input[data-size="1"] {
[data-size="1"] {
flex-grow: 1;
flex-shrink: 4;
}
input[data-size="2"] {
[data-size="2"] {
flex-grow: 2;
flex-shrink: 3;
}
input[data-size="3"] {
[data-size="3"] {
flex-grow: 3;
flex-shrink: 2;
}
input[data-size="4"] {
[data-size="4"] {
flex-grow: 4;
flex-shrink: 1;
}

View File

@@ -96,11 +96,11 @@
@media(max-width: $screen-xs-max) {
.nameparts-form-group {
display: block;
input:not(:first-child) {
input:not(:first-child), select:not(:first-child) {
border-top-right-radius: 0;
border-top-left-radius: 0;
}
input:not(:last-child) {
input:not(:last-child), select:not(:last-child) {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
@@ -110,32 +110,32 @@
.nameparts-form-group {
display: flex;
flex-direction: row;
input {
input, select {
width: auto;
flex-basis: 0;
flex-grow: 1;
}
input:not(:first-child) {
input:not(:first-child), select:not(:first-child) {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
input:not(:last-child) {
input:not(:last-child), select:not(:last-child) {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
input[data-size="1"] {
[data-size="1"] {
flex-grow: 1;
flex-shrink: 4;
}
input[data-size="2"] {
[data-size="2"] {
flex-grow: 2;
flex-shrink: 3;
}
input[data-size="3"] {
[data-size="3"] {
flex-grow: 3;
flex-shrink: 2;
}
input[data-size="4"] {
[data-size="4"] {
flex-grow: 4;
flex-shrink: 1;
}