diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py
index 2c6afdca2c..04501aa428 100644
--- a/src/pretix/base/forms/questions.py
+++ b/src/pretix/base/forms/questions.py
@@ -333,23 +333,41 @@ class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):
def guess_country(event):
# Try to guess the initial country from either the country of the merchant
# or the locale. This will hopefully save at least some users some scrolling :)
- locale = get_language_without_region()
country = event.settings.region or event.settings.invoice_address_from_country
if not country:
- valid_countries = countries.countries
- if '-' in locale:
- parts = locale.split('-')
- # TODO: does this actually work?
- if parts[1].upper() in valid_countries:
- country = Country(parts[1].upper())
- elif parts[0].upper() in valid_countries:
- country = Country(parts[0].upper())
- else:
- if locale.upper() in valid_countries:
- country = Country(locale.upper())
+ country = get_country_by_locale(get_language_without_region())
return country
+def get_country_by_locale(locale):
+ country = None
+ valid_countries = countries.countries
+ if '-' in locale:
+ parts = locale.split('-')
+ # TODO: does this actually work?
+ if parts[1].upper() in valid_countries:
+ country = Country(parts[1].upper())
+ elif parts[0].upper() in valid_countries:
+ country = Country(parts[0].upper())
+ else:
+ if locale.upper() in valid_countries:
+ country = Country(locale.upper())
+ return country
+
+
+def guess_phone_prefix(event):
+ with language(get_babel_locale()):
+ country = str(guess_country(event))
+ return get_phone_prefix(country)
+
+
+def get_phone_prefix(country):
+ for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
+ if country in values:
+ return prefix
+ return None
+
+
class QuestionCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
option_template_name = 'pretixbase/forms/widgets/checkbox_option_with_links.html'
@@ -780,25 +798,26 @@ class BaseQuestionsForm(forms.Form):
if q.valid_datetime_max:
field.validators.append(MaxDateTimeValidator(q.valid_datetime_max))
elif q.type == Question.TYPE_PHONENUMBER:
- with language(get_babel_locale()):
- default_country = guess_country(event)
- default_prefix = None
- for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
- if str(default_country) in values:
- default_prefix = prefix
+ if initial:
try:
- initial = PhoneNumber().from_string(initial.answer) if initial else "+{}.".format(default_prefix)
+ initial = PhoneNumber().from_string(initial.answer)
except NumberParseException:
initial = None
- field = PhoneNumberField(
- label=label, required=required,
- help_text=help_text,
- # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
- # a country code but no number as an initial value. It's a bit hacky, but should be stable for
- # the future.
- initial=initial,
- widget=WrappedPhoneNumberPrefixWidget()
- )
+
+ if not initial:
+ phone_prefix = guess_phone_prefix(event)
+ if phone_prefix:
+ initial = "+{}.".format(phone_prefix)
+
+ field = PhoneNumberField(
+ label=label, required=required,
+ help_text=help_text,
+ # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
+ # a country code but no number as an initial value. It's a bit hacky, but should be stable for
+ # the future.
+ initial=initial,
+ widget=WrappedPhoneNumberPrefixWidget()
+ )
field.question = q
if answers:
# Cache the answer object for later use
diff --git a/src/pretix/base/migrations/0206_customer_phone.py b/src/pretix/base/migrations/0206_customer_phone.py
new file mode 100644
index 0000000000..d430863f13
--- /dev/null
+++ b/src/pretix/base/migrations/0206_customer_phone.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.2.9 on 2022-01-12 10:59
+
+import phonenumber_field.modelfields
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0205_itemvariation_require_approval'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='customer',
+ name='phone',
+ field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, region=None),
+ ),
+ ]
diff --git a/src/pretix/base/models/customers.py b/src/pretix/base/models/customers.py
index 9f5007690c..520a2f3e15 100644
--- a/src/pretix/base/models/customers.py
+++ b/src/pretix/base/models/customers.py
@@ -29,6 +29,7 @@ from django.db.models import F, Q
from django.utils.crypto import get_random_string, salted_hmac
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes import ScopedManager, scopes_disabled
+from phonenumber_field.modelfields import PhoneNumberField
from pretix.base.banlist import banned
from pretix.base.models.base import LoggedModel
@@ -45,6 +46,7 @@ class Customer(LoggedModel):
organizer = models.ForeignKey(Organizer, related_name='customers', on_delete=models.CASCADE)
identifier = models.CharField(max_length=190, db_index=True, unique=True)
email = models.EmailField(db_index=True, null=True, blank=False, verbose_name=_('E-mail'), max_length=190)
+ phone = PhoneNumberField(null=True, blank=True, verbose_name=_('Phone number'))
password = models.CharField(verbose_name=_('Password'), max_length=128)
name_cached = models.CharField(max_length=255, verbose_name=_('Full name'), blank=True)
name_parts = models.JSONField(default=dict)
@@ -87,6 +89,7 @@ class Customer(LoggedModel):
self.name_parts = {}
self.name_cached = ''
self.email = None
+ self.phone = None
self.save()
self.all_logentries().update(data={}, shredded=True)
self.orders.all().update(customer=None)
diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py
index 1a44359f99..18cc8c4761 100644
--- a/src/pretix/control/forms/organizer.py
+++ b/src/pretix/control/forms/organizer.py
@@ -44,12 +44,16 @@ from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes.forms import SafeModelMultipleChoiceField
from i18nfield.forms import I18nFormField, I18nTextarea
+from phonenumber_field.formfields import PhoneNumberField
from pytz import common_timezones
from pretix.api.models import WebHook
from pretix.api.webhooks import get_all_webhook_events
from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
-from pretix.base.forms.questions import NamePartsFormField
+from pretix.base.forms.questions import (
+ NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale,
+ get_phone_prefix,
+)
from pretix.base.forms.widgets import SplitDateTimePickerWidget
from pretix.base.models import (
Customer, Device, EventMetaProperty, Gate, GiftCard, Membership,
@@ -535,11 +539,21 @@ class CustomerUpdateForm(forms.ModelForm):
class Meta:
model = Customer
- fields = ['is_active', 'name_parts', 'email', 'is_verified', 'locale']
+ fields = ['is_active', 'name_parts', 'email', 'is_verified', 'phone', 'locale']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
+ if not self.instance.phone and (self.instance.organizer.settings.region or self.instance.locale):
+ country_code = self.instance.organizer.settings.region or get_country_by_locale(self.instance.locale)
+ phone_prefix = get_phone_prefix(country_code)
+ if phone_prefix:
+ self.initial['phone'] = "+{}.".format(phone_prefix)
+ self.fields['phone'] = PhoneNumberField(
+ label=_('Phone'),
+ required=False,
+ widget=WrappedPhoneNumberPrefixWidget()
+ )
self.fields['name_parts'] = NamePartsFormField(
max_length=255,
required=False,
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/customer.html b/src/pretix/control/templates/pretixcontrol/organizers/customer.html
index a332fa03ac..fff361b1f3 100644
--- a/src/pretix/control/templates/pretixcontrol/organizers/customer.html
+++ b/src/pretix/control/templates/pretixcontrol/organizers/customer.html
@@ -48,6 +48,10 @@
{% trans "Name" %}
{{ customer.name }}
+ {% if customer.phone %}
+ {% trans "Phone" %}
+ {{ customer.phone }}
+ {% endif %}
{% trans "Locale" %}
{{ display_locale }}
{% trans "Registration date" %}
diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py
index b2e66a02c4..d43644f00d 100644
--- a/src/pretix/presale/checkoutflow.py
+++ b/src/pretix/presale/checkoutflow.py
@@ -697,7 +697,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
self.cart_session.get('email', '') or
wd.get('email', '')
),
- 'phone': wd.get('phone', None)
+ 'phone': self.cart_session.get('phone', '') or wd.get('phone', None)
}
initial.update(self.cart_session.get('contact_form_data', {}))
@@ -709,6 +709,8 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
if self.cart_customer:
initial['email'] = self.cart_customer.email
initial['email_repeat'] = self.cart_customer.email
+ if not initial['phone']:
+ initial['phone'] = str(self.cart_customer.phone)
f = ContactForm(data=self.request.POST if self.request.method == "POST" else None,
event=self.request.event,
diff --git a/src/pretix/presale/forms/checkout.py b/src/pretix/presale/forms/checkout.py
index 1729f2231d..5d58bc24f0 100644
--- a/src/pretix/presale/forms/checkout.py
+++ b/src/pretix/presale/forms/checkout.py
@@ -42,15 +42,11 @@ from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from phonenumber_field.formfields import PhoneNumberField
-from phonenumber_field.phonenumber import PhoneNumber
-from phonenumbers import NumberParseException
-from phonenumbers.data import _COUNTRY_CODE_TO_REGION_CODE
from pretix.base.forms.questions import (
BaseInvoiceAddressForm, BaseQuestionsForm, WrappedPhoneNumberPrefixWidget,
- guess_country,
+ guess_phone_prefix,
)
-from pretix.base.i18n import get_babel_locale, language
from pretix.base.validators import EmailBanlistValidator
from pretix.presale.signals import contact_form_fields
@@ -75,27 +71,20 @@ class ContactForm(forms.Form):
)
if self.event.settings.order_phone_asked:
- with language(get_babel_locale()):
- default_country = guess_country(self.event)
- default_prefix = None
- for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
- if str(default_country) in values:
- default_prefix = prefix
- try:
- initial = self.initial.pop('phone', None)
- initial = PhoneNumber().from_string(initial) if initial else "+{}.".format(default_prefix)
- except NumberParseException:
- initial = None
- self.fields['phone'] = PhoneNumberField(
- label=_('Phone number'),
- required=self.event.settings.order_phone_required,
- help_text=self.event.settings.checkout_phone_helptext,
+ if not self.initial.get('phone'):
+ phone_prefix = guess_phone_prefix(self.event)
+ if phone_prefix:
# We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
# a country code but no number as an initial value. It's a bit hacky, but should be stable for
# the future.
- initial=initial,
- widget=WrappedPhoneNumberPrefixWidget()
- )
+ self.initial['phone'] = "+{}.".format(phone_prefix)
+
+ self.fields['phone'] = PhoneNumberField(
+ label=_('Phone number'),
+ required=self.event.settings.order_phone_required,
+ help_text=self.event.settings.checkout_phone_helptext,
+ widget=WrappedPhoneNumberPrefixWidget()
+ )
if not self.request.session.get('iframe_session', False):
# There is a browser quirk in Chrome that leads to incorrect initial scrolling in iframes if there
diff --git a/src/pretix/presale/forms/customer.py b/src/pretix/presale/forms/customer.py
index 9c5ef2ec49..8018ae0a4d 100644
--- a/src/pretix/presale/forms/customer.py
+++ b/src/pretix/presale/forms/customer.py
@@ -31,8 +31,12 @@ from django.contrib.auth.password_validation import (
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
+from phonenumber_field.formfields import PhoneNumberField
-from pretix.base.forms.questions import NamePartsFormField
+from pretix.base.forms.questions import (
+ NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale,
+ get_phone_prefix,
+)
from pretix.base.i18n import get_language_without_region
from pretix.base.models import Customer
from pretix.base.services.mail import mail
@@ -137,6 +141,19 @@ class RegistrationForm(forms.Form):
self.request = request
super().__init__(*args, **kwargs)
+ event = getattr(request, "event", None)
+ if event and event.settings.order_phone_asked:
+ if event.settings.region or event.organizer.settings.region:
+ country_code = event.settings.region or event.organizer.settings.region
+ phone_prefix = get_phone_prefix(country_code)
+ if phone_prefix:
+ self.initial['phone'] = "+{}.".format(phone_prefix)
+ self.fields['phone'] = PhoneNumberField(
+ label=_('Phone'),
+ required=event.settings.order_phone_required,
+ widget=WrappedPhoneNumberPrefixWidget()
+ )
+
self.fields['name_parts'] = NamePartsFormField(
max_length=255,
required=True,
@@ -394,7 +411,7 @@ class ChangeInfoForm(forms.ModelForm):
class Meta:
model = Customer
- fields = ('name_parts', 'email')
+ fields = ('name_parts', 'email', 'phone')
def __init__(self, request=None, *args, **kwargs):
self.request = request
@@ -408,6 +425,18 @@ class ChangeInfoForm(forms.ModelForm):
label=_('Name'),
)
+ if not self.initial.get('phone') and (request.organizer.settings.region or self.instance.locale):
+ country_code = self.instance.organizer.settings.region or get_country_by_locale(self.instance.locale)
+ phone_prefix = get_phone_prefix(country_code)
+ if phone_prefix:
+ self.initial['phone'] = "+{}.".format(phone_prefix)
+
+ self.fields['phone'] = PhoneNumberField(
+ label=_('Phone'),
+ required=False,
+ widget=WrappedPhoneNumberPrefixWidget()
+ )
+
def clean_password_current(self):
old_pw = self.cleaned_data.get('password_current')
diff --git a/src/pretix/presale/forms/waitinglist.py b/src/pretix/presale/forms/waitinglist.py
index 386c39b90f..cbca5c3616 100644
--- a/src/pretix/presale/forms/waitinglist.py
+++ b/src/pretix/presale/forms/waitinglist.py
@@ -23,12 +23,10 @@ from django import forms
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.utils.translation import gettext_lazy as _
from phonenumber_field.formfields import PhoneNumberField
-from phonenumbers.data import _COUNTRY_CODE_TO_REGION_CODE
from pretix.base.forms.questions import (
- NamePartsFormField, WrappedPhoneNumberPrefixWidget, guess_country,
+ NamePartsFormField, WrappedPhoneNumberPrefixWidget, guess_phone_prefix,
)
-from pretix.base.i18n import get_babel_locale, language
from pretix.base.models import Quota, WaitingListEntry
from pretix.presale.views.event import get_grouped_items
@@ -93,21 +91,17 @@ class WaitingListForm(forms.ModelForm):
del self.fields['name_parts']
if event.settings.waiting_list_phones_asked:
- with language(get_babel_locale()):
- default_country = guess_country(self.event)
- for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
- if str(default_country) in values and not self.initial.get('phone'):
- # We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
- # a country code but no number as an initial value. It's a bit hacky, but should be stable for
- # the future.
- self.initial['phone'] = "+{}.".format(prefix)
+ if not self.initial.get('phone'):
+ phone_prefix = guess_phone_prefix(event)
+ if phone_prefix:
+ self.initial['phone'] = "+{}.".format(phone_prefix)
- self.fields['phone'] = PhoneNumberField(
- label=_("Phone number"),
- required=event.settings.waiting_list_phones_required,
- help_text=event.settings.waiting_list_phones_explanation_text,
- widget=WrappedPhoneNumberPrefixWidget()
- )
+ self.fields['phone'] = PhoneNumberField(
+ label=_("Phone number"),
+ required=event.settings.waiting_list_phones_required,
+ help_text=event.settings.waiting_list_phones_explanation_text,
+ widget=WrappedPhoneNumberPrefixWidget()
+ )
else:
del self.fields['phone']
diff --git a/src/pretix/presale/templates/pretixpresale/organizers/customer_profile.html b/src/pretix/presale/templates/pretixpresale/organizers/customer_profile.html
index 3b3a4102ba..c6506471cb 100644
--- a/src/pretix/presale/templates/pretixpresale/organizers/customer_profile.html
+++ b/src/pretix/presale/templates/pretixpresale/organizers/customer_profile.html
@@ -24,6 +24,10 @@
{% trans "Name" %}
{{ customer.name }}
+ {% if customer.phone %}
+ {% trans "Phone" %}
+ {{ customer.phone }}
+ {% endif %}