Projet

Général

Profil

0003-add-support-for-file-validation-8402.patch

Benjamin Dauvergne, 10 novembre 2015 09:19

Télécharger (27,6 ko)

Voir les différences:

Subject: [PATCH 3/4] add support for file validation (#8402)

 tests/conftest.py              |  26 ++++++++++
 tests/test_backoffice_pages.py |  56 ++++++++++++++++++++
 tests/test_form_pages.py       |  54 +++++++++++++++++++
 tests/test_formdef.py          |  27 +++++++++-
 tests/test_formdef_import.py   |  14 +++++
 wcs/fields.py                  | 115 ++++++++++++++++++++++++++++++++++-------
 wcs/file_validation.py         | 111 +++++++++++++++++++++++++++++++++++++++
 wcs/forms/common.py            |  59 ++++++++++++++++++---
 wcs/qommon/static/js/fargo.js  |  56 ++++++++++++++++++++
 wcs/root.py                    |   6 ++-
 10 files changed, 495 insertions(+), 29 deletions(-)
 create mode 100644 wcs/file_validation.py
 create mode 100644 wcs/qommon/static/js/fargo.js
tests/conftest.py
1
import os
2
import ConfigParser
3

  
1 4
import pytest
2 5

  
3 6
def pytest_addoption(parser):
......
7 10
def pytest_runtest_setup(item):
8 11
    if 'postgresql' in item.keywords and item.config.option.without_postgresql_tests is True:
9 12
        pytest.skip('skipped (PostgreSQL are disabled on command line)')
13

  
14
@pytest.fixture
15
def fargo_url(request, pub):
16
    config = ConfigParser.ConfigParser()
17
    path = os.path.join(pub.app_dir, 'site-options.cfg')
18
    url = 'http://fargo.example.net/'
19
    if os.path.exists(path):
20
        config.read([path])
21
    if not config.has_section('options'):
22
        config.add_section('options')
23
    config.set('options', 'fargo_url', url)
24
    with file(path, 'w') as site_option:
25
        config.write(site_option)
26

  
27
    def fin():
28
        config = ConfigParser.ConfigParser()
29
        if os.path.exists(path):
30
            config.read([path])
31
            config.remove_option('options', 'fargo_url')
32
            with file(path, 'w') as site_option:
33
                config.write(site_option)
34
    request.addfinalizer(fin)
35
    return url
tests/test_backoffice_pages.py
4 4
import re
5 5
import shutil
6 6
import StringIO
7
import hashlib
7 8

  
8 9
import pytest
10
from webtest import Upload
11
import mock
9 12

  
10 13
from quixote import cleanup, get_publisher
11 14
from wcs.qommon import errors, sessions
......
935 938
    assert '>cat1<' in resp.body
936 939
    assert '>Misc<' in resp.body
937 940
    assert resp.body.index('>Misc<') < resp.body.index('>cat1<')
941

  
942

  
943
def test_backoffice_file_field_validation(pub, fargo_url):
944
    user = create_user(pub, is_admin=True)
945
    user.name_identifiers = ['12345']
946
    user.store()
947
    FormDef.wipe()
948
    formdef = FormDef()
949
    formdef.name = 'form title'
950
    formdef.fields = [fields.FileField(
951
        id='0', label='1st field', type='file',
952
        document_type={
953
            'id': 'justificatif-de-domicile',
954
            'fargo': True,
955
            'mimetypes': ['application/pdf'],
956
            'label': 'PDF files',
957
        })
958
    ]
959
    formdef.store()
960
    formdef.data_class().wipe()
961
    upload = Upload('test.pdf', 'foobar', 'application/pdf')
962
    digest = hashlib.sha256('foobar').hexdigest()
963
    app = login(get_app(pub))
964
    resp = app.get('/form-title/')
965
    resp.forms[0]['f0$file'] = upload
966
    resp = resp.forms[0].submit('submit')
967
    assert 'Check values then click submit.' in resp.body
968
    resp = resp.forms[0].submit('submit')
969
    assert resp.status_int == 302
970
    with mock.patch('wcs.file_validation.http_get_page') as http_get_page:
971
        json_response = json.dumps({
972
            'err': 0,
973
            'data': {
974
                'type': 'justificatif-de-domicile',
975
                'label': 'Justificatif de domicile',
976
                'creator': 'Jean Bono',
977
                'created': '2014-01-01T01:01:01',
978
                'start': '2014-01-01T01:01:01',
979
                'end': '2014-01-01T01:01:01',
980
                'metadata': [{
981
                        'name': 'code_postal',
982
                        'label': 'Code postal',
983
                        'value': '13400',
984
                }],
985
            },
986
        })
987
        http_get_page.return_value = None, 200, json_response, None
988
        resp = app.get('/backoffice/management/form-title/1/')
989
        http_get_page.assert_called_once_with('http://fargo.example.net/metadata/12345/c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2/justificatif-de-domicile/')
990
        assert 'class="value valid"' in resp.body
991
        assert 'Justificatif de domicile validated by Jean Bono on 2014-01-01T01:01:01' in resp.body
992
        assert 'Code postal: 13400' in resp.body
993
        assert 'Valid from 2014-01-01T01:01:01 to 2014-01-01T01:01:01' in resp.body
tests/test_form_pages.py
1
import json
1 2
import pytest
2 3
import hashlib
3 4
import os
......
7 8
import zipfile
8 9
import base64
9 10
from webtest import Upload
11
import mock
10 12

  
11 13
from quixote.http_request import Upload as QuixoteUpload
12 14
from wcs.qommon.form import UploadedFile
......
1912 1914
    assert formdef.data_class().count() == 1
1913 1915
    assert formdef.data_class().select()[0].data['1'] == 'foobar3'
1914 1916
    assert formdef.data_class().select()[0].data['3'] == 'xxx3'
1917

  
1918

  
1919
def test_file_field_validation(pub, fargo_url):
1920
    user = create_user(pub)
1921
    user.name_identifiers = ['12345']
1922
    user.store()
1923
    FormDef.wipe()
1924
    formdef = FormDef()
1925
    formdef.name = 'form title'
1926
    formdef.fields = [fields.FileField(
1927
        id='0', label='1st field', type='file',
1928
        document_type={
1929
            'id': 'justificatif-de-domicile',
1930
            'fargo': True,
1931
            'mimetypes': ['application/pdf'],
1932
            'label': 'PDF files',
1933
        })
1934
    ]
1935
    formdef.store()
1936
    formdef.data_class().wipe()
1937
    upload = Upload('test.pdf', 'foobar', 'application/pdf')
1938
    digest = hashlib.sha256('foobar').hexdigest()
1939
    app = login(get_app(pub), username='foo', password='foo')
1940
    resp = app.get('/form-title/')
1941
    resp.forms[0]['f0$file'] = upload
1942
    resp = resp.forms[0].submit('submit')
1943
    assert 'Check values then click submit.' in resp.body
1944
    resp = resp.forms[0].submit('submit')
1945
    assert resp.status_int == 302
1946
    with mock.patch('wcs.file_validation.http_get_page') as http_get_page:
1947
        json_response = json.dumps({
1948
            'err': 0,
1949
            'data': {
1950
                'type': 'justificatif-de-domicile',
1951
                'label': 'Justificatif de domicile',
1952
                'creator': 'Jean Bono',
1953
                'created': '2014-01-01T01:01:01',
1954
                'start': '2014-01-01T01:01:01',
1955
                'end': '2014-01-01T01:01:01',
1956
                'metadata': [{
1957
                        'name': 'code_postal',
1958
                        'label': 'Code postal',
1959
                        'value': '13400',
1960
                }],
1961
            },
1962
        })
1963
        http_get_page.return_value = None, 200, json_response, None
1964
        resp = resp.follow()
1965
        http_get_page.assert_called_once_with('http://fargo.example.net/metadata/12345/c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2/justificatif-de-domicile/')
1966
        http_get_page.reset_mock()
1967
        assert 'The form has been recorded' in resp.body
1968
        assert 'class="value valid"' in resp.body
tests/test_formdef.py
4 4

  
5 5
import pytest
6 6

  
7
from mock import patch
8

  
7 9
from quixote import cleanup
8 10
from wcs import formdef
9 11
from wcs.formdef import FormDef
10 12
from wcs.workflows import Workflow
11
from wcs.fields import StringField
13
from wcs.fields import StringField, FileField
12 14

  
13 15
from utilities import create_temporary_pub
14 16

  
......
132 134

  
133 135
    with pytest.raises(AttributeError):
134 136
        assert substs.foobar
137

  
138
def test_file_field_migration():
139
    with patch('wcs.file_validation.get_document_types') as get_document_types:
140
        get_document_types.return_value = {
141
                'justificatif-de-domicile': {
142
                    'id': 'justificatif-de-domicile',
143
                    'label': 'Justificatif de domicile',
144
                    'fargo': True,
145
                },
146
        }
147
        FormDef.wipe()
148
        formdef = FormDef()
149
        formdef.name = 'foo'
150
        file_type = ['image/*', 'application/pdf,application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.spreadsheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
151
        formdef.fields = [FileField(type='file', id='1', label='file')]
152
        formdef.fields[0].__dict__['file_type'] = file_type
153
        formdef.store()
154
        formdef = FormDef.get(1)
155
        assert 'file_type' not in formdef.fields[0].__dict__
156
        assert formdef.fields[0].document_type
157
        assert formdef.fields[0].document_type['id'] == '_legacy'
158
        assert formdef.fields[0].document_type['mimetypes'] == ['image/*', 'application/pdf,application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.spreadsheet,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
159
        assert formdef.fields[0].document_type['label'] == ','.join(file_type)
tests/test_formdef_import.py
227 227
    cat.store()
228 228
    assert FormDef.import_from_xml_tree(formdef_xml_with_id, include_id=False).category_id == '2'
229 229
    assert FormDef.import_from_xml_tree(formdef_xml_with_id, include_id=True).category_id is None
230

  
231

  
232
def test_file_field():
233
    formdef = FormDef()
234
    formdef.name = 'foo'
235
    formdef.fields = [fields.FileField(type='file', id='1', document_type={
236
        'id': 'justificatif-de-domicile',
237
        'fargo': True,
238
        'mimetypes': ['application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/*'],
239
    })]
240
    assert_xml_import_export_works(formdef, include_id=True)
241
    assert_xml_import_export_works(formdef)
242
    assert_json_import_export_works(formdef, include_id=True)
243
    assert_json_import_export_works(formdef)
wcs/fields.py
30 30
from qommon.strftime import strftime
31 31

  
32 32
import data_sources
33
import file_validation
33 34

  
34 35

  
35 36
class PrefillSelectionWidget(CompositeWidget):
......
674 675
class FileField(WidgetField):
675 676
    key = 'file'
676 677
    description = N_('File Upload')
677
    file_type = []
678
    document_type = None
678 679
    max_file_size = None
679 680

  
680 681
    widget_class = FileWithPreviewWidget
681 682
    extra_attributes = ['file_type', 'max_file_size']
682 683

  
684
    def __init__(self, *args, **kwargs):
685
        super(FileField, self).__init__(*args, **kwargs)
686
        self.document_type = self.document_type or {}
687

  
688
    @property
689
    def file_type(self):
690
        return (self.document_type or {}).get('mimetypes', [])
691

  
683 692
    def fill_admin_form(self, form):
684 693
        WidgetField.fill_admin_form(self, form)
685
        file_types = [
686
                ('audio/*', _('Sound files')),
687
                ('video/*', _('Video files')),
688
                ('image/*', _('Image files'))]
689
        filetypes_cfg = get_cfg('filetypes', {})
690
        if filetypes_cfg:
691
            for file_type in filetypes_cfg.values():
692
                file_types.append((
693
                    ','.join(file_type['mimetypes']), file_type['label']))
694
        if self.file_type:
695
            known_file_types = [x[0] for x in file_types]
696
            for file_type in self.file_type:
697
                if not file_type in known_file_types:
698
                    file_types.append((file_type, file_type))
699
        form.add(CheckboxesWidget, 'file_type', title=_('File type suggestion'),
700
                value=self.file_type, elements=file_types, inline=True,
701
                advanced=not(self.file_type))
694
        document_types = self.get_document_types()
695
        cur_dt = self.document_type
696
        # SingleSelectWidget compare the value and not the keys, so if we want
697
        # the current value not to be hidden, we must reset it with the corresponding
698
        # value from settings based on the 'id'
699
        document_type_id = self.document_type.get('id')
700
        if document_type_id in document_types \
701
               and self.document_type != document_types[document_type_id]:
702
            self.document_type = document_types[document_type_id]
703
        options = [(None, '---', {})]
704
        options += [(doc_type, doc_type['label'], key) for key, doc_type in document_types.iteritems()]
705
        form.add(SingleSelectWidget, 'document_type', title=_('File type suggestion'),
706
                value=self.document_type, options=options,
707
                advanced=not(self.document_type))
702 708
        form.add(FileSizeWidget, 'max_file_size', title=('Max file size'),
703 709
                value=self.max_file_size,
704 710
                advanced=not(self.max_file_size))
705 711

  
706 712
    def get_admin_attributes(self):
707
        return WidgetField.get_admin_attributes(self) + ['file_type',
708
                'max_file_size']
713
        return WidgetField.get_admin_attributes(self) + ['document_type',
714
                                                         'max_file_size']
709 715

  
710 716
    def get_view_value(self, value):
711 717
        return htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % (
......
732 738
            if value and hasattr(value, 'token'):
733 739
                get_request().form[self.field_key + '$token'] = value.token
734 740

  
741
    def get_document_types(self):
742
        document_types = {
743
            '_audio': {
744
                'label': _('Sound files'),
745
                'mimetypes': ['audio/*'],
746
            },
747
            '_video': {
748
                'label': _('Video files'),
749
                'mimetypes': ['video/*'],
750
            },
751
            '_image': {
752
                'label': _('Image files'),
753
                'mimetypes': ['image/*'],
754
            }
755
        }
756
        # Local document types
757
        document_types.update(get_cfg('filetypes', {}))
758
        # Remote documents types
759
        document_types.update(file_validation.get_document_types())
760
        for key, document_type in document_types.iteritems():
761
            document_type['id'] = key
762
        # add current file type if it does not exist anymore in the settings
763
        cur_dt = self.document_type
764
        if cur_dt and cur_dt['id'] not in document_types:
765
            document_types[cur_dt['id']] = cur_dt
766
        return document_types
767

  
768
    def migrate(self):
769
        if 'file_type' in self.__dict__:
770
            self.document_type = {}
771
            if self.__dict__['file_type']:
772
                file_type = self.__dict__['file_type']
773
                document_types = self.get_document_types()
774
                for key, value in document_types.iteritems():
775
                    if self.file_type == value.get('mimetypes'):
776
                        self.document_type = value.copy()
777
                        self.document_type['id'] = key
778
                else:
779
                    # self.file_type is a combination of file type, we create a
780
                    # virtual one from them
781
                    self.document_type = {
782
                        'id': '_legacy',
783
                        'label': ','.join(file_type),
784
                        'mimetypes': file_type,
785
                    }
786
            del self.__dict__['file_type']
787
            return True
788
        return False
789

  
790
    def export_to_xml(self, charset, include_id=False):
791
        # convert some sub-fields to strings as export_to_xml() only supports
792
        # dictionnaries with strings values
793
        if self.document_type and self.document_type.get('mimetypes'):
794
            old_value = self.document_type['mimetypes']
795
            self.document_type['mimetypes'] = '|'.join(self.document_type['mimetypes'])
796
        result = super(FileField, self).export_to_xml(charset, include_id=include_id)
797
        if self.document_type and self.document_type.get('mimetypes'):
798
            self.document_type['mimetypes'] = old_value
799
        return result
800

  
801
    def init_with_xml(self, element, charset, include_id=False):
802
        super(FileField, self).init_with_xml(element, charset, include_id=include_id)
803
        # translate fields flattened to strings
804
        if self.document_type and self.document_type.get('mimetypes'):
805
            self.document_type['mimetypes'] = self.document_type['mimetypes'].split('|')
806
        if self.document_type and self.document_type.get('fargo'):
807
            self.document_type['fargo'] = self.document_type['fargo'] == 'True'
808

  
809

  
735 810
register_field_class(FileField)
736 811

  
737 812

  
wcs/file_validation.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2010  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
import json
18
import urlparse
19
import hashlib
20
import urllib
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
25

  
26

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

  
30
def fargo_get(path):
31
    fargo_url = get_publisher().get_site_option('fargo_url')
32
    url = urlparse.urljoin(fargo_url, path)
33
    response, status, data, auth_header = http_get_page(url)
34
    if status == 200:
35
        return json_loads(data)
36
    return None
37

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

  
41
def get_document_types():
42
    if not has_file_validation():
43
        return {}
44
    response = fargo_get('/document-types/')
45
    publisher = get_publisher()
46
    if response.get('err') == 0:
47
        result = {}
48
        for schema in response['data']:
49
            d = {
50
                'id': schema['name'],
51
                'label': schema['label'],
52
                'fargo': True,
53
            }
54
            if 'mimetypes' in schema:
55
                d['mimetypes'] = shema['mimetypes']
56
            result[d['id']] = d
57

  
58
        return result
59
    return {}
60

  
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):
80
    '''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):
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'])
102
    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
    }
wcs/forms/common.py
15 15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import sys
18
import hashlib
19
import urlparse
18 20

  
19 21
from quixote import get_publisher, get_request, get_response, get_session, redirect
20 22
from quixote.directory import Directory
21 23
from quixote.html import TemplateIO, htmltext
22 24

  
23
from wcs.fields import WidgetField
25
from wcs.fields import WidgetField, FileField
26
from wcs import file_validation
24 27

  
25 28
from qommon import template
26 29
from qommon import get_logger
......
395 398
                continue
396 399

  
397 400
            r += htmltext('<div class="field"><span class="label">%s</span> ') % f.label
398
            r += htmltext('<div class="value">')
399
            s = f.get_view_value(value)
400
            s = s.replace(str('[download]'), str('%sdownload' % form_url))
401
            r += s
402
            r += htmltext('</div></div>')
403

  
401
            if isinstance(f, FileField):
402
                r += htmltext(self.display_file_field(form_url, f, value))
403
            else: # normal display
404
                r += htmltext('<div class="value">')
405
                s = f.get_view_value(value)
406
                s = s.replace(str('[download]'), str('%sdownload' % form_url))
407
                r += s
408
                r += htmltext('</div>')
409
            r += htmltext('</div>')
404 410

  
405 411
        if on_page:
406 412
            r += htmltext('</div>')
......
529 535
        else:
530 536
            return redirect('files/%s/' % fn)
531 537

  
538
    def display_file_field(self, form_url, field, value):
539
        r = TemplateIO(html=True)
540
        status = None
541
        if file_validation.has_file_validation():
542
            status = file_validation.validate_upload(self.filled, field, value)
543
            if status is False:
544
                extra_class = ' invalid'
545
            elif status is None:
546
                extra_class = ''
547
            else:
548
                extra_class = ' valid'
549
            r += htmltext('<div class="value%s">' % extra_class)
550
        else:
551
            r += htmltext('<div class="value">')
552
        s = field.get_view_value(value)
553
        s = s.replace(str('[download]'), str('%sdownload' % form_url))
554
        r += s
555
        if status is not None and get_request().is_in_backoffice():
556
            r += htmltext('<div class="file-validation">')
557
            if status:
558
                r += htmltext(_('<p>%s validated by %s on %s</p>')) % (
559
                    status['label'], status['creator'], status['created'])
560
                r += htmltext('<ul>')
561
                for meta in status['metadata']:
562
                    r += htmltext(_('<li>%(label)s: %(value)s</li>')) % {
563
                        'label': meta['label'],
564
                        'value': meta['value']
565
                    }
566
                r += htmltext('</ul>')
567
                r += htmltext('<p>%s</p>') % (_('Valid from %(start)s to %(end)s') % {
568
                    'start': status['start'],
569
                    'end': status['end'],
570
                })
571
            else:
572
                r += file_validation.validation_link(self.filled, field, value)
573
            r += htmltext('</div>')
574
        r += htmltext('</div>')
575
        return str(r)
576

  
532 577
    def _q_lookup(self, component):
533 578
        if component == 'files':
534 579
            self.check_receiver()
wcs/qommon/static/js/fargo.js
1

  
2
$(function() {
3
  var iframe = $('<iframe frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>');
4
  var dialog = $("<div></div>").append(iframe).appendTo("body").dialog({
5
        autoOpen: false,
6
        modal: true,
7
        resizable: false,
8
        width: "auto",
9
        height: "auto",
10
        close: function () {
11
            iframe.attr("src", "");
12
        }
13
  });
14
  $('.file-validation a').click(function (e) {
15
    e.preventDefault();
16
    var src = $(e.target).attr('href');
17
    var title = $(e.target).data("title");
18
    var width = $(e.target).data("width");
19
    var height = $(e.target).data("height");
20
    iframe.attr({
21
        width: parseInt(width),
22
        height: parseInt(height),
23
        src: src
24
    });
25
    dialog.dialog("option", "title", title);
26
    dialog.dialog("open");
27
  });
28
  $('p.use-file-from-fargo span').click(function(e) {
29
    e.preventDefault();
30
    var base_widget = $(this).parents('.file-upload-widget');
31
    document.fargo_set_token = function (token, title) {
32
       if (token) {
33
         $(base_widget).find('.filename').text(title);
34
         $(base_widget).find('.fileinfo').show();
35
         $(base_widget).find('input[type=hidden]').val(token);
36
         $(base_widget).find('input[type=file]').hide();
37
       }
38
       document.fargo_close_dialog();
39
    }
40
    document.fargo_close_dialog = function () {
41
       document.fargo_set_token = undefined;
42
       dialog.dialog('close');
43
    }
44
    var src = $(this).data('src');
45
    var title = $(this).data("title");
46
    var width = $(this).data("width");
47
    var height = $(this).data("height");
48
    iframe.attr({
49
        width: parseInt(width),
50
        height: parseInt(height),
51
        src: src
52
    });
53
    dialog.dialog("option", "title", title);
54
    dialog.dialog("open");
55
  });
56
});
wcs/root.py
192 192
    _q_exports = ['admin', 'backoffice', 'forms', 'login', 'logout', 'saml',
193 193
            'ident', 'register', 'afterjobs', 'themes', 'myspace', 'user', 'roles',
194 194
            'pages', ('tmp-upload', 'tmp_upload'), 'api', '__version__',
195
            'tryauth', 'auth', 'preview']
195
            'tryauth', 'auth', 'preview', ('reload-top', 'reload_top')]
196 196

  
197 197
    api = ApiDirectory()
198 198
    themes = template.ThemesDirectory()
......
308 308
        # or a form ?
309 309
        return forms.root.RootDirectory()._q_lookup(component)
310 310

  
311
    def reload_top(self):
312
        get_response().filter = {}
313
        return htmltext('<html><body><script>window.top.document.location.reload();</script></body></html>')
314

  
311 315
    admin = None
312 316
    backoffice = None
313 317

  
314
-