0001-general-support-prefilling-with-verified-profile-fie.patch
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 |
- |