Projet

Général

Profil

0001-general-support-prefilling-with-verified-profile-fie.patch

bon patch wcs. - Frédéric Péters, 29 juin 2016 10:44

Télécharger (16,7 ko)

Voir les différences:

Subject: [PATCH] general: support prefilling with "verified" profile fields
 (#12366)

 tests/test_form_pages.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test_prefill.py    | 56 +++++++++++++++++++++++++++++++-------------
 wcs/fields.py            | 13 ++++++-----
 wcs/forms/root.py        | 40 +++++++++++++++++++++++--------
 wcs/sql.py               | 18 ++++++++++----
 wcs/users.py             |  5 ++++
 6 files changed, 156 insertions(+), 37 deletions(-)
tests/test_form_pages.py
1387 1387
    assert 'widget-prefilled' in resp.body
1388 1388
    assert not 'Value has been automatically prefilled.' in resp.body
1389 1389

  
1390
def test_form_page_profile_prefill(pub):
1391
    user = create_user(pub)
1392
    formdef = create_formdef()
1393
    formdef.data_class().wipe()
1394
    formdef.fields = [fields.StringField(id='0', label='string',
1395
        prefill={'type': 'user', 'value': 'email'})]
1396
    formdef.store()
1397

  
1398
    resp = get_app(pub).get('/test/')
1399
    assert resp.forms[0]['f0'].value == ''
1400

  
1401
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
1402
    assert resp.forms[0]['f0'].value == 'foo@localhost'
1403

  
1390 1404
def test_form_page_formula_prefill(pub):
1391 1405
    user = create_user(pub)
1392 1406
    formdef = create_formdef()
......
2890 2904
    assert resp.headers['Set-Cookie'].startswith('wcs-')
2891 2905
    assert 'httponly' in resp.headers['Set-Cookie']
2892 2906
    assert 'secure' in resp.headers['Set-Cookie']
2907

  
2908
def test_form_page_profile_verified_prefill(pub):
2909
    user = create_user(pub)
2910
    formdef = create_formdef()
2911
    formdef.data_class().wipe()
2912
    formdef.fields = [fields.StringField(id='0', label='string',
2913
        prefill={'type': 'user', 'value': 'email'})]
2914
    formdef.store()
2915

  
2916
    resp = get_app(pub).get('/test/')
2917
    assert resp.form['f0'].value == ''
2918

  
2919
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
2920
    assert resp.form['f0'].value == 'foo@localhost'
2921
    assert not 'readonly' in resp.form['f0'].attrs
2922
    resp.form['f0'].value = 'Hello'
2923
    resp = resp.form.submit('submit')
2924
    assert 'Check values then click submit.' in resp.body
2925
    assert resp.form['f0'].value == 'Hello'
2926

  
2927
    user.verified_fields = ['email']
2928
    user.store()
2929

  
2930
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
2931
    assert resp.form['f0'].value == 'foo@localhost'
2932
    assert 'readonly' in resp.form['f0'].attrs
2933

  
2934
    resp.form['f0'].value = 'Hello' # try changing the value
2935
    resp = resp.form.submit('submit')
2936
    assert 'Check values then click submit.' in resp.body
2937
    assert resp.form['f0'].value == 'foo@localhost'  # it is reverted
2938

  
2939
    resp.form['f0'].value = 'Hello' # try again changing the value
2940
    resp = resp.form.submit('submit')
2941

  
2942
    assert formdef.data_class().count() == 1
2943
    assert formdef.data_class().select()[0].data['0'] == 'foo@localhost'
2944

  
2945
    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
2946
    assert resp.form['f0'].value == 'foo@localhost'
2947
    resp = resp.form.submit('submit')
2948
    assert 'Check values then click submit.' in resp.body
2949
    resp.form['f0'].value = 'Hello' # try changing
2950
    resp = resp.form.submit('previous')
2951
    assert 'readonly' in resp.form['f0'].attrs
2952
    assert not 'Check values then click submit.' in resp.body
2953
    assert resp.form['f0'].value == 'foo@localhost'
tests/test_prefill.py
1 1
import sys
2 2
import shutil
3 3

  
4
import pytest
5

  
4 6
from quixote import cleanup, get_request
5 7
from wcs.qommon.http_request import HTTPRequest
6 8
from wcs import fields
......
17 19
    req = HTTPRequest(None, {})
18 20
    pub._set_request(req)
19 21

  
22
@pytest.fixture
23
def user(request):
24
    pub.user_class.wipe()
20 25
    user = pub.user_class(name='user')
21 26
    user.id = 'user'
22 27
    user.email = 'test@example.net'
23

  
24
    req._user = user
25

  
28
    get_request()._user = user
29
    return user
26 30

  
27 31
def teardown_module(module):
28 32
    shutil.rmtree(pub.APP_DIR)
......
31 35
def test_prefill_string():
32 36
    field = fields.Field()
33 37
    field.prefill = {'type': 'string', 'value': 'test'}
38
    assert field.get_prefill_value() == ('test', False)
34 39

  
35
    assert field.get_prefill_value() == 'test'
40
def test_prefill_user(user):
41
    field = fields.Field()
42
    field.prefill = {'type': 'user', 'value': 'email'}
43
    assert field.get_prefill_value(user=get_request().user) == ('test@example.net', False)
36 44

  
45
def test_prefill_user_attribute(user):
46
    from wcs.admin.settings import UserFieldsFormDef
47
    formdef = UserFieldsFormDef(pub)
48
    formdef.fields = [fields.StringField(id='3', label='test', type='string', varname='plop')]
49
    formdef.store()
37 50

  
38
def test_prefill_user():
39 51
    field = fields.Field()
40
    field.prefill = {'type': 'user', 'value': 'email'}
52
    field.prefill = {'type': 'user', 'value': '3'}
53
    assert field.get_prefill_value(user=get_request().user) == (None, False)
41 54

  
42
    assert field.get_prefill_value(user=get_request().user) == 'test@example.net'
55
    user.form_data = {'3': 'Plop'}
56
    user.store()
57
    assert field.get_prefill_value(user=get_request().user) == ('Plop', False)
43 58

  
59
def test_prefill_verified_user_attribute(user):
60
    from wcs.admin.settings import UserFieldsFormDef
61
    formdef = UserFieldsFormDef(pub)
62
    formdef.fields = [fields.StringField(id='3', label='test', type='string', varname='plop')]
63
    formdef.store()
44 64

  
45
def test_prefill_formula():
46 65
    field = fields.Field()
47
    field.prefill = {'type': 'formula', 'value': 'str(2+5)'}
66
    field.prefill = {'type': 'user', 'value': '3'}
67
    assert field.get_prefill_value(user=get_request().user) == (None, False)
48 68

  
49
    assert field.get_prefill_value() == '7'
69
    user.form_data = {'3': 'Plop'}
70
    user.verified_fields = ['3']
71
    user.store()
72
    assert field.get_prefill_value(user=get_request().user) == ('Plop', True)
50 73

  
74
def test_prefill_formula():
75
    field = fields.Field()
76
    field.prefill = {'type': 'formula', 'value': 'str(2+5)'}
77
    assert field.get_prefill_value() == ('7', False)
51 78

  
52 79
def test_prefill_formula_with_error():
53 80
    field = fields.Field()
54 81
    field.prefill = {'type': 'formula', 'value': 'foobar'}
82
    assert field.get_prefill_value() == (None, False)
55 83

  
56
    assert field.get_prefill_value() is None
57

  
58

  
59
def test_prefill_formula_substition_variable():
84
def test_prefill_formula_substitution_variable():
60 85
    pub.substitutions.get_context_variables = lambda: {'test': 'value'}
61 86
    field = fields.Field()
62 87
    field.prefill = {'type': 'formula', 'value': 'test'}
63

  
64
    assert field.get_prefill_value() == 'value'
88
    assert field.get_prefill_value() == ('value', False)
wcs/fields.py
276 276
    def get_prefill_value(self, user=None):
277 277
        t = self.prefill.get('type')
278 278
        if t == 'string':
279
            return self.prefill.get('value')
279
            return (self.prefill.get('value'), False)
280 280

  
281 281
        elif t == 'user' and user:
282 282
            x = self.prefill.get('value')
283 283
            if x == 'email':
284
                return user.email
284
                return (user.email, 'email' in (user.verified_fields or []))
285 285
            elif user.form_data:
286 286
                userform = user.get_formdef()
287 287
                for userfield in userform.fields:
288 288
                    if userfield.id == x:
289
                        return user.form_data.get(x)
289
                        return (user.form_data.get(x),
290
                                str(userfield.id) in (user.verified_fields or []))
290 291

  
291 292
        elif t == 'formula':
292 293
            formula = self.prefill.get('value')
......
295 296
                    get_publisher().get_global_eval_dict(),
296 297
                    get_publisher().substitutions.get_context_variables())
