From feb1378760471e0f9d28d3e499bcc206faa2f179 Mon Sep 17 00:00:00 2001
From: Paul Marillonnet <pmarillonnet@entrouvert.com>
Date: Mon, 21 Nov 2022 16:35:34 +0100
Subject: [PATCH 3/5] fields: prefill users' phone number value (#69838)

---
 tests/form_pages/test_all.py | 91 +++++++++++++++++++++++++++++++++---
 tests/test_prefill.py        | 51 +++++++++++++++++++-
 wcs/fields.py                |  6 +++
 wcs/users.py                 | 20 ++++++++
 4 files changed, 161 insertions(+), 7 deletions(-)

diff --git a/tests/form_pages/test_all.py b/tests/form_pages/test_all.py
index be030e83a..4e2e2b479 100644
--- a/tests/form_pages/test_all.py
+++ b/tests/form_pages/test_all.py
@@ -16,6 +16,7 @@ from django.utils.encoding import force_bytes, force_text
 from webtest import Hidden, Radio, Upload
 
 from wcs import fields
+from wcs.admin.settings import UserFieldsFormDef
 from wcs.blocks import BlockDef
 from wcs.carddef import CardDef
 from wcs.categories import Category
@@ -71,8 +72,15 @@ def pub(request):
     pub = create_temporary_pub(lazy_mode=bool('lazy' in request.param))
     pub.cfg['identification'] = {'methods': ['password']}
     pub.cfg['language'] = {'language': 'en'}
+    pub.cfg['users'] = {
+        'field_phone': '_phone',
+    }
     pub.write_cfg()
 
+    formdef = UserFieldsFormDef(pub)
+    formdef.fields = [fields.StringField(id='_phone', label='phone', type='string', varname='phone')]
+    formdef.store()
+
     Category.wipe()
     cat = Category(name='foobar')
     cat.store()
@@ -107,6 +115,7 @@ def create_user(pub):
     user = pub.user_class()
     user.name = 'User Name'
     user.email = 'foo@localhost'
+    user.form_data = {'_phone': '+33123456789'}
     user.store()
     account = PasswordAccount(id='foo')
     account.set_password('foo')
@@ -2065,6 +2074,82 @@ def test_form_tracking_code_as_user(pub, nocache):
     resp = app.get('/code/%s/load' % tracking_code, headers={'User-agent': 'Googlebot'}, status=403)
 
 
