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

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

Add StudentApplication? view components.

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