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

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

Add permission and role for transcript officers.

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