297 298
                if ret:
298
                    return str(ret)
299
                    return (str(ret), False)
299 300
            except:
300 301
                pass
301 302

  
302 303
        elif t == 'geolocation':
303
            return None
304
            return (None, False)
304 305

  
305
        return None
306
        return (None, False)
306 307

  
307 308
    def get_prefill_attributes(self):
308 309
        t = self.prefill.get('type')
wcs/forms/root.py
379 379
                k = field.id
380 380
                v = None
381 381
                prefilled = False
382
                if data.has_key(k):
383
                    v = data[k]
384
                elif field.prefill:
382
                verified = False
383

  
384
                if field.prefill:
385 385
                    prefill_user = get_request().user
386 386
                    if get_request().is_in_backoffice():
387 387
                        prefill_user = get_publisher().substitutions.get_context_variables(
388 388
                                ).get('form_user')
389
                    v = field.get_prefill_value(user=prefill_user)
389
                    v, verified = field.get_prefill_value(user=prefill_user)
390

  
391
                    # always set additional attributes as they will be used for
392
                    # "live prefill", regardless of existing data.
393
                    form.get_widget('f%s' % k).prefill_attributes = field.get_prefill_attributes()
394

  
395
                if data.has_key(k):
396
                    v = data[k]
397
                elif field.prefill:
