Projet

Général

Profil

0002-rewrite-file-validation-10444.patch

Benjamin Dauvergne, 06 avril 2016 18:38

Télécharger (51,7 ko)

Voir les différences:

Subject: [PATCH 2/4] rewrite file validation (#10444)

- metadata fields are now directly added to the form, as part of the
  FileWithPreviewWidget sub-widgets (it's a CompositeWidget)
- existing validated metadatas are proposed for prefilling
- if prefilled no file is uploadee, and a special upload class NoUpload is used
  instead of a PicklableUpload, it only contains the metadatas.
- prefilled fields are disabled,
- on upload of a new file all prefilled fields are emptied and re-enabled.
- modify XML import/export to encode metadata sub-field as JSON in exports
- add test on import/export of FileField with metadata
- rewritten tests around file validation
 tests/test_backoffice_pages.py            | 250 +++++++++++++++++++++++++-----
 tests/test_form_pages.py                  | 110 +++++++++----
 tests/test_formdata.py                    | 109 +++++++++++++
 tests/test_formdef_import.py              |  15 ++
 wcs/backoffice/management.py              |   2 +-
 wcs/fields.py                             |  25 ++-
 wcs/file_validation.py                    | 129 ++++++++-------
 wcs/forms/common.py                       |  60 ++++---
 wcs/qommon/form.py                        | 103 ++++++++++--
 wcs/qommon/static/css/qommon.css          |  10 ++
 wcs/qommon/static/js/qommon.fileupload.js |  45 +++++-
 11 files changed, 698 insertions(+), 160 deletions(-)
tests/test_backoffice_pages.py
15 15
from quixote import cleanup, get_publisher
16 16
from wcs.qommon import errors, sessions
17 17
from qommon.ident.password_accounts import PasswordAccount
18
from qommon.misc import json_loads
18 19
from wcs.qommon.http_request import HTTPRequest
19 20
from wcs.roles import Role
20 21
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem,
......
1665 1666
    assert resp.body == 'foo(%s);' % menu_json_str
1666 1667
    assert resp.headers['content-type'] == 'application/javascript'
1667 1668

  
1669
def test_backoffice_file_field_fargo_no_metadata(pub, fargo_url):
1670
    document_type = {
1671
        'id': 'justificatif-de-domicile',
1672
        'fargo': True,
1673
        'mimetypes': ['application/pdf'],
1674
        'label': 'Justificatif de domicile',
1675
    }
1676
    user = create_user(pub, is_admin=True)
1677
    user.name_identifiers = ['12345']
1678
    user.store()
1679
    FormDef.wipe()
1680
    formdef = FormDef()
1681
    formdef.name = 'form title'
1682
    formdef.fields = [fields.FileField(
1683
        id='0', label='1st field', type='file',
1684
        document_type=document_type)]
1685
    formdef.store()
1686
    formdef.data_class().wipe()
1687
    upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf')
1688
    digest = hashlib.sha256('%PDF-1.4').hexdigest()
1689
    app = login(get_app(pub))
1690
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
1691
        resp = app.get('/form-title/')
1692
        assert fargo_get.call_count == 0
1693
    resp.forms[0]['f0$file'] = upload
1694
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
1695
        resp = resp.forms[0].submit('submit')
1696
        assert fargo_get.call_count == 0
1697
    assert 'Check values then click submit.' in resp.body
1698
    resp = resp.forms[0].submit('submit').follow()
1699
    assert formdef.data_class().count() == 1
1700
    formdata = formdef.data_class().select()[0]
1701
    form_id = formdata.id
1702
    assert not hasattr(formdata.data['0'], 'metadata')
1703
    assert not '0_structured' in formdata.data
1704
    resp = app.get('/backoffice/management/form-title/%s/' % form_id)
1705
    assert not 'Validate' in resp.body
1706
    with mock.patch('wcs.file_validation.http_post_request') as http_post_request:
1707
        resp = app.get('/backoffice/management/form-title/%s/validate?field_id=0' % form_id)
1708
        assert http_post_request.call_count == 0
1709
    resp = resp.follow()
1710
    assert not 'Valid ' in resp.body
1711
    assert not 'Validate' in resp.body
1712

  
1713

  
1668 1714
def test_backoffice_file_field_validation(pub, fargo_url):
1715
    document_type = {
1716
        'id': 'justificatif-de-domicile',
1717
        'fargo': True,
1718
        'mimetypes': ['application/pdf'],
1719
        'label': 'Justificatif de domicile',
1720
        'metadata': [
1721
            {'label': 'Nom', 'varname': 'nom', 'type': 'string'},
1722
            {'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'},
1723
            {'label': 'Numéro', 'varname': 'numero', 'type': 'string'},
1724
            {'label': 'Rue', 'varname': 'rue', 'type': 'string'},
1725
            {'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'},
1726
            {'label': 'Ville', 'varname': 'ville', 'type': 'string'},
1727
        ],
1728
    }
1729
    metadata = {
1730
        'nom': 'Doe',
1731
        'prenoms': 'John',
1732
        'numero': '169',
1733
        'rue': 'rue du château',
1734
        'code-postal': '75014',
1735
        'ville': 'PARIS',
1736
    }
1669 1737
    user = create_user(pub, is_admin=True)
1670 1738
    user.name_identifiers = ['12345']
1671 1739
    user.store()
......
1674 1742
    formdef.name = 'form title'
1675 1743
    formdef.fields = [fields.FileField(
1676 1744
        id='0', label='1st field', type='file',
1677
        document_type={
1678
            'id': 'justificatif-de-domicile',
1679
            'fargo': True,
1680
            'mimetypes': ['application/pdf'],
1681
            'label': 'PDF files',
1682
        })
1683
    ]
1745
        document_type=document_type)]
1684 1746
    formdef.store()
1685 1747
    formdef.data_class().wipe()
1686 1748
    upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf')
1687 1749
    digest = hashlib.sha256('%PDF-1.4').hexdigest()
1688 1750
    app = login(get_app(pub))
1689
    resp = app.get('/form-title/')
1751
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
1752
        fargo_get.return_value = {'result': 1, 'data': {'results': []}}
1753
        resp = app.get('/form-title/')
1754
        fargo_get.assert_called_once_with(
1755
            'api/validation/justificatif-de-domicile/?user_nameid=12345')
1690 1756
    resp.forms[0]['f0$file'] = upload
