source: main/waeup.kofa/trunk/src/waeup/kofa/students/fileviewlets.py @ 12447

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

Remove redundant code and set download_filename for pdf files.

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