## $Id: fileviewlets.py 12062 2014-11-26 13:03:56Z henrik $ ## ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## import os import grok from zope.component import getUtility from zope.interface import Interface from waeup.ikoba.interfaces import ( IIkobaObject, IExtFileStore, IFileStoreNameChooser) from waeup.ikoba.interfaces import MessageFactory as _ from waeup.ikoba.browser import DEFAULT_IMAGE_PATH from waeup.ikoba.utils.helpers import ( string_from_bytes, file_size, get_fileformat) from waeup.ikoba.browser.layout import ( default_filedisplay_template, default_fileupload_template) from waeup.ikoba.customers.interfaces import ( ICustomer, ICustomersUtils) from waeup.ikoba.customers.documents import ( CustomerSampleDocument, CustomerPDFDocument ) from waeup.ikoba.customers.browser import ( CustomerBaseDisplayFormPage, CustomerBaseManageFormPage, CustomerFilesUploadPage, DocumentDisplayFormPage, DocumentManageFormPage, DocumentEditFormPage, PDFDocumentSlipPage,) grok.context(IIkobaObject) # Make IIkobaObject the default context grok.templatedir('browser_templates') ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif', 'fpm') def handle_file_delete(context, view, download_name): """Handle deletion of customer file. """ store = getUtility(IExtFileStore) store.deleteFileByContext(context, attr=download_name) context.writeLogMessage(view, 'deleted: %s' % download_name) view.flash(_('${a} deleted.', mapping={'a': download_name})) return def handle_file_upload(upload, context, view, max_size, download_name=None): """Handle upload of customer file. Returns `True` in case of success or `False`. Please note that file pointer passed in (`upload`) most probably points to end of file when leaving this function. """ # Check some file requirements first size = file_size(upload) if size > max_size: view.flash(_('Uploaded file is too big.'), type="danger") return False upload.seek(0) # file pointer moved when determining size dummy,ext = os.path.splitext(upload.filename) # fpm files are expected to be fingerprint minutiae, file # format is not yet checked if ext == '.fpm': file_format = 'fpm' else: file_format = get_fileformat(None, upload.read(512)) upload.seek(0) # same here if file_format is None: view.flash(_('Could not determine file type.'), type="danger") return False basename, expected_ext = os.path.splitext(download_name) if expected_ext: if '.' + file_format != expected_ext: view.flash(_('${a} file extension expected.', mapping={'a': expected_ext[1:]}), type="danger") return False else: if not file_format in ALLOWED_FILE_EXTENSIONS: view.flash( _('Only the following extensions are allowed: ${a}', mapping={'a': ', '.join(ALLOWED_FILE_EXTENSIONS)}), type="danger") return False download_name += '.' + file_format store = getUtility(IExtFileStore) file_id = IFileStoreNameChooser(context).chooseName(attr=download_name) store.createFile(file_id, upload) context.writeLogMessage(view, 'uploaded: %s (%s)' % ( download_name,upload.filename)) view.flash(_('File ${a} uploaded.', mapping={'a': download_name})) return True class FileManager(grok.ViewletManager): """Viewlet manager for uploading files, preferably scanned images. """ grok.name('files') class FileDisplay(grok.Viewlet): """Base file display viewlet. """ grok.baseclass() grok.viewletmanager(FileManager) template = default_filedisplay_template grok.order(1) label = _(u'File') title = _(u'Scan') download_name = u'filename.jpg' @property def file_exists(self): image = getUtility(IExtFileStore).getFileByContext( self.context, attr=self.download_name) if image: return True else: return False class FileUpload(FileDisplay): """Base upload viewlet. """ grok.baseclass() grok.viewletmanager(FileManager) template = default_fileupload_template tab_redirect = '#tab2-top' mus = 1024 * 150 upload_button =_('Upload selected file') delete_button = _('Delete') @property def show_viewlet(self): customers_utils = getUtility(ICustomersUtils) if self.__name__ in customers_utils.SKIP_UPLOAD_VIEWLETS: return False return True @property def input_name(self): return "%s" % self.__name__ def update(self): self.max_upload_size = string_from_bytes(self.mus) delete_button = self.request.form.get( 'delete_%s' % self.input_name, None) upload_button = self.request.form.get( 'upload_%s' % self.input_name, None) if delete_button: handle_file_delete( context=self.context, view=self.view, download_name=self.download_name) self.view.redirect( self.view.url( self.context, self.view.__name__) + self.tab_redirect) return if upload_button: upload = self.request.form.get(self.input_name, None) if upload: # We got a fresh upload handle_file_upload(upload, self.context, self.view, self.mus, self.download_name) self.view.redirect( self.view.url( self.context, self.view.__name__) + self.tab_redirect) else: self.view.flash(_('No local file selected.'), type="danger") self.view.redirect( self.view.url( self.context, self.view.__name__) + self.tab_redirect) return class Image(grok.View): """Renders images for customers. """ grok.baseclass() grok.name('none.jpg') download_name = u'none.jpg' def render(self): # A filename chooser turns a context into a filename suitable # for file storage. image = getUtility(IExtFileStore).getFileByContext( self.context, attr=self.download_name) if image is None: # show placeholder image self.response.setHeader('Content-Type', 'image/jpeg') return open(DEFAULT_IMAGE_PATH, 'rb').read() dummy,ext = os.path.splitext(image.name) if ext == '.jpg': self.response.setHeader('Content-Type', 'image/jpeg') elif ext == '.fpm': self.response.setHeader('Content-Type', 'application/binary') elif ext == '.png': self.response.setHeader('Content-Type', 'image/png') elif ext == '.pdf': self.response.setHeader('Content-Type', 'application/pdf') elif ext == '.tif': self.response.setHeader('Content-Type', 'image/tiff') return image # File viewlets for customer base page class PassportDisplay(FileDisplay): """Passport display viewlet. """ grok.order(1) grok.context(ICustomer) grok.view(CustomerBaseDisplayFormPage) grok.require('waeup.viewCustomer') grok.template('imagedisplay') label = _(u'Passport Picture') download_name = u'passport.jpg' class PassportManageUpload(FileUpload): """Passport upload viewlet for officers. """ grok.order(1) grok.context(ICustomer) grok.view(CustomerBaseManageFormPage) grok.require('waeup.manageCustomer') grok.template('imageupload') label = _(u'Passport Picture (jpg only)') mus = 1024 * 50 download_name = u'passport.jpg' tab_redirect = '#tab2' class PassportEditUpload(PassportManageUpload): """Passport upload viewlet for customers. """ grok.view(CustomerFilesUploadPage) grok.require('waeup.uploadCustomerFile') class Passport(Image): """Renders jpeg passport picture. """ grok.name('passport.jpg') download_name = u'passport.jpg' grok.context(ICustomer) grok.require('waeup.viewCustomer') # File viewlets for customer documents class SampleScanManageUpload(FileUpload): """Scan upload viewlet for officers. """ grok.order(1) grok.context(CustomerSampleDocument) grok.view(DocumentManageFormPage) grok.require('waeup.manageCustomer') label = _(u'Sample Scan') title = _(u'Sample Scan') mus = 1024 * 50 download_name = u'sample' tab_redirect = '#tab2' class SampleScanEditUpload(SampleScanManageUpload): """Scan upload viewlet for customer. """ grok.view(DocumentEditFormPage) grok.require('waeup.handleCustomer') class SampleScanDisplay(FileDisplay): """Scan display viewlet. """ grok.order(1) grok.context(CustomerSampleDocument) grok.require('waeup.viewCustomer') grok.view(DocumentDisplayFormPage) label = _(u'Sample Scan') title = _(u'Sample Scan') download_name = u'sample' class SampleScanImage(Image): """Scan document. """ grok.name('sample') grok.context(CustomerSampleDocument) grok.require('waeup.viewCustomer') download_name = u'sample' class SampleScanPDFSlip(SampleScanDisplay): grok.view(PDFDocumentSlipPage) class PDFScanManageUpload(FileUpload): """Scan upload viewlet for officers. """ grok.view(DocumentManageFormPage) grok.context(CustomerPDFDocument) grok.require('waeup.manageCustomer') label = _(u'PDF File') title = _(u'PDF File') mus = 1024 * 200 download_name = u'sample.pdf' tab_redirect = '#tab2' class PDFScanEditUpload(PDFScanManageUpload): """Scan upload viewlet for customer. """ grok.view(DocumentEditFormPage) grok.require('waeup.handleCustomer') class PDFScanDisplay(FileDisplay): """Scan display viewlet. """ grok.order(1) grok.context(CustomerPDFDocument) grok.require('waeup.viewCustomer') grok.view(DocumentDisplayFormPage) label = _(u'PDF Scan') title = _(u'PDF Scan') download_name = u'sample.pdf' class PDFScanImage(Image): """Scan document. """ grok.name('sample.pdf') grok.context(CustomerPDFDocument) grok.require('waeup.viewCustomer') download_name = u'sample.pdf' class PDFScanSlip(PDFScanDisplay): grok.view(PDFDocumentSlipPage)