1691
    resp = resp.forms[0].submit('submit')
1757
    for i, meta_field in enumerate(document_type['metadata']):
1758
        resp.forms[0]['f0$f%s' % i] = metadata[meta_field['varname']]
1759
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
1760
        fargo_get.return_value = {'result': 1, 'data': {'results': []}}
1761
        resp = resp.forms[0].submit('submit')
1762
        fargo_get.assert_called_once_with(
1763
            'api/validation/justificatif-de-domicile/?user_nameid=12345')
1764
    for key in metadata:
1765
        assert 'value="%s"' % metadata[key] in resp.body
1692 1766
    assert 'Check values then click submit.' in resp.body
1693
    resp = resp.forms[0].submit('submit')
1694
    assert resp.status_int == 302
1767
    resp = resp.forms[0].submit('submit').follow()
1768
    for metadata_field in document_type['metadata']:
1769

  
1770
        fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']])
1771
        assert fragment in resp.body
1695 1772
    assert formdef.data_class().count() == 1
1696
    form_id = formdef.data_class().select()[0].id
1697
    with mock.patch('wcs.file_validation.http_get_page') as http_get_page:
1698
        json_response = json.dumps({
1699
            'err': 0,
1700
            'data': {
1701
                'type': 'justificatif-de-domicile',
1702
                'label': 'Justificatif de domicile',
1703
                'creator': 'Jean Bono',
1704
                'created': '2014-01-01T01:01:01',
1705
                'start': '2014-01-01T01:01:01',
1706
                'end': '2014-01-01T01:01:01',
1707
                'metadata': [{
1708
                        'name': 'code_postal',
1709
                        'label': 'Code postal',
1710
                        'value': '13400',
1711
                }],
1712
            },
1773
    formdata = formdef.data_class().select()[0]
1774
    form_id = formdata.id
1775
    assert formdata.data['0'].metadata == metadata
1776
    assert formdata.data['0_structured'] == metadata
1777
    resp = app.get('/backoffice/management/form-title/%s/' % form_id)
1778
    assert 'Validate' in resp.body
1779
    for metadata_field in document_type['metadata']:
1780

  
1781
        fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']])
1782
        assert fragment in resp.body
1783
    with mock.patch('wcs.file_validation.http_post_request') as http_post_request:
1784
        payload = {
1785
            'user_nameid': '12345',
1786
            'origin': 'example.net',
1787
            'creator': 'admin',
1788
            'content_hash': digest,
1789
        }
1790
        payload.update(metadata)
1791
        result = {
1792
            'result': 1,
1793
            'data': payload.copy()
1794
        }
1795
        result['data'].update({
1796
            'url': 'zob',
1797
            'created': '1970-01-01T10:10:10',
1798
            'start': '1970-01-01',
1799
            'end': '1978-01-01',
1800
            'display': 'John Doe, 169 rue du château, 75014 PARIS'
1713 1801
        })
1714
        http_get_page.return_value = None, 200, json_response, None
1715
        resp = app.get('/backoffice/management/form-title/%s/' % form_id)
1716
        http_get_page.assert_called_once_with('http://fargo.example.net/metadata/12345/%s/justificatif-de-domicile/' % digest)
1717
        assert 'class="value valid"' in resp.body
1718
        assert 'Justificatif de domicile validated by Jean Bono on 2014-01-01T01:01:01' in resp.body
1719
        assert 'Code postal: 13400' in resp.body
1720
        assert 'Valid from 2014-01-01T01:01:01 to 2014-01-01T01:01:01' in resp.body
1802
        http_post_request.return_value = None, 201, json.dumps(result), None
1803
        resp = app.get('/backoffice/management/form-title/%s/validate?field_id=0' % form_id)
1804
        assert http_post_request.call_count == 1
1805
        assert http_post_request.call_args[0][0] == 'http://fargo.example.net/api/validation/justificatif-de-domicile/'
1806
        assert json_loads(http_post_request.call_args[0][1]) == payload
1807
        assert http_post_request.call_args[1] == {'headers': {'Content-Type': 'application/json'}}
1808
    resp = resp.follow()
1809

  
1810
    assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body
1811

  
1812

  
1813
def test_backoffice_file_validation_no_upload(pub, fargo_url):
1814
    document_type = {
1815
        'id': 'justificatif-de-domicile',
1816
        'fargo': True,
1817
        'mimetypes': ['application/pdf'],
1818
        'label': 'Justificatif de domicile',
1819
        'metadata': [
1820
            {'label': 'Nom', 'varname': 'nom', 'type': 'string'},
1821
            {'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'},
1822
            {'label': 'Numéro', 'varname': 'numero', 'type': 'string'},
1823
            {'label': 'Rue', 'varname': 'rue', 'type': 'string'},
1824
            {'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'},
1825
            {'label': 'Ville', 'varname': 'ville', 'type': 'string'},
1826
        ],
1827
    }
1828
    metadata = {
1829
        'nom': 'Doe',
1830
        'prenoms': 'John',
1831
        'numero': '169',
1832
        'rue': 'rue du château',
1833
        'code-postal': '75014',
1834
        'ville': 'PARIS',
1835
    }
1836
    validation = {
1837
        'url': 'zob',
1838
        'creator': 'admin',
1839
        'created': '1970-01-01T10:10:10',
1840
        'start': '1970-01-01',
1841
        'end': '1978-01-01',
1842
        'display': 'John Doe, 169 rue du château, 75014 PARIS'
1843
    }
1844
    validation.update(metadata)
1845

  
1846
    user = create_user(pub, is_admin=True)
1847
    user.name_identifiers = ['12345']
1848
    user.store()
1849
    FormDef.wipe()
1850
    formdef = FormDef()
1851
    formdef.name = 'form title'
1852
    formdef.fields = [fields.FileField(
1853
        id='0', label='1st field', type='file',
1854
        document_type=document_type)]
1855
    formdef.store()
1856
    formdef.data_class().wipe()
1857
    app = login(get_app(pub))
1858
    return_value = {'result': 1, 'data': {'results': [validation]}}
1859
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
1860
        fargo_get.return_value = return_value
1861
        resp = app.get('/form-title/')
1862
        fargo_get.assert_called_once_with(
1863
            'api/validation/justificatif-de-domicile/?user_nameid=12345')
1864
        assert validation['display'] in resp.body
1865
    resp.forms[0]['f0$validation_url'] = 'zob'
1866
    for i, meta_field in enumerate(document_type['metadata']):
1867
        resp.forms[0]['f0$f%s' % i] = metadata[meta_field['varname']]
1868
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get, \
1869
            mock.patch('wcs.file_validation.http_get_page') as http_get_page:
1870
        fargo_get.return_value = return_value
1871
        return_value = {
1872
            'result': 1,
1873
            'data': validation,
1874
        }
1875
        http_get_page.return_value = None, 200, json.dumps(return_value), None
1876
        resp = resp.forms[0].submit('submit')
1877
        fargo_get.assert_called_once_with(
1878
            'api/validation/justificatif-de-domicile/?user_nameid=12345')
1879
        http_get_page.assert_called_with('zob')
1880
        for key in metadata:
1881
            assert 'value="%s"' % metadata[key] in resp.body
1882
        assert 'Check values then click submit.' in resp.body
1883
        resp = resp.forms[0].submit('submit').follow()
1884
    assert formdef.data_class().count() == 1
1885
    formdata = formdef.data_class().select()[0]
1886
    form_id = formdata.id
1887
    assert formdata.data['0'].metadata == validation
1888
    assert formdata.data['0_structured'] == validation
1889
    for metadata_field in document_type['metadata']:
1890

  
1891
        fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']])
1892
        assert fragment in resp.body
1893
    resp = app.get('/backoffice/management/form-title/%s/' % form_id)
1894
    assert not 'Validate' in resp.body
1895
    for metadata_field in document_type['metadata']:
1896

  
1897
        fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']])
1898
        assert fragment in resp.body
1899
    assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body
1900

  
1721 1901

  
1722 1902
def test_360_user_view(pub):
1723 1903
    if not pub.is_using_postgresql():
tests/test_form_pages.py
1
# -*- coding: utf-8 -*-
1 2
import json
2 3
import pytest
3 4
import hashlib
......
2355 2356
    assert formdef.data_class().select()[0].data['1'] == 'foobar3'
2356 2357

  
2357 2358
def test_file_field_validation(pub, fargo_url):
2359
    document_type = {
2360
        'id': 'justificatif-de-domicile',
2361
        'fargo': True,
2362
        'label': 'Justificatif de domicile',
2363
        'metadata': [
2364
            {'label': 'Nom', 'varname': 'nom', 'type': 'string'},
2365
            {'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'},
2366
            {'label': 'Numéro', 'varname': 'numero', 'type': 'string'},
2367
            {'label': 'Rue', 'varname': 'rue', 'type': 'string'},
2368
            {'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'},
2369
            {'label': 'Ville', 'varname': 'ville', 'type': 'string'},
2370
        ],
2371
    }
2372
    metadata = {
2373
        'nom': 'Doe',
2374
        'prenoms': 'John',
2375
        'numero': '169',
2376
        'rue': 'rue du château',
2377
        'code-postal': '75014',
2378
        'ville': 'PARIS',
2379
    }
2358 2380
    user = create_user(pub)
2359 2381
    user.name_identifiers = ['12345']
2360 2382
    user.store()
......
2363 2385
    formdef.name = 'form title'
2364 2386
    formdef.fields = [fields.FileField(
2365 2387
        id='0', label='1st field', type='file',
2366
        document_type={
2367
            'id': 'justificatif-de-domicile',
2368
            'fargo': True,
2369
            'mimetypes': ['application/pdf'],
2370
            'label': 'PDF files',
2371
        })
2388
        document_type=document_type)
2372 2389
    ]
2373 2390
    formdef.store()
2374 2391
    formdef.data_class().wipe()
2375 2392
    upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf')
2376
    digest = hashlib.sha256('%PDF-1.4').hexdigest()
2377 2393
    app = login(get_app(pub), username='foo', password='foo')
2378
    resp = app.get('/form-title/')
2394
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
2395
        fargo_get.return_value = {'result': 1, 'data': {'results': []}}
2396
        resp = app.get('/form-title/')
2397
        fargo_get.assert_called_once_with(
2398
            'api/validation/justificatif-de-domicile/?user_nameid=12345')
2379 2399
    resp.forms[0]['f0$file'] = upload
2400
    for i, meta_field in enumerate(document_type['metadata']):
2401
        resp.forms[0]['f0$f%s' % i] = metadata[meta_field['varname']]
2402
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
2403
        fargo_get.return_value = {'result': 1, 'data': {'results': []}}
2404
        resp = resp.forms[0].submit('submit')
2405
        fargo_get.assert_called_once_with(
2406
            'api/validation/justificatif-de-domicile/?user_nameid=12345')
2407
    assert 'Check values then click submit.' in resp.body
2380 2408
    resp = resp.forms[0].submit('submit')
2409
    assert resp.status_int == 302
2410
    resp = resp.follow()
2411
    assert 'The form has been recorded' in resp.body
2412
    assert formdef.data_class().count() == 1
2413
    formdata = formdef.data_class().select()[0]
2414
    assert formdata.data['0'].metadata == metadata
2415
    assert formdata.data['0_structured'] == metadata
2416

  
2417

  
2418
def test_file_field_fargo_no_metadata(pub, fargo_url):
2419
    document_type = {
2420
        'id': 'justificatif-de-domicile',
2421
        'fargo': True,
2422
        'label': 'Justificatif de domicile',
2423
    }
2424
    user = create_user(pub)
2425
    user.name_identifiers = ['12345']
2426
    user.store()
2427
    FormDef.wipe()
2428
    formdef = FormDef()
2429
    formdef.name = 'form title'
2430
    formdef.fields = [fields.FileField(
2431
        id='0', label='1st field', type='file',
2432
        document_type=document_type)
2433
    ]
2434
    formdef.store()
2435
    formdef.data_class().wipe()
2436
    upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf')
2437
    app = login(get_app(pub), username='foo', password='foo')
2438
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
2439
        resp = app.get('/form-title/')
2440
        assert fargo_get.call_count == 0
2441
    resp.forms[0]['f0$file'] = upload
2442
    with mock.patch('wcs.file_validation.fargo_get') as fargo_get:
2443
        resp = resp.forms[0].submit('submit')
2444
        assert fargo_get.call_count == 0
2381 2445
    assert 'Check values then click submit.' in resp.body
2382 2446
    resp = resp.forms[0].submit('submit')
2383 2447
    assert resp.status_int == 302
2384
    with mock.patch('wcs.file_validation.http_get_page') as http_get_page:
2385
        json_response = json.dumps({
2386
            'err': 0,
2387
            'data': {
2388
                'type': 'justificatif-de-domicile',
2389
                'label': 'Justificatif de domicile',
2390
                'creator': 'Jean Bono',
2391
                'created': '2014-01-01T01:01:01',
2392
                'start': '2014-01-01T01:01:01',
2393
                'end': '2014-01-01T01:01:01',
2394
                'metadata': [{
2395
                        'name': 'code_postal',
2396
                        'label': 'Code postal',
2397
                        'value': '13400',
2398
                }],
2399
            },
2400
        })
2401
        http_get_page.return_value = None, 200, json_response, None
2402
        resp = resp.follow()
2403
        http_get_page.assert_called_once_with('http://fargo.example.net/metadata/12345/%s/justificatif-de-domicile/' % digest)
2404
        http_get_page.reset_mock()
2405
        assert 'The form has been recorded' in resp.body
2406
        assert 'class="value valid"' in resp.body
2448
    resp = resp.follow()
2449
    assert 'The form has been recorded' in resp.body
2450
    assert formdef.data_class().count() == 1
2451
    formdata = formdef.data_class().select()[0]
2452
    assert not hasattr(formdata.data['0'], 'metadata')
2453
    assert not '0_structured' in formdata.data
2454

  
2407 2455

  
2408 2456
def test_form_string_field_autocomplete(pub):
2409 2457
    formdef = create_formdef()
tests/test_formdata.py
1
# -*- coding: utf-8 -*-
1 2
import pytest
2 3
import sys
3 4
import shutil
......
11 12
from wcs.formdata import Evolution
12 13
from wcs.workflows import Workflow, WorkflowCriticalityLevel
13 14
from wcs.wf.anonymise import AnonymiseWorkflowStatusItem
15
from wcs.qommon.form import NoUpload
16
import mock
14 17

  
15 18
from utilities import create_temporary_pub, clean_temporary_pub
16 19

  
......
148 151
    assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0')
149 152
    assert isinstance(substvars.get('form_var_foo_raw'), Upload)
150 153

  
154

  
155
def test_file_field_fargo_no_metadata(pub):
156
    document_types = {
157
        'justificatif-de-domicile': {
158
            'id': 'justificatif-de-domicile',
159
            'fargo': True,
160
            'label': 'Justificatif de domicile',
161
        }
162
    }
163
    formdef.data_class().wipe()
164
    formdef.fields = [fields.FileField(id='0', label='file', varname='foo',
165
                                       document_type=document_types['justificatif-de-domicile'])]
166
    formdef.store()
167
    formdata = formdef.data_class()()
168
    upload = Upload('test.txt', 'text/plain', 'ascii')
169
    upload.receive(['first line', 'second line'])
170
    formdata.data = {'0': upload}
171
    formdata.id = 1
172
    substvars = formdata.get_substitution_variables()
173
    assert substvars.get('form_var_foo') == 'test.txt'
174
    assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0')
175
    assert isinstance(substvars.get('form_var_foo_raw'), Upload)
176

  
177

  
178
def test_file_field_with_metadata(pub):
179
    document_types = {
180
        'justificatif-de-domicile': {
181
            'id': 'justificatif-de-domicile',
182
            'fargo': True,
183
            'label': 'Justificatif de domicile',
184
            'metadata': [
185
                {'label': 'Nom', 'varname': 'nom', 'type': 'string'},
186
                {'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'},
187
                {'label': 'Numéro', 'varname': 'numero', 'type': 'string'},
188
                {'label': 'Rue', 'varname': 'rue', 'type': 'string'},
189
                {'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'},
190
                {'label': 'Ville', 'varname': 'ville', 'type': 'string'},
191
            ],
192
        }
193
    }
194
    formdef.data_class().wipe()
195
    formdef.fields = [fields.FileField(id='0', label='file', varname='foo',
196
                                       document_type=document_types['justificatif-de-domicile'])]
197
    formdef.store()
198
    formdata = formdef.data_class()()
199
    upload = Upload('test.txt', 'text/plain', 'ascii')
200
    upload.receive(['first line', 'second line'])
201
    upload.metadata = {
202
        'nom': 'Doe',
203
        'prenoms': 'John',
204
        'numero': '169',
205
        'rue': 'rue du château',
206
        'code-postal': '75014',
207
        'ville': 'PARIS',
208
    }
209
    formdata.data = {'0': upload, '0_structured': upload.metadata}
210
    formdata.id = 1
211
    substvars = formdata.get_substitution_variables()
212
    assert substvars.get('form_var_foo') == 'test.txt'
213
    assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0')
214
    assert isinstance(substvars.get('form_var_foo_raw'), Upload)
215
    for key in upload.metadata:
216
        assert substvars.get('form_var_foo_%s' % key) == upload.metadata[key]
217

  
218

  
219
def test_file_field_no_file_with_metadata(pub):
220
    document_types = {
221
        'justificatif-de-domicile': {
222
            'id': 'justificatif-de-domicile',
223
            'fargo': True,
224
            'label': 'Justificatif de domicile',
225
            'metadata': [
226
                {'label': 'Nom', 'varname': 'nom', 'type': 'string'},
227
                {'label': 'Prénom(s)', 'varname': 'prenoms', 'type': 'string'},
228
                {'label': 'Numéro', 'varname': 'numero', 'type': 'string'},
229
                {'label': 'Rue', 'varname': 'rue', 'type': 'string'},
230
                {'label': 'Code postal', 'varname': 'code-postal', 'type': 'string'},
231
                {'label': 'Ville', 'varname': 'ville', 'type': 'string'},
232
            ],
233
        }
234
    }
235
    formdef.data_class().wipe()
236
    formdef.fields = [fields.FileField(id='0', label='file', varname='foo',
237
                                       document_type=document_types['justificatif-de-domicile'])]
238
    formdef.store()
239
    formdata = formdef.data_class()()
240
    metadata = {
241
        'nom': 'Doe',
242
        'prenoms': 'John',
243
        'numero': '169',
244
        'rue': 'rue du château',
245
        'code-postal': '75014',
246
        'ville': 'PARIS',
247
    }
248
    with mock.patch('wcs.file_validation.get_validation', return_value=metadata):
249
        upload = NoUpload('http://whatever.com/')
250
    formdata.data = {'0': upload, '0_structured': upload.metadata}
251
    formdata.id = 1
252
    substvars = formdata.get_substitution_variables()
253
    assert isinstance(substvars.get('form_var_foo'), NoUpload)
254
    assert not 'form_var_foo_url' in substvars
255
    assert not 'form_var_foo_raw' in substvars
256
    for key in upload.metadata:
257
        assert substvars.get('form_var_foo_%s' % key) == upload.metadata[key]
258

  
259

  
151 260
def test_get_submitter(pub):
152 261
    formdef.data_class().wipe()
153 262
    formdef.fields = [fields.StringField(id='0', label='email', varname='foo',
tests/test_formdef_import.py
242 242
    assert_xml_import_export_works(formdef)
243 243
    assert_json_import_export_works(formdef, include_id=True)
244 244
    assert_json_import_export_works(formdef)
245
    formdef = FormDef()
246
    formdef.name = 'foo'
247
    formdef.fields = [fields.FileField(type='file', id='1', document_type={
248
        'id': 'justificatif-de-domicile',
249
        'fargo': True,
250
        'mimetypes': ['application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/*'],
251
        'metadata': [
252
            {'varname': 'nom', 'label': 'Nom', 'type': 'string'},
253
            {'varname': 'rue', 'label': 'Rue', 'type': 'string'},
254
        ]
255
    })]
256
    assert_xml_import_export_works(formdef, include_id=True)
257
    assert_xml_import_export_works(formdef)
258
    assert_json_import_export_works(formdef, include_id=True)
259
    assert_json_import_export_works(formdef)
245 260

  
246 261
def test_unknown_data_source():
247 262
    formdef = FormDef()
wcs/backoffice/management.py
1642 1642

  
1643 1643

  
1644 1644
class FormBackOfficeStatusPage(FormStatusPage):
1645
    _q_exports = ['', 'download', 'json', 'action', 'inspect']
1645
    _q_exports = ['', 'download', 'json', 'action', 'inspect', 'validate']
1646 1646
    form_page_class = FormFillPage
1647 1647

  
1648 1648
    def html_top(self, title = None):
wcs/fields.py
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import json
17 18
import time
18 19
import random
19 20
import re
......
720 721
        self.document_type = self.document_type or {}
721 722

  
722 723
    @property
724
    def metadata(self):
725
        return self.document_type.get('metadata', [])
726

  
727
    @property
723 728
    def file_type(self):
724 729
        return (self.document_type or {}).get('mimetypes', [])
725 730

  
......
752 757
                'document_type', 'max_file_size', 'allow_portfolio_picking']
753 758

  
754 759
    def get_view_value(self, value):
755
        return htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % (
760
        r = TemplateIO(html=True)
761
        if not getattr(value, 'no_file', False):
762
            r += htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % (
756 763
                value.base_filename, self.id, value)
764
        for meta_field in self.metadata:
765
            if 'varname' in meta_field:
766
                metadata_value = getattr(value, 'metadata', {}).get(meta_field['varname'], '')
767
                r += htmltext('<p>%s&nbsp;: %s</p>') % (meta_field['label'], metadata_value)
768
        return r.getvalue()
757 769

  
758 770
    def get_csv_value(self, value, hint=None, **kwargs):
759 771
        if not value:
......
788 800
            value = get_request().get_field(self.field_key)
789 801
            if value and hasattr(value, 'token'):
790 802
                get_request().form[self.field_key + '$token'] = value.token
803
        kwargs['document_type'] = self.document_type
791 804

  
792 805
    def get_document_types(self):
793 806
        document_types = {
......
854 867
        if self.document_type and self.document_type.get('mimetypes'):
855 868
            old_value = self.document_type['mimetypes']
856 869
            self.document_type['mimetypes'] = '|'.join(self.document_type['mimetypes'])
870
        if self.document_type and self.document_type.get('metadata'):
871
            self.document_type['metadata'] = json.dumps(self.document_type['metadata'])
857 872
        result = super(FileField, self).export_to_xml(charset, include_id=include_id)
858 873
        if self.document_type and self.document_type.get('mimetypes'):
859 874
            self.document_type['mimetypes'] = old_value
875
        if self.document_type and self.document_type.get('metadata'):
876
            self.document_type['metadata'] = json.loads(self.document_type['metadata'])
860 877
        return result
861 878

  
862 879
    def init_with_xml(self, element, charset, include_id=False):
......
866 883
            self.document_type['mimetypes'] = self.document_type['mimetypes'].split('|')
867 884
        if self.document_type and self.document_type.get('fargo'):
868 885
            self.document_type['fargo'] = self.document_type['fargo'] == 'True'
886
        if self.document_type and self.document_type.get('metadata'):
887
            self.document_type['metadata'] = json.loads(self.document_type['metadata'])
888

  
889
    def store_structured_value(self, data, field_id):
890
        value = data.get(field_id)
891
        return getattr(value, 'metadata', {})
869 892

  
870 893

  
871 894
register_field_class(FileField)
wcs/file_validation.py
19 19
import hashlib
20 20
import urllib
21 21

  
22
from qommon.misc import http_get_page, json_loads
23
from quixote import get_publisher, get_response
24
from quixote.html import htmltext
22
from qommon import get_logger
23
from qommon.misc import http_get_page, json_loads, http_post_request, ConnectionError
24
from quixote import get_publisher, get_request
25 25

  
26 26

  
27 27
def has_file_validation():
28 28
    return get_publisher().get_site_option('fargo_url') is not None
29 29

  
30

  
30 31
def fargo_get(path):
31 32
    fargo_url = get_publisher().get_site_option('fargo_url')
32 33
    url = urlparse.urljoin(fargo_url, path)
......
35 36
        return json_loads(data)
36 37
    return None
37 38

  
39

  
38 40
def sha256_of_upload(upload):
39 41
    return hashlib.sha256(upload.get_content()).hexdigest()
40 42

  
43

  
41 44
def get_document_types():
42 45
    if not has_file_validation():
43 46
        return {}
44
    response = fargo_get('/document-types/')
45
    publisher = get_publisher()
47
    try:
48
        response = fargo_get('/document-types/')
49
    except ConnectionError:
50
        get_logger().warning('unable to retrieve document types from fargo')
51
        return {}
46 52
    if response.get('err') == 0:
47 53
        result = {}
48 54
        for schema in response['data']:
......
52 58
                'fargo': True,
53 59
            }
54 60
            if 'mimetypes' in schema:
55
                d['mimetypes'] = shema['mimetypes']
61
                d['mimetypes'] = schema['mimetypes']
56 62
            result[d['id']] = d
57

  
63
            d['metadata'] = schema.get('metadata', [])
58 64
        return result
59 65
    return {}
60 66

  
61
def validation_path(filled, field, upload):
62
    user = filled.get_user()
63
    if not user:
64
        return None
65
    if not user.name_identifiers:
66
        return None
67
    if not field.document_type or not field.document_type.get('fargo'):
67

  
68
def get_validation(url):
69
    try:
70
        response, status, data, auth_header = http_get_page(url)
71
    except ConnectionError:
72
        get_logger().warning('unable to retrieve validation from fargo')
68 73
        return None
69
    name_id = user.name_identifiers[0]
70
    sha_256 = sha256_of_upload(upload)
71
    document_type = field.document_type['id']
72
    path = '%s/%s/%s/' % (
73
        urllib.quote(name_id),
74
        urllib.quote(sha_256),
75
        urllib.quote(document_type),
76
    )
77
    return path
78

  
79
def validate_upload(filled, field, upload):
74
    if status == 200:
75
        return json_loads(data)['data']
76
    return None
77

  
78

  
79
def get_validations(document_type):
80
    request = get_request()
81
    document_type_id = document_type['id']
82
    qs = {}
83
    if not request.user:
84
        return []
85
    if request.user.name_identifiers:
86
        qs['user_nameid'] = request.user.name_identifiers[0]
87
    elif request.user.email:
88
        qs['user_email'] = request.user.email
89
    else:
90
        return []
91
    path = 'api/validation/%s/' % urllib.quote(document_type_id)
92
    try:
93
        validations = fargo_get(path + '?%s' % urllib.urlencode(qs))
94
    except ConnectionError:
95
        get_logger().warning('unable to retrieve validations from fargo')
96
        return []
97
    if validations and validations.get('data', {}).get('results', []):
98
        return validations['data']['results']
99
    return []
100

  
101

  
102
def is_valid(filled, field, upload):
80 103
    '''Check validation of the uploaded file with Fargo'''
81
    path = validation_path(filled, field, upload)
82
    if not path:
83
        return None
84
    response = fargo_get('metadata/' + path)
85
    if response is None:
86
        return None
87
    if response['err'] == 1:
88
        return False
89
    return response['data']
104
    return 'url' in getattr(upload, 'metadata', {})
90 105

  
91
def get_document_type_label(document_type):
92
    return get_document_types().get(document_type, {}).get('label')
93 106

  
94
def validation_link(filled, field, upload):
107
def validate(filled, field, upload):
95 108
    '''Compute link to Fargo to validate the given document'''
96
    path = validation_path(filled, field, upload)
97
    if not path:
98
        return ''
99
    get_response().add_css_include('../js/smoothness/jquery-ui-1.10.0.custom.min.css')
100
    get_response().add_javascript(['jquery-ui.js', 'jquery.js', 'fargo.js'])
101
    label = get_document_type_label(field.document_type['id'])
109
    document_type_id = field.document_type['id']
110
    path = 'api/validation/%s/' % urllib.quote(document_type_id)
102 111
    fargo_url = get_publisher().get_site_option('fargo_url')
103
    url = urlparse.urljoin(fargo_url, 'validation/' + path)
104
    next_url = get_publisher().get_frontoffice_url() + '/reload-top'
105
    url += '?next=%s' % urllib.quote(next_url)
106
    title = _('Validate as a %s') % label
107
    return htmltext('<a data-title="%(title)s" data-width="800" '
108
                    'data-height="500" href="%(url)s">%(title)s</a>') % {
109
        'title': title,
110
        'url': url,
111
    }
112
    url = urlparse.urljoin(fargo_url, path)
113
    payload = {}
114
    if filled.user:
115
        if filled.user.name_identifiers:
116
            payload['user_nameid'] = filled.user.name_identifiers[0]
117
        else:
118
            payload['user_email'] = filled.user.email
119
    payload['origin'] = get_request().get_server()
120
    payload['creator'] = get_request().user.display_name
121
    payload['content_hash'] = sha256_of_upload(upload)
122
    for meta_field in field.metadata:
123
        if 'varname' in meta_field:
124
            payload[meta_field['varname']] = upload.metadata.get(meta_field['varname'], '')
125
    headers = {'Content-Type': 'application/json'}
126
    try:
127
        response, status, response_payload, auth_header = http_post_request(url,
128
                                                                            json.dumps(payload),
129
                                                                            headers=headers)
130
    except ConnectionError:
131
        get_logger().warning('unable to validate document on fargo for %s', filled.get_display_id())
132
        return
133
    if status == 201:
134
        upload.metadata = json_loads(response_payload)['data']
135
        filled.data['%s_structured' % field.id] = upload.metadata
136
        filled.store()
wcs/forms/common.py
97 97
    def html_top(self, title = None):
98 98
        template.html_top(title = title, default_org = _('Forms'))
99 99

  
100
    def validate(self):
101
        if not file_validation.has_file_validation():
102
            return redirect('.')
103
        field_id = get_request().form.get('field_id')
104
        if not field_id:
105
            return redirect('.')
106
        for field in self.formdef.fields:
107
            if field.id == field_id:
108
                break
109
        else:
110
            return redirect('.')
111
        if field.key != 'file':
112
            return redirect('.')
113
        if not field.document_type.get('fargo', False):
114
            return redirect('.')
115
        if not field.document_type.get('metadata', []):
116
            return redirect('.')
117
        value = self.filled.data.get(field_id)
118
        file_validation.validate(self.filled, field, value)
119
        return redirect('.')
120

  
100 121
    def __init__(self, formdef, filled, register_workflow_subdirs=True):
101 122
        get_publisher().substitutions.feed(filled)
102 123
        self.formdef = formdef
......
612 633

  
613 634
    def display_file_field(self, form_url, field, value):
614 635
        r = TemplateIO(html=True)
615
        status = None
616
        if file_validation.has_file_validation():
617
            status = file_validation.validate_upload(self.filled, field, value)
618
            if status is False:
636
        validated = None
637
        is_fargo_dt = field.document_type.get('fargo', False)
638
        has_metadata = bool(field.document_type.get('metadata', []))
639
        if file_validation.has_file_validation() and is_fargo_dt and has_metadata:
640
            validated = file_validation.is_valid(self.filled, field, value)
641
            if validated is False:
619 642
                extra_class = ' invalid'
620
            elif status is None:
621
                extra_class = ''
622 643
            else:
623 644
                extra_class = ' valid'
624 645
            r += htmltext('<div class="value%s">' % extra_class)
......
627 648
        s = field.get_view_value(value)
628 649
        s = s.replace(str('[download]'), str('%sdownload' % form_url))
629 650
        r += s
630
        if status is not None and get_request().is_in_backoffice():
631
            if status is False:
632
                r += htmltext(' <span class="validation-status">%s</span>') % _('not validated')
651
        if validated is not None and get_request().is_in_backoffice():
633 652
            r += htmltext('<div class="file-validation">')
634
            if status:
635
                r += htmltext('<p class="validation-status">%s</p>') % _(
636
                        '%(label)s validated by %(creator)s on %(created)s') % status
637
                r += htmltext('<ul>')
638
                for meta in status['metadata']:
639
                    r += htmltext(_('<li>%(label)s: %(value)s</li>')) % {
640
                        'label': meta['label'],
641
                        'value': meta['value']
642
                    }
643
                r += htmltext('</ul>')
653
            if validated:
654
                r += htmltext('<p class="validation-validated">%s</p>') % _(
655
                    'validated by %(creator)s on %(created)s') % value.metadata
644 656
                r += htmltext('<p>%s</p>') % (_('Valid from %(start)s to %(end)s') % {
645
                    'start': status['start'],
646
                    'end': status['end'],
657
                    'start': value.metadata['start'],
658
                    'end': value.metadata['end'],
647 659
                })
648
            else:
649
                r += file_validation.validation_link(self.filled, field, value)
660
            elif self.filled.user:
661
                r += htmltext('<form method="post" action="./validate?field_id=%s">'
662
                              '<button>%s</button></form>') % (
663
                    field.id, _('Validate'))
650 664
            r += htmltext('</div>')
651 665
        r += htmltext('</div>')
652 666
        return str(r)
wcs/qommon/form.py
60 60
import misc
61 61
from strftime import strftime
62 62
from publisher import get_cfg
63
from wcs import file_validation
63 64

  
64 65
QuixoteForm = Form
65 66

  
......
582 583
            self.value = None
583 584

  
584 585

  
586
class NoUpload(object):
587
    no_file = True
588
    metadata = None
589

  
590
    def __init__(self, validation_url):
591
        self.metadata = file_validation.get_validation(validation_url)
592

  
593

  
585 594
class FileWithPreviewWidget(CompositeWidget):
586 595
    """Widget that proposes a File Upload widget but that stores the file
587 596
    ondisk so it has a "readonly" mode where the filename is shown."""
......
590 599
    max_file_size = None
591 600
    file_type = None
592 601

  
593
    max_file_size_bytes = None # will be filled automatically
602
    max_file_size_bytes = None  # will be filled automatically
594 603

  
595 604
    def __init__(self, name, value=None, **kwargs):
605
        from wcs import fields
606

  
596 607
        CompositeWidget.__init__(self, name, value, **kwargs)
597 608
        self.value = value
598
        self.preview = kwargs.get('readonly')
609
        self.document_type = kwargs.pop('document_type', None) or {}
610
        self.readonly = kwargs.get('readonly')
599 611
        self.max_file_size = kwargs.pop('max_file_size', None)
600 612
        self.allow_portfolio_picking = kwargs.pop('allow_portfolio_picking', True)
601 613
        if self.max_file_size:
602 614
            self.max_file_size_bytes = FileSizeWidget.parse_file_size(self.max_file_size)
603 615
        self.add(HiddenWidget, 'token')
604
        if not self.preview:
616
        if not self.readonly:
605 617
            attrs = {'data-url': get_publisher().get_root_url() + 'tmp-upload'}
606 618
            self.file_type = kwargs.pop('file_type', None)
607 619
            if self.file_type:
......
610 622
                # this could be used for client size validation of file size
611 623
                attrs['data-max-file-size'] = str(self.max_file_size_bytes)
612 624
            self.add(FileWidget, 'file', render_br=False, attrs=attrs)
625
        if self.document_type.get('metadata'):
626
            if self.readonly:
627
                self.add(HiddenWidget, 'validation_url')
628
            else:
629
                validations = file_validation.get_validations(self.document_type)
630
                if validations:
631
                    options = [('', _('Known documents'), '')]
632
                    options += [(v['url'], v['display'], v['url']) for v in validations]
633
                    self.add(SingleSelectWidget, 'validation_url', options=options,
634
                             attrs={'data-validations': json.dumps(validations)})
635
            for i, meta_field in enumerate(self.metadata):
636
                field = fields.get_field_class_by_type(meta_field.get('type', 'string'))()
637
                field.id = i
638
                field.init_with_json(meta_field, include_id=False)
639
                if meta_field.get('varname'):
640
                    subvalue = getattr(value, 'metadata', {}).get(meta_field['varname'])
641
                else:
642
                    subvalue = None
643
                # required only if composite field is required
644
                field.required = field.required and self.required
645
                field.extra_css_class = 'subwidget'
646
                if self.readonly:
647
                    # preview
648
                    field.add_to_view_form(self, subvalue)
649
                else:
650
                    field.add_to_form(self, subvalue)
613 651
        if value:
614 652
            self.set_value(value)
615 653

  
......
625 663
                self.get_widget('token').set_value(self.value.token)
626 664

  
627 665
    def render_content(self):
628
        get_response().add_javascript(['jquery.js', 'jquery-ui.js',
629
                        'jquery.iframe-transport.js', 'jquery.fileupload.js',
630
                        'qommon.fileupload.js'])
666
        get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'jquery.iframe-transport.js',
667
                                       'jquery.fileupload.js', 'qommon.fileupload.js'])
631 668

  
632 669
        temp = get_session().get_tempfile(self.get('token')) or {}
633 670

  
......
638 675
        r += htmltext('<div class="fileprogress" style="display: none;">')
639 676
        r += htmltext(' <div class="bar">%s</div>' % _('Upload in progress...'))
640 677
        r += htmltext('</div>')
641
        r += htmltext('<div class="fileinfo"><span class="filename">%s</span>' % temp.get('base_filename', ''))
642
        if not self.preview:
643
            r += htmltext(' <a href="#" class="remove" title="%s">%s</a>' % (
644
                                    _('Remove this file'),
645
                                    _('remove')))
678
        r += htmltext('<div class="fileinfo"><span class="filename">%s</span>'
679
                      % temp.get('base_filename', ''))
680
        if not self.readonly:
681
            r += htmltext(' <a href="#" class="remove" title="%s">%s</a>'
682
                          % (_('Remove this file'), _('remove')))
646 683
        elif temp:
647 684
            filetype = mimetypes.guess_type(temp.get('orig_filename', ''))
648 685
            if filetype and filetype[0] and filetype[0].startswith('image'):
649
                r += htmltext('<img alt="" src="tempfile?t=%s&thumbnail=1" />' % \
650
                                             self.get('token'))
686
                r += htmltext('<img alt="" src="tempfile?t=%s&thumbnail=1" />' %
687
                              self.get('token'))
651 688

  
652 689
        r += htmltext('</div>')
653 690
        return r.getvalue()
654 691

  
692
    @property
693
    def metadata(self):
694
        return self.document_type.get('metadata', [])
695

  
655 696
    def _parse(self, request):
656 697
        self.value = None
657 698
        if self.get('token'):
658 699
            token = self.get('token')
700
        elif self.get('validation_url'):
701
            self.value = NoUpload(self.get('validation_url'))
702
            return
659 703
        elif self.get('file'):
660 704
            token = get_session().add_tempfile(self.get('file'))
661 705
            request.form[self.get_widget('token').get_name()] = token
......
664 708

  
665 709
        session = get_session()
666 710
        if token and session.tempfiles and session.tempfiles.has_key(token):
667
            temp = session.tempfiles[token]
668 711
            self.value = session.get_tempfile_content(token)
669 712

  
670 713
        if self.value is None:
671
            # there's no file, the other checks are irrelevant.
714
            # there's no file, check all metadata field are empty too
715
            # if not file and required metadata field become required
716
            if (self.get_widget('file')
717
                    and not self.required
718
                    and any([self.get('f%s' % i) for i, meta_field in enumerate(self.metadata)])):
719
                self.get_widget('file').required = True
720
                for i, meta_field in enumerate(self.metadata):
721
                    name = 'f%s' % i
722
                    required = meta_field.get('required', True)
723
                    if required:
724
                        widget = self.get_widget(name)
725
                        widget.required = True
726
                        if not self.get(name):
727
                            widget.set_error(self.REQUIRED_ERROR)
672 728
            return
673 729

  
730
        # There is some file, check all required metadata files have been filled
731
        for i, meta_field in enumerate(self.metadata):
732
            name = 'f%s' % i
733
            required = meta_field.get('required', True)
734
            if required:
735
                widget = self.get_widget(name)
736
                widget.required = True
737
                if not self.get(name):
738
                    widget.set_error(self.REQUIRED_ERROR)
739

  
740
        if self.metadata:
741
            self.value.metadata = {}
742
            for i, meta_field in enumerate(self.metadata):
743
                name = 'f%s' % i
744
                if 'varname' in meta_field:
745
                    self.value.metadata[meta_field['varname']] = self.get(name)
746

  
674 747
        # Don't trust the browser supplied MIME type, update the Upload object
675 748
        # with a MIME type created with magic (or based on the extension if the
676 749
        # 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

  
90 100
div.form .title, form.quixote .title {
91 101
	font-weight: bold;
92 102
}
wcs/qommon/static/js/qommon.fileupload.js
6 6
        } else {
7 7
            $(base_widget).find('.fileinfo').hide();
8 8
        }
