0004-fields-add-a-PhoneField-70486.patch
src/authentic2/forms/fields.py | ||
---|---|---|
17 | 17 |
import io |
18 | 18 |
import warnings |
19 | 19 | |
20 |
import phonenumbers |
|
20 | 21 |
import PIL.Image |
21 | 22 |
from django import forms |
23 |
from django.conf import settings |
|
22 | 24 |
from django.core import validators |
23 | 25 |
from django.core.files import File |
24 |
from django.forms import CharField, EmailField, FileField, ModelChoiceField, ValidationError |
|
26 |
from django.forms import CharField, EmailField, FileField, ModelChoiceField, MultiValueField, ValidationError
|
|
25 | 27 |
from django.forms.fields import FILE_INPUT_CONTRADICTION |
26 | 28 |
from django.utils.translation import gettext_lazy as _ |
27 | 29 | |
... | ... | |
32 | 34 |
EmailInput, |
33 | 35 |
NewPasswordInput, |
34 | 36 |
PasswordInput, |
37 |
PhoneWidget, |
|
35 | 38 |
ProfileImageInput, |
36 | 39 |
) |
37 | 40 |
from authentic2.manager.utils import label_from_role |
... | ... | |
209 | 212 |
self.validate(value) |
210 | 213 |
self.run_validators(value) |
211 | 214 |
return value |
215 | ||
216 | ||
217 |
class PhoneField(MultiValueField): |
|
218 |
widget = PhoneWidget |
|
219 | ||
220 |
def __init__(self, **kwargs): |
|
221 |
fields = ( |
|
222 |
CharField(max_length=8, initial=settings.DEFAULT_COUNTRY_CODE), |
|
223 |
CharField(max_length=16, required=False), |
|
224 |
) |
|
225 |
super().__init__(error_messages=None, fields=fields, require_all_fields=False, **kwargs) |
|
226 | ||
227 |
def compress(self, data_list): |
|
228 |
from authentic2.attribute_kinds import clean_number |
|
229 | ||
230 |
if data_list and data_list[1]: |
|
231 |
country_code = data_list[0] |
|
232 |
data_list[0] = '+%s' % data_list[0] |
|
233 |
data_list[1] = clean_number(data_list[1]) |
|
234 |
dial = settings.PHONE_COUNTRY_CODES.get(country_code, settings.DEFAULT_COUNTRY_CODE).get('lang') |
|
235 |
try: |
|
236 |
pn = phonenumbers.parse(''.join(data_list), dial) |
|
237 |
except phonenumbers.NumberParseException: |
|
238 |
raise ValidationError(_('Invalid phone number.')) |
|
239 |
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164) |
|
240 |
return '' |
tests/test_fields.py | ||
---|---|---|
18 | 18 |
import pytest |
19 | 19 |
from django.core.exceptions import ValidationError |
20 | 20 | |
21 |
from authentic2.attribute_kinds import PhoneNumberField |
|
22 | 21 |
from authentic2.forms.passwords import NewPasswordField |
22 |
from authentic2.forms.fields import PhoneField |
|
23 | 23 | |
24 | 24 | |
25 |
def test_phonenumber_field(): |
|
26 |
field = PhoneNumberField() |
|
25 |
def test_phonenumber_field(settings): |
|
26 |
settings.DEFAULT_COUNTRY_CODE = '33' |
|
27 |
field = PhoneField() |
|
27 | 28 | |
29 |
positive = [ |
|
30 |
{'input': ['33', '01 01 01 01 01'], 'output': '+33101010101'}, |
|
31 |
# undialable numbers are still parsed and usable as identifiers |
|
32 |
{'input': ['33', '01 01 01'], 'output': '+33010101'}, |
|
33 |
{'input': ['33', '01 01 01 010101'], 'output': '+3310101010101'}, |
|
34 |
] |
|
28 | 35 |
# positive |
29 |
for value in ['01 01 01 01 01', '+01 01 01 01 01', ' + 01/01.01-01.01', '+01/01.01-01.01']: |
|
30 |
field.clean(value) |
|
36 |
for value in positive: |
|
37 |
output = field.clean(value['input']) |
|
38 |
assert output == value['output'] |
|
31 | 39 | |
32 | 40 |
# negative |
33 |
for value in ['01a01']: |
|
41 |
for value in [ |
|
42 |
['33', '01a01'], |
|
43 |
['33', '+01 01 01 01 01'], |
|
44 |
['33', ' + 01/01.01-01.01'], |
|
45 |
['33', '+01/01.01-01.01'], |
|
46 |
]: |
|
34 | 47 |
with pytest.raises(ValidationError): |
35 | 48 |
field.clean(value) |
36 | 49 | |
37 |
- |