Projet

Général

Profil

0001-add-support-for-file-validation.patch

Benjamin Dauvergne, 28 septembre 2015 17:25

Télécharger (10,6 ko)

Voir les différences:

Subject: [PATCH] add support for file validation

 wcs/fields.py                 |  12 ++++-
 wcs/file_validation.py        | 107 ++++++++++++++++++++++++++++++++++++++++++
 wcs/forms/common.py           |  30 +++++++++++-
 wcs/qommon/static/js/fargo.js |  56 ++++++++++++++++++++++
 wcs/root.py                   |   7 ++-
 5 files changed, 209 insertions(+), 3 deletions(-)
 create mode 100644 wcs/file_validation.py
 create mode 100644 wcs/qommon/static/js/fargo.js
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):
......
666 667
    key = 'file'
667 668
    description = N_('File Upload')
668 669
    file_type = []
670
    document_type = None
669 671
    max_file_size = None
670 672

  
671 673
    widget_class = FileWithPreviewWidget
......
693 695
        form.add(FileSizeWidget, 'max_file_size', title=('Max file size'),
694 696
                value=self.max_file_size,
695 697
                advanced=not(self.max_file_size))
698
        document_types = file_validation.get_document_types()
699
        if document_types:
700
            document_types = [(None, '---')] + document_types
701
            form.add(SingleSelectWidget, 'document_type', title=_('Document type'),
702
                    value=self.document_type, options=document_types)
696 703

  
697 704
    def get_admin_attributes(self):
698
        return WidgetField.get_admin_attributes(self) + ['file_type',
705
        l = WidgetField.get_admin_attributes(self) + ['file_type',
699 706
                'max_file_size']
707
        if file_validation.get_document_types():
708
            l += ['document_type']
709
        return l
700 710

  
701 711
    def get_view_value(self, value):
702 712
        return htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % (
wcs/file_validation.py
1
import json
2
import urlparse
3
import hashlib
4
import urllib
5

  
6
from qommon.misc import http_get_page
7
from quixote import get_publisher, get_response
8
from quixote.html import htmltext
9

  
10

  
11
def json_encode_helper(d, charset):
12
    '''Encode a JSON structure into local charset'''
13
    if isinstance(d, unicode):
14
        return d.encode(charset)
15
    elif isinstance(d, list):
16
        return [json_encode_helper(e, charset) for e in d]
17
    elif isinstance(d, dict):
18
        new_d = {}
19
        for k, v in d.iteritems():
20
            new_d[json_encode_helper(k, charset)] = json_encode_helper(v, charset)
21
        return new_d
22
    else:
23
        return d
24

  
25

  
26
def json_encode(d, charset=None):
27
    return json_encode_helper(d, charset or get_publisher().site_charset)
28

  
29

  
30
def has_file_validation():
31
    return get_publisher().get_site_option('fargo_url') is not None
32

  
33

  
34
def fargo_get(path):
35
    fargo_url = get_publisher().get_site_option('fargo_url')
36
    url = urlparse.urljoin(fargo_url, path)
37
    response, status, data, auth_header = http_get_page(url)
38
    if status == 200:
39
        return json_encode(json.loads(data))
40

  
41

  
42
def sha256_of_upload(upload):
43
    return hashlib.sha256(upload.get_content()).hexdigest()
44

  
45

  
46
def get_document_types():
47
    if not has_file_validation():
48
        return
49
    response = fargo_get('/document-types/')
50
    publisher = get_publisher()
51
    if response.get('err') == 0:
52
        result = []
53
        for schema in response['data']:
54
            result.append(
55
                (
56
                    str(schema['name']),
57
                    schema['label'].encode(publisher.site_charset)
58
                )
59
            )
60
        return result
61

  
62
def validation_path(filled, field, upload):
63
    user = filled.get_user()
64
    if not user:
65
        return
66
    if not user.name_identifiers:
67
        return
68
    if not field.document_type:
69
        return
70
    name_id = user.name_identifiers[0]
71
    sha_256 = sha256_of_upload(upload)
72
    document_type = field.document_type
73
    path = '%s/%s/%s/' % (
74
        urllib.quote(name_id),
75
        urllib.quote(sha_256),
76
        urllib.quote(document_type),
77
    )
78
    return path
79

  
80
def validate_upload(filled, field, upload):
81
    path = validation_path(filled, field, upload)
82
    if not path:
83
        return
84
    response = fargo_get('metadata/' + path)
85
    if response is None:
86
        return
87
    if response['err'] == 1:
88
        return False
89
    return response['data']
90

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

  
94
def validation_link(filled, field, upload):
95
    path = validation_path(filled, field, upload)
96
    if not path:
97
        return
98
    get_response().add_css_include('../js/smoothness/jquery-ui-1.10.0.custom.min.css')
99
    get_response().add_javascript(['jquery-ui.js', 'jquery.js', 'fargo.js'])
100
    label = get_document_type_label(field.document_type)
101
    fargo_url = get_publisher().get_site_option('fargo_url')
102
    url = urlparse.urljoin(fargo_url, 'validation/' + path)
103
    url += '?next=%s' % urllib.quote(get_publisher().get_frontoffice_url() + '/reload-top')
104
    return htmltext(_('<a data-title="Validate as a %(document_type_label)s" data-width="800" data-height="500" href="%(url)s">Validate as a %(document_type_label)s</a>')) % {
105
        'url': url,
106
        'document_type_label': label,
107
    }
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
......
399 402
            s = f.get_view_value(value)
400 403
            s = s.replace(str('[download]'), str('%sdownload' % form_url))
401 404
            r += s
405
            if isinstance(f, FileField):
406
                s = self.file_validation_status(f, value)
407
                if s:
408
                    r += htmltext(str(s))
402 409
            r += htmltext('</div></div>')
403 410

  
404 411

  
......
529 536
        else:
530 537
            return redirect('files/%s/' % fn)
531 538

  
539
    def file_validation_status(self, field, value):
540
        status = file_validation.validate_upload(self.filled, field, value)
541
        if status is None:
542
            return
543
        r = TemplateIO(html=True)
544
        r += htmltext('<div class="file-validation">')
545
        if status is False:
546
            r += file_validation.validation_link(self.filled, field, value)
547
        else:
548
            r += htmltext(_('<p>%s validated by %s on %s</p>')) % (
549
                status['label'], status['creator'], status['created'])
550
            r += htmltext('<ul>')
551
            for meta in status['metadata']:
552
                r += htmltext(_('<li>%(label)s: %(value)s</li>')) % {
553
                    'label': meta['label'],
554
                    'value': meta['value']
555
                }
556
            r += htmltext('</ul>')
557
        r += htmltext('</div>')
558
        return str(r)
559

  
532 560
    def _q_lookup(self, component):
533 561
        if component == 'files':
534 562
            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
        r = TemplateIO(html=True)
313
        r += htmltext('<script>window.top.document.location.reload();</script>')
314
        return r.getvalue()
315

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

  
314
-