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

Last change on this file since 11658 was 11612, checked in by Henrik Bettermann, 11 years ago

Remove bugs found after running tests in waeup.uniben

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