0001-general-remove-support-for-advanced-fargo-features-1.patch
tests/test_backoffice_pages.py | ||
---|---|---|
10 | 10 | |
11 | 11 |
import pytest |
12 | 12 |
from webtest import Upload |
13 |
import mock |
|
14 | 13 | |
15 | 14 |
from quixote import cleanup, get_publisher |
16 | 15 |
from wcs.qommon import errors, sessions |
17 | 16 |
from qommon.ident.password_accounts import PasswordAccount |
18 |
from qommon.misc import json_loads |
|
19 | 17 |
from wcs.qommon.http_request import HTTPRequest |
20 | 18 |
from wcs.roles import Role |
21 | 19 |
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem, |
... | ... | |
2500 | 2498 |
assert resp.body == 'foo(%s);' % menu_json_str |
2501 | 2499 |
assert resp.headers['content-type'] == 'application/javascript' |
2502 | 2500 | |
2503 |
def test_backoffice_file_field_fargo_no_metadata(pub, fargo_url): |
|
2504 |
document_type = { |
|
2505 |
'id': 'justificatif-de-domicile', |
|
2506 |
'fargo': True, |
|
2507 |
'mimetypes': ['application/pdf'], |
|
2508 |
'label': 'Justificatif de domicile', |
|
2509 |
} |
|
2510 |
user = create_user(pub, is_admin=True) |
|
2511 |
user.name_identifiers = ['12345'] |
|
2512 |
user.store() |
|
2513 |
FormDef.wipe() |
|
2514 |
formdef = FormDef() |
|
2515 |
formdef.name = 'form title' |
|
2516 |
formdef.fields = [fields.FileField( |
|
2517 |
id='0', label='1st field', type='file', |
|
2518 |
document_type=document_type)] |
|
2519 |
formdef.store() |
|
2520 |
formdef.data_class().wipe() |
|
2521 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
|
2522 |
digest = hashlib.sha256('%PDF-1.4').hexdigest() |
|
2523 |
app = login(get_app(pub)) |
|
2524 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
2525 |
resp = app.get('/form-title/') |
|
2526 |
assert fargo_get.call_count == 0 |
|
2527 |
resp.forms[0]['f0$file'] = upload |
|
2528 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
2529 |
resp = resp.forms[0].submit('submit') |
|
2530 |
assert fargo_get.call_count == 0 |
|
2531 |
assert 'Check values then click submit.' in resp.body |
|
2532 |
resp = resp.forms[0].submit('submit').follow() |
|
2533 |
assert formdef.data_class().count() == 1 |
|
2534 |
formdata = formdef.data_class().select()[0] |
|
2535 |
form_id = formdata.id |
|
2536 |
assert not hasattr(formdata.data['0'], 'metadata') |
|
2537 |
assert not '0_structured' in formdata.data |
|
2538 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
2539 |
assert not 'Validate' in resp.body |
|
2540 |
with mock.patch('wcs.portfolio.fargo_post_json') as fargo_post_json: |
|
2541 |
resp = app.get('/backoffice/management/form-title/%s/validate?field_id=0' % form_id) |
|
2542 |
assert fargo_post_json.call_count == 0 |
|
2543 |
resp = resp.follow() |
|
2544 |
assert not 'Valid ' in resp.body |
|
2545 |
assert not 'Validate' in resp.body |
|
2546 | ||
2547 | ||
2548 |
def test_backoffice_file_field_validation(pub, fargo_url): |
|
2549 |
document_type = { |
|
2550 |
'id': 'justificatif-de-domicile', |
|
2551 |
'fargo': True, |
|
2552 |
'mimetypes': ['application/pdf'], |
|
2553 |
'label': 'Justificatif de domicile', |
|
2554 |
'metadata': [ |
|
2555 |
{'label': 'Nom', 'varname': 'nom', 'type': 'string'}, |
|
2556 |
{'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'}, |
|
2557 |
{'label': 'Numéro', 'varname': 'numero', 'type': 'string'}, |
|
2558 |
{'label': 'Rue', 'varname': 'rue', 'type': 'string'}, |
|
2559 |
{'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'}, |
|
2560 |
{'label': 'Ville', 'varname': 'ville', 'type': 'string'}, |
|
2561 |
], |
|
2562 |
} |
|
2563 |
metadata = { |
|
2564 |
'nom': 'Doe', |
|
2565 |
'prenoms': 'John', |
|
2566 |
'numero': '169', |
|
2567 |
'rue': 'rue du château', |
|
2568 |
'code-postal': '75014', |
|
2569 |
'ville': 'PARIS', |
|
2570 |
} |
|
2571 |
user = create_user(pub, is_admin=True) |
|
2572 |
user.name_identifiers = ['12345'] |
|
2573 |
user.store() |
|
2574 |
FormDef.wipe() |
|
2575 |
formdef = FormDef() |
|
2576 |
formdef.name = 'form title' |
|
2577 |
formdef.fields = [fields.FileField( |
|
2578 |
id='0', label='1st field', type='file', |
|
2579 |
document_type=document_type)] |
|
2580 |
formdef.store() |
|
2581 |
formdef.data_class().wipe() |
|
2582 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
|
2583 |
digest = hashlib.sha256('%PDF-1.4').hexdigest() |
|
2584 |
app = login(get_app(pub)) |
|
2585 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
2586 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
2587 |
resp = app.get('/form-title/') |
|
2588 |
fargo_get.assert_called_once_with( |
|
2589 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
2590 |
resp.forms[0]['f0$file'] = upload |
|
2591 |
for i, meta_field in enumerate(document_type['metadata']): |
|
2592 |
resp.forms[0]['f0$f%s' % i] = metadata[meta_field['varname']] |
|
2593 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
2594 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
2595 |
resp = resp.forms[0].submit('submit') |
|
2596 |
fargo_get.assert_called_once_with( |
|
2597 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
2598 |
for key in metadata: |
|
2599 |
assert 'value="%s"' % metadata[key] in resp.body |
|
2600 |
assert 'Check values then click submit.' in resp.body |
|
2601 |
resp = resp.forms[0].submit('submit').follow() |
|
2602 |
for metadata_field in document_type['metadata']: |
|
2603 | ||
2604 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']]) |
|
2605 |
assert fragment in resp.body |
|
2606 |
assert formdef.data_class().count() == 1 |
|
2607 |
formdata = formdef.data_class().select()[0] |
|
2608 |
form_id = formdata.id |
|
2609 |
assert formdata.data['0'].metadata == metadata |
|
2610 |
assert formdata.data['0_structured'] == metadata |
|
2611 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
2612 |
assert 'Validate' in resp.body |
|
2613 |
for metadata_field in document_type['metadata']: |
|
2614 | ||
2615 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']]) |
|
2616 |
assert fragment in resp.body |
|
2617 |
with mock.patch('wcs.portfolio.fargo_post_json') as fargo_post_json: |
|
2618 |
payload = { |
|
2619 |
'user_nameid': '12345', |
|
2620 |
'origin': 'example.net', |
|
2621 |
'creator': 'admin', |
|
2622 |
'content_hash': digest, |
|
2623 |
} |
|
2624 |
payload.update(metadata) |
|
2625 |
result = { |
|
2626 |
'result': 1, |
|
2627 |
'data': payload.copy() |
|
2628 |
} |
|
2629 |
result['data'].update({ |
|
2630 |
'url': 'zob', |
|
2631 |
'created': '1970-01-01T10:10:10Z', |
|
2632 |
'start': '1970-01-01', |
|
2633 |
'end': '1978-01-01', |
|
2634 |
'display': 'John Doe, 169 rue du château, 75014 PARIS' |
|
2635 |
}) |
|
2636 |
fargo_post_json.return_value = 201, result |
|
2637 |
resp = app.get('/backoffice/management/form-title/%s/validate?field_id=0' % form_id) |
|
2638 |
assert fargo_post_json.call_count == 1 |
|
2639 |
assert fargo_post_json.call_args[0][0] == '/api/validation/justificatif-de-domicile/' |
|
2640 |
assert fargo_post_json.call_args[0][1] == payload |
|
2641 |
resp = resp.follow() |
|
2642 | ||
2643 |
assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body |
|
2644 | ||
2645 | ||
2646 |
def test_backoffice_file_validation_no_upload(pub, fargo_url, fargo_secret): |
|
2647 |
document_type = { |
|
2648 |
'id': 'justificatif-de-domicile', |
|
2649 |
'fargo': True, |
|
2650 |
'mimetypes': ['application/pdf'], |
|
2651 |
'label': 'Justificatif de domicile', |
|
2652 |
'metadata': [ |
|
2653 |
{'label': 'Nom', 'varname': 'nom', 'type': 'string'}, |
|
2654 |
{'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'}, |
|
2655 |
{'label': 'Numéro', 'varname': 'numero', 'type': 'string'}, |
|
2656 |
{'label': 'Rue', 'varname': 'rue', 'type': 'string'}, |
|
2657 |
{'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'}, |
|
2658 |
{'label': 'Ville', 'varname': 'ville', 'type': 'string'}, |
|
2659 |
], |
|
2660 |
} |
|
2661 |
metadata = { |
|
2662 |
'nom': 'Doe', |
|
2663 |
'prenoms': 'John', |
|
2664 |
'numero': '169', |
|
2665 |
'rue': 'rue du château', |
|
2666 |
'code-postal': '75014', |
|
2667 |
'ville': 'PARIS', |
|
2668 |
} |
|
2669 |
validation = { |
|
2670 |
'url': 'zob', |
|
2671 |
'creator': 'admin', |
|
2672 |
'created': '1970-01-01T10:10:10Z', |
|
2673 |
'start': '1970-01-01', |
|
2674 |
'end': '1978-01-01', |
|
2675 |
'display': 'John Doe, 169 rue du château, 75014 PARIS' |
|
2676 |
} |
|
2677 |
validation.update(metadata) |
|
2678 | ||
2679 |
user = create_user(pub, is_admin=True) |
|
2680 |
user.name_identifiers = ['12345'] |
|
2681 |
user.store() |
|
2682 |
FormDef.wipe() |
|
2683 |
formdef = FormDef() |
|
2684 |
formdef.name = 'form title' |
|
2685 |
formdef.fields = [fields.FileField( |
|
2686 |
id='0', label='1st field', type='file', |
|
2687 |
document_type=document_type)] |
|
2688 |
formdef.store() |
|
2689 |
formdef.data_class().wipe() |
|
2690 |
app = login(get_app(pub)) |
|
2691 |
return_value = {'result': 1, 'data': {'results': [validation]}} |
|
2692 |
with mock.patch('wcs.portfolio.http_get_page') as http_get_page: |
|
2693 |
http_get_page.return_value = None, 200, json.dumps(return_value), None |
|
2694 |
resp = app.get('/form-title/') |
|
2695 |
assert http_get_page.call_count == 1 |
|
2696 |
assert http_get_page.call_args[0][0].startswith( |
|
2697 |
'http://fargo.example.net/api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
2698 |
assert validation['display'] in resp.body |
|
2699 |
resp.forms[0]['f0$validation_url'] = 'zob' |
|
2700 |
for i, meta_field in enumerate(document_type['metadata']): |
|
2701 |
resp.forms[0]['f0$f%s' % i] = metadata[meta_field['varname']] |
|
2702 |
with mock.patch('wcs.portfolio.http_get_page') as http_get_page: |
|
2703 |
return_value2 = { |
|
2704 |
'result': 1, |
|
2705 |
'data': validation, |
|
2706 |
} |
|
2707 | ||
2708 |
def side_effect(url): |
|
2709 |
if url.startswith('http://fargo.example.net/zob'): |
|
2710 |
return None, 200, json.dumps(return_value2), None |
|
2711 |
else: |
|
2712 |
return None, 200, json.dumps(return_value), None |
|
2713 |
http_get_page.side_effect = side_effect |
|
2714 |
resp = resp.forms[0].submit('submit') |
|
2715 |
assert http_get_page.call_count == 3 |
|
2716 |
assert 'api/validation/justificatif-de-domicile/?user_nameid=12345' in http_get_page.call_args_list[0][0][0] |
|
2717 |
assert http_get_page.call_args_list[1][0][0].startswith('http://fargo.example.net/zob') |
|
2718 |
assert http_get_page.call_args_list[2][0][0].startswith('http://fargo.example.net/zob') |
|
2719 |
for key in metadata: |
|
2720 |
assert 'value="%s"' % metadata[key] in resp.body |
|
2721 |
assert 'Check values then click submit.' in resp.body |
|
2722 |
resp = resp.forms[0].submit('submit').follow() |
|
2723 |
assert formdef.data_class().count() == 1 |
|
2724 |
formdata = formdef.data_class().select()[0] |
|
2725 |
form_id = formdata.id |
|
2726 |
assert formdata.data['0'].metadata == validation |
|
2727 |
assert formdata.data['0_structured'] == validation |
|
2728 |
for metadata_field in document_type['metadata']: |
|
2729 | ||
2730 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']]) |
|
2731 |
assert fragment in resp.body |
|
2732 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
2733 |
assert not 'Validate' in resp.body |
|
2734 |
for metadata_field in document_type['metadata']: |
|
2735 | ||
2736 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']]) |
|
2737 |
assert fragment in resp.body |
|
2738 |
assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body |
|
2739 | ||
2740 | ||
2741 | 2501 |
def test_360_user_view(pub): |
2742 | 2502 |
if not pub.is_using_postgresql(): |
2743 | 2503 |
pytest.skip('this requires SQL') |
tests/test_fields.py | ||
---|---|---|
6 | 6 |
import pytest |
7 | 7 | |
8 | 8 |
from quixote import cleanup |
9 |
from quixote.http_request import HTTPRequest, Upload |
|
9 |
from quixote.http_request import Upload |
|
10 |
from qommon.http_request import HTTPRequest |
|
10 | 11 |
from wcs.qommon import sessions |
11 | 12 |
from wcs import fields |
12 | 13 |
from wcs.qommon.form import Form |
tests/test_form_pages.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | 1 |
import json |
3 | 2 |
import pytest |
4 | 3 |
import hashlib |
... | ... | |
3074 | 3073 |
assert formdef.data_class().select()[0].data['3'] == '1' |
3075 | 3074 |
assert formdef.data_class().select()[0].data['3_display'] == 'barbar' |
3076 | 3075 | |
3077 |
def test_file_field_validation(pub, fargo_url): |
|
3078 |
document_type = { |
|
3079 |
'id': 'justificatif-de-domicile', |
|
3080 |
'fargo': True, |
|
3081 |
'label': 'Justificatif de domicile', |
|
3082 |
'metadata': [ |
|
3083 |
{'label': 'Nom', 'varname': 'nom', 'type': 'string'}, |
|
3084 |
{'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'}, |
|
3085 |
{'label': 'Numéro', 'varname': 'numero', 'type': 'string'}, |
|
3086 |
{'label': 'Rue', 'varname': 'rue', 'type': 'string'}, |
|
3087 |
{'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'}, |
|
3088 |
{'label': 'Ville', 'varname': 'ville', 'type': 'string'}, |
|
3089 |
], |
|
3090 |
} |
|
3091 |
metadata = { |
|
3092 |
'nom': 'Doe', |
|
3093 |
'prenoms': 'John', |
|
3094 |
'numero': '169', |
|
3095 |
'rue': 'rue du château', |
|
3096 |
'code-postal': '75014', |
|
3097 |
'ville': 'PARIS', |
|
3098 |
} |
|
3099 |
user = create_user(pub) |
|
3100 |
user.name_identifiers = ['12345'] |
|
3101 |
user.store() |
|
3102 |
FormDef.wipe() |
|
3103 |
formdef = FormDef() |
|
3104 |
formdef.name = 'form title' |
|
3105 |
formdef.fields = [fields.FileField( |
|
3106 |
id='0', label='1st field', type='file', |
|
3107 |
document_type=document_type) |
|
3108 |
] |
|
3109 |
formdef.store() |
|
3110 |
formdef.data_class().wipe() |
|
3111 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
|
3112 |
app = login(get_app(pub), username='foo', password='foo') |
|
3113 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
3114 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
3115 |
resp = app.get('/form-title/') |
|
3116 |
fargo_get.assert_called_once_with( |
|
3117 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
3118 |
resp.forms[0]['f0$file'] = upload |
|
3119 |
for i, meta_field in enumerate(document_type['metadata']): |
|
3120 |
resp.forms[0]['f0$f%s' % i] = metadata[meta_field['varname']] |
|
3121 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
3122 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
3123 |
resp = resp.forms[0].submit('submit') |
|
3124 |
fargo_get.assert_called_once_with( |
|
3125 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
3126 |
assert 'Check values then click submit.' in resp.body |
|
3127 |
resp = resp.forms[0].submit('submit') |
|
3128 |
assert resp.status_int == 302 |
|
3129 |
resp = resp.follow() |
|
3130 |
assert 'The form has been recorded' in resp.body |
|
3131 |
assert formdef.data_class().count() == 1 |
|
3132 |
formdata = formdef.data_class().select()[0] |
|
3133 |
assert formdata.data['0'].metadata == metadata |
|
3134 |
assert formdata.data['0_structured'] == metadata |
|
3135 | ||
3136 | ||
3137 |
def test_file_field_fargo_no_metadata(pub, fargo_url): |
|
3138 |
document_type = { |
|
3139 |
'id': 'justificatif-de-domicile', |
|
3140 |
'fargo': True, |
|
3141 |
'label': 'Justificatif de domicile', |
|
3142 |
} |
|
3143 |
user = create_user(pub) |
|
3144 |
user.name_identifiers = ['12345'] |
|
3145 |
user.store() |
|
3146 |
FormDef.wipe() |
|
3147 |
formdef = FormDef() |
|
3148 |
formdef.name = 'form title' |
|
3149 |
formdef.fields = [fields.FileField( |
|
3150 |
id='0', label='1st field', type='file', |
|
3151 |
document_type=document_type) |
|
3152 |
] |
|
3153 |
formdef.store() |
|
3154 |
formdef.data_class().wipe() |
|
3155 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
|
3156 |
app = login(get_app(pub), username='foo', password='foo') |
|
3157 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
3158 |
resp = app.get('/form-title/') |
|
3159 |
assert fargo_get.call_count == 0 |
|
3160 |
resp.forms[0]['f0$file'] = upload |
|
3161 |
with mock.patch('wcs.portfolio.fargo_get') as fargo_get: |
|
3162 |
resp = resp.forms[0].submit('submit') |
|
3163 |
assert fargo_get.call_count == 0 |
|
3164 |
assert 'Check values then click submit.' in resp.body |
|
3165 |
resp = resp.forms[0].submit('submit') |
|
3166 |
assert resp.status_int == 302 |
|
3167 |
resp = resp.follow() |
|
3168 |
assert 'The form has been recorded' in resp.body |
|
3169 |
assert formdef.data_class().count() == 1 |
|
3170 |
formdata = formdef.data_class().select()[0] |
|
3171 |
assert not hasattr(formdata.data['0'], 'metadata') |
|
3172 |
assert not '0_structured' in formdata.data |
|
3173 | ||
3174 | 3076 |
def test_form_string_field_autocomplete(pub): |
3175 | 3077 |
formdef = create_formdef() |
3176 | 3078 |
formdef.fields = [fields.StringField(id='0', label='string', type='string', required=False)] |
tests/test_formdata.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | 1 |
import pytest |
3 | 2 |
import sys |
4 | 3 |
import shutil |
... | ... | |
15 | 14 |
from wcs.wf.anonymise import AnonymiseWorkflowStatusItem |
16 | 15 |
from wcs.wf.wscall import JournalWsCallErrorPart |
17 | 16 |
from wcs.wf.register_comment import JournalEvolutionPart |
18 |
from wcs.qommon.form import NoUpload |
|
19 |
import mock |
|
20 | 17 | |
21 | 18 |
from utilities import create_temporary_pub, clean_temporary_pub |
22 | 19 | |
... | ... | |
159 | 156 |
assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0') |
160 | 157 |
assert isinstance(substvars.get('form_var_foo_raw'), Upload) |
161 | 158 | |
162 |
formdata.data = {'0': None} |
|
163 |
substvars = formdata.get_substitution_variables() |
|
164 |
assert substvars['form_var_foo'] is None |
|
165 |
assert substvars['form_var_foo_raw'] is None |
|
166 |
assert substvars['form_var_foo_url'] is None |
|
167 | ||
168 |
formdata.data = {} |
|
169 |
substvars = formdata.get_substitution_variables() |
|
170 |
assert substvars['form_var_foo'] is None |
|
171 |
assert substvars['form_var_foo_raw'] is None |
|
172 |
assert substvars['form_var_foo_url'] is None |
|
173 | ||
174 |
def test_file_field_fargo_no_metadata(pub): |
|
175 |
document_types = { |
|
176 |
'justificatif-de-domicile': { |
|
177 |
'id': 'justificatif-de-domicile', |
|
178 |
'fargo': True, |
|
179 |
'label': 'Justificatif de domicile', |
|
180 |
} |
|
181 |
} |
|
182 |
formdef.data_class().wipe() |
|
183 |
formdef.fields = [fields.FileField(id='0', label='file', varname='foo', |
|
184 |
document_type=document_types['justificatif-de-domicile'])] |
|
185 |
formdef.store() |
|
186 |
formdata = formdef.data_class()() |
|
187 |
upload = Upload('test.txt', 'text/plain', 'ascii') |
|
188 |
upload.receive(['first line', 'second line']) |
|
189 |
formdata.data = {'0': upload} |
|
190 |
formdata.id = 1 |
|
191 |
substvars = formdata.get_substitution_variables() |
|
192 |
assert substvars.get('form_var_foo') == 'test.txt' |
|
193 |
assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0') |
|
194 |
assert isinstance(substvars.get('form_var_foo_raw'), Upload) |
|
195 | ||
196 | ||
197 |
def test_file_field_with_metadata(pub): |
|
198 |
document_types = { |
|
199 |
'justificatif-de-domicile': { |
|
200 |
'id': 'justificatif-de-domicile', |
|
201 |
'fargo': True, |
|
202 |
'label': 'Justificatif de domicile', |
|
203 |
'metadata': [ |
|
204 |
{'label': 'Nom', 'varname': 'nom', 'type': 'string'}, |
|
205 |
{'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'}, |
|
206 |
{'label': 'Numéro', 'varname': 'numero', 'type': 'string'}, |
|
207 |
{'label': 'Rue', 'varname': 'rue', 'type': 'string'}, |
|
208 |
{'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'}, |
|
209 |
{'label': 'Ville', 'varname': 'ville', 'type': 'string'}, |
|
210 |
], |
|
211 |
} |
|
212 |
} |
|
213 |
formdef.data_class().wipe() |
|
214 |
formdef.fields = [fields.FileField(id='0', label='file', varname='foo', |
|
215 |
document_type=document_types['justificatif-de-domicile'])] |
|
216 |
formdef.store() |
|
217 |
formdata = formdef.data_class()() |
|
218 |
upload = Upload('test.txt', 'text/plain', 'ascii') |
|
219 |
upload.receive(['first line', 'second line']) |
|
220 |
upload.metadata = { |
|
221 |
'nom': 'Doe', |
|
222 |
'prenoms': 'John', |
|
223 |
'numero': '169', |
|
224 |
'rue': 'rue du château', |
|
225 |
'code-postal': '75014', |
|
226 |
'ville': 'PARIS', |
|
227 |
} |
|
228 |
formdata.data = {'0': upload, '0_structured': upload.metadata} |
|
229 |
formdata.id = 1 |
|
230 |
substvars = formdata.get_substitution_variables() |
|
231 |
assert substvars.get('form_var_foo') == 'test.txt' |
|
232 |
assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0') |
|
233 |
assert isinstance(substvars.get('form_var_foo_raw'), Upload) |
|
234 |
for key in upload.metadata: |
|
235 |
assert substvars.get('form_var_foo_%s' % key) == upload.metadata[key] |
|
236 | ||
237 | ||
238 |
def test_file_field_no_file_with_metadata(pub): |
|
239 |
document_types = { |
|
240 |
'justificatif-de-domicile': { |
|
241 |
'id': 'justificatif-de-domicile', |
|
242 |
'fargo': True, |
|
243 |
'label': 'Justificatif de domicile', |
|
244 |
'metadata': [ |
|
245 |
{'label': 'Nom', 'varname': 'nom', 'type': 'string'}, |
|
246 |
{'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'}, |
|
247 |
{'label': 'Numéro', 'varname': 'numero', 'type': 'string'}, |
|
248 |
{'label': 'Rue', 'varname': 'rue', 'type': 'string'}, |
|
249 |
{'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'}, |
|
250 |
{'label': 'Ville', 'varname': 'ville', 'type': 'string'}, |
|
251 |
], |
|
252 |
} |
|
253 |
} |
|
254 |
formdef.data_class().wipe() |
|
255 |
formdef.fields = [fields.FileField(id='0', label='file', varname='foo', |
|
256 |
document_type=document_types['justificatif-de-domicile'])] |
|
257 |
formdef.store() |
|
258 |
formdata = formdef.data_class()() |
|
259 |
metadata = { |
|
260 |
'nom': 'Doe', |
|
261 |
'prenoms': 'John', |
|
262 |
'numero': '169', |
|
263 |
'rue': 'rue du château', |
|
264 |
'code-postal': '75014', |
|
265 |
'ville': 'PARIS', |
|
266 |
} |
|
267 |
with mock.patch('wcs.portfolio.get_validation', return_value=metadata): |
|
268 |
upload = NoUpload('http://whatever.com/') |
|
269 |
formdata.data = {'0': upload, '0_structured': upload.metadata} |
|
270 |
formdata.id = 1 |
|
271 |
substvars = formdata.get_substitution_variables() |
|
272 |
assert isinstance(substvars.get('form_var_foo'), NoUpload) |
|
273 |
assert substvars['form_var_foo_url'] is None |
|
274 |
assert isinstance(substvars.get('form_var_foo_raw'), NoUpload) |
|
275 |
for key in upload.metadata: |
|
276 |
assert substvars.get('form_var_foo_%s' % key) == upload.metadata[key] |
|
277 | ||
278 | ||
279 | 159 |
def test_get_submitter(pub): |
280 | 160 |
formdef.data_class().wipe() |
281 | 161 |
formdef.fields = [fields.StringField(id='0', label='email', varname='foo', |
tests/test_formdef.py | ||
---|---|---|
6 | 6 | |
7 | 7 |
import pytest |
8 | 8 | |
9 |
from mock import patch |
|
10 | ||
11 | 9 |
from quixote import cleanup |
12 | 10 |
from wcs import formdef |
13 | 11 |
from wcs.formdef import FormDef |
... | ... | |
170 | 168 |
'application/vnd.ms-excel', |
171 | 169 |
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], |
172 | 170 |
'label': 'Documents'}} |
173 |
with patch('wcs.portfolio.get_document_types') as get_document_types: |
|
174 |
get_document_types.return_value = { |
|
175 |
'justificatif-de-domicile': { |
|
176 |
'id': 'justificatif-de-domicile', |
|
177 |
'label': 'Justificatif de domicile', |
|
178 |
'fargo': True, |
|
179 |
}, |
|
180 |
} |
|
181 |
FormDef.wipe() |
|
182 |
formdef = FormDef() |
|
183 |
formdef.name = 'foo' |
|
184 |
formdef.fields = [ |
|
185 |
FileField(type='file', id='1', label='images & docs'), |
|
186 |
FileField(type='file', id='2', label='images')] |
|
187 |
formdef.fields[0].__dict__['file_type'] = ['image/*', 'application/pdf,application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.spreadsheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'] |
|
188 |
formdef.fields[1].__dict__['file_type'] = ['image/*'] |
|
189 |
formdef.store() |
|
190 |
formdef = FormDef.get(1) |
|
191 |
assert 'file_type' not in formdef.fields[0].__dict__ |
|
192 |
assert formdef.fields[0].document_type |
|
193 |
assert formdef.fields[0].document_type['id'] == '_legacy' |
|
194 |
assert formdef.fields[0].document_type['mimetypes'] == ['image/*', 'application/pdf,application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.spreadsheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'] |
|
195 |
assert formdef.fields[1].document_type['label'] == 'Image files' |
|
196 |
assert formdef.fields[0].document_type['label'] == 'Image files, Documents' |
|
171 | ||
172 |
FormDef.wipe() |
|
173 |
formdef = FormDef() |
|
174 |
formdef.name = 'foo' |
|
175 |
formdef.fields = [ |
|
176 |
FileField(type='file', id='1', label='images & docs'), |
|
177 |
FileField(type='file', id='2', label='images')] |
|
178 |
formdef.fields[0].__dict__['file_type'] = ['image/*', 'application/pdf,application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.spreadsheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'] |
|
179 |
formdef.fields[1].__dict__['file_type'] = ['image/*'] |
|
180 |
formdef.store() |
|
181 |
formdef = FormDef.get(1) |
|
182 |
assert 'file_type' not in formdef.fields[0].__dict__ |
|
183 |
assert formdef.fields[0].document_type |
|
184 |
assert formdef.fields[0].document_type['id'] == '_legacy' |
|
185 |
assert formdef.fields[0].document_type['mimetypes'] == ['image/*', 'application/pdf,application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.spreadsheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'] |
|
186 |
assert formdef.fields[1].document_type['label'] == 'Image files' |
|
187 |
assert formdef.fields[0].document_type['label'] == 'Image files, Documents' |
tests/test_formdef_import.py | ||
---|---|---|
243 | 243 |
assert_xml_import_export_works(formdef) |
244 | 244 |
assert_json_import_export_works(formdef, include_id=True) |
245 | 245 |
assert_json_import_export_works(formdef) |
246 |
formdef = FormDef() |
|
247 |
formdef.name = 'foo' |
|
248 |
formdef.fields = [fields.FileField(type='file', id='1', document_type={ |
|
249 |
'id': 'justificatif-de-domicile', |
|
250 |
'fargo': True, |
|
251 |
'mimetypes': ['application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/*'], |
|
252 |
'metadata': [ |
|
253 |
{'varname': 'nom', 'label': 'Nom', 'type': 'string'}, |
|
254 |
{'varname': 'rue', 'label': 'Rue', 'type': 'string'}, |
|
255 |
] |
|
256 |
})] |
|
257 |
assert_xml_import_export_works(formdef, include_id=True) |
|
258 |
assert_xml_import_export_works(formdef) |
|
259 |
assert_json_import_export_works(formdef, include_id=True) |
|
260 |
assert_json_import_export_works(formdef) |
|
261 | 246 | |
262 | 247 |
def test_invalid_field_type(): |
263 | 248 |
formdef = FormDef() |
wcs/backoffice/management.py | ||
---|---|---|
1838 | 1838 | |
1839 | 1839 | |
1840 | 1840 |
class FormBackOfficeStatusPage(FormStatusPage): |
1841 |
_q_exports_orig = ['', 'download', 'json', 'action', 'inspect', 'validate']
|
|
1841 |
_q_exports_orig = ['', 'download', 'json', 'action', 'inspect'] |
|
1842 | 1842 |
form_page_class = FormFillPage |
1843 | 1843 | |
1844 | 1844 |
def html_top(self, title = None): |
wcs/fields.py | ||
---|---|---|
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
import datetime |
18 |
import json |
|
19 | 18 |
import time |
20 | 19 |
import random |
21 | 20 |
import re |
... | ... | |
754 | 753 |
self.document_type = self.document_type or {} |
755 | 754 | |
756 | 755 |
@property |
757 |
def metadata(self): |
|
758 |
return self.document_type.get('metadata', []) |
|
759 | ||
760 |
@property |
|
761 | 756 |
def file_type(self): |
762 |
return self.document_type.get('mimetypes', [])
|
|
757 |
return (self.document_type or {}).get('mimetypes', [])
|
|
763 | 758 | |
764 | 759 |
def fill_admin_form(self, form): |
765 | 760 |
WidgetField.fill_admin_form(self, form) |
766 | 761 |
document_types = self.get_document_types() |
767 |
cur_dt = self.document_type |
|
762 |
cur_dt = self.document_type or {}
|
|
768 | 763 |
# SingleSelectWidget compare the value and not the keys, so if we want |
769 | 764 |
# the current value not to be hidden, we must reset it with the corresponding |
770 | 765 |
# value from settings based on the 'id' |
... | ... | |
780 | 775 |
form.add(FileSizeWidget, 'max_file_size', title=_('Max file size'), |
781 | 776 |
value=self.max_file_size, |
782 | 777 |
advanced=not(self.max_file_size)) |
783 |
form.add(CheckboxWidget, 'allow_portfolio_picking', |
|
784 |
title=_('Allow user to pick a file from a portfolio'), |
|
785 |
value=self.allow_portfolio_picking, |
|
786 |
advanced=(self.allow_portfolio_picking is True)) |
|
778 |
if portfolio.has_portfolio(): |
|
779 |
form.add(CheckboxWidget, 'allow_portfolio_picking', |
|
780 |
title=_('Allow user to pick a file from a portfolio'), |
|
781 |
value=self.allow_portfolio_picking, |
|
782 |
advanced=(self.allow_portfolio_picking is True)) |
|
787 | 783 | |
788 | 784 |
def get_admin_attributes(self): |
789 | 785 |
return WidgetField.get_admin_attributes(self) + [ |
790 | 786 |
'document_type', 'max_file_size', 'allow_portfolio_picking'] |
791 | 787 | |
792 | 788 |
def get_view_value(self, value): |
793 |
r = TemplateIO(html=True) |
|
794 |
if not getattr(value, 'no_file', False): |
|
795 |
r += htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % ( |
|
789 |
return htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % ( |
|
796 | 790 |
value.base_filename, self.id, value) |
797 |
for meta_field in self.metadata: |
|
798 |
if 'varname' in meta_field: |
|
799 |
metadata_value = getattr(value, 'metadata', {}).get(meta_field['varname'], '') |
|
800 |
r += htmltext('<p>%s : %s</p>') % (meta_field['label'], metadata_value) |
|
801 |
return r.getvalue() |
|
802 | 791 | |
803 | 792 |
def get_csv_value(self, value, hint=None, **kwargs): |
804 | 793 |
if not value: |
... | ... | |
833 | 822 |
value = get_request().get_field(self.field_key) |
834 | 823 |
if value and hasattr(value, 'token'): |
835 | 824 |
get_request().form[self.field_key + '$token'] = value.token |
836 |
kwargs['document_type'] = self.document_type |
|
837 | 825 | |
838 | 826 |
def get_document_types(self): |
839 | 827 |
document_types = { |
... | ... | |
852 | 840 |
} |
853 | 841 |
# Local document types |
854 | 842 |
document_types.update(get_cfg('filetypes', {})) |
855 |
# Remote documents types |
|
856 |
document_types.update(portfolio.get_document_types()) |
|
857 | 843 |
for key, document_type in document_types.iteritems(): |
858 | 844 |
document_type['id'] = key |
859 | 845 |
# add current file type if it does not exist anymore in the settings |
860 |
cur_dt = self.document_type |
|
846 |
cur_dt = self.document_type or {}
|
|
861 | 847 |
if cur_dt and cur_dt['id'] not in document_types: |
862 | 848 |
document_types[cur_dt['id']] = cur_dt |
863 | 849 |
return document_types |
... | ... | |
900 | 886 |
if self.document_type and self.document_type.get('mimetypes'): |
901 | 887 |
old_value = self.document_type['mimetypes'] |
902 | 888 |
self.document_type['mimetypes'] = '|'.join(self.document_type['mimetypes']) |
903 |
if self.document_type and self.document_type.get('metadata'): |
|
904 |
self.document_type['metadata'] = json.dumps(self.document_type['metadata']) |
|
905 | 889 |
result = super(FileField, self).export_to_xml(charset, include_id=include_id) |
906 | 890 |
if self.document_type and self.document_type.get('mimetypes'): |
907 | 891 |
self.document_type['mimetypes'] = old_value |
908 |
if self.document_type and self.document_type.get('metadata'): |
|
909 |
self.document_type['metadata'] = json.loads(self.document_type['metadata']) |
|
910 | 892 |
return result |
911 | 893 | |
912 | 894 |
def init_with_xml(self, element, charset, include_id=False): |
... | ... | |
916 | 898 |
self.document_type['mimetypes'] = self.document_type['mimetypes'].split('|') |
917 | 899 |
if self.document_type and self.document_type.get('fargo'): |
918 | 900 |
self.document_type['fargo'] = self.document_type['fargo'] == 'True' |
919 |
if self.document_type and self.document_type.get('metadata'): |
|
920 |
self.document_type['metadata'] = json.loads(self.document_type['metadata']) |
|
921 | ||
922 |
def store_structured_value(self, data, field_id): |
|
923 |
value = data.get(field_id) |
|
924 |
return getattr(value, 'metadata', {}) |
|
925 | ||
926 |
def __setstate__(self, state): |
|
927 |
self.__dict__ = state |
|
928 |
self.document_type = self.document_type or {} |
|
929 | 901 | |
930 | 902 | |
931 | 903 |
register_field_class(FileField) |
wcs/forms/common.py | ||
---|---|---|
99 | 99 |
def html_top(self, title = None): |
100 | 100 |
template.html_top(title = title, default_org = _('Forms')) |
101 | 101 | |
102 |
def validate(self): |
|
103 |
if not portfolio.has_portfolio(): |
|
104 |
return redirect('.') |
|
105 |
field_id = get_request().form.get('field_id') |
|
106 |
if not field_id: |
|
107 |
return redirect('.') |
|
108 |
for field in self.formdef.fields: |
|
109 |
if field.id == field_id: |
|
110 |
break |
|
111 |
else: |
|
112 |
return redirect('.') |
|
113 |
if field.key != 'file': |
|
114 |
return redirect('.') |
|
115 |
if not field.document_type.get('fargo', False): |
|
116 |
return redirect('.') |
|
117 |
if not field.document_type.get('metadata', []): |
|
118 |
return redirect('.') |
|
119 |
value = self.filled.data.get(field_id) |
|
120 |
portfolio.validate(self.filled, field, value) |
|
121 |
return redirect('.') |
|
122 | ||
123 | 102 |
def __init__(self, formdef, filled, register_workflow_subdirs=True): |
124 | 103 |
get_publisher().substitutions.feed(filled) |
125 | 104 |
self.formdef = formdef |
... | ... | |
683 | 662 | |
684 | 663 |
def display_file_field(self, form_url, field, value): |
685 | 664 |
r = TemplateIO(html=True) |
686 |
validated = None |
|
687 |
is_fargo_dt = field.document_type.get('fargo', False) |
|
688 |
has_metadata = bool(field.document_type.get('metadata', [])) |
|
689 |
if portfolio.has_portfolio() and is_fargo_dt and has_metadata: |
|
690 |
validated = portfolio.is_valid(self.filled, field, value) |
|
691 |
if validated is False: |
|
692 |
extra_class = ' invalid' |
|
693 |
else: |
|
694 |
extra_class = ' valid' |
|
695 |
r += htmltext('<div class="value%s">' % extra_class) |
|
696 |
else: |
|
697 |
r += htmltext('<div class="value">') |
|
665 |
r += htmltext('<div class="value">') |
|
698 | 666 |
s = field.get_view_value(value) |
699 | 667 |
s = s.replace(str('[download]'), str('%sdownload' % form_url)) |
700 | 668 |
r += s |
701 |
if validated is not None and get_request().is_in_backoffice(): |
|
702 |
r += htmltext('<div class="file-validation">') |
|
703 |
if validated: |
|
704 |
creator = value.metadata['creator'] |
|
705 |
created = misc.localstrftime( |
|
706 |
time.localtime( |
|
707 |
misc.parse_isotime(value.metadata['created']))) |
|
708 |
r += htmltext('<p class="validation-validated">%s</p>') % _( |
|
709 |
'validated by %(creator)s on %(created)s') % { |
|
710 |
'creator': creator, 'created': created} |
|
711 |
r += htmltext('<p>%s</p>') % (_('Valid from %(start)s to %(end)s') % { |
|
712 |
'start': strftime( |
|
713 |
misc.date_format(), |
|
714 |
misc.get_as_datetime(value.metadata['start'])), |
|
715 |
'end': strftime( |
|
716 |
misc.date_format(), |
|
717 |
misc.get_as_datetime(value.metadata['end'])), |
|
718 |
}) |
|
719 |
elif self.filled.user: |
|
720 |
r += htmltext('<form method="post" action="./validate?field_id=%s">' |
|
721 |
'<button>%s</button></form>') % ( |
|
722 |
field.id, _('Validate')) |
|
723 |
r += htmltext('</div>') |
|
724 | 669 |
r += htmltext('</div>') |
725 | 670 |
return str(r) |
726 | 671 |
wcs/portfolio.py | ||
---|---|---|
80 | 80 |
return hashlib.sha256(upload.get_content()).hexdigest() |
81 | 81 | |
82 | 82 | |
83 |
def get_document_types(): |
|
84 |
if not has_portfolio(): |
|
85 |
return {} |
|
86 |
try: |
|
87 |
response = fargo_get('/document-types/') |
|
88 |
except ConnectionError: |
|
89 |
get_logger().warning('unable to retrieve document types from fargo') |
|
90 |
return {} |
|
91 |
if response.get('err') == 0: |
|
92 |
result = {} |
|
93 |
for schema in response['data']: |
|
94 |
d = { |
|
95 |
'id': schema['name'], |
|
96 |
'label': schema['label'], |
|
97 |
'fargo': True, |
|
98 |
} |
|
99 |
if 'mimetypes' in schema: |
|
100 |
d['mimetypes'] = schema['mimetypes'] |
|
101 |
result[d['id']] = d |
|
102 |
d['metadata'] = schema.get('metadata', []) |
|
103 |
return result |
|
104 |
return {} |
|
105 | ||
106 | ||
107 |
def get_validation(url): |
|
108 |
try: |
|
109 |
result = fargo_get(url) |
|
110 |
except ConnectionError: |
|
111 |
get_logger().warning('unable to retrieve validation from fargo') |
|
112 |
return None |
|
113 |
return result['data'] if result else None |
|
114 | ||
115 | ||
116 |
def get_validations(document_type): |
|
117 |
request = get_request() |
|
118 |
document_type_id = document_type['id'] |
|
119 |
qs = {} |
|
120 |
if not request.user: |
|
121 |
return [] |
|
122 |
if request.user.name_identifiers: |
|
123 |
qs['user_nameid'] = request.user.name_identifiers[0] |
|
124 |
elif request.user.email: |
|
125 |
qs['user_email'] = request.user.email |
|
126 |
else: |
|
127 |
return [] |
|
128 |
path = 'api/validation/%s/' % urllib.quote(document_type_id) |
|
129 |
try: |
|
130 |
validations = fargo_get(path + '?%s' % urllib.urlencode(qs)) |
|
131 |
except ConnectionError: |
|
132 |
get_logger().warning('unable to retrieve validations from fargo') |
|
133 |
return [] |
|
134 |
if validations and validations.get('data', {}).get('results', []): |
|
135 |
return validations['data']['results'] |
|
136 |
return [] |
|
137 | ||
138 | ||
139 |
def is_valid(filled, field, upload): |
|
140 |
'''Check validation of the uploaded file with Fargo''' |
|
141 |
return 'url' in getattr(upload, 'metadata', {}) |
|
142 | ||
143 | ||
144 |
def validate(filled, field, upload): |
|
145 |
'''Compute link to Fargo to validate the given document''' |
|
146 |
document_type_id = field.document_type['id'] |
|
147 |
url = '/api/validation/%s/' % urllib.quote(document_type_id) |
|
148 |
payload = {} |
|
149 |
if filled.user: |
|
150 |
if filled.user.name_identifiers: |
|
151 |
payload['user_nameid'] = filled.user.name_identifiers[0] |
|
152 |
else: |
|
153 |
payload['user_email'] = filled.user.email |
|
154 |
payload['origin'] = get_request().get_server() |
|
155 |
payload['creator'] = get_request().user.display_name |
|
156 |
payload['content_hash'] = sha256_of_upload(upload) |
|
157 |
for meta_field in field.metadata: |
|
158 |
if 'varname' in meta_field: |
|
159 |
payload[meta_field['varname']] = upload.metadata.get(meta_field['varname'], '') |
|
160 |
try: |
|
161 |
status, response = fargo_post_json(url, payload) |
|
162 |
except ConnectionError: |
|
163 |
get_logger().warning('unable to validate document on fargo for %s', filled.get_display_id()) |
|
164 |
return |
|
165 |
if status == 201: |
|
166 |
upload.metadata = response['data'] |
|
167 |
filled.data['%s_structured' % field.id] = upload.metadata |
|
168 |
filled.store() |
|
169 | ||
170 | ||
171 | 83 |
def push_document(user, filename, stream): |
172 | 84 |
if not user: |
173 | 85 |
return |
wcs/qommon/form.py | ||
---|---|---|
65 | 65 |
import misc |
66 | 66 |
from strftime import strftime |
67 | 67 |
from publisher import get_cfg |
68 |
from wcs import portfolio |
|
69 | 68 |
from . import ezt |
70 | 69 | |
71 | 70 |
QuixoteForm = Form |
... | ... | |
601 | 600 |
self.value = None |
602 | 601 | |
603 | 602 | |
604 |
class NoUpload(object): |
|
605 |
no_file = True |
|
606 |
metadata = None |
|
607 | ||
608 |
def __init__(self, validation_url): |
|
609 |
self.metadata = portfolio.get_validation(validation_url) |
|
610 | ||
611 | ||
612 | 603 |
class FileWithPreviewWidget(CompositeWidget): |
613 | 604 |
"""Widget that proposes a File Upload widget but that stores the file |
614 | 605 |
ondisk so it has a "readonly" mode where the filename is shown.""" |
... | ... | |
617 | 608 |
max_file_size = None |
618 | 609 |
file_type = None |
619 | 610 | |
620 |
max_file_size_bytes = None # will be filled automatically
|
|
611 |
max_file_size_bytes = None # will be filled automatically |
|
621 | 612 | |
622 | 613 |
def __init__(self, name, value=None, **kwargs): |
623 |
from wcs import fields |
|
624 | ||
625 | 614 |
CompositeWidget.__init__(self, name, value, **kwargs) |
626 | 615 |
self.value = value |
627 |
self.document_type = kwargs.pop('document_type', None) or {} |
|
628 | 616 |
self.readonly = kwargs.get('readonly') |
629 | 617 |
self.max_file_size = kwargs.pop('max_file_size', None) |
630 | 618 |
self.allow_portfolio_picking = kwargs.pop('allow_portfolio_picking', True) |
... | ... | |
635 | 623 |
hint = '' |
636 | 624 |
if self.allow_portfolio_picking: |
637 | 625 |
root_url = get_publisher().get_root_url() |
638 |
if (portfolio.has_portfolio() |
|
639 |
and get_request().user |
|
640 |
and not self.readonly): |
|
626 |
if get_request().user: |
|
641 | 627 |
get_response().add_javascript(['fargo.js']) |
642 | 628 |
params = (root_url, |
643 | 629 |
_('Pick a file from the portfolio'), |
... | ... | |
657 | 643 |
# this could be used for client size validation of file size |
658 | 644 |
attrs['data-max-file-size'] = str(self.max_file_size_bytes) |
659 | 645 |
self.add(FileWidget, 'file', hint=hint, render_br=False, attrs=attrs) |
660 |
if self.document_type.get('metadata'): |
|
661 |
if self.readonly: |
|
662 |
self.add(HiddenWidget, 'validation_url') |
|
663 |
else: |
|
664 |
validations = portfolio.get_validations(self.document_type) |
|
665 |
if validations: |
|
666 |
options = [('', _('Known documents'), '')] |
|
667 |
options += [(v['url'], v['display'], v['url']) for v in validations] |
|
668 |
for validation in validations: |
|
669 |
for i, meta_field in enumerate(self.metadata): |
|
670 |
# translate varname to f<self.id>$f<subwidget.id> |
|
671 |
if meta_field['varname'] in validation: |
|
672 |
value = validation.pop(meta_field['varname']) |
|
673 |
validation['f%s' % i] = value |
|
674 |
self.add(SingleSelectWidget, 'validation_url', options=options, |
|
675 |
attrs={'data-validations': json.dumps(validations)}) |
|
676 |
for i, meta_field in enumerate(self.metadata): |
|
677 |
field = fields.get_field_class_by_type(meta_field.get('type', 'string'))() |
|
678 |
field.id = i |
|
679 |
field.init_with_json(meta_field, include_id=False) |
|
680 |
if meta_field.get('varname'): |
|
681 |
subvalue = getattr(value, 'metadata', {}).get(meta_field['varname']) |
|
682 |
else: |
|
683 |
subvalue = None |
|
684 |
# required only if composite field is required |
|
685 |
field.required = field.required and self.required |
|
686 |
field.extra_css_class = 'subwidget' |
|
687 |
if self.readonly: |
|
688 |
# preview |
|
689 |
field.add_to_view_form(self, subvalue) |
|
690 |
else: |
|
691 |
field.add_to_form(self, subvalue) |
|
692 | 646 |
if value: |
693 | 647 |
self.set_value(value) |
694 | 648 | |
... | ... | |
704 | 658 |
self.get_widget('token').set_value(self.value.token) |
705 | 659 | |
706 | 660 |
def render_content(self): |
707 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'jquery.iframe-transport.js', |
|
708 |
'jquery.fileupload.js', 'qommon.fileupload.js']) |
|
661 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', |
|
662 |
'jquery.iframe-transport.js', 'jquery.fileupload.js', |
|
663 |
'qommon.fileupload.js']) |
|
709 | 664 | |
710 | 665 |
temp = get_session().get_tempfile(self.get('token')) or {} |
711 | 666 | |
712 | 667 |
r = TemplateIO(html=True) |
713 |
if self.get_widget('file'):
|
|
714 |
r += self.get_widget('file').render()
|
|
668 |
for widget in self.get_widgets():
|
|
669 |
r += widget.render()
|
|
715 | 670 | |
716 | 671 |
r += htmltext('<div class="fileprogress" style="display: none;">') |
717 | 672 |
r += htmltext(' <div class="bar">%s</div>' % _('Upload in progress...')) |
718 | 673 |
r += htmltext('</div>') |
719 |
r += htmltext('<div class="fileinfo"><span class="filename">%s</span>' |
|
720 |
% temp.get('base_filename', '')) |
|
674 |
r += htmltext('<div class="fileinfo"><span class="filename">%s</span>' % temp.get('base_filename', '')) |
|
721 | 675 |
if not self.readonly: |
722 |
r += htmltext(' <a href="#" class="remove" title="%s">%s</a>' |
|
723 |
% (_('Remove this file'), _('remove'))) |
|
676 |
r += htmltext(' <a href="#" class="remove" title="%s">%s</a>' % ( |
|
677 |
_('Remove this file'), |
|
678 |
_('remove'))) |
|
724 | 679 |
elif temp: |
725 | 680 |
filetype = mimetypes.guess_type(temp.get('orig_filename', '')) |
726 | 681 |
include_image = False |
... | ... | |
736 | 691 |
r += htmltext('<img alt="" src="tempfile?t=%s&thumbnail=1" />' % |
737 | 692 |
self.get('token')) |
738 | 693 |
r += htmltext('</div>') |
739 | ||
740 |
for widget in self.get_widgets(): |
|
741 |
if widget is self.get_widget('file'): |
|
742 |
continue |
|
743 |
r += widget.render() |
|
744 | ||
745 | 694 |
return r.getvalue() |
746 | 695 | |
747 |
@property |
|
748 |
def metadata(self): |
|
749 |
return self.document_type.get('metadata', []) |
|
750 | ||
751 | 696 |
def _parse(self, request): |
752 | 697 |
self.value = None |
753 | 698 |
if self.get('token'): |
754 | 699 |
token = self.get('token') |
755 |
elif self.get('validation_url'): |
|
756 |
self.value = NoUpload(self.get('validation_url')) |
|
757 |
return |
|
758 | 700 |
elif self.get('file'): |
759 | 701 |
token = get_session().add_tempfile(self.get('file')) |
760 | 702 |
request.form[self.get_widget('token').get_name()] = token |
... | ... | |
763 | 705 | |
764 | 706 |
session = get_session() |
765 | 707 |
if token and session.tempfiles and session.tempfiles.has_key(token): |
708 |
temp = session.tempfiles[token] |
|
766 | 709 |
self.value = session.get_tempfile_content(token) |
767 | 710 | |
768 | 711 |
if self.value is None: |
769 |
# there's no file, check all metadata field are empty too |
|
770 |
# if not file and required metadata field become required |
|
771 |
if (self.get_widget('file') |
|
772 |
and not self.required |
|
773 |
and any([self.get('f%s' % i) for i, meta_field in enumerate(self.metadata)])): |
|
774 |
self.get_widget('file').required = True |
|
775 |
self.get_widget('file').set_error(self.REQUIRED_ERROR) |
|
776 |
for i, meta_field in enumerate(self.metadata): |
|
777 |
name = 'f%s' % i |
|
778 |
required = meta_field.get('required', True) |
|
779 |
if required: |
|
780 |
widget = self.get_widget(name) |
|
781 |
widget.required = True |
|
782 |
if not self.get(name): |
|
783 |
widget.set_error(self.REQUIRED_ERROR) |
|
712 |
# there's no file, the other checks are irrelevant. |
|
784 | 713 |
return |
785 | 714 | |
786 |
# There is some file, check all required metadata files have been filled |
|
787 |
for i, meta_field in enumerate(self.metadata): |
|
788 |
name = 'f%s' % i |
|
789 |
required = meta_field.get('required', True) |
|
790 |
if required: |
|
791 |
widget = self.get_widget(name) |
|
792 |
widget.required = True |
|
793 |
if not self.get(name): |
|
794 |
widget.set_error(self.REQUIRED_ERROR) |
|
795 | ||
796 |
if self.metadata: |
|
797 |
self.value.metadata = {} |
|
798 |
for i, meta_field in enumerate(self.metadata): |
|
799 |
name = 'f%s' % i |
|
800 |
if 'varname' in meta_field: |
|
801 |
self.value.metadata[meta_field['varname']] = self.get(name) |
|
802 | ||
803 | 715 |
# Don't trust the browser supplied MIME type, update the Upload object |
804 | 716 |
# with a MIME type created with magic (or based on the extension if the |
805 | 717 |
# module is missing). |
wcs/qommon/static/css/qommon.css | ||
---|---|---|
87 | 87 |
display: inline-block; |
88 | 88 |
} |
89 | 89 | |
90 |
/* nested widgets */ |
|
91 |
.widget .subwidget { |
|
92 |
padding-left: 2em; |
|
93 |
} |
|
94 |
div.FileWithPreviewWidget .validation { |
|
95 |
display: inline-block; |
|
96 |
font-size: xx-small; |
|
97 |
margin-right: 2em; |
|
98 |
} |
|
99 | ||
100 | 90 |
p.use-file-from-fargo span { |
101 | 91 |
border-bottom: 1px dotted #999; |
102 | 92 |
cursor: pointer; |
wcs/qommon/static/js/qommon.fileupload.js | ||
---|---|---|
7 | 7 |
} else { |
8 | 8 |
$(base_widget).find('.fileinfo').hide(); |
9 | 9 |
} |
10 |
function disable() { |
|
11 |
base_widget.find('.subwidget input').prop('disabled', true); |
|
12 |
$('form').on('submit', function () { |
|
13 |
$('input').prop('disabled', false); |
|
14 |
}); |
|
15 |
} |
|
16 |
function enable() { |
|
17 |
if (base_widget.find('.subwidget input:disabled').length) { |
|
18 |
base_widget.find('.subwidget input').val(''); |
|
19 |
base_widget.find('.subwidget input').prop('disabled', false); |
|
20 |
} |
|
21 |
} |
|
22 | 10 |
$(this).find('input[type=file]').fileupload({ |
23 | 11 |
dataType: 'json', |
24 | 12 |
add: function (e, data) { |
... | ... | |
32 | 20 |
$(base_widget).find('.fileprogress').hide(); |
33 | 21 |
$(base_widget).find('.filename').text(data.result[0].name); |
34 | 22 |
$(base_widget).find('.fileinfo').show(); |
35 |
$(base_widget).find('input[name$="$token"]').val(data.result[0].token); |
|
36 |
$(base_widget).find('select[name$="$validation_url"]').val(''); |
|
37 |
enable(); |
|
23 |
$(base_widget).find('input[type=hidden]').val(data.result[0].token); |
|
38 | 24 |
$(base_widget).parents('form').find('input[name=submit]').prop('disabled', false); |
39 | 25 |
$(this).hide(); |
40 | 26 |
$(base_widget).find('.use-file-from-fargo').hide(); |
... | ... | |
45 | 31 |
} |
46 | 32 |
}); |
47 | 33 |
$(this).find('a.remove').click(function() { |
48 |
$(base_widget).find('input[name$="$token"]').val('');
|
|
34 |
$(base_widget).find('input[type=hidden]').val('');
|
|
49 | 35 |
$(base_widget).find('.fileinfo').hide(); |
50 | 36 |
$(base_widget).find('input[type=file]').show(); |
51 | 37 |
$(base_widget).find('.use-file-from-fargo').show(); |
... | ... | |
55 | 41 |
$(base_widget).find('input[type=file]').click(); |
56 | 42 |
return false; |
57 | 43 |
}); |
58 |
if ($(this).find('select[name$="$validation_url"] option:selected').val()) { |
|
59 |
disable(); |
|
60 |
} |
|
61 |
$(this).find('select[name$="$validation_url"]').on('change', function() { |
|
62 |
var url = $(this).find(':selected').val(); |
|
63 |
if (url) { |
|
64 |
var validations = $(this).data('validations'); |
|
65 | ||
66 |
for (var i = 0; i < validations.length; i++) { |
|
67 |
if (validations[i].url == url) { |
|
68 |
base_widget.find('a.remove').trigger('click'); |
|
69 |
for (var item in validations[i]) { |
|
70 |
if (! item) { |
|
71 |
continue; |
|
72 |
} |
|
73 |
var $input = base_widget.find('input[name$="$' + item + '"]'); |
|
74 |
if ($input.length) { |
|
75 |
$input.val(validations[i][item]); |
|
76 |
} |
|
77 |
} |
|
78 |
disable(); |
|
79 |
} |
|
80 |
} |
|
81 |
} else { |
|
82 |
enable(); |
|
83 |
} |
|
84 |
}); |
|
85 | 44 |
}); |
86 | 45 |
}); |
87 |
- |