9
        function disable() {
10
            base_widget.find('.subwidget input').prop('disabled', true);
11
            $('form').on('submit', function () {
12
                $('input').prop('disabled', false);
13
            });
14
        }
15
        function enable() {
16
            if (base_widget.find('.subwidget input:disabled').length) {
17
                base_widget.find('.subwidget input').val('');
18
                base_widget.find('.subwidget input').prop('disabled', false);
19
            }
20
        }
9 21
        $(this).find('input[type=file]').fileupload({
10 22
            dataType: 'json',
11 23
            add: function (e, data) {
......
19 31
                $(base_widget).find('.fileprogress').hide();
20 32
                $(base_widget).find('.filename').text(data.result[0].name);
21 33
                $(base_widget).find('.fileinfo').show();
22
                $(base_widget).find('input[type=hidden]').val(data.result[0].token);
34
                $(base_widget).find('input[name$="$token"]').val(data.result[0].token);
35
                $(base_widget).find('select[name$="$validation_url"]').val('');
36
                enable();
23 37
                $(base_widget).parents('form').find('input[name=submit]').prop('disabled', false);
24 38
                $(this).hide();
25 39
            },
......
29 43
            }
30 44
        });
31 45
        $(this).find('a.remove').click(function() {
32
            $(base_widget).find('input[type=hidden]').val('');
46
            $(base_widget).find('input[name$="$token"]').val('');
33 47
            $(base_widget).find('.fileinfo').hide();
34 48
            $(base_widget).find('input[type=file]').show();
35 49
            return false;
......
38 52
            $(base_widget).find('input[type=file]').click();
39 53
            return false;
40 54
        });
55
        if ($(this).find('select[name$="$validation_url"] option:selected').val()) {
56
            disable();
57
        }
58
        $(this).find('select[name$="$validation_url"]').on('change', function() {
59
            var url = $(this).find(':selected').val();
60
            if (url) {
61
                var validations = $(this).data('validations');
62

  
63
                for (var i = 0; i < validations.length; i++) {
64
                    if (validations[i].url == url) {
65
                        base_widget.find('a.remove').trigger('click');
66
                        for (var item in validations[i]) {
67
                            if (! item) {
68
                                continue;
69
                            }
70
                            var $input = base_widget.find('input[name$="$' + item + '"]');
71
                            if ($input.length) {
72
                                $input.val(validations[i][item]);
73
                            }
74
                        }
75
                        disable();
76
                    }
77
                }
78
            } else {
79
                enable();
80
            }
81
        });
41 82
    });
42 83
});
43
-