source: main/waeup.sirp/trunk/src/waeup/sirp/students/viewlets.py @ 7280

Last change on this file since 7280 was 7280, checked in by Henrik Bettermann, 13 years ago

Use the viewlet manager 'FileManager?' to render either images or placeholders of uploaded files.

  • Property svn:keywords set to Id
File size: 13.8 KB
Line 
1## $Id: viewlets.py 7280 2011-12-06 12:27:21Z henrik $
2##
3## Copyright (C) 2011 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##
18import os
19import grok
20from zope.component import getUtility
21from zope.interface import Interface
22from waeup.sirp.interfaces import (
23    IWAeUPObject, IExtFileStore, IFileStoreNameChooser)
24from waeup.sirp.utils.helpers import string_from_bytes, file_size
25from waeup.sirp.browser import DEFAULT_IMAGE_PATH
26from waeup.sirp.browser.viewlets import PrimaryNavTab
27from waeup.sirp.browser.layout import default_primary_nav_template
28from waeup.sirp.students.browser import (
29    StudentClearanceDisplayFormPage, StudentClearanceManageFormPage,
30    write_log_message, StudentBaseManageFormPage, StudentBaseDisplayFormPage,
31    StudentFilesUploadPage, ExportPDFClearanceSlipPage)
32from waeup.sirp.students.interfaces import IStudent, IStudentClearance
33
34grok.context(IWAeUPObject) # Make IWAeUPObject the default context
35grok.templatedir('browser_templates')
36
37ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif')
38
39class StudentManageSidebar(grok.ViewletManager):
40    grok.name('left_studentmanage')
41
42class StudentManageLink(grok.Viewlet):
43    """A link displayed in the student box which shows up for StudentNavigation
44    objects.
45
46    """
47    grok.baseclass()
48    grok.viewletmanager(StudentManageSidebar)
49    grok.context(IWAeUPObject)
50    grok.view(Interface)
51    grok.order(5)
52    grok.require('waeup.viewStudent')
53
54    link = 'index'
55    text = u'Base Data'
56
57    def render(self):
58        url = self.view.url(self.context.getStudent(), self.link)
59        return u'<div class="portlet"><a href="%s">%s</a></div>' % (
60                url, self.text)
61
62class StudentManageBaseLink(StudentManageLink):
63    grok.order(1)
64    link = 'index'
65    text = u'Base Data'
66
67class StudentManageClearanceLink(StudentManageLink):
68    grok.order(2)
69    link = 'view_clearance'
70    text = u'Clearance Data'
71
72class StudentManagePersonalLink(StudentManageLink):
73    grok.order(2)
74    link = 'view_personal'
75    text = u'Personal Data'
76
77class StudentManageStudyCourseLink(StudentManageLink):
78    grok.order(3)
79    link = 'studycourse'
80    text = u'Study Course'
81
82class StudentManagePaymentsLink(StudentManageLink):
83    grok.order(4)
84    grok.require('waeup.payStudent')
85    link = 'payments'
86    text = u'Payments'
87
88class StudentManageAccommodationLink(StudentManageLink):
89    grok.order(5)
90    grok.require('waeup.handleAccommodation')
91    link = 'accommodation'
92    text = u'Accommodation Data'
93
94class StudentManageHistoryLink(StudentManageLink):
95    grok.order(6)
96    link = 'history'
97    text = u'History'
98
99
100class StudentMenu(grok.ViewletManager):
101    grok.name('top_student')
102
103class StudentLink(grok.Viewlet):
104    """A link displayed in the student box which shows up for StudentNavigation
105    objects.
106
107    """
108    grok.baseclass()
109    grok.viewletmanager(StudentMenu)
110    grok.context(IWAeUPObject)
111    grok.view(Interface)
112    grok.order(5)
113    grok.require('waeup.viewStudent')
114    template = grok.PageTemplateFile('browser_templates/plainactionbutton.pt')
115
116    link = 'index'
117    text = u'Base Data'
118
119    @property
120    def target_url(self):
121        """Get a URL to the target...
122        """
123        return self.view.url(self.context.getStudent(), self.link)
124
125class StudentBaseLink(StudentLink):
126    grok.order(1)
127    link = 'index'
128    text = u'Base Data'
129
130class StudentClearanceLink(StudentLink):
131    grok.order(2)
132    link = 'view_clearance'
133    text = u'Clearance Data'
134
135class StudentPersonalLink(StudentLink):
136    grok.order(2)
137    link = 'view_personal'
138    text = u'Personal Data'
139
140class StudentStudyCourseLink(StudentLink):
141    grok.order(3)
142    link = 'studycourse'
143    text = u'Study Course'
144
145class StudentPaymentsLink(StudentLink):
146    grok.order(4)
147    link = 'payments'
148    text = u'Payments'
149
150class StudentAccommodationLink(StudentLink):
151    grok.order(5)
152    link = 'accommodation'
153    text = u'Accommodation'
154
155class StudentHistoryLink(StudentLink):
156    grok.order(6)
157    link = 'history'
158    text = u'History'
159
160class StudentsTab(PrimaryNavTab):
161    """Students tab in primary navigation.
162    """
163
164    grok.context(IWAeUPObject)
165    grok.order(4)
166    grok.require('waeup.viewStudentsTab')
167
168    pnav = 4
169    tab_title = u'Students'
170
171    @property
172    def link_target(self):
173        return self.view.application_url('students')
174
175class PrimaryStudentNavManager(grok.ViewletManager):
176    """Viewlet manager for the primary navigation tab.
177    """
178    grok.name('primary_nav_student')
179
180class PrimaryStudentNavTab(grok.Viewlet):
181    """Base for primary student nav tabs.
182    """
183    grok.baseclass()
184    grok.viewletmanager(PrimaryStudentNavManager)
185    template = default_primary_nav_template
186    grok.order(1)
187    grok.require('waeup.Authenticated')
188    pnav = 0
189    tab_title = u'Some Text'
190
191    @property
192    def link_target(self):
193        return self.view.application_url()
194
195    @property
196    def active(self):
197        view_pnav = getattr(self.view, 'pnav', 0)
198        if view_pnav == self.pnav:
199            return 'active'
200        return ''
201
202class MyStudentDataTab(PrimaryStudentNavTab):
203    """MyData-tab in primary navigation.
204    """
205    grok.order(3)
206    grok.require('waeup.viewMyStudentDataTab')
207    pnav = 4
208    tab_title = u'My Data'
209
210    @property
211    def link_target(self):
212        rel_link = '/students/%s' % self.request.principal.id
213        return self.view.application_url() + rel_link
214
215def handle_file_delete(context, view, download_name):
216    """Handle deletion of student file.
217
218    """
219    store = getUtility(IExtFileStore)
220    store.deleteFileByContext(context, attr=download_name)
221    write_log_message(view, 'deleted: %s' % download_name)
222    view.flash('%s deleted.' % download_name)
223    return
224
225def handle_file_upload(upload, context, view, max_size, download_name=None):
226    """Handle upload of student file.
227
228    Returns `True` in case of success or `False`.
229
230    Please note that file pointer passed in (`upload`) most probably
231    points to end of file when leaving this function.
232    """
233    # Check some file requirements first
234    if upload.filename.count('.') == 0:
235        view.flash('File name has no extension.')
236        return False
237    if upload.filename.count('.') > 1:
238        view.flash('File name contains more than one dot.')
239        return False
240    basename, expected_ext = os.path.splitext(download_name)
241    dummy, ext = os.path.splitext(upload.filename)
242    ext.lower()
243    if expected_ext:
244        if ext != expected_ext:
245            view.flash('%s file extension expected.' %
246                expected_ext.replace('.',''))
247            return False
248    else:
249        if not ext.replace('.','') in ALLOWED_FILE_EXTENSIONS:
250            view.flash(
251                'Only the following extension are allowed: %s' %
252                ', '.join(ALLOWED_FILE_EXTENSIONS))
253            return False
254        download_name += ext
255    size = file_size(upload)
256    if size > max_size:
257        view.flash('Uploaded file is too big.')
258        return False
259    upload.seek(0) # file pointer moved when determining size
260    store = getUtility(IExtFileStore)
261    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
262    store.createFile(file_id, upload)
263    write_log_message(view, 'uploaded: %s (%s)' % (download_name,upload.filename))
264    view.flash('File %s uploaded.' % download_name)
265    return True
266
267class FileManager(grok.ViewletManager):
268    """Viewlet manager for uploading files, preferably scanned images.
269    """
270    grok.name('files')
271
272class FileDisplay(grok.Viewlet):
273    """Base file display viewlet.
274    """
275    grok.baseclass()
276    grok.context(IStudentClearance)
277    grok.viewletmanager(FileManager)
278    grok.view(StudentClearanceDisplayFormPage)
279    grok.template('filedisplay')
280    grok.order(1)
281    grok.require('waeup.viewStudent')
282    label = u'File:'
283    title = u'Scan'
284    download_name = u'filename.jpg'
285
286    @property
287    def file_exists(self):
288        image = getUtility(IExtFileStore).getFileByContext(
289            self.context, attr=self.download_name)
290        if image:
291            return True
292        else:
293            return False
294
295class FileUpload(FileDisplay):
296    """Base upload viewlet.
297    """
298    grok.baseclass()
299    grok.context(IStudentClearance)
300    grok.viewletmanager(FileManager)
301    grok.view(StudentClearanceManageFormPage)
302    grok.template('fileupload')
303    grok.require('waeup.uploadStudentFile')
304    tab_redirect = ''
305    mus = 1024 * 150
306
307    @property
308    def input_name(self):
309        return "%s" % self.__name__
310
311    def update(self):
312        self.max_upload_size = string_from_bytes(self.mus)
313        delete_button = self.request.form.get(
314            'delete_%s' % self.input_name, None)
315        upload_button = self.request.form.get(
316            'upload_%s' % self.input_name, None)
317        if delete_button:
318            handle_file_delete(
319                context=self.context, view=self.view,
320                download_name=self.download_name)
321            self.view.redirect(
322                self.view.url(
323                    self.context, self.view.__name__) + self.tab_redirect)
324            return
325        if upload_button:
326            upload = self.request.form.get(self.input_name, None)
327            if upload:
328                # We got a fresh upload
329                handle_file_upload(upload,
330                    self.context, self.view, self.mus, self.download_name)
331                self.view.redirect(
332                    self.view.url(
333                        self.context, self.view.__name__) + self.tab_redirect)
334            else:
335                self.view.flash('No local file selected.')
336                self.view.redirect(
337                    self.view.url(
338                        self.context, self.view.__name__) + self.tab_redirect)
339        return
340
341class PassportDisplay(FileDisplay):
342    """Passport display viewlet.
343    """
344    grok.order(1)
345    grok.context(IStudent)
346    grok.view(StudentBaseDisplayFormPage)
347    grok.require('waeup.viewStudent')
348    grok.template('imagedisplay')
349    label = u'Passport Picture:'
350    download_name = u'passport.jpg'
351
352class PassportUploadManage(FileUpload):
353    """Passport upload viewlet for officers.
354    """
355    grok.order(1)
356    grok.context(IStudent)
357    grok.view(StudentBaseManageFormPage)
358    grok.require('waeup.manageStudent')
359    grok.template('imageupload')
360    label = u'Passport Picture (jpg only):'
361    mus = 1024 * 50
362    download_name = u'passport.jpg'
363    tab_redirect = '#tab-2'
364
365class PassportUploadEdit(PassportUploadManage):
366    """Passport upload viewlet for students.
367    """
368    grok.view(StudentFilesUploadPage)
369    grok.require('waeup.uploadStudentFile')
370
371class BirthCertificateDisplay(FileDisplay):
372    """Birth Certificate display viewlet.
373    """
374    grok.order(1)
375    label = u'Birth Certificate:'
376    title = u'Birth Certificate Scan'
377    download_name = u'birth_certificate'
378
379class BirthCertificateSlip(BirthCertificateDisplay):
380    grok.view(ExportPDFClearanceSlipPage)
381
382class BirthCertificateUpload(FileUpload):
383    """Birth Certificate upload viewlet.
384    """
385    grok.order(1)
386    label = u'Birth Certificate:'
387    title = u'Birth Certificate Scan'
388    mus = 1024 * 150
389    download_name = u'birth_certificate'
390    tab_redirect = '#tab-2'
391
392class AcceptanceLetterDisplay(FileDisplay):
393    """Acceptance Letter display viewlet.
394    """
395    grok.order(1)
396    label = u'Acceptance Letter:'
397    title = u'Acceptance Letter Scan'
398    download_name = u'acceptance_letter'
399
400class AcceptanceLetterSlip(AcceptanceLetterDisplay):
401    grok.view(ExportPDFClearanceSlipPage)
402
403class AcceptanceLetterUpload(FileUpload):
404    """AcceptanceLetter upload viewlet.
405    """
406    grok.order(2)
407    label = u'Acceptance Letter:'
408    title = u'Acceptance Letter Scan'
409    mus = 1024 * 150
410    download_name = u'acceptance_letter'
411    tab_redirect = '#tab-2'
412
413class Image(grok.View):
414    """Renders jpeg images for students.
415    """
416    grok.baseclass()
417    grok.name('none.jpg')
418    grok.context(IStudentClearance)
419    grok.require('waeup.viewStudent')
420    download_name = u'none.jpg'
421
422    def render(self):
423        # A filename chooser turns a context into a filename suitable
424        # for file storage.
425        image = getUtility(IExtFileStore).getFileByContext(
426            self.context, attr=self.download_name)
427        if image is None:
428            # show placeholder image
429            self.response.setHeader('Content-Type', 'image/jpeg')
430            return open(DEFAULT_IMAGE_PATH, 'rb').read()
431        dummy,ext = os.path.splitext(image.name)
432        if ext == '.jpg':
433            self.response.setHeader('Content-Type', 'image/jpeg')
434        elif ext == '.png':
435            self.response.setHeader('Content-Type', 'image/png')
436        elif ext == '.pdf':
437            self.response.setHeader('Content-Type', 'application/pdf')
438        elif ext == '.tif':
439            self.response.setHeader('Content-Type', 'image/tiff')
440        return image
441
442class Passport(Image):
443    """Renders jpeg passport picture.
444    """
445    grok.name('passport.jpg')
446    download_name = u'passport.jpg'
447    grok.context(IStudent)
448
449class BirthCertificateImage(Image):
450    """Renders birth certificate jpeg scan.
451    """
452    grok.name('birth_certificate')
453    download_name = u'birth_certificate'
454
455class AcceptanceLetterImage(Image):
456    """Renders acceptance letter jpeg scan.
457    """
458    grok.name('acceptance_letter')
459    download_name = u'acceptance_letter'
Note: See TracBrowser for help on using the repository browser.