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

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

Add document file viewlets. Tests will follow.

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