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, |
... | ... | |
1577 | 1578 |
assert resp.body == 'foo(%s);' % menu_json_str |
1578 | 1579 |
assert resp.headers['content-type'] == 'application/javascript' |
1579 | 1580 | |
1581 |
def test_backoffice_file_field_fargo_no_metadata(pub, fargo_url): |
|
1582 |
document_type = { |
|
1583 |
'id': 'justificatif-de-domicile', |
|
1584 |
'fargo': True, |
|
1585 |
'mimetypes': ['application/pdf'], |
|
1586 |
'label': 'Justificatif de domicile', |
|
1587 |
} |
|
1588 |
user = create_user(pub, is_admin=True) |
|
1589 |
user.name_identifiers = ['12345'] |
|
1590 |
user.store() |
|
1591 |
FormDef.wipe() |
|
1592 |
formdef = FormDef() |
|
1593 |
formdef.name = 'form title' |
|
1594 |
formdef.fields = [fields.FileField( |
|
1595 |
id='0', label='1st field', type='file', |
|
1596 |
document_type=document_type)] |
|
1597 |
formdef.store() |
|
1598 |
formdef.data_class().wipe() |
|
1599 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
|
1600 |
digest = hashlib.sha256('%PDF-1.4').hexdigest() |
|
1601 |
app = login(get_app(pub)) |
|
1602 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
1603 |
resp = app.get('/form-title/') |
|
1604 |
assert fargo_get.call_count == 0 |
|
1605 |
resp.forms[0]['f0$file'] = upload |
|
1606 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
1607 |
resp = resp.forms[0].submit('submit') |
|
1608 |
assert fargo_get.call_count == 0 |
|
1609 |
assert 'Check values then click submit.' in resp.body |
|
1610 |
resp = resp.forms[0].submit('submit').follow() |
|
1611 |
assert formdef.data_class().count() == 1 |
|
1612 |
formdata = formdef.data_class().select()[0] |
|
1613 |
form_id = formdata.id |
|
1614 |
assert not hasattr(formdata.data['0'], 'metadata') |
|
1615 |
assert not '0_structured' in formdata.data |
|
1616 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
1617 |
assert not 'Validate' in resp.body |
|
1618 |
with mock.patch('wcs.file_validation.http_post_request') as http_post_request: |
|
1619 |
resp = app.get('/backoffice/management/form-title/%s/validate?field_id=0' % form_id) |
|
1620 |
assert http_post_request.call_count == 0 |
|
1621 |
resp = resp.follow() |
|
1622 |
assert not 'Valid ' in resp.body |
|
1623 |
assert not 'Validate' in resp.body |
|
1624 | ||
1625 | ||
1580 | 1626 |
def test_backoffice_file_field_validation(pub, fargo_url): |
1627 |
document_type = { |
|
1628 |
'id': 'justificatif-de-domicile', |
|
1629 |
'fargo': True, |
|
1630 |
'mimetypes': ['application/pdf'], |
|
1631 |
'label': 'Justificatif de domicile', |
|
1632 |
'metadata': [ |
|
1633 |
{'label': 'Nom', 'name': 'nom'}, |
|
1634 |
{'label': 'Prénom(s)', 'name': 'prenoms'}, |
|
1635 |
{'label': 'Numéro', 'name': 'numero'}, |
|
1636 |
{'label': 'Rue', 'name': 'rue'}, |
|
1637 |
{'label': 'Code postal', 'name': 'code-postal'}, |
|
1638 |
{'label': 'Ville', 'name': 'ville'}, |
|
1639 |
], |
|
1640 |
} |
|
1641 |
metadata = { |
|
1642 |
'nom': 'Doe', |
|
1643 |
'prenoms': 'John', |
|
1644 |
'numero': '169', |
|
1645 |
'rue': 'rue du château', |
|
1646 |
'code-postal': '75014', |
|
1647 |
'ville': 'PARIS', |
|
1648 |
} |
|
1581 | 1649 |
user = create_user(pub, is_admin=True) |
1582 | 1650 |
user.name_identifiers = ['12345'] |
1583 | 1651 |
user.store() |
... | ... | |
1586 | 1654 |
formdef.name = 'form title' |
1587 | 1655 |
formdef.fields = [fields.FileField( |
1588 | 1656 |
id='0', label='1st field', type='file', |
1589 |
document_type={ |
|
1590 |
'id': 'justificatif-de-domicile', |
|
1591 |
'fargo': True, |
|
1592 |
'mimetypes': ['application/pdf'], |
|
1593 |
'label': 'PDF files', |
|
1594 |
}) |
|
1595 |
] |
|
1657 |
document_type=document_type)] |
|
1596 | 1658 |
formdef.store() |
1597 | 1659 |
formdef.data_class().wipe() |
1598 | 1660 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
1599 | 1661 |
digest = hashlib.sha256('%PDF-1.4').hexdigest() |
1600 | 1662 |
app = login(get_app(pub)) |
1601 |
resp = app.get('/form-title/') |
|
1663 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
1664 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
1665 |
resp = app.get('/form-title/') |
|
1666 |
fargo_get.assert_called_once_with( |
|
1667 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
1602 | 1668 |
resp.forms[0]['f0$file'] = upload |
1603 |
resp = resp.forms[0].submit('submit') |
|
1669 |
for key in metadata: |
|
1670 |
resp.forms[0]['f0$%s' % key] = metadata[key] |
|
1671 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
1672 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
1673 |
resp = resp.forms[0].submit('submit') |
|
1674 |
fargo_get.assert_called_once_with( |
|
1675 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
1676 |
for key in metadata: |
|
1677 |
assert 'value="%s"' % metadata[key] in resp.body |
|
1604 | 1678 |
assert 'Check values then click submit.' in resp.body |
1605 |
resp = resp.forms[0].submit('submit') |
|
1606 |
assert resp.status_int == 302 |
|
1679 |
resp = resp.forms[0].submit('submit').follow() |
|
1680 |
for metadata_field in document_type['metadata']: |
|
1681 | ||
1682 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['name']]) |
|
1683 |
assert fragment in resp.body |
|
1607 | 1684 |
assert formdef.data_class().count() == 1 |
1608 |
form_id = formdef.data_class().select()[0].id |
|
1609 |
with mock.patch('wcs.file_validation.http_get_page') as http_get_page: |
|
1610 |
json_response = json.dumps({ |
|
1611 |
'err': 0, |
|
1612 |
'data': { |
|
1613 |
'type': 'justificatif-de-domicile', |
|
1614 |
'label': 'Justificatif de domicile', |
|
1615 |
'creator': 'Jean Bono', |
|
1616 |
'created': '2014-01-01T01:01:01', |
|
1617 |
'start': '2014-01-01T01:01:01', |
|
1618 |
'end': '2014-01-01T01:01:01', |
|
1619 |
'metadata': [{ |
|
1620 |
'name': 'code_postal', |
|
1621 |
'label': 'Code postal', |
|
1622 |
'value': '13400', |
|
1623 |
}], |
|
1624 |
}, |
|
1685 |
formdata = formdef.data_class().select()[0] |
|
1686 |
form_id = formdata.id |
|
1687 |
assert formdata.data['0'].metadata == metadata |
|
1688 |
assert formdata.data['0_structured'] == metadata |
|
1689 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
1690 |
assert 'Validate' in resp.body |
|
1691 |
for metadata_field in document_type['metadata']: |
|
1692 | ||
1693 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['name']]) |
|
1694 |
assert fragment in resp.body |
|
1695 |
with mock.patch('wcs.file_validation.http_post_request') as http_post_request: |
|
1696 |
payload = { |
|
1697 |
'user_nameid': '12345', |
|
1698 |
'origin': 'example.net', |
|
1699 |
'creator': 'admin', |
|
1700 |
'content_hash': digest, |
|
1701 |
} |
|
1702 |
payload.update(metadata) |
|
1703 |
result = { |
|
1704 |
'result': 1, |
|
1705 |
'data': payload.copy() |
|
1706 |
} |
|
1707 |
result['data'].update({ |
|
1708 |
'url': 'zob', |
|
1709 |
'created': '1970-01-01T10:10:10', |
|
1710 |
'start': '1970-01-01', |
|
1711 |
'end': '1978-01-01', |
|
1712 |
'display': 'John Doe, 169 rue du château, 75014 PARIS' |
|
1625 | 1713 |
}) |
1626 |
http_get_page.return_value = None, 200, json_response, None |
|
1627 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
1628 |
http_get_page.assert_called_once_with('http://fargo.example.net/metadata/12345/%s/justificatif-de-domicile/' % digest) |
|
1629 |
assert 'class="value valid"' in resp.body |
|
1630 |
assert 'Justificatif de domicile validated by Jean Bono on 2014-01-01T01:01:01' in resp.body |
|
1631 |
assert 'Code postal: 13400' in resp.body |
|
1632 |
assert 'Valid from 2014-01-01T01:01:01 to 2014-01-01T01:01:01' in resp.body |
|
1714 |
http_post_request.return_value = None, 201, json.dumps(result), None |
|
1715 |
resp = app.get('/backoffice/management/form-title/%s/validate?field_id=0' % form_id) |
|
1716 |
assert http_post_request.call_count == 1 |
|
1717 |
assert http_post_request.call_args[0][0] == 'http://fargo.example.net/api/validation/justificatif-de-domicile/' |
|
1718 |
assert json_loads(http_post_request.call_args[0][1]) == payload |
|
1719 |
assert http_post_request.call_args[1] == {'headers': {'Content-Type': 'application/json'}} |
|
1720 |
resp = resp.follow() |
|
1721 | ||
1722 |
assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body |
|
1723 | ||
1724 | ||
1725 |
def test_backoffice_file_validation_no_upload(pub, fargo_url): |
|
1726 |
document_type = { |
|
1727 |
'id': 'justificatif-de-domicile', |
|
1728 |
'fargo': True, |
|
1729 |
'mimetypes': ['application/pdf'], |
|
1730 |
'label': 'Justificatif de domicile', |
|
1731 |
'metadata': [ |
|
1732 |
{'label': 'Nom', 'name': 'nom'}, |
|
1733 |
{'label': 'Prénom(s)', 'name': 'prenoms'}, |
|
1734 |
{'label': 'Numéro', 'name': 'numero'}, |
|
1735 |
{'label': 'Rue', 'name': 'rue'}, |
|
1736 |
{'label': 'Code postal', 'name': 'code-postal'}, |
|
1737 |
{'label': 'Ville', 'name': 'ville'}, |
|
1738 |
], |
|
1739 |
} |
|
1740 |
metadata = { |
|
1741 |
'nom': 'Doe', |
|
1742 |
'prenoms': 'John', |
|
1743 |
'numero': '169', |
|
1744 |
'rue': 'rue du château', |
|
1745 |
'code-postal': '75014', |
|
1746 |
'ville': 'PARIS', |
|
1747 |
} |
|
1748 |
validation = { |
|
1749 |
'url': 'zob', |
|
1750 |
'creator': 'admin', |
|
1751 |
'created': '1970-01-01T10:10:10', |
|
1752 |
'start': '1970-01-01', |
|
1753 |
'end': '1978-01-01', |
|
1754 |
'display': 'John Doe, 169 rue du château, 75014 PARIS' |
|
1755 |
} |
|
1756 |
validation.update(metadata) |
|
1757 | ||
1758 |
user = create_user(pub, is_admin=True) |
|
1759 |
user.name_identifiers = ['12345'] |
|
1760 |
user.store() |
|
1761 |
FormDef.wipe() |
|
1762 |
formdef = FormDef() |
|
1763 |
formdef.name = 'form title' |
|
1764 |
formdef.fields = [fields.FileField( |
|
1765 |
id='0', label='1st field', type='file', |
|
1766 |
document_type=document_type)] |
|
1767 |
formdef.store() |
|
1768 |
formdef.data_class().wipe() |
|
1769 |
app = login(get_app(pub)) |
|
1770 |
return_value = {'result': 1, 'data': {'results': [validation]}} |
|
1771 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
1772 |
fargo_get.return_value = return_value |
|
1773 |
resp = app.get('/form-title/') |
|
1774 |
fargo_get.assert_called_once_with( |
|
1775 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
1776 |
assert validation['display'] in resp.body |
|
1777 |
resp.forms[0]['f0$validation_url'] = 'zob' |
|
1778 |
for key in metadata: |
|
1779 |
resp.forms[0]['f0$%s' % key] = metadata[key] |
|
1780 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get, \ |
|
1781 |
mock.patch('wcs.file_validation.http_get_page') as http_get_page: |
|
1782 |
fargo_get.return_value = return_value |
|
1783 |
return_value = { |
|
1784 |
'result': 1, |
|
1785 |
'data': validation, |
|
1786 |
} |
|
1787 |
http_get_page.return_value = None, 200, json.dumps(return_value), None |
|
1788 |
resp = resp.forms[0].submit('submit') |
|
1789 |
fargo_get.assert_called_once_with( |
|
1790 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
1791 |
http_get_page.assert_called_with('zob') |
|
1792 |
for key in metadata: |
|
1793 |
assert 'value="%s"' % metadata[key] in resp.body |
|
1794 |
assert 'Check values then click submit.' in resp.body |
|
1795 |
resp = resp.forms[0].submit('submit').follow() |
|
1796 |
assert formdef.data_class().count() == 1 |
|
1797 |
formdata = formdef.data_class().select()[0] |
|
1798 |
form_id = formdata.id |
|
1799 |
assert formdata.data['0'].metadata == validation |
|
1800 |
assert formdata.data['0_structured'] == validation |
|
1801 |
for metadata_field in document_type['metadata']: |
|
1802 | ||
1803 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['name']]) |
|
1804 |
assert fragment in resp.body |
|
1805 |
resp = app.get('/backoffice/management/form-title/%s/' % form_id) |
|
1806 |
assert not 'Validate' in resp.body |
|
1807 |
for metadata_field in document_type['metadata']: |
|
1808 | ||
1809 |
fragment = '%s : %s' % (metadata_field['label'], metadata[metadata_field['name']]) |
|
1810 |
assert fragment in resp.body |
|
1811 |
assert 'Valid from 1970-01-01 to 1978-01-01' in resp.body |
|
1812 | ||
1633 | 1813 | |
1634 | 1814 |
def test_360_user_view(pub): |
1635 | 1815 |
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 |
... | ... | |
2346 | 2347 |
assert formdef.data_class().select()[0].data['1'] == 'foobar3' |
2347 | 2348 | |
2348 | 2349 |
def test_file_field_validation(pub, fargo_url): |
2350 |
document_type = { |
|
2351 |
'id': 'justificatif-de-domicile', |
|
2352 |
'fargo': True, |
|
2353 |
'label': 'Justificatif de domicile', |
|
2354 |
'metadata': [ |
|
2355 |
{'label': 'Nom', 'name': 'nom'}, |
|
2356 |
{'label': 'Prénom(s)', 'name': 'prenoms'}, |
|
2357 |
{'label': 'Numéro', 'name': 'numero'}, |
|
2358 |
{'label': 'Rue', 'name': 'rue'}, |
|
2359 |
{'label': 'Code postal', 'name': 'code-postal'}, |
|
2360 |
{'label': 'Ville', 'name': 'ville'}, |
|
2361 |
], |
|
2362 |
} |
|
2363 |
metadata = { |
|
2364 |
'nom': 'Doe', |
|
2365 |
'prenoms': 'John', |
|
2366 |
'numero': '169', |
|
2367 |
'rue': 'rue du château', |
|
2368 |
'code-postal': '75014', |
|
2369 |
'ville': 'PARIS', |
|
2370 |
} |
|
2349 | 2371 |
user = create_user(pub) |
2350 | 2372 |
user.name_identifiers = ['12345'] |
2351 | 2373 |
user.store() |
... | ... | |
2354 | 2376 |
formdef.name = 'form title' |
2355 | 2377 |
formdef.fields = [fields.FileField( |
2356 | 2378 |
id='0', label='1st field', type='file', |
2357 |
document_type={ |
|
2358 |
'id': 'justificatif-de-domicile', |
|
2359 |
'fargo': True, |
|
2360 |
'mimetypes': ['application/pdf'], |
|
2361 |
'label': 'PDF files', |
|
2362 |
}) |
|
2379 |
document_type=document_type) |
|
2363 | 2380 |
] |
2364 | 2381 |
formdef.store() |
2365 | 2382 |
formdef.data_class().wipe() |
2366 | 2383 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
2367 |
digest = hashlib.sha256('%PDF-1.4').hexdigest() |
|
2368 | 2384 |
app = login(get_app(pub), username='foo', password='foo') |
2369 |
resp = app.get('/form-title/') |
|
2385 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
2386 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
2387 |
resp = app.get('/form-title/') |
|
2388 |
fargo_get.assert_called_once_with( |
|
2389 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
2370 | 2390 |
resp.forms[0]['f0$file'] = upload |
2391 |
for key in metadata: |
|
2392 |
resp.forms[0]['f0$%s' % key] = metadata[key] |
|
2393 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
2394 |
fargo_get.return_value = {'result': 1, 'data': {'results': []}} |
|
2395 |
resp = resp.forms[0].submit('submit') |
|
2396 |
fargo_get.assert_called_once_with( |
|
2397 |
'api/validation/justificatif-de-domicile/?user_nameid=12345') |
|
2398 |
assert 'Check values then click submit.' in resp.body |
|
2371 | 2399 |
resp = resp.forms[0].submit('submit') |
2400 |
assert resp.status_int == 302 |
|
2401 |
resp = resp.follow() |
|
2402 |
assert 'The form has been recorded' in resp.body |
|
2403 |
assert formdef.data_class().count() == 1 |
|
2404 |
formdata = formdef.data_class().select()[0] |
|
2405 |
assert formdata.data['0'].metadata == metadata |
|
2406 |
assert formdata.data['0_structured'] == metadata |
|
2407 | ||
2408 | ||
2409 |
def test_file_field_fargo_no_metadata(pub, fargo_url): |
|
2410 |
document_type = { |
|
2411 |
'id': 'justificatif-de-domicile', |
|
2412 |
'fargo': True, |
|
2413 |
'label': 'Justificatif de domicile', |
|
2414 |
} |
|
2415 |
user = create_user(pub) |
|
2416 |
user.name_identifiers = ['12345'] |
|
2417 |
user.store() |
|
2418 |
FormDef.wipe() |
|
2419 |
formdef = FormDef() |
|
2420 |
formdef.name = 'form title' |
|
2421 |
formdef.fields = [fields.FileField( |
|
2422 |
id='0', label='1st field', type='file', |
|
2423 |
document_type=document_type) |
|
2424 |
] |
|
2425 |
formdef.store() |
|
2426 |
formdef.data_class().wipe() |
|
2427 |
upload = Upload('test.pdf', '%PDF-1.4', 'application/pdf') |
|
2428 |
app = login(get_app(pub), username='foo', password='foo') |
|
2429 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
2430 |
resp = app.get('/form-title/') |
|
2431 |
assert fargo_get.call_count == 0 |
|
2432 |
resp.forms[0]['f0$file'] = upload |
|
2433 |
with mock.patch('wcs.file_validation.fargo_get') as fargo_get: |
|
2434 |
resp = resp.forms[0].submit('submit') |
|
2435 |
assert fargo_get.call_count == 0 |
|
2372 | 2436 |
assert 'Check values then click submit.' in resp.body |
2373 | 2437 |
resp = resp.forms[0].submit('submit') |
2374 | 2438 |
assert resp.status_int == 302 |
2375 |
with mock.patch('wcs.file_validation.http_get_page') as http_get_page: |
|
2376 |
json_response = json.dumps({ |
|
2377 |
'err': 0, |
|
2378 |
'data': { |
|
2379 |
'type': 'justificatif-de-domicile', |
|
2380 |
'label': 'Justificatif de domicile', |
|
2381 |
'creator': 'Jean Bono', |
|
2382 |
'created': '2014-01-01T01:01:01', |
|
2383 |
'start': '2014-01-01T01:01:01', |
|
2384 |
'end': '2014-01-01T01:01:01', |
|
2385 |
'metadata': [{ |
|
2386 |
'name': 'code_postal', |
|
2387 |
'label': 'Code postal', |
|
2388 |
'value': '13400', |
|
2389 |
}], |
|
2390 |
}, |
|
2391 |
}) |
|
2392 |
http_get_page.return_value = None, 200, json_response, None |
|
2393 |
resp = resp.follow() |
|
2394 |
http_get_page.assert_called_once_with('http://fargo.example.net/metadata/12345/%s/justificatif-de-domicile/' % digest) |
|
2395 |
http_get_page.reset_mock() |
|
2396 |
assert 'The form has been recorded' in resp.body |
|
2397 |
assert 'class="value valid"' in resp.body |
|
2439 |
resp = resp.follow() |
|
2440 |
assert 'The form has been recorded' in resp.body |
|
2441 |
assert formdef.data_class().count() == 1 |
|
2442 |
formdata = formdef.data_class().select()[0] |
|
2443 |
assert not hasattr(formdata.data['0'], 'metadata') |
|
2444 |
assert not '0_structured' in formdata.data |
|
2445 | ||
2398 | 2446 | |
2399 | 2447 |
def test_form_string_field_autocomplete(pub): |
2400 | 2448 |
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', 'name': 'nom'}, |
|
186 |
{'label': 'Prénom(s)', 'name': 'prenoms'}, |
|
187 |
{'label': 'Numéro', 'name': 'numero'}, |
|
188 |
{'label': 'Rue', 'name': 'rue'}, |
|
189 |
{'label': 'Code postal', 'name': 'code-postal'}, |
|
190 |
{'label': 'Ville', 'name': 'ville'}, |
|
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', 'name': 'nom'}, |
|
227 |
{'label': 'Prénom(s)', 'name': 'prenoms'}, |
|
228 |
{'label': 'Numéro', 'name': 'numero'}, |
|
229 |
{'label': 'Rue', 'name': 'rue'}, |
|
230 |
{'label': 'Code postal', 'name': 'code-postal'}, |
|
231 |
{'label': 'Ville', 'name': 'ville'}, |
|
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 |
{'name': 'nom', 'label': 'Nom'}, |
|
253 |
{'name': 'rue', 'label': 'Rue'}, |
|
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 | ||
---|---|---|
1623 | 1623 | |
1624 | 1624 | |
1625 | 1625 |
class FormBackOfficeStatusPage(FormStatusPage): |
1626 |
_q_exports = ['', 'download', 'json', 'action', 'inspect'] |
|
1626 |
_q_exports = ['', 'download', 'json', 'action', 'inspect', 'validate']
|
|
1627 | 1627 |
form_page_class = FormFillPage |
1628 | 1628 | |
1629 | 1629 |
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 |
metadata_value = getattr(value, 'metadata', {}).get(meta_field['name'], '') |
|
766 |
r += htmltext('<p>%s : %s</p>') % (meta_field['label'], metadata_value) |
|
767 |
return r.getvalue() |
|
757 | 768 | |
758 | 769 |
def get_csv_value(self, value, hint=None): |
759 | 770 |
if not value: |
... | ... | |
788 | 799 |
value = get_request().get_field(self.field_key) |
789 | 800 |
if value and hasattr(value, 'token'): |
790 | 801 |
get_request().form[self.field_key + '$token'] = value.token |
802 |
kwargs['document_type'] = self.document_type |
|
791 | 803 | |
792 | 804 |
def get_document_types(self): |
793 | 805 |
document_types = { |
... | ... | |
854 | 866 |
if self.document_type and self.document_type.get('mimetypes'): |
855 | 867 |
old_value = self.document_type['mimetypes'] |
856 | 868 |
self.document_type['mimetypes'] = '|'.join(self.document_type['mimetypes']) |
869 |
if self.document_type and self.document_type.get('metadata'): |
|
870 |
self.document_type['metadata'] = json.dumps(self.document_type['metadata']) |
|
857 | 871 |
result = super(FileField, self).export_to_xml(charset, include_id=include_id) |
858 | 872 |
if self.document_type and self.document_type.get('mimetypes'): |
859 | 873 |
self.document_type['mimetypes'] = old_value |
874 |
if self.document_type and self.document_type.get('metadata'): |
|
875 |
self.document_type['metadata'] = json.loads(self.document_type['metadata']) |
|
860 | 876 |
return result |
861 | 877 | |
862 | 878 |
def init_with_xml(self, element, charset, include_id=False): |
... | ... | |
866 | 882 |
self.document_type['mimetypes'] = self.document_type['mimetypes'].split('|') |
867 | 883 |
if self.document_type and self.document_type.get('fargo'): |
868 | 884 |
self.document_type['fargo'] = self.document_type['fargo'] == 'True' |
885 |
if self.document_type and self.document_type.get('metadata'): |
|
886 |
self.document_type['metadata'] = json.loads(self.document_type['metadata']) |
|
887 | ||
888 |
def store_structured_value(self, data, field_id): |
|
889 |
value = data.get(field_id) |
|
890 |
return getattr(value, 'metadata', {}) |
|
869 | 891 | |
870 | 892 | |
871 | 893 |
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.misc import http_get_page, json_loads, http_post_request |
|
23 |
from quixote import get_publisher, get_request |
|
25 | 24 | |
26 | 25 | |
27 | 26 |
def has_file_validation(): |
28 | 27 |
return get_publisher().get_site_option('fargo_url') is not None |
29 | 28 | |
29 | ||
30 | 30 |
def fargo_get(path): |
31 | 31 |
fargo_url = get_publisher().get_site_option('fargo_url') |
32 | 32 |
url = urlparse.urljoin(fargo_url, path) |
... | ... | |
35 | 35 |
return json_loads(data) |
36 | 36 |
return None |
37 | 37 | |
38 | ||
38 | 39 |
def sha256_of_upload(upload): |
39 | 40 |
return hashlib.sha256(upload.get_content()).hexdigest() |
40 | 41 | |
42 | ||
41 | 43 |
def get_document_types(): |
42 | 44 |
if not has_file_validation(): |
43 | 45 |
return {} |
44 | 46 |
response = fargo_get('/document-types/') |
45 |
publisher = get_publisher() |
|
46 | 47 |
if response.get('err') == 0: |
47 | 48 |
result = {} |
48 | 49 |
for schema in response['data']: |
... | ... | |
52 | 53 |
'fargo': True, |
53 | 54 |
} |
54 | 55 |
if 'mimetypes' in schema: |
55 |
d['mimetypes'] = shema['mimetypes'] |
|
56 |
d['mimetypes'] = schema['mimetypes']
|
|
56 | 57 |
result[d['id']] = d |
57 | ||
58 |
d['metadata'] = schema.get('metadata', []) |
|
58 | 59 |
return result |
59 | 60 |
return {} |
60 | 61 | |
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'): |
|
68 |
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): |
|
62 | ||
63 |
def get_validation(url): |
|
64 |
response, status, data, auth_header = http_get_page(url) |
|
65 |
if status == 200: |
|
66 |
return json_loads(data)['data'] |
|
67 |
return None |
|
68 | ||
69 | ||
70 |
def get_validations(document_type): |
|
71 |
request = get_request() |
|
72 |
document_type_id = document_type['id'] |
|
73 |
qs = {} |
|
74 |
if not request.user: |
|
75 |
return [] |
|
76 |
if request.user.name_identifiers: |
|
77 |
qs['user_nameid'] = request.user.name_identifiers[0] |
|
78 |
elif request.user.email: |
|
79 |
qs['user_email'] = request.user.email |
|
80 |
else: |
|
81 |
return [] |
|
82 |
path = 'api/validation/%s/' % urllib.quote(document_type_id) |
|
83 |
validations = fargo_get(path + '?%s' % urllib.urlencode(qs)) |
|
84 |
if validations and validations.get('data', {}).get('results', []): |
|
85 |
return validations['data']['results'] |
|
86 |
return [] |
|
87 | ||
88 | ||
89 |
def is_valid(filled, field, upload): |
|
80 | 90 |
'''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'] |
|
90 | ||
91 |
def get_document_type_label(document_type): |
|
92 |
return get_document_types().get(document_type, {}).get('label') |
|
93 | ||
94 |
def validation_link(filled, field, upload): |
|
91 |
return 'url' in getattr(upload, 'metadata', {}) |
|
92 | ||
93 | ||
94 |
def validate(filled, field, upload): |
|
95 | 95 |
'''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']) |
|
96 |
document_type_id = field.document_type['id'] |
|
97 |
path = 'api/validation/%s/' % urllib.quote(document_type_id) |
|
102 | 98 |
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 |
} |
|
99 |
url = urlparse.urljoin(fargo_url, path) |
|
100 |
payload = {} |
|
101 |
if filled.user: |
|
102 |
if filled.user.name_identifiers: |
|
103 |
payload['user_nameid'] = filled.user.name_identifiers[0] |
|
104 |
else: |
|
105 |
payload['user_email'] = filled.user.email |
|
106 |
payload['origin'] = get_request().get_server() |
|
107 |
payload['creator'] = get_request().user.display_name |
|
108 |
payload['content_hash'] = sha256_of_upload(upload) |
|
109 |
for meta_field in field.metadata: |
|
110 |
payload[meta_field['name']] = upload.metadata.get(meta_field['name'], '') |
|
111 |
headers = {'Content-Type': 'application/json'} |
|
112 |
response, status, response_payload, auth_header = http_post_request(url, json.dumps(payload), |
|
113 |
headers=headers) |
|
114 |
if status == 201: |
|
115 |
upload.metadata = json_loads(response_payload)['data'] |
|
116 |
filled.data['%s_structured' % field.id] = upload.metadata |
|
117 |
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): |
596 | 605 |
CompositeWidget.__init__(self, name, value, **kwargs) |
597 | 606 |
self.value = value |
607 |
self.document_type = kwargs.pop('document_type', None) or {} |
|
598 | 608 |
self.preview = kwargs.get('readonly') |
599 | 609 |
self.max_file_size = kwargs.pop('max_file_size', None) |
600 | 610 |
self.allow_portfolio_picking = kwargs.pop('allow_portfolio_picking', True) |
... | ... | |
610 | 620 |
# this could be used for client size validation of file size |
611 | 621 |
attrs['data-max-file-size'] = str(self.max_file_size_bytes) |
612 | 622 |
self.add(FileWidget, 'file', render_br=False, attrs=attrs) |
623 |
if self.document_type.get('metadata'): |
|
624 |
if self.preview: |
|
625 |
self.add(HiddenWidget, 'validation_url') |
|
626 |
else: |
|
627 |
validations = file_validation.get_validations(self.document_type) |
|
628 |
if validations: |
|
629 |
options = [('', _('Known documents'), '')] |
|
630 |
options += [(v['url'], v['display'], v['url']) for v in validations] |
|
631 |
self.add(SingleSelectWidget, 'validation_url', options=options, |
|
632 |
attrs={'data-validations': json.dumps(validations)}) |
|
633 |
for meta_field in self.metadata: |
|
634 |
subvalue = getattr(value, 'metadata', {}).get(meta_field['name']) |
|
635 |
self.add(StringWidget, meta_field['name'], |
|
636 |
title=meta_field['label'], |
|
637 |
required=meta_field.get('required', True) and self.required, |
|
638 |
readonly=self.preview, |
|
639 |
value=subvalue) |
|
640 |
self.get_widget(meta_field['name']).extra_css_class = 'subwidget' |
|
613 | 641 |
if value: |
614 | 642 |
self.set_value(value) |
615 | 643 | |
... | ... | |
625 | 653 |
self.get_widget('token').set_value(self.value.token) |
626 | 654 | |
627 | 655 |
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']) |
|
656 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'jquery.iframe-transport.js', |
|
657 |
'jquery.fileupload.js', 'qommon.fileupload.js']) |
|
631 | 658 | |
632 | 659 |
temp = get_session().get_tempfile(self.get('token')) or {} |
633 | 660 | |
... | ... | |
638 | 665 |
r += htmltext('<div class="fileprogress" style="display: none;">') |
639 | 666 |
r += htmltext(' <div class="bar">%s</div>' % _('Upload in progress...')) |
640 | 667 |
r += htmltext('</div>') |
641 |
r += htmltext('<div class="fileinfo"><span class="filename">%s</span>' % temp.get('base_filename', '')) |
|
668 |
r += htmltext('<div class="fileinfo"><span class="filename">%s</span>' |
|
669 |
% temp.get('base_filename', '')) |
|
642 | 670 |
if not self.preview: |
643 |
r += htmltext(' <a href="#" class="remove" title="%s">%s</a>' % ( |
|
644 |
_('Remove this file'), |
|
645 |
_('remove'))) |
|
671 |
r += htmltext(' <a href="#" class="remove" title="%s">%s</a>' |
|
672 |
% (_('Remove this file'), _('remove'))) |
|
646 | 673 |
elif temp: |
647 | 674 |
filetype = mimetypes.guess_type(temp.get('orig_filename', '')) |
648 | 675 |
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'))
|
|
676 |
r += htmltext('<img alt="" src="tempfile?t=%s&thumbnail=1" />' % |
|
677 |
self.get('token')) |
|
651 | 678 | |
652 | 679 |
r += htmltext('</div>') |
653 | 680 |
return r.getvalue() |
654 | 681 | |
682 |
@property |
|
683 |
def metadata(self): |
|
684 |
return self.document_type.get('metadata', []) |
|
685 | ||
655 | 686 |
def _parse(self, request): |
656 | 687 |
self.value = None |
657 | 688 |
if self.get('token'): |
658 | 689 |
token = self.get('token') |
690 |
elif self.get('validation_url'): |
|
691 |
self.value = NoUpload(self.get('validation_url')) |
|
692 |
return |
|
659 | 693 |
elif self.get('file'): |
660 | 694 |
token = get_session().add_tempfile(self.get('file')) |
661 | 695 |
request.form[self.get_widget('token').get_name()] = token |
... | ... | |
664 | 698 | |
665 | 699 |
session = get_session() |
666 | 700 |
if token and session.tempfiles and session.tempfiles.has_key(token): |
667 |
temp = session.tempfiles[token] |
|
668 | 701 |
self.value = session.get_tempfile_content(token) |
669 | 702 | |
670 | 703 |
if self.value is None: |
671 |
# there's no file, the other checks are irrelevant. |
|
704 |
# there's no file, check all metadata field are empty too |
|
705 |
# if not file and required metadata field become required |
|
706 |
if (self.get_widget('file') |
|
707 |
and not self.required |
|
708 |
and any([self.get(meta_field['name']) for meta_field in self.metadata])): |
|
709 |
self.get_widget('file').required = True |
|
710 |
for meta_field in self.metadata: |
|
711 |
required = meta_field.get('required', True) |
|
712 |
if required: |
|
713 |
widget = self.get_widget(meta_field['name']) |
|
714 |
widget.required = True |
|
715 |
if not self.get(meta_field['name']): |
|
716 |
widget.set_error(self.REQUIRED_ERROR) |
|
672 | 717 |
return |
673 | 718 | |
719 |
# There is some file, check all required metadata files have been filled |
|
720 |
for meta_field in self.metadata: |
|
721 |
required = meta_field.get('required', True) |
|
722 |
if required: |
|
723 |
widget = self.get_widget(meta_field['name']) |
|
724 |
widget.required = True |
|
725 |
if not self.get(meta_field['name']): |
|
726 |
widget.set_error(self.REQUIRED_ERROR) |
|
727 | ||
728 |
if self.metadata: |
|
729 |
self.value.metadata = {} |
|
730 |
for meta_field in self.metadata: |
|
731 |
self.value.metadata[meta_field['name']] = self.get(meta_field['name']) |
|
732 | ||
674 | 733 |
# Don't trust the browser supplied MIME type, update the Upload object |
675 | 734 |
# with a MIME type created with magic (or based on the extension if the |
676 | 735 |
# module is missing). |
wcs/qommon/static/css/qommon.css | ||
---|---|---|
79 | 79 |
vertical-align: middle; |
80 | 80 |
} |
81 | 81 | |
82 |
/* nested widgets */ |
|
83 |
.widget .subwidget { |
|
84 |
padding-left: 2em; |
|
85 |
} |
|
86 |
div.FileWithPreviewWidget .validation { |
|
87 |
display: inline-block; |
|
88 |
font-size: xx-small; |
|
89 |
margin-right: 2em; |
|
90 |
} |
|
91 | ||
82 | 92 |
div.form .title, form.quixote .title { |
83 | 93 |
font-weight: bold; |
84 | 94 |
} |
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 |
- |