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

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

The StudentApplication? class is deprecated. We want to store the application_slip pdf file file instead.

Prepare everything in the students package for downloading such a pdf file.

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