+def test_form_phone_prefill(pub, nocache):
+    create_user(pub)
+    formdef = create_formdef()
+    formdef.data_class().wipe()
+    formdef.fields = [fields.StringField(id='0', label='string', prefill={'type': 'user', 'value': 'phone'})]
+    formdef.store()
+
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+    assert resp.pyquery('input#form_f0').val() == '01 23 45 67 89'
+    resp.forms[0]['f0'] = '+33987654321'
+    resp = resp.forms[0].submit('submit')
+    assert resp.pyquery('button.form-submit').val() == 'Submit'
+
+
+def test_form_phone_prefill_phone_fr_validation(pub, nocache):
+    create_user(pub)
+    pub.load_site_options()
+    if not pub.site_options.has_section('options'):
+        pub.site_options.add_section('options')
+    pub.site_options.set('options', 'default-country-code', 'FR')
+    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
+        pub.site_options.write(fd)
+    formdef = create_formdef()
+    formdef.data_class().wipe()
+    field = fields.StringField(id='0', label='string', prefill={'type': 'user', 'value': 'phone'})
+    field.validation = {'type': 'phone-fr'}
+    formdef.fields = [field]
+    formdef.store()
+
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+    assert resp.pyquery('input#form_f0').val() == '01 23 45 67 89'
+    resp.forms[0]['f0'] = '0987654321'
+    resp = resp.forms[0].submit('submit')
+    assert resp.pyquery('button.form-submit').val() == 'Submit'
+
+
+def test_form_phone_prefill_phone_validation(pub, nocache):
+    user = create_user(pub)
+    pub.load_site_options()
+    if not pub.site_options.has_section('options'):
+        pub.site_options.add_section('options')
+    pub.site_options.set('options', 'default-country-code', 'BE')
+    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
+        pub.site_options.write(fd)
+    formdef = create_formdef()
+    formdef.data_class().wipe()
+    field = fields.StringField(id='0', label='string', prefill={'type': 'user', 'value': 'phone'})
+    field.validation = {'type': 'phone'}
+    formdef.fields = [field]
+    formdef.store()
+
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+    assert resp.pyquery('input#form_f0').val() == '01 23 45 67 89'
+    resp.forms[0]['f0'] = '0987654321'
+    resp = resp.forms[0].submit('submit')
+    assert resp.pyquery('button.form-submit').val() == 'Submit'
+
+    user.form_data['_phone'] = '+3281000000'
+    user.store()
+
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+    assert resp.pyquery('input#form_f0').val() == '081 00 00 00'
+    resp.forms[0]['f0'] = '0987654321'
+    resp = resp.forms[0].submit('submit')
+    assert resp.pyquery('button.form-submit').val() == 'Submit'
+
+    user.form_data['_phone'] = '+99981000000'
+    user.store()
+
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+    assert resp.pyquery('input#form_f0').val() == '+99981000000'
+    resp.forms[0]['f0'] = '0987654321'
+    resp = resp.forms[0].submit('submit')
+    assert resp.pyquery('button.form-submit').val() == 'Submit'
+
+
 def test_form_tracking_code_prefill(pub, nocache):
     create_user(pub)
     formdef = create_formdef()
@@ -3368,8 +3453,6 @@ def test_form_page_profile_prefill(pub):
 def test_form_page_profile_first_name_prefill(pub):
     user = create_user(pub)
 
