source: main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py @ 9294

Last change on this file since 9294 was 9273, checked in by Henrik Bettermann, 12 years ago

Add special permission and view for triggering transitions

  • Property svn:keywords set to Id
File size: 28.1 KB
RevLine 
[9138]13## $Id: viewlets.py 9273 2012-10-02 16:50:57Z henrik $
[7191]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
[7741]22from zope.i18n import translate
[7811]23from waeup.kofa.interfaces import (
[8467]24    IKofaObject, IExtFileStore, IFileStoreNameChooser)
[7811]25from waeup.kofa.interfaces import MessageFactory as _
26from waeup.kofa.utils.helpers import string_from_bytes, file_size
27from waeup.kofa.browser import DEFAULT_IMAGE_PATH
28from waeup.kofa.browser.viewlets import (
[7591]29    PrimaryNavTab, ManageActionButton, AddActionButton)
[8106]30from waeup.kofa.browser.layout import (
[8467]31    default_primary_nav_template, default_filedisplay_template,
32    default_fileupload_template)
33from waeup.kofa.students.workflow import (
34    ADMITTED, PAID, REQUESTED, RETURNING, CLEARED, REGISTERED,
35    VALIDATED)
[7811]36from waeup.kofa.students.browser import (
[8735]37    StudentClearanceManageFormPage,
[8467]38    StudentBaseManageFormPage, StudentFilesUploadPage,
39    ExportPDFClearanceSlipPage, StudentsContainerPage,
[7591]40    StudentsContainerManagePage, StudentBaseDisplayFormPage,
41    StudentClearanceDisplayFormPage, StudentPersonalDisplayFormPage,
42    StudyCourseDisplayFormPage, StudyLevelDisplayFormPage,
43    CourseTicketDisplayFormPage, OnlinePaymentDisplayFormPage,
[8119]44    AccommodationManageFormPage, BedTicketDisplayFormPage,
45    StudentClearanceEditFormPage, StudentPersonalEditFormPage)
[7811]46from waeup.kofa.students.interfaces import (
[8467]47    IStudentsContainer, IStudent, IStudentStudyCourse, IStudentAccommodation,
48    IStudentStudyLevel, ICourseTicket, IStudentOnlinePayment, IBedTicket,
[7591]49    )
[8467]50from waeup.kofa.utils.helpers import get_fileformat
[6642]51
[7819]52grok.context(IKofaObject) # Make IKofaObject the default context
[6687]53grok.templatedir('browser_templates')
[6642]54
[7123]55ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif')
56
[6687]57class StudentManageSidebar(grok.ViewletManager):
58    grok.name('left_studentmanage')
[6642]59
[6687]60class StudentManageLink(grok.Viewlet):
[6660]61    """A link displayed in the student box which shows up for StudentNavigation
[6642]62    objects.
63
64    """
65    grok.baseclass()
[6687]66    grok.viewletmanager(StudentManageSidebar)
[7819]67    grok.context(IKofaObject)
[6642]68    grok.view(Interface)
69    grok.order(5)
[6660]70    grok.require('waeup.viewStudent')
[6642]71
72    link = 'index'
[7738]73    text = _(u'Base Data')
[6642]74
75    def render(self):
[8736]76        url = self.view.url(self.context.student, self.link)
[7833]77        # Here we know that the cookie has been set
78        lang = self.request.cookies.get('kofa.language')
[7811]79        text = translate(self.text, 'waeup.kofa',
[7741]80            target_language=lang)
[7459]81        return u'<li><a href="%s">%s</a></li>' % (
[7741]82                url, text)
[6642]83
[7459]84class StudentManageApplicationLink(StudentManageLink):
[6687]85    grok.order(1)
[7351]86    link = 'application_slip'
[7738]87    text = _(u'Application Slip')
[7340]88
89    def render(self):
[7351]90        slip = getUtility(IExtFileStore).getFileByContext(
[8736]91            self.context.student, attr=self.link)
[7351]92        if slip:
93            url = self.view.url(self.context,self.link)
[7459]94            return u'<li><a href="%s">%s</a></li>' % (
[7340]95                    url, self.text)
96        return ''
97
[7459]98class StudentManageBaseLink(StudentManageLink):
99    grok.order(2)
100    link = 'index'
[7738]101    text = _(u'Base Data')
[7459]102
[6687]103class StudentManageClearanceLink(StudentManageLink):
[7340]104    grok.order(3)
[6687]105    link = 'view_clearance'
[7738]106    text = _(u'Clearance Data')
[6687]107
108class StudentManagePersonalLink(StudentManageLink):
[7340]109    grok.order(4)
[6687]110    link = 'view_personal'
[7738]111    text = _(u'Personal Data')
[6687]112
113class StudentManageStudyCourseLink(StudentManageLink):
[7340]114    grok.order(5)
[6687]115    link = 'studycourse'
[7738]116    text = _(u'Study Course')
[6687]117
118class StudentManagePaymentsLink(StudentManageLink):
[7340]119    grok.order(6)
[7181]120    grok.require('waeup.payStudent')
[6687]121    link = 'payments'
[7738]122    text = _(u'Payments')
[6687]123
124class StudentManageAccommodationLink(StudentManageLink):
[7340]125    grok.order(7)
[7181]126    grok.require('waeup.handleAccommodation')
[6687]127    link = 'accommodation'
[7738]128    text = _(u'Accommodation')
[6687]129
130class StudentManageHistoryLink(StudentManageLink):
[7340]131    grok.order(8)
[6687]132    link = 'history'
[7738]133    text = _(u'History')
[6687]134
[7591]135
136class StudentsContainerManageActionButton(ManageActionButton):
137    grok.order(1)
138    grok.context(IStudentsContainer)
139    grok.view(StudentsContainerPage)
140    grok.require('waeup.manageStudent')
[7738]141    text = _('Manage student section')
[7591]142
143class StudentsContainerAddActionButton(AddActionButton):
144    grok.order(1)
145    grok.context(IStudentsContainer)
146    grok.view(StudentsContainerManagePage)
147    grok.require('waeup.manageStudent')
[7738]148    text = _('Add student')
[7591]149    target = 'addstudent'
150
151class ContactActionButton(ManageActionButton):
[9191]152    grok.order(5)
[7591]153    grok.context(IStudent)
154    grok.view(StudentBaseDisplayFormPage)
155    grok.require('waeup.manageStudent')
156    icon = 'actionicon_mail.png'
[7738]157    text = _('Send email')
[7591]158    target = 'contactstudent'
159
160class StudentBaseManageActionButton(ManageActionButton):
161    grok.order(1)
162    grok.context(IStudent)
163    grok.view(StudentBaseDisplayFormPage)
164    grok.require('waeup.manageStudent')
[7738]165    text = _('Manage')
[7591]166    target = 'manage_base'
167
[9273]168class StudentTrigTransActionButton(ManageActionButton):
169    grok.order(2)
170    grok.context(IStudent)
171    grok.view(StudentBaseDisplayFormPage)
172    grok.require('waeup.triggerTransition')
173    icon = 'actionicon_trigtrans.png'
174    text = _(u'Trigger transition')
175    target = 'trigtrans'
176
[9191]177class AdmissionSlipActionButton(ManageActionButton):
178    grok.order(4)
179    grok.context(IStudent)
180    grok.view(StudentBaseDisplayFormPage)
181    grok.require('waeup.viewStudent')
182    icon = 'actionicon_pdf.png'
183    text = _('Download admission letter')
184    target = 'admission_slip.pdf'
185
[9138]186class StudentTransfernButton(ManageActionButton):
[9191]187    grok.order(6)
[9124]188    grok.context(IStudent)
189    grok.view(StudentBaseDisplayFormPage)
190    grok.require('waeup.manageStudent')
[9138]191    text = _('Transfer student')
192    target = 'transfer'
193    icon = 'actionicon_redo.png'
194
195class StudentDeactivateActionButton(ManageActionButton):
[9191]196    grok.order(7)
[9138]197    grok.context(IStudent)
198    grok.view(StudentBaseDisplayFormPage)
199    grok.require('waeup.manageStudent')
[9124]200    text = _('Deactivate account')
201    target = 'deactivate'
202    icon = 'actionicon_traffic_lights_red.png'
203
204    @property
205    def target_url(self):
206        if self.context.suspended:
207            return ''
208        return self.view.url(self.view.context, self.target)
209
[9145]210    @property
211    def onclick(self):
212        return "return window.confirm(%s);" % _(
213            "'A history message will be added. Are you sure?'")
214
[9124]215class StudentActivateActionButton(ManageActionButton):
[9191]216    grok.order(7)
[9124]217    grok.context(IStudent)
218    grok.view(StudentBaseDisplayFormPage)
219    grok.require('waeup.manageStudent')
220    text = _('Activate account')
221    target = 'activate'
222    icon = 'actionicon_traffic_lights_green.png'
223
224    @property
225    def target_url(self):
226        if not self.context.suspended:
227            return ''
228        return self.view.url(self.view.context, self.target)
229
[9145]230    @property
231    def onclick(self):
232        return "return window.confirm(%s);" % _(
233            "'A history message will be added. Are you sure?'")
234
[7591]235class StudentClearanceManageActionButton(ManageActionButton):
236    grok.order(1)
237    grok.context(IStudent)
238    grok.view(StudentClearanceDisplayFormPage)
239    grok.require('waeup.manageStudent')
[7738]240    text = _('Manage')
[8119]241    target = 'manage_clearance'
[7591]242
243class StudentClearActionButton(ManageActionButton):
244    grok.order(2)
245    grok.context(IStudent)
246    grok.view(StudentClearanceDisplayFormPage)
247    grok.require('waeup.clearStudent')
[7738]248    text = _('Clear student')
[7591]249    target = 'clear'
250    icon = 'actionicon_accept.png'
251
252    @property
253    def target_url(self):
254        if self.context.state != REQUESTED:
255            return ''
256        return self.view.url(self.view.context, self.target)
257
258class StudentRejectClearanceActionButton(ManageActionButton):
259    grok.order(3)
260    grok.context(IStudent)
261    grok.view(StudentClearanceDisplayFormPage)
262    grok.require('waeup.clearStudent')
[7738]263    text = _('Reject clearance')
[7591]264    target = 'reject_clearance'
265    icon = 'actionicon_reject.png'
266
267    @property
268    def target_url(self):
269        if self.context.state not in (REQUESTED, CLEARED):
270            return ''
271        return self.view.url(self.view.context, self.target)
272
273class ClearanceSlipActionButton(ManageActionButton):
274    grok.order(4)
275    grok.context(IStudent)
276    grok.view(StudentClearanceDisplayFormPage)
277    grok.require('waeup.viewStudent')
278    icon = 'actionicon_pdf.png'
[7738]279    text = _('Download clearance slip')
[7591]280    target = 'clearance.pdf'
281
[8119]282class ClearanceViewActionButton(ManageActionButton):
283    grok.order(1)
284    grok.context(IStudent)
285    grok.view(StudentClearanceEditFormPage)
286    grok.require('waeup.viewStudent')
287    icon = 'actionicon_view.png'
288    text = _('View')
289    target = 'view_clearance'
290
291class PersonalViewActionButton(ManageActionButton):
292    grok.order(1)
293    grok.context(IStudent)
294    grok.view(StudentPersonalEditFormPage)
295    grok.require('waeup.viewStudent')
296    icon = 'actionicon_view.png'
297    text = _('View')
298    target = 'view_personal'
299
[8903]300class StudentPersonalManageActionButton(ManageActionButton):
[7591]301    grok.order(1)
302    grok.context(IStudent)
303    grok.view(StudentPersonalDisplayFormPage)
[8903]304    grok.require('waeup.manageStudent')
305    text = _('Manage')
306    target = 'manage_personal'
307
308class StudentPersonalEditActionButton(ManageActionButton):
309    grok.order(2)
310    grok.context(IStudent)
311    grok.view(StudentPersonalDisplayFormPage)
312    grok.require('waeup.handleStudent')
[7738]313    text = _('Edit')
[7591]314    target = 'edit_personal'
315
316class StudyCourseManageActionButton(ManageActionButton):
317    grok.order(1)
318    grok.context(IStudentStudyCourse)
319    grok.view(StudyCourseDisplayFormPage)
320    grok.require('waeup.manageStudent')
[7738]321    text = _('Manage')
[7591]322    target = 'manage'
323
[9138]324    @property
325    def target_url(self):
326        if self.context.is_current:
327            return self.view.url(self.view.context, self.target)
328        return False
329
[8078]330class StudyLevelManageActionButton(ManageActionButton):
[7591]331    grok.order(1)
332    grok.context(IStudentStudyLevel)
333    grok.view(StudyLevelDisplayFormPage)
334    grok.require('waeup.manageStudent')
[7738]335    text = _('Manage')
[7591]336    target = 'manage'
337
[9138]338    @property
339    def target_url(self):
340        is_current = self.context.__parent__.is_current
341        if not is_current:
342            return ''
343        return self.view.url(self.view.context, self.target)
344
[7591]345class StudentValidateCoursesActionButton(ManageActionButton):
346    grok.order(3)
347    grok.context(IStudentStudyLevel)
348    grok.view(StudyLevelDisplayFormPage)
349    grok.require('waeup.validateStudent')
[7738]350    text = _('Validate courses')
[7591]351    target = 'validate_courses'
352    icon = 'actionicon_accept.png'
353
354    @property
355    def target_url(self):
[9138]356        is_current = self.context.__parent__.is_current
[8736]357        if self.context.student.state != REGISTERED or \
[9138]358            str(self.context.__parent__.current_level) != self.context.__name__ or\
359            not is_current:
[7591]360            return ''
361        return self.view.url(self.view.context, self.target)
362
363class StudentRejectCoursesActionButton(ManageActionButton):
364    grok.order(4)
365    grok.context(IStudentStudyLevel)
366    grok.view(StudyLevelDisplayFormPage)
367    grok.require('waeup.validateStudent')
[7738]368    text = _('Reject courses')
[7591]369    target = 'reject_courses'
370    icon = 'actionicon_reject.png'
371
372    @property
373    def target_url(self):
[9138]374        is_current = self.context.__parent__.is_current
[8736]375        if self.context.student.state not in (VALIDATED, REGISTERED) or \
[9138]376            str(self.context.__parent__.current_level) != self.context.__name__ or\
377            not is_current:
[7591]378            return ''
379        return self.view.url(self.view.context, self.target)
380
[8078]381class CourseRegistrationSlipActionButton(ManageActionButton):
382    grok.order(5)
383    grok.context(IStudentStudyLevel)
384    grok.view(StudyLevelDisplayFormPage)
385    grok.require('waeup.viewStudent')
386    icon = 'actionicon_pdf.png'
387    text = _('Download course registration slip')
388    target = 'course_registration.pdf'
389
[9138]390    @property
391    def target_url(self):
392        is_current = self.context.__parent__.is_current
393        if not is_current:
394            return ''
395        return self.view.url(self.view.context, self.target)
396
[7591]397class CourseTicketManageActionButton(ManageActionButton):
398    grok.order(1)
399    grok.context(ICourseTicket)
400    grok.view(CourseTicketDisplayFormPage)
401    grok.require('waeup.manageStudent')
[7738]402    text = _('Manage')
[7591]403    target = 'manage'
404
405#class OnlinePaymentManageActionButton(ManageActionButton):
406#    grok.order(1)
407#    grok.context(IStudentPaymentsContainer)
408#    grok.view(PaymentsDisplayFormPage)
409#    grok.require('waeup.manageStudent')
410#    text = 'Manage payments'
411#    target = 'manage'
412
413class PaymentReceiptActionButton(ManageActionButton):
[9070]414    grok.order(9) # This button should always be the last one.
[7591]415    grok.context(IStudentOnlinePayment)
416    grok.view(OnlinePaymentDisplayFormPage)
417    grok.require('waeup.viewStudent')
418    icon = 'actionicon_pdf.png'
[8262]419    text = _('Download payment slip')
420    target = 'payment_slip.pdf'
[7591]421
422    @property
423    def target_url(self):
[8262]424        #if self.context.p_state != 'paid':
425        #    return ''
[7591]426        return self.view.url(self.view.context, self.target)
427
[8420]428class ApprovePaymentActionButton(ManageActionButton):
[9070]429    grok.order(8)
[7591]430    grok.context(IStudentOnlinePayment)
431    grok.view(OnlinePaymentDisplayFormPage)
[8420]432    grok.require('waeup.managePortal')
[8435]433    icon = 'actionicon_accept.png'
[8420]434    text = _('Approve payment')
435    target = 'approve'
[7591]436
437    @property
438    def target_url(self):
[7888]439        if self.context.p_state == 'paid':
[7591]440            return ''
441        return self.view.url(self.view.context, self.target)
442
443class AddBedTicketActionButton(ManageActionButton):
444    grok.order(1)
445    grok.context(IStudentAccommodation)
446    grok.view(AccommodationManageFormPage)
447    grok.require('waeup.handleAccommodation')
448    icon = 'actionicon_home.png'
[7738]449    text = _('Book accommodation')
[7591]450    target = 'add'
451
452class BedTicketSlipActionButton(ManageActionButton):
453    grok.order(1)
454    grok.context(IBedTicket)
455    grok.view(BedTicketDisplayFormPage)
456    grok.require('waeup.handleAccommodation')
457    icon = 'actionicon_pdf.png'
[7738]458    text = _('Download bed allocation slip')
[7591]459    target = 'bed_allocation.pdf'
460
461class RelocateStudentActionButton(ManageActionButton):
462    grok.order(2)
463    grok.context(IBedTicket)
464    grok.view(BedTicketDisplayFormPage)
465    grok.require('waeup.manageHostels')
466    icon = 'actionicon_reload.png'
[7738]467    text = _('Relocate student')
[7591]468    target = 'relocate'
469
470class StudentBaseActionButton(ManageActionButton):
471    grok.order(1)
472    grok.context(IStudent)
473    grok.view(StudentBaseDisplayFormPage)
474    grok.require('waeup.handleStudent')
[7876]475    text = _('Edit')
[7591]476    target = 'edit_base'
477
478class StudentPasswordActionButton(ManageActionButton):
479    grok.order(2)
480    grok.context(IStudent)
481    grok.view(StudentBaseDisplayFormPage)
482    grok.require('waeup.handleStudent')
483    icon = 'actionicon_key.png'
[7738]484    text = _('Change password')
[7591]485    target = 'change_password'
486
487class StudentPassportActionButton(ManageActionButton):
488    grok.order(3)
489    grok.context(IStudent)
490    grok.view(StudentBaseDisplayFormPage)
491    grok.require('waeup.handleStudent')
492    icon = 'actionicon_portrait.png'
[7738]493    text = _('Change portrait')
[7591]494    target = 'change_portrait'
495
496    @property
497    def target_url(self):
[7671]498        if self.context.state != ADMITTED:
[7591]499            return ''
500        return self.view.url(self.view.context, self.target)
501
502class StudentClearanceStartActionButton(ManageActionButton):
503    grok.order(1)
504    grok.context(IStudent)
505    grok.view(StudentClearanceDisplayFormPage)
506    grok.require('waeup.handleStudent')
507    icon = 'actionicon_start.gif'
[7738]508    text = _('Start clearance')
[7591]509    target = 'start_clearance'
510
511    @property
512    def target_url(self):
[7671]513        if self.context.state != ADMITTED:
[7591]514            return ''
515        return self.view.url(self.view.context, self.target)
516
517class StudentClearanceEditActionButton(ManageActionButton):
518    grok.order(1)
519    grok.context(IStudent)
520    grok.view(StudentClearanceDisplayFormPage)
521    grok.require('waeup.handleStudent')
[7738]522    text = _('Edit')
[7591]523    target = 'cedit'
524
525    @property
526    def target_url(self):
527        if self.context.clearance_locked:
528            return ''
529        return self.view.url(self.view.context, self.target)
530
[8476]531class StartSessionActionButton(ManageActionButton):
[7591]532    grok.order(1)
533    grok.context(IStudentStudyCourse)
534    grok.view(StudyCourseDisplayFormPage)
535    grok.require('waeup.handleStudent')
536    icon = 'actionicon_start.gif'
[8920]537    text = _('Start new session')
[8471]538    target = 'start_session'
[7591]539
540    @property
541    def target_url(self):
[9138]542        if self.context.next_session_allowed and self.context.is_current:
[8471]543            return self.view.url(self.view.context, self.target)
544        return False
[7591]545
546class AddStudyLevelActionButton(AddActionButton):
547    grok.order(1)
548    grok.context(IStudentStudyCourse)
549    grok.view(StudyCourseDisplayFormPage)
550    grok.require('waeup.handleStudent')
[7738]551    text = _('Add course list')
[7591]552    target = 'add'
553
554    @property
555    def target_url(self):
[8736]556        student = self.view.context.student
[7671]557        condition1 = student.state != PAID
[7591]558        condition2 = str(student['studycourse'].current_level) in \
559            self.view.context.keys()
[9138]560        condition3 = not self.context.is_current
561        if condition1 or condition2 or condition3:
[7591]562            return ''
563        return self.view.url(self.view.context, self.target)
564
565class StudyLevelEditActionButton(ManageActionButton):
[8078]566    grok.order(2)
[7591]567    grok.context(IStudentStudyLevel)
568    grok.view(StudyLevelDisplayFormPage)
569    grok.require('waeup.handleStudent')
[8920]570    text = _('Edit course list')
[7591]571    target = 'edit'
572
573    @property
574    def target_url(self):
[8736]575        student = self.view.context.student
[9257]576        condition1 = student.state == PAID
577        condition2 = self.view.context.is_current_level
[9138]578        is_current = self.context.__parent__.is_current
[9257]579        if condition1 and condition2 and is_current:
580            return self.view.url(self.view.context, self.target)
581        return ''
[7591]582
[7184]583class StudentsTab(PrimaryNavTab):
584    """Students tab in primary navigation.
585    """
586
[7819]587    grok.context(IKofaObject)
[7184]588    grok.order(4)
[7240]589    grok.require('waeup.viewStudentsTab')
[7184]590
591    pnav = 4
[7674]592    tab_title = _(u'Students')
[7184]593
594    @property
595    def link_target(self):
596        return self.view.application_url('students')
597
[6687]598class PrimaryStudentNavManager(grok.ViewletManager):
599    """Viewlet manager for the primary navigation tab.
600    """
601    grok.name('primary_nav_student')
602
603class PrimaryStudentNavTab(grok.Viewlet):
604    """Base for primary student nav tabs.
605    """
606    grok.baseclass()
607    grok.viewletmanager(PrimaryStudentNavManager)
[7243]608    template = default_primary_nav_template
[6687]609    grok.order(1)
[7184]610    grok.require('waeup.Authenticated')
[8467]611    pnav = 0
[6687]612    tab_title = u'Some Text'
613
614    @property
615    def link_target(self):
616        return self.view.application_url()
617
618    @property
619    def active(self):
620        view_pnav = getattr(self.view, 'pnav', 0)
621        if view_pnav == self.pnav:
622            return 'active'
623        return ''
624
[7240]625class MyStudentDataTab(PrimaryStudentNavTab):
[7459]626    """MyData dropdown tab in primary navigation.
[6687]627    """
628    grok.order(3)
[7240]629    grok.require('waeup.viewMyStudentDataTab')
[7459]630    grok.template('mydatadropdowntabs')
[6687]631    pnav = 4
[7738]632    tab_title = _(u'My Data')
[6687]633
634    @property
[7459]635    def active(self):
636        view_pnav = getattr(self.view, 'pnav', 0)
637        if view_pnav == self.pnav:
638            return 'active dropdown'
639        return 'dropdown'
[7097]640
[7459]641    @property
642    def targets(self):
[9180]643        student = grok.getSite()['students'][self.request.principal.id]
644        student_url = self.view.url(student)
[9178]645        app_slip = getUtility(IExtFileStore).getFileByContext(
[9180]646            student, 'application_slip')
[7459]647        targets = []
[9178]648        if app_slip:
649            targets = [{'url':student_url + '/application_slip', 'title':'Application Slip'},]
[7459]650        targets += [
651            {'url':student_url, 'title':'Base Data'},
[8467]652            {'url':student_url + '/view_clearance',
653             'title':_('Clearance Data')},
[7738]654            {'url':student_url + '/view_personal', 'title':_('Personal Data')},
655            {'url':student_url + '/studycourse', 'title':_('Study Course')},
656            {'url':student_url + '/payments', 'title':_('Payments')},
[8467]657            {'url':student_url + '/accommodation',
658             'title':_('Accommodation Data')},
[7738]659            {'url':student_url + '/history', 'title':_('History')},
[7459]660            ]
661        return targets
662
[7107]663def handle_file_delete(context, view, download_name):
664    """Handle deletion of student file.
665
666    """
667    store = getUtility(IExtFileStore)
668    store.deleteFileByContext(context, attr=download_name)
[8735]669    context.writeLogMessage(view, 'deleted: %s' % download_name)
[7738]670    view.flash(_('${a} deleted.', mapping = {'a':download_name}))
[7107]671    return
672
[7106]673def handle_file_upload(upload, context, view, max_size, download_name=None):
674    """Handle upload of student file.
[7097]675
676    Returns `True` in case of success or `False`.
677
678    Please note that file pointer passed in (`upload`) most probably
679    points to end of file when leaving this function.
680    """
[7106]681    # Check some file requirements first
[8467]682    size = file_size(upload)
683    if size > max_size:
684        view.flash(_('Uploaded file is too big.'))
[7106]685        return False
[8467]686    upload.seek(0) # file pointer moved when determining size
687    file_format = get_fileformat(None, upload.read(512))
688    upload.seek(0) # same here
689    if file_format is None:
690        view.flash(_('Could not determine file type.'))
[7106]691        return False
692    basename, expected_ext = os.path.splitext(download_name)
[7123]693    if expected_ext:
[8467]694        if '.' + file_format != expected_ext:
[7738]695            view.flash(_('${a} file extension expected.',
[8467]696                mapping = {'a':expected_ext[1:]}))
[7123]697            return False
698    else:
[8467]699        if not file_format in ALLOWED_FILE_EXTENSIONS:
[7123]700            view.flash(
[8467]701                _('Only the following extensions are allowed: ${a}',
[7738]702                mapping = {'a':', '.join(ALLOWED_FILE_EXTENSIONS)}))
[7123]703            return False
[8467]704        download_name += '.' + file_format
[7097]705    store = getUtility(IExtFileStore)
[7106]706    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
[7097]707    store.createFile(file_id, upload)
[8735]708    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
[8467]709        download_name,upload.filename))
[7738]710    view.flash(_('File ${a} uploaded.', mapping = {'a':download_name}))
[7097]711    return True
712
713class FileManager(grok.ViewletManager):
714    """Viewlet manager for uploading files, preferably scanned images.
715    """
716    grok.name('files')
717
718class FileDisplay(grok.Viewlet):
719    """Base file display viewlet.
720    """
721    grok.baseclass()
[7993]722    grok.context(IStudent)
[7097]723    grok.viewletmanager(FileManager)
724    grok.view(StudentClearanceDisplayFormPage)
[8106]725    template = default_filedisplay_template
[7097]726    grok.order(1)
727    grok.require('waeup.viewStudent')
[7738]728    label = _(u'File')
729    title = _(u'Scan')
[7106]730    download_name = u'filename.jpg'
[7097]731
[7107]732    @property
733    def file_exists(self):
734        image = getUtility(IExtFileStore).getFileByContext(
735            self.context, attr=self.download_name)
736        if image:
737            return True
738        else:
739            return False
740
[7097]741class FileUpload(FileDisplay):
742    """Base upload viewlet.
743    """
744    grok.baseclass()
[7993]745    grok.context(IStudent)
[7097]746    grok.viewletmanager(FileManager)
747    grok.view(StudentClearanceManageFormPage)
[8106]748    template = default_fileupload_template
[7127]749    grok.require('waeup.uploadStudentFile')
[7491]750    tab_redirect = '?tab2'
[7097]751    mus = 1024 * 150
[7735]752    upload_button =_('Upload new file')
753    delete_button = _('Delete attachment')
[8135]754    show_viewlet = True
[7097]755
[7117]756    @property
757    def input_name(self):
758        return "%s" % self.__name__
759
[7097]760    def update(self):
761        self.max_upload_size = string_from_bytes(self.mus)
[7117]762        delete_button = self.request.form.get(
763            'delete_%s' % self.input_name, None)
764        upload_button = self.request.form.get(
765            'upload_%s' % self.input_name, None)
[7108]766        if delete_button:
[7107]767            handle_file_delete(
768                context=self.context, view=self.view,
769                download_name=self.download_name)
770            self.view.redirect(
[7134]771                self.view.url(
772                    self.context, self.view.__name__) + self.tab_redirect)
[7107]773            return
[7108]774        if upload_button:
775            upload = self.request.form.get(self.input_name, None)
776            if upload:
777                # We got a fresh upload
[7111]778                handle_file_upload(upload,
779                    self.context, self.view, self.mus, self.download_name)
780                self.view.redirect(
[7134]781                    self.view.url(
782                        self.context, self.view.__name__) + self.tab_redirect)
[7117]783            else:
[7738]784                self.view.flash(_('No local file selected.'))
[7117]785                self.view.redirect(
[7134]786                    self.view.url(
787                        self.context, self.view.__name__) + self.tab_redirect)
[7097]788        return
789
[7112]790class PassportDisplay(FileDisplay):
791    """Passport display viewlet.
792    """
793    grok.order(1)
794    grok.context(IStudent)
795    grok.view(StudentBaseDisplayFormPage)
796    grok.require('waeup.viewStudent')
797    grok.template('imagedisplay')
[7738]798    label = _(u'Passport Picture')
[7112]799    download_name = u'passport.jpg'
800
801class PassportUploadManage(FileUpload):
802    """Passport upload viewlet for officers.
803    """
804    grok.order(1)
805    grok.context(IStudent)
806    grok.view(StudentBaseManageFormPage)
[7136]807    grok.require('waeup.manageStudent')
[7112]808    grok.template('imageupload')
[7738]809    label = _(u'Passport Picture (jpg only)')
[7112]810    mus = 1024 * 50
811    download_name = u'passport.jpg'
[7491]812    tab_redirect = '?tab2'
[7112]813
814class PassportUploadEdit(PassportUploadManage):
815    """Passport upload viewlet for students.
816    """
[7114]817    grok.view(StudentFilesUploadPage)
[7127]818    grok.require('waeup.uploadStudentFile')
[7112]819
[7097]820class BirthCertificateDisplay(FileDisplay):
[7112]821    """Birth Certificate display viewlet.
[7097]822    """
823    grok.order(1)
[7738]824    label = _(u'Birth Certificate')
825    title = _(u'Birth Certificate Scan')
[7123]826    download_name = u'birth_certificate'
[7097]827
[7280]828class BirthCertificateSlip(BirthCertificateDisplay):
829    grok.view(ExportPDFClearanceSlipPage)
830
[7127]831class BirthCertificateUpload(FileUpload):
[7097]832    """Birth Certificate upload viewlet.
833    """
834    grok.order(1)
[7738]835    label = _(u'Birth Certificate')
836    title = _(u'Birth Certificate Scan')
[7097]837    mus = 1024 * 150
[7123]838    download_name = u'birth_certificate'
[7491]839    tab_redirect = '?tab2'
[7097]840
841class Image(grok.View):
[7351]842    """Renders images for students.
[7097]843    """
[7106]844    grok.baseclass()
[7097]845    grok.name('none.jpg')
[7993]846    grok.context(IStudent)
[7097]847    grok.require('waeup.viewStudent')
[7106]848    download_name = u'none.jpg'
[7097]849
850    def render(self):
851        # A filename chooser turns a context into a filename suitable
852        # for file storage.
853        image = getUtility(IExtFileStore).getFileByContext(
[7106]854            self.context, attr=self.download_name)
[7097]855        if image is None:
856            # show placeholder image
[7123]857            self.response.setHeader('Content-Type', 'image/jpeg')
[7097]858            return open(DEFAULT_IMAGE_PATH, 'rb').read()
[7123]859        dummy,ext = os.path.splitext(image.name)
860        if ext == '.jpg':
861            self.response.setHeader('Content-Type', 'image/jpeg')
862        elif ext == '.png':
863            self.response.setHeader('Content-Type', 'image/png')
864        elif ext == '.pdf':
865            self.response.setHeader('Content-Type', 'application/pdf')
866        elif ext == '.tif':
867            self.response.setHeader('Content-Type', 'image/tiff')
[7097]868        return image
869
[7112]870class Passport(Image):
871    """Renders jpeg passport picture.
872    """
873    grok.name('passport.jpg')
874    download_name = u'passport.jpg'
875    grok.context(IStudent)
876
[7351]877class ApplicationSlipImage(Image):
878    """Renders application slip scan.
879    """
880    grok.name('application_slip')
881    download_name = u'application_slip'
882
[7097]883class BirthCertificateImage(Image):
[7351]884    """Renders birth certificate scan.
[7097]885    """
[7123]886    grok.name('birth_certificate')
887    download_name = u'birth_certificate'
Note: See TracBrowser for help on using the repository browser.