390 398
                    if get_request().is_in_backoffice() and (
391 399
                            field.prefill and field.prefill.get('type') == 'geoloc'):
392 400
                        # turn off prefilling from geolocation attributes if
......
394 402
                        v = None
395 403
                    if v:
396 404
                        prefilled = True
397
                        if field.prefill and field.prefill.get('type') != 'string':
405
                        if field.prefill and field.prefill.get('type') != 'string' and not verified:
398 406
                            # unless we prefilled with a fixed string, we
399 407
                            # inform the user the field value has been
400 408
                            # prefilled.
......
402 410
                                    _('Value has been automatically prefilled.'))
403 411
                        form.get_widget('f%s' % k).prefilled = True
404 412

  
405
                if field.prefill:
406
                    # always set additional attributes as they will be used for
407
                    # "live prefill", regardless of existing data.
408
                    form.get_widget('f%s' % k).prefill_attributes = field.get_prefill_attributes()
409

  
410 413
                if not prefilled and form.get_widget('f%s' % k):
411 414
                    form.get_widget('f%s' % k).clear_error()
412 415

  
......
414 417
                    if not isinstance(v, str) and field.convert_value_to_str:
415 418
                        v = field.convert_value_to_str(v)
416 419
                    form.get_widget('f%s' % k).set_value(v)
420
                    if verified:
421
                        form.get_widget('f%s' % k).readonly = 'readonly'
422
                        form.get_widget('f%s' % k).attrs['readonly'] = 'readonly'
417 423
                    req.form['f%s' % k] = v
418 424
                    one = True
419 425

  
......
622 628
        except TypeError:
623 629
            step = 0
624 630

  
631
        # reset verified fields, making sure the user cannot alter them.
632
        prefill_user = get_request().user
633
        if get_request().is_in_backoffice():
634
            prefill_user = get_publisher().substitutions.get_context_variables().get('form_user')
635
        if prefill_user:
636
            for field in self.formdef.fields:
637
                if not field.prefill:
638
                    continue
639
                if not 'f%s' % field.id in get_request().form:
640
                    continue
641
                v, verified = field.get_prefill_value(user=prefill_user)
642
                if verified:
643
                    get_request().form['f%s' % field.id] = v
644

  
625 645
        if step == 0:
626 646
            try:
627 647
                page_no = int(form.get_widget('page').parse())
wcs/sql.py
447 447
                                    roles text[],
448 448
                                    is_admin bool,
449 449
                                    anonymous bool,
