0002-rewrite-file-validation-10444.patch
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:10Z', |
|
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:10Z', |
|
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.http_get_page') as http_get_page: |
|
1860 |
http_get_page.return_value = None, 200, json.dumps(return_value), None |
|
1861 |
resp = app.get('/form-title/') |
|
1862 |
http_get_page.assert_called_once_with( |
|
1863 |
'http://fargo.example.net/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.http_get_page') as http_get_page: |
|
1869 |
return_value2 = { |
|
1870 |
'result': 1, |
|
1871 |
'data': validation, |
|
1872 |
} |
|
1873 | ||
1874 |
def side_effect(url): |
|
1875 |
if url == 'zob': |
|
1876 |
return None, 200, json.dumps(return_value2), None |
|
1877 |
else: |
|
1878 |
return None, 200, json.dumps(return_value), None |
|
1879 |
http_get_page.side_effect = side_effect |
|
1880 |
resp = resp.forms[0].submit('submit') |
|
1881 |
assert http_get_page.call_args_list[0][0][0].endswith( |
|
1882 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
1883 |
assert http_get_page.call_args_list[1][0][0] == 'zob' |
|
1884 |
assert http_get_page.call_args_list[2][0][0] == 'zob' |
|
1885 |
for key in metadata: |
|
1886 |
assert 'value="%s"' % metadata[key] in resp.body |
|
1887 |
assert 'Check values then click submit.' in resp.body |
|
1888 |
resp = resp.forms[0].submit('submit').follow() |
|
1889 |
assert formdef.data_class().count() == 1 |
|
1890 |
formdata = formdef.data_class().select()[0] |
|
1891 |
form_id = formdata.id |
|
1892 |
assert formdata.data['0'].metadata == validation |
|
1893 |
assert formdata.data['0_structured'] == validation |
|
1894 |
for metadata_field in document_type['metadata']: |
|
1895 | ||
1896 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']]) |
|
1897 |
assert fragment in resp.body |
|
1898 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
1899 |
assert not 'Validate' in resp.body |
|
1900 |
for metadata_field in document_type['metadata']: |
|
1901 | ||
1902 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['varname']]) |
|
1903 |
assert fragment in resp.body |
|
1904 |
assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body |
|
1905 | ||
1721 | 1906 | |
1722 | 1907 |
def test_360_user_view(pub): |
1723 | 1908 |
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 : %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 | ||
---|---|---|
17 | 17 |
import sys |
18 | 18 |
import hashlib |
19 | 19 |
import urlparse |
20 |
import time |
|
20 | 21 | |
21 | 22 |
from quixote import get_publisher, get_request, get_response, get_session, redirect |
22 | 23 |
from quixote.directory import Directory |
... | ... | |
97 | 98 |
def html_top(self, title = None): |
98 | 99 |
template.html_top(title = title, default_org = _('Forms')) |
99 | 100 | |
101 |
def validate(self): |
|
102 |
if not file_validation.has_file_validation(): |
|
103 |
return redirect('.') |
|
104 |
field_id = get_request().form.get('field_id') |
|
105 |
if not field_id: |
|
106 |
return redirect('.') |
|
107 |
for field in self.formdef.fields: |
|
108 |
if field.id == field_id: |
|
109 |
break |
|
110 |
else: |
|
111 |
return redirect('.') |
|
112 |
if field.key != 'file': |
|
113 |
return redirect('.') |
|
114 |
if not field.document_type.get('fargo', False): |
|
115 |
return redirect('.') |
|
116 |
if not field.document_type.get('metadata', []): |
|
117 |
return redirect('.') |
|
118 |
value = self.filled.data.get(field_id) |
|
119 |
file_validation.validate(self.filled, field, value) |
|
120 |
return redirect('.') |
|
121 | ||
100 | 122 |
def __init__(self, formdef, filled, register_workflow_subdirs=True): |
101 | 123 |
get_publisher().substitutions.feed(filled) |
102 | 124 |
self.formdef = formdef |
... | ... | |
612 | 634 | |
613 | 635 |
def display_file_field(self, form_url, field, value): |
614 | 636 |
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: |
|
637 |
validated = None |
|
638 |
is_fargo_dt = field.document_type.get('fargo', False) |
|
639 |
has_metadata = bool(field.document_type.get('metadata', [])) |
|
640 |
if file_validation.has_file_validation() and is_fargo_dt and has_metadata: |
|
641 |
validated = file_validation.is_valid(self.filled, field, value) |
|
642 |
if validated is False: |
|
619 | 643 |
extra_class = ' invalid' |
620 |
elif status is None: |
|
621 |
extra_class = '' |
|
622 | 644 |
else: |
623 | 645 |
extra_class = ' valid' |
624 | 646 |
r += htmltext('<div class="value%s">' % extra_class) |
... | ... | |
627 | 649 |
s = field.get_view_value(value) |
628 | 650 |
s = s.replace(str('[download]'), str('%sdownload' % form_url)) |
629 | 651 |
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') |
|
652 |
if validated is not None and get_request().is_in_backoffice(): |
|
633 | 653 |
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>') |
|
654 |
if validated: |
|
655 |
creator = value.metadata['creator'] |
|
656 |
created = misc.localstrftime( |
|
657 |
time.localtime( |
|
658 |
misc.parse_isotime(value.metadata['created']))) |
|
659 |
r += htmltext('<p class="validation-validated">%s</p>') % _( |
|
660 |
'validated by %(creator)s on %(created)s') % { |
|
661 |
'creator': creator, 'created': created} |
|
644 | 662 |
r += htmltext('<p>%s</p>') % (_('Valid from %(start)s to %(end)s') % { |
645 |
'start': status['start'], |
|
646 |
'end': status['end'], |
|
663 |
'start': strftime( |
|
664 |
misc.date_format(), |
|
665 |
misc.get_as_datetime(value.metadata['start'])), |
|
666 |
'end': strftime( |
|
667 |
misc.date_format(), |
|
668 |
misc.get_as_datetime(value.metadata['end'])), |
|
647 | 669 |
}) |
648 |
else: |
|
649 |
r += file_validation.validation_link(self.filled, field, value) |
|
670 |
elif self.filled.user: |
|
671 |
r += htmltext('<form method="post" action="./validate?field_id=%s">' |
|
672 |
'<button>%s</button></form>') % ( |
|
673 |
field.id, _('Validate')) |
|
650 | 674 |
r += htmltext('</div>') |
651 | 675 |
r += htmltext('</div>') |
652 | 676 |
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 |
for validation in validations: |
|
634 |
for i, meta_field in enumerate(self.metadata): |
|
635 |
# translate varname to f<self.id>$f<subwidget.id> |
|
636 |
if meta_field['varname'] in validation: |
|
637 |
value = validation.pop(meta_field['varname']) |
|
638 |
validation['f%s' % i] = value |
|
639 |
self.add(SingleSelectWidget, 'validation_url', options=options, |
|
640 |
attrs={'data-validations': json.dumps(validations)}) |
|
641 |
for i, meta_field in enumerate(self.metadata): |
|
642 |
field = fields.get_field_class_by_type(meta_field.get('type', 'string'))() |
|
643 |
field.id = i |
|
644 |
field.init_with_json(meta_field, include_id=False) |
|
645 |
if meta_field.get('varname'): |
|
646 |
subvalue = getattr(value, 'metadata', {}).get(meta_field['varname']) |
|
647 |
else: |
|
648 |
subvalue = None |
|
649 |
# required only if composite field is required |
|
650 |
field.required = field.required and self.required |
|
651 |
field.extra_css_class = 'subwidget' |
|
652 |
if self.readonly: |
|
653 |
# preview |
|
654 |
field.add_to_view_form(self, subvalue) |
|
655 |
else: |
|
656 |
field.add_to_form(self, subvalue) |
|
613 | 657 |
if value: |
614 | 658 |
self.set_value(value) |
615 | 659 | |
... | ... | |
625 | 669 |
self.get_widget('token').set_value(self.value.token) |
626 | 670 | |
627 | 671 |
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']) |
|
672 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'jquery.iframe-transport.js', |
|
673 |
'jquery.fileupload.js', 'qommon.fileupload.js']) |
|
631 | 674 | |
632 | 675 |
temp = get_session().get_tempfile(self.get('token')) or {} |
633 | 676 | |
634 | 677 |
r = TemplateIO(html=True) |
635 |
for widget in self.get_widgets():
|
|
636 |
r += widget.render()
|
|
678 |
if self.get_widget('file'):
|
|
679 |
r += self.get_widget('file').render()
|
|
637 | 680 | |
638 | 681 |
r += htmltext('<div class="fileprogress" style="display: none;">') |
639 | 682 |
r += htmltext(' <div class="bar">%s</div>' % _('Upload in progress...')) |
640 | 683 |
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')))
|
|
684 |
r += htmltext('<div class="fileinfo"><span class="filename">%s</span>' |
|
685 |
% temp.get('base_filename', ''))
|
|
686 |
if not self.readonly:
|
|
687 |
r += htmltext(' <a href="#" class="remove" title="%s">%s</a>'
|
|
688 |
% (_('Remove this file'), _('remove')))
|
|
646 | 689 |
elif temp: |
647 | 690 |
filetype = mimetypes.guess_type(temp.get('orig_filename', '')) |
648 | 691 |
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')) |
|
651 | ||
692 |
r += htmltext('<img alt="" src="tempfile?t=%s&thumbnail=1" />' % |
|
693 |
self.get('token')) |
|
652 | 694 |
r += htmltext('</div>') |
695 | ||
696 |
for widget in self.get_widgets(): |
|
697 |
if widget is self.get_widget('file'): |
|
698 |
continue |
|
699 |
r += widget.render() |
|
700 | ||
653 | 701 |
return r.getvalue() |
654 | 702 | |
703 |
@property |
|
704 |
def metadata(self): |
|
705 |
return self.document_type.get('metadata', []) |
|
706 | ||
655 | 707 |
def _parse(self, request): |
656 | 708 |
self.value = None |
657 | 709 |
if self.get('token'): |
658 | 710 |
token = self.get('token') |
711 |
elif self.get('validation_url'): |
|
712 |
self.value = NoUpload(self.get('validation_url')) |
|
713 |
return |
|
659 | 714 |
elif self.get('file'): |
660 | 715 |
token = get_session().add_tempfile(self.get('file')) |
661 | 716 |
request.form[self.get_widget('token').get_name()] = token |
... | ... | |
664 | 719 | |
665 | 720 |
session = get_session() |
666 | 721 |
if token and session.tempfiles and session.tempfiles.has_key(token): |
667 |
temp = session.tempfiles[token] |
|
668 | 722 |
self.value = session.get_tempfile_content(token) |
669 | 723 | |
670 | 724 |
if self.value is None: |
671 |
# there's no file, the other checks are irrelevant. |
|
725 |
# there's no file, check all metadata field are empty too |
|
726 |
# if not file and required metadata field become required |
|
727 |
if (self.get_widget('file') |
|
728 |
and not self.required |
|
729 |
and any([self.get('f%s' % i) for i, meta_field in enumerate(self.metadata)])): |
|
730 |
self.get_widget('file').required = True |
|
731 |
self.get_widget('file').set_error(self.REQUIRED_ERROR) |
|
732 |
for i, meta_field in enumerate(self.metadata): |
|
733 |
name = 'f%s' % i |
|
734 |
required = meta_field.get('required', True) |
|
735 |
if required: |
|
736 |
widget = self.get_widget(name) |
|
737 |
widget.required = True |
|
738 |
if not self.get(name): |
|
739 |
widget.set_error(self.REQUIRED_ERROR) |
|
672 | 740 |
return |
673 | 741 | |
742 |
# There is some file, check all required metadata files have been filled |
|
743 |
for i, meta_field in enumerate(self.metadata): |
|
744 |
name = 'f%s' % i |
|
745 |
required = meta_field.get('required', True) |
|
746 |
if required: |
|
747 |
widget = self.get_widget(name) |
|
748 |
widget.required = True |
|
749 |
if not self.get(name): |
|
750 |
widget.set_error(self.REQUIRED_ERROR) |
|
751 | ||
752 |
if self.metadata: |
|
753 |
self.value.metadata = {} |
|
754 |
for i, meta_field in enumerate(self.metadata): |
|
755 |
name = 'f%s' % i |
|
756 |
if 'varname' in meta_field: |
|
757 |
self.value.metadata[meta_field['varname']] = self.get(name) |
|
758 | ||
674 | 759 |
# Don't trust the browser supplied MIME type, update the Upload object |
675 | 760 |
# with a MIME type created with magic (or based on the extension if the |
676 | 761 |
# 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 |
- |