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

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

Add more tests.

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