450
                                    verified_fields text[],
450 451
                                    name_identifiers text[],
451 452
                                    lasso_dump text,
452 453
                                    last_seen timestamp)''' % table_name)
......
455 456
    existing_fields = set([x[0] for x in cur.fetchall()])
456 457

  
457 458
    needed_fields = set(['id', 'name', 'email', 'roles', 'is_admin',
458
        'anonymous', 'name_identifiers',
459
        'anonymous', 'name_identifiers', 'verified_fields',
459 460
        'lasso_dump', 'last_seen', 'fts'])
460 461

  
461 462
    from admin.settings import UserFieldsFormDef
......
489 490
        cur.execute('''CREATE INDEX %s_fts ON %s USING gin(fts)''' % (
490 491
                                table_name, table_name))
491 492

  
493
    if not 'verified_fields' in existing_fields:
494
        cur.execute('ALTER TABLE %s ADD COLUMN verified_fields text[]' % table_name)
495

  
492 496
    # delete obsolete fields
493 497
    for field in (existing_fields - needed_fields):
494 498
        cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field))
......
1395 1399
        ('is_admin', 'bool'),
1396 1400
        ('anonymous', 'bool'),
1397 1401
        ('name_identifiers', 'varchar[]'),
1402
        ('verified_fields', 'varchar[]'),
1398 1403
        ('lasso_dump', 'text'),
1399 1404
        ('last_seen', 'timestamp')
1400 1405
    ]
......
1404 1409
    def __init__(self, name=None):
1405 1410
        self.name = name
1406 1411
        self.name_identifiers = []
1412
        self.verified_fields = []
1407 1413
        self.roles = []
1408 1414

  
1409 1415
    @guard_postgres
......
1415 1421
                'is_admin': self.is_admin,
1416 1422
                'anonymous': self.anonymous,
1417 1423
                'name_identifiers': self.name_identifiers,
1424
                'verified_fields': self.verified_fields,
1418 1425
                'lasso_dump': self.lasso_dump,
1419 1426
                'last_seen': None,
1420 1427
        }
......
1480 1487
    def _row2ob(cls, row):
1481 1488
        o = cls()
1482 1489
        (o.id, o.name, o.email, o.roles, o.is_admin, o.anonymous,
1483
         o.name_identifiers, o.lasso_dump,
1484
         o.last_seen) = tuple(row[:9])
1490
         o.name_identifiers, o.verified_fields, o.lasso_dump,
1491
         o.last_seen) = tuple(row[:10])
1485 1492
        if o.last_seen:
1486 1493
            o.last_seen = time.mktime(o.last_seen.timetuple())
1487 1494
        if o.roles:
......
1843 1850
    return result
1844 1851

  
1845 1852

  
1846
SQL_LEVEL = 15
1853
SQL_LEVEL = 16
1847 1854

  
1848 1855
def migrate_global_views(conn, cur):
1849 1856
    cur.execute('''SELECT COUNT(*) FROM information_schema.tables
......
1889 1896
        # 14: add criticality_level to tables & views
1890 1897
        # 15: add geolocation to formdata
1891 1898
        migrate_views(conn, cur)
1892
    if sql_level < 12:
1899
    if sql_level < 16:
1893 1900
        # 3: introduction of _structured for user fields
1894 1901
        # 4: removal of identification_token
1895 1902
        # 12: (first part) add fts to users
1903
        # 16: add verified_fields to users
1896 1904
        do_user_table()
1897 1905
    if sql_level < 6:
1898 1906
        # 6: add actions_roles_array to tables and views
wcs/users.py
33 33
    anonymous = False
34 34
    form_data = None  # dumping ground for custom fields
35 35

  
36
    verified_fields = None
36 37
    name_identifiers = None
37 38
    lasso_dump = None
38 39

  
......
42 43
        StorableObject.__init__(self)
43 44
        self.name = name
44 45
        self.name_identifiers = []
46
        self.verified_fields = []
45 47
        self.roles = []
46 48

  
47 49
    def get_hash(self):
......
71 73
                    changed = True
72 74
                    break
73 75

  
76
        if not self.verified_fields:
77
            self.verified_fields = []
78

  
74 79
        if changed:
75 80
            self.store()
76 81

  
77
-