source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py @ 12033

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

propset svn:keywords "Id"

  • Property svn:keywords set to Id
File size: 8.5 KB
Line 
1## $Id: files.py 11997 2014-11-19 17:02:48Z 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 ICustomer, ICustomersUtils
34from waeup.ikoba.customers.browser import (
35    CustomerBaseDisplayFormPage, CustomerBaseManageFormPage,
36    CustomerFilesUploadPage)
37
38grok.context(IIkobaObject)  # Make IIkobaObject the default context
39grok.templatedir('browser_templates')
40
41ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif', 'fpm')
42
43
44def handle_file_delete(context, view, download_name):
45    """Handle deletion of customer file.
46
47    """
48    store = getUtility(IExtFileStore)
49    store.deleteFileByContext(context, attr=download_name)
50    context.writeLogMessage(view, 'deleted: %s' % download_name)
51    view.flash(_('${a} deleted.', mapping={'a': download_name}))
52    return
53
54
55def handle_file_upload(upload, context, view, max_size, download_name=None):
56    """Handle upload of customer file.
57
58    Returns `True` in case of success or `False`.
59
60    Please note that file pointer passed in (`upload`) most probably
61    points to end of file when leaving this function.
62    """
63    # Check some file requirements first
64    size = file_size(upload)
65    if size > max_size:
66        view.flash(_('Uploaded file is too big.'), type="danger")
67        return False
68    upload.seek(0)  # file pointer moved when determining size
69    dummy,ext = os.path.splitext(upload.filename)
70    # fpm files are expected to be fingerprint minutiae, file
71    # format is not yet checked
72    if ext == '.fpm':
73        file_format = 'fpm'
74    else:
75        file_format = get_fileformat(None, upload.read(512))
76        upload.seek(0)  # same here
77    if file_format is None:
78        view.flash(_('Could not determine file type.'), type="danger")
79        return False
80    basename, expected_ext = os.path.splitext(download_name)
81    if expected_ext:
82        if '.' + file_format != expected_ext:
83            view.flash(_('${a} file extension expected.',
84                mapping={'a': expected_ext[1:]}), type="danger")
85            return False
86    else:
87        if not file_format in ALLOWED_FILE_EXTENSIONS:
88            view.flash(
89                _('Only the following extensions are allowed: ${a}',
90                mapping={'a': ', '.join(ALLOWED_FILE_EXTENSIONS)}),
91                type="danger")
92            return False
93        download_name += '.' + file_format
94    store = getUtility(IExtFileStore)
95    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
96    store.createFile(file_id, upload)
97    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
98        download_name,upload.filename))
99    view.flash(_('File ${a} uploaded.', mapping={'a': download_name}))
100    return True
101
102
103class FileManager(grok.ViewletManager):
104    """Viewlet manager for uploading files, preferably scanned images.
105    """
106    grok.name('files')
107
108
109class FileDisplay(grok.Viewlet):
110    """Base file display viewlet.
111    """
112    grok.baseclass()
113    grok.viewletmanager(FileManager)
114    template = default_filedisplay_template
115    grok.order(1)
116    label = _(u'File')
117    title = _(u'Scan')
118    download_name = u'filename.jpg'
119
120    @property
121    def file_exists(self):
122        image = getUtility(IExtFileStore).getFileByContext(
123            self.context, attr=self.download_name)
124        if image:
125            return True
126        else:
127            return False
128
129
130class FileUpload(FileDisplay):
131    """Base upload viewlet.
132    """
133    grok.baseclass()
134    grok.viewletmanager(FileManager)
135    template = default_fileupload_template
136    tab_redirect = '#tab2-top'
137    mus = 1024 * 150
138    upload_button =_('Upload selected file')
139    delete_button = _('Delete')
140
141    @property
142    def show_viewlet(self):
143        customers_utils = getUtility(ICustomersUtils)
144        if self.__name__ in customers_utils.SKIP_UPLOAD_VIEWLETS:
145            return False
146        return True
147
148    @property
149    def input_name(self):
150        return "%s" % self.__name__
151
152    def update(self):
153        self.max_upload_size = string_from_bytes(self.mus)
154        delete_button = self.request.form.get(
155            'delete_%s' % self.input_name, None)
156        upload_button = self.request.form.get(
157            'upload_%s' % self.input_name, None)
158        if delete_button:
159            handle_file_delete(
160                context=self.context, view=self.view,
161                download_name=self.download_name)
162            self.view.redirect(
163                self.view.url(
164                    self.context, self.view.__name__) + self.tab_redirect)
165            return
166        if upload_button:
167            upload = self.request.form.get(self.input_name, None)
168            if upload:
169                # We got a fresh upload
170                handle_file_upload(upload,
171                    self.context, self.view, self.mus, self.download_name)
172                self.view.redirect(
173                    self.view.url(
174                        self.context, self.view.__name__) + self.tab_redirect)
175            else:
176                self.view.flash(_('No local file selected.'), type="danger")
177                self.view.redirect(
178                    self.view.url(
179                        self.context, self.view.__name__) + self.tab_redirect)
180        return
181
182
183class Image(grok.View):
184    """Renders images for customers.
185    """
186    grok.baseclass()
187    grok.name('none.jpg')
188    download_name = u'none.jpg'
189
190    def render(self):
191        # A filename chooser turns a context into a filename suitable
192        # for file storage.
193        image = getUtility(IExtFileStore).getFileByContext(
194            self.context, attr=self.download_name)
195        if image is None:
196            # show placeholder image
197            self.response.setHeader('Content-Type', 'image/jpeg')
198            return open(DEFAULT_IMAGE_PATH, 'rb').read()
199        dummy,ext = os.path.splitext(image.name)
200        if ext == '.jpg':
201            self.response.setHeader('Content-Type', 'image/jpeg')
202        elif ext == '.fpm':
203            self.response.setHeader('Content-Type', 'application/binary')
204        elif ext == '.png':
205            self.response.setHeader('Content-Type', 'image/png')
206        elif ext == '.pdf':
207            self.response.setHeader('Content-Type', 'application/pdf')
208        elif ext == '.tif':
209            self.response.setHeader('Content-Type', 'image/tiff')
210        return image
211
212# File viewlets for customer base page
213
214class PassportDisplay(FileDisplay):
215    """Passport display viewlet.
216    """
217    grok.order(1)
218    grok.context(ICustomer)
219    grok.view(CustomerBaseDisplayFormPage)
220    grok.require('waeup.viewCustomer')
221    grok.template('imagedisplay')
222    label = _(u'Passport Picture')
223    download_name = u'passport.jpg'
224
225
226class PassportUploadManage(FileUpload):
227    """Passport upload viewlet for officers.
228    """
229    grok.order(1)
230    grok.context(ICustomer)
231    grok.view(CustomerBaseManageFormPage)
232    grok.require('waeup.manageCustomer')
233    grok.template('imageupload')
234    label = _(u'Passport Picture (jpg only)')
235    mus = 1024 * 50
236    download_name = u'passport.jpg'
237    tab_redirect = '#tab2'
238
239
240class PassportUploadEdit(PassportUploadManage):
241    """Passport upload viewlet for customers.
242    """
243    grok.view(CustomerFilesUploadPage)
244    grok.require('waeup.uploadCustomerFile')
245
246
247class Passport(Image):
248    """Renders jpeg passport picture.
249    """
250    grok.name('passport.jpg')
251    download_name = u'passport.jpg'
252    grok.context(ICustomer)
253    grok.require('waeup.viewCustomer')
254
255# File viewlets for customer documents
Note: See TracBrowser for help on using the repository browser.