-    from wcs.admin.settings import UserFieldsFormDef
-
     user_formdef = UserFieldsFormDef(pub)
     user_formdef.fields = [
         fields.StringField(
@@ -5788,8 +5871,6 @@ def test_form_page_verified_prefill_error_page(pub):
 def test_form_page_profile_verified_date_prefill(pub):
     user = create_user(pub)
 
-    from wcs.admin.settings import UserFieldsFormDef
-
     user_formdef = UserFieldsFormDef(pub)
     user_formdef.fields.append(fields.DateField(id='_date', label='date', type='date'))
     user_formdef.store()
@@ -5840,8 +5921,6 @@ def test_form_page_profile_verified_date_prefill(pub):
 def test_form_page_profile_date_as_locked_string_prefill(pub):
     user = create_user(pub)
 
-    from wcs.admin.settings import UserFieldsFormDef
-
     user_formdef = UserFieldsFormDef(pub)
     user_formdef.fields.append(fields.DateField(id='_date', label='date', type='date'))
     user_formdef.store()
diff --git a/tests/test_prefill.py b/tests/test_prefill.py
index d7cbc3322..79c565297 100644
--- a/tests/test_prefill.py
+++ b/tests/test_prefill.py
@@ -1,4 +1,5 @@
 import datetime
+import os
 import shutil
 
 import pytest
@@ -20,6 +21,10 @@ def setup_module(module):
 
     req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
     pub._set_request(req)
+    pub.cfg['users'] = {
+        'field_phone': '_phone',
+    }
+    pub.write_cfg()
 
 
 @pytest.fixture
@@ -27,6 +32,7 @@ def user(request):
     pub.user_class.wipe()
     user = pub.user_class(name='user')
     user.email = 'test@example.net'
+    user.form_data = {'_phone': '+33123456789'}
     user.store()
     get_request()._user = user
     return user
@@ -66,12 +72,55 @@ def test_prefill_string_carddef():
     assert field.get_prefill_value() == ('{{cards|objects:"unknown"|first|get:"foo"}}', False)
 
 
-def test_prefill_user(user):
+def test_prefill_user_email(user):
     field = fields.Field()
     field.prefill = {'type': 'user', 'value': 'email'}
     assert field.get_prefill_value(user=get_request().user) == ('test@example.net', False)
 
 
+def test_prefill_user_phone(user):
+    field = fields.Field()
+    field.prefill = {'type': 'user', 'value': 'phone'}
+    assert field.get_prefill_value(user=get_request().user) == ('01 23 45 67 89', False)
+
+
+def test_prefill_user_phone_fr_validation(user):
+    pub.load_site_options()
+    if not pub.site_options.has_section('options'):
+        pub.site_options.add_section('options')
+    pub.site_options.set('options', 'default-country-code', 'FR')
+    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
+        pub.site_options.write(fd)
+    field = fields.Field()
+    field.validation = {'type': 'phone-fr'}
+    field.prefill = {'type': 'user', 'value': 'phone'}
+    assert field.get_prefill_value(user=get_request().user) == ('01 23 45 67 89', False)
+
+
+def test_prefill_user_phone_validation(user):
+    pub.load_site_options()
+    if not pub.site_options.has_section('options'):
+        pub.site_options.add_section('options')
+    pub.site_options.set('options', 'default-country-code', 'BE')
+    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
+        pub.site_options.write(fd)
+    field = fields.Field()
+    field.validation = {'type': 'phone'}
+    field.prefill = {'type': 'user', 'value': 'phone'}
+    assert field.get_prefill_value(user=get_request().user) == ('01 23 45 67 89', False)
+
+    user = get_request().user
+    user.form_data['_phone'] = '+3281000000'
+    user.store()
+
+    assert field.get_prefill_value(user=get_request().user) == ('081 00 00 00', False)
+
+    user.form_data['_phone'] = '+99981000000'
+    user.store()
+
+    assert field.get_prefill_value(user=get_request().user) == ('+99981000000', False)
+
+
 def test_prefill_user_attribute(user):
     from wcs.admin.settings import UserFieldsFormDef
 
diff --git a/wcs/fields.py b/wcs/fields.py
index 0ef9b97e4..5766ebc86 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -539,6 +539,12 @@ class Field:
             x = self.prefill.get('value')
             if x == 'email':
                 return (user.email, explicit_lock or 'email' in (user.verified_fields or []))
+            elif x == 'phone':
+                country_code = get_publisher().get_site_option('default-country-code')
+                if getattr(self, 'validation', None) and self.validation.get('type') == 'phone-fr':
+                    country_code = 'FR'
+                phone = user.get_formatted_phone(country_code=country_code)
+                return (phone, explicit_lock or 'phone' in (user.verified_fields or []))
             elif user.form_data:
                 userform = user.get_formdef()
                 for userfield in userform.fields:
diff --git a/wcs/users.py b/wcs/users.py
index 3df9a5e95..7e9dc71bb 100644
--- a/wcs/users.py
+++ b/wcs/users.py
@@ -19,6 +19,7 @@ import datetime
 import json
 import urllib.parse
 
+import phonenumbers
 from quixote import get_publisher
 
 import wcs.qommon.storage as st
@@ -85,6 +86,25 @@ class User(StorableObject):
         if changed:
             self.store()
 
+    def get_formatted_phone(self, country_code):
+        users_cfg = get_cfg('users', {})
+        phone = self.form_data.get('phone')
+        if not phone:
+            field_phone = users_cfg.get('field_phone')
+            phone = self.form_data.get(field_phone)
+        try:
+            pn = phonenumbers.parse(phone)
+        except phonenumbers.NumberParseException:
+            try:
+                pn = phonenumbers.parse(phone, country_code)
+            except phonenumbers.NumberParseException:
+                return phone
+
+        if phonenumbers.is_valid_number(pn):
+            return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.NATIONAL)
+        else:
+            return phone
+
     @invalidate_substitution_cache
     def store(self, *args, **kwargs):
         return super().store(*args, **kwargs)
-- 
2.38.1

