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

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

Merge Bootstrap branch into trunk.

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