source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/fileviewlets.py @ 12059

Last change on this file since 12059 was 12058, checked in by Henrik Bettermann, 10 years ago

Add test for pdf file viewlets.

  • Property svn:keywords set to Id
File size: 10.7 KB
Line 
1## $Id: fileviewlets.py 12058 2014-11-25 17:09:05Z henrik $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
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, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18
19import os
20import grok
21from zope.component import getUtility
22from zope.interface import Interface
23from waeup.ikoba.interfaces import (
24    IIkobaObject, IExtFileStore, IFileStoreNameChooser)
25from waeup.ikoba.interfaces import MessageFactory as _
26from waeup.ikoba.browser import DEFAULT_IMAGE_PATH
27from waeup.ikoba.utils.helpers import (
28    string_from_bytes, file_size, get_fileformat)
29from waeup.ikoba.browser.layout import (
30    default_filedisplay_template,
31    default_fileupload_template)
32
33from waeup.ikoba.customers.interfaces import (
34    ICustomer, ICustomersUtils)
35
36from waeup.ikoba.customers.documents import (
37    CustomerSampleDocument, CustomerPDFDocument
38    )
39from waeup.ikoba.customers.browser import (
40    CustomerBaseDisplayFormPage, CustomerBaseManageFormPage,
41    CustomerFilesUploadPage,
42    DocumentDisplayFormPage, DocumentManageFormPage, DocumentEditFormPage,
43    ExportPDFDocumentsOverviewPage)
44
45grok.context(IIkobaObject)  # Make IIkobaObject the default context
46grok.templatedir('browser_templates')
47
48ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif', 'fpm')
49
50
51def handle_file_delete(context, view, download_name):
52    """Handle deletion of customer file.
53
54    """
55    store = getUtility(IExtFileStore)
56    store.deleteFileByContext(context, attr=download_name)
57    context.writeLogMessage(view, 'deleted: %s' % download_name)
58    view.flash(_('${a} deleted.', mapping={'a': download_name}))
59    return
60
61
62def handle_file_upload(upload, context, view, max_size, download_name=None):
63    """Handle upload of customer file.
64
65    Returns `True` in case of success or `False`.
66
67    Please note that file pointer passed in (`upload`) most probably
68    points to end of file when leaving this function.
69    """
70    # Check some file requirements first
71    size = file_size(upload)
72    if size > max_size:
73        view.flash(_('Uploaded file is too big.'), type="danger")
74        return False
75    upload.seek(0)  # file pointer moved when determining size
76    dummy,ext = os.path.splitext(upload.filename)
77    # fpm files are expected to be fingerprint minutiae, file
78    # format is not yet checked
79    if ext == '.fpm':
80        file_format = 'fpm'
81    else:
82        file_format = get_fileformat(None, upload.read(512))
83        upload.seek(0)  # same here
84    if file_format is None:
85        view.flash(_('Could not determine file type.'), type="danger")
86        return False
87    basename, expected_ext = os.path.splitext(download_name)
88    if expected_ext:
89        if '.' + file_format != expected_ext:
90            view.flash(_('${a} file extension expected.',
91                mapping={'a': expected_ext[1:]}), type="danger")
92            return False
93    else:
94        if not file_format in ALLOWED_FILE_EXTENSIONS:
95            view.flash(
96                _('Only the following extensions are allowed: ${a}',
97                mapping={'a': ', '.join(ALLOWED_FILE_EXTENSIONS)}),
98                type="danger")
99            return False
100        download_name += '.' + file_format
101    store = getUtility(IExtFileStore)
102    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
103    store.createFile(file_id, upload)
104    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
105        download_name,upload.filename))
106    view.flash(_('File ${a} uploaded.', mapping={'a': download_name}))
107    return True
108
109
110class FileManager(grok.ViewletManager):
111    """Viewlet manager for uploading files, preferably scanned images.
112    """
113    grok.name('files')
114
115
116class FileDisplay(grok.Viewlet):
117    """Base file display viewlet.
118    """
119    grok.baseclass()
120    grok.viewletmanager(FileManager)
121    template = default_filedisplay_template
122    grok.order(1)
123    label = _(u'File')
124    title = _(u'Scan')
125    download_name = u'filename.jpg'
126
127    @property
128    def file_exists(self):
129        image = getUtility(IExtFileStore).getFileByContext(
130            self.context, attr=self.download_name)
131        if image:
132            return True
133        else:
134            return False
135
136
137class FileUpload(FileDisplay):
138    """Base upload viewlet.
139    """
140    grok.baseclass()
141    grok.viewletmanager(FileManager)
142    template = default_fileupload_template
143    tab_redirect = '#tab2-top'
144    mus = 1024 * 150
145    upload_button =_('Upload selected file')
146    delete_button = _('Delete')
147
148    @property
149    def show_viewlet(self):
150        customers_utils = getUtility(ICustomersUtils)
151        if self.__name__ in customers_utils.SKIP_UPLOAD_VIEWLETS:
152            return False
153        return True
154
155    @property
156    def input_name(self):
157        return "%s" % self.__name__
158
159    def update(self):
160        self.max_upload_size = string_from_bytes(self.mus)
161        delete_button = self.request.form.get(
162            'delete_%s' % self.input_name, None)
163        upload_button = self.request.form.get(
164            'upload_%s' % self.input_name, None)
165        if delete_button:
166            handle_file_delete(
167                context=self.context, view=self.view,
168                download_name=self.download_name)
169            self.view.redirect(
170                self.view.url(
171                    self.context, self.view.__name__) + self.tab_redirect)
172            return
173        if upload_button:
174            upload = self.request.form.get(self.input_name, None)
175            if upload:
176                # We got a fresh upload
177                handle_file_upload(upload,
178                    self.context, self.view, self.mus, self.download_name)
179                self.view.redirect(
180                    self.view.url(
181                        self.context, self.view.__name__) + self.tab_redirect)
182            else:
183                self.view.flash(_('No local file selected.'), type="danger")
184                self.view.redirect(
185                    self.view.url(
186                        self.context, self.view.__name__) + self.tab_redirect)
187        return
188
189
190class Image(grok.View):
191    """Renders images for customers.
192    """
193    grok.baseclass()
194    grok.name('none.jpg')
195    download_name = u'none.jpg'
196
197    def render(self):
198        # A filename chooser turns a context into a filename suitable
199        # for file storage.
200        image = getUtility(IExtFileStore).getFileByContext(
201            self.context, attr=self.download_name)
202        if image is None:
203            # show placeholder image
204            self.response.setHeader('Content-Type', 'image/jpeg')
205            return open(DEFAULT_IMAGE_PATH, 'rb').read()
206        dummy,ext = os.path.splitext(image.name)
207        if ext == '.jpg':
208            self.response.setHeader('Content-Type', 'image/jpeg')
209        elif ext == '.fpm':
210            self.response.setHeader('Content-Type', 'application/binary')
211        elif ext == '.png':
212            self.response.setHeader('Content-Type', 'image/png')
213        elif ext == '.pdf':
214            self.response.setHeader('Content-Type', 'application/pdf')
215        elif ext == '.tif':
216            self.response.setHeader('Content-Type', 'image/tiff')
217        return image
218
219# File viewlets for customer base page
220
221class PassportDisplay(FileDisplay):
222    """Passport display viewlet.
223    """
224    grok.order(1)
225    grok.context(ICustomer)
226    grok.view(CustomerBaseDisplayFormPage)
227    grok.require('waeup.viewCustomer')
228    grok.template('imagedisplay')
229    label = _(u'Passport Picture')
230    download_name = u'passport.jpg'
231
232
233class PassportManageUpload(FileUpload):
234    """Passport upload viewlet for officers.
235    """
236    grok.order(1)
237    grok.context(ICustomer)
238    grok.view(CustomerBaseManageFormPage)
239    grok.require('waeup.manageCustomer')
240    grok.template('imageupload')
241    label = _(u'Passport Picture (jpg only)')
242    mus = 1024 * 50
243    download_name = u'passport.jpg'
244    tab_redirect = '#tab2'
245
246
247class PassportEditUpload(PassportManageUpload):
248    """Passport upload viewlet for customers.
249    """
250    grok.view(CustomerFilesUploadPage)
251    grok.require('waeup.uploadCustomerFile')
252
253
254class Passport(Image):
255    """Renders jpeg passport picture.
256    """
257    grok.name('passport.jpg')
258    download_name = u'passport.jpg'
259    grok.context(ICustomer)
260    grok.require('waeup.viewCustomer')
261
262# File viewlets for customer documents
263
264class SampleManageUpload(FileUpload):
265    """Scan upload viewlet for officers.
266    """
267    grok.order(1)
268    grok.context(CustomerSampleDocument)
269    grok.view(DocumentManageFormPage)
270    grok.require('waeup.manageCustomer')
271    label = _(u'Sample Scan')
272    title = _(u'Sample Scan')
273    mus = 1024 * 50
274    download_name = u'sample'
275    tab_redirect = '#tab2'
276
277
278class SampleEditUpload(SampleManageUpload):
279    """Scan upload viewlet for customer.
280    """
281    grok.view(DocumentEditFormPage)
282    grok.require('waeup.handleCustomer')
283
284
285class SampleDisplay(FileDisplay):
286    """Scan display viewlet.
287    """
288    grok.order(1)
289    grok.context(CustomerSampleDocument)
290    grok.require('waeup.viewCustomer')
291    grok.view(DocumentDisplayFormPage)
292    label = _(u'Sample Scan')
293    title = _(u'Sample Scan')
294    download_name = u'sample'
295
296
297class SampleScanImage(Image):
298    """Scan document.
299    """
300    grok.name('sample')
301    grok.context(CustomerSampleDocument)
302    grok.require('waeup.viewCustomer')
303    download_name = u'sample'
304
305
306class PDFManageUpload(FileUpload):
307    """Scan upload viewlet for officers.
308    """
309    grok.view(DocumentManageFormPage)
310    grok.context(CustomerPDFDocument)
311    grok.require('waeup.manageCustomer')
312    label = _(u'PDF File')
313    title = _(u'PDF File')
314    mus = 1024 * 200
315    download_name = u'sample.pdf'
316    tab_redirect = '#tab2'
317
318
319class PDFEditUpload(PDFManageUpload):
320    """Scan upload viewlet for customer.
321    """
322    grok.view(DocumentEditFormPage)
323    grok.require('waeup.handleCustomer')
324
325
326class PDFDisplay(FileDisplay):
327    """Scan display viewlet.
328    """
329    grok.order(1)
330    grok.context(CustomerPDFDocument)
331    grok.require('waeup.viewCustomer')
332    grok.view(DocumentDisplayFormPage)
333    label = _(u'PDF Scan')
334    title = _(u'PDF Scan')
335    download_name = u'sample.pdf'
336
337
338class PDFScanImage(Image):
339    """Scan document.
340    """
341    grok.name('sample.pdf')
342    grok.context(CustomerPDFDocument)
343    grok.require('waeup.viewCustomer')
344    download_name = u'sample.pdf'
Note: See TracBrowser for help on using the repository browser.