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

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

Fingerprint file upload enabled.

  • Property svn:keywords set to Id
File size: 33.4 KB
Line 
13## $Id: viewlets.py 11608 2014-04-30 11:49:18Z 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, GRADUATED, TRANSCRIPT)
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', 'fp')
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        if not self.link:
85            return ''
86        return u'<li><a href="%s">%s</a></li>' % (
87                url, text)
88
89class StudentManageApplicationLink(StudentManageLink):
90    grok.order(1)
91    link = 'application_slip'
92    text = _(u'Application Slip')
93
94    def render(self):
95        slip = getUtility(IExtFileStore).getFileByContext(
96            self.context.student, attr=self.link)
97        if slip:
98            lang = self.request.cookies.get('kofa.language')
99            text = translate(self.text, 'waeup.kofa',
100                target_language=lang)
101            url = self.view.url(self.context.student,self.link)
102            return u'<li><a href="%s">%s</a></li>' % (
103                    url, text)
104        return ''
105
106class StudentManageBaseLink(StudentManageLink):
107    grok.order(2)
108    link = 'index'
109    text = _(u'Base Data')
110
111class StudentManageClearanceLink(StudentManageLink):
112    grok.order(3)
113    grok.name('studentmanageclearancelink')
114    link = 'view_clearance'
115    text = _(u'Clearance Data')
116
117class StudentManagePersonalLink(StudentManageLink):
118    grok.order(4)
119    grok.name('studentmanagepersonallink')
120    link = 'view_personal'
121    text = _(u'Personal Data')
122
123class StudentManageStudyCourseLink(StudentManageLink):
124    grok.order(5)
125    link = 'studycourse'
126    text = _(u'Study Course')
127
128class StudentManagePaymentsLink(StudentManageLink):
129    grok.order(6)
130    grok.require('waeup.viewStudent')
131    link = 'payments'
132    text = _(u'Payments')
133
134class StudentManageAccommodationLink(StudentManageLink):
135    grok.order(7)
136    grok.name('studentmanageaccommodationlink')
137    grok.require('waeup.handleAccommodation')
138    link = 'accommodation'
139    text = _(u'Accommodation')
140
141class StudentManageHistoryLink(StudentManageLink):
142    grok.order(8)
143    link = 'history'
144    text = _(u'History')
145
146
147class StudentsContainerManageActionButton(ManageActionButton):
148    grok.order(1)
149    grok.context(IStudentsContainer)
150    grok.view(StudentsContainerPage)
151    grok.require('waeup.manageStudent')
152    text = _('Manage student section')
153
154class StudentsContainerAddActionButton(AddActionButton):
155    grok.order(1)
156    grok.context(IStudentsContainer)
157    grok.view(StudentsContainerManagePage)
158    grok.require('waeup.manageStudent')
159    text = _('Add student')
160    target = 'addstudent'
161
162class ContactActionButton(ManageActionButton):
163    grok.order(5)
164    grok.context(IStudent)
165    grok.view(StudentBaseDisplayFormPage)
166    grok.require('waeup.manageStudent')
167    icon = 'actionicon_mail.png'
168    text = _('Send email')
169    target = 'contactstudent'
170
171class StudentBaseManageActionButton(ManageActionButton):
172    grok.order(1)
173    grok.context(IStudent)
174    grok.view(StudentBaseDisplayFormPage)
175    grok.require('waeup.manageStudent')
176    text = _('Manage')
177    target = 'manage_base'
178
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
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
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
206class StudentTransferButton(ManageActionButton):
207    grok.order(6)
208    grok.context(IStudent)
209    grok.view(StudentBaseDisplayFormPage)
210    grok.require('waeup.manageStudent')
211    text = _('Transfer student')
212    target = 'transfer'
213    icon = 'actionicon_redo.png'
214
215class StudentDeactivateActionButton(ManageActionButton):
216    grok.order(7)
217    grok.context(IStudent)
218    grok.view(StudentBaseDisplayFormPage)
219    grok.require('waeup.manageStudent')
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
230    @property
231    def onclick(self):
232        return "return window.confirm(%s);" % _(
233            "'A history message will be added. Are you sure?'")
234
235class StudentActivateActionButton(ManageActionButton):
236    grok.order(7)
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
250    @property
251    def onclick(self):
252        return "return window.confirm(%s);" % _(
253            "'A history message will be added. Are you sure?'")
254
255class StudentClearanceManageActionButton(ManageActionButton):
256    grok.order(1)
257    grok.context(IStudent)
258    grok.view(StudentClearanceDisplayFormPage)
259    grok.require('waeup.manageStudent')
260    text = _('Manage')
261    target = 'manage_clearance'
262
263class StudentClearActionButton(ManageActionButton):
264    grok.order(2)
265    grok.context(IStudent)
266    grok.view(StudentClearanceDisplayFormPage)
267    grok.require('waeup.clearStudent')
268    text = _('Clear student')
269    target = 'clear'
270    icon = 'actionicon_accept.png'
271
272    @property
273    def target_url(self):
274        if clearance_disabled_message(self.context):
275            return ''
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')
285    text = _('Reject clearance')
286    target = 'reject_clearance'
287    icon = 'actionicon_reject.png'
288
289    @property
290    def target_url(self):
291        if clearance_disabled_message(self.context):
292            return ''
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'
303    text = _('Download clearance slip')
304    target = 'clearance_slip.pdf'
305
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
324class StudentPersonalManageActionButton(ManageActionButton):
325    grok.order(1)
326    grok.context(IStudent)
327    grok.view(StudentPersonalDisplayFormPage)
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')
337    text = _('Edit')
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')
345    text = _('Manage')
346    target = 'manage'
347
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
354class StudyCourseTranscriptActionButton(ManageActionButton):
355    grok.order(2)
356    grok.context(IStudentStudyCourse)
357    grok.view(StudyCourseDisplayFormPage)
358    grok.require('waeup.viewTranscript')
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)
373    grok.require('waeup.viewTranscript')
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
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
399class StudyLevelManageActionButton(ManageActionButton):
400    grok.order(1)
401    grok.context(IStudentStudyLevel)
402    grok.view(StudyLevelDisplayFormPage)
403    grok.require('waeup.manageStudent')
404    text = _('Manage')
405    target = 'manage'
406
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
414class StudentValidateCoursesActionButton(ManageActionButton):
415    grok.order(3)
416    grok.context(IStudentStudyLevel)
417    grok.view(StudyLevelDisplayFormPage)
418    grok.require('waeup.validateStudent')
419    text = _('Validate courses')
420    target = 'validate_courses'
421    icon = 'actionicon_accept.png'
422
423    @property
424    def target_url(self):
425        is_current = self.context.__parent__.is_current
426        if self.context.student.state != REGISTERED or \
427            str(self.context.__parent__.current_level) != self.context.__name__ or\
428            not is_current:
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')
437    text = _('Reject courses')
438    target = 'reject_courses'
439    icon = 'actionicon_reject.png'
440
441    @property
442    def target_url(self):
443        is_current = self.context.__parent__.is_current
444        if self.context.student.state not in (VALIDATED, REGISTERED) or \
445            str(self.context.__parent__.current_level) != self.context.__name__ or\
446            not is_current:
447            return ''
448        return self.view.url(self.view.context, self.target)
449
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')
457    target = 'course_registration_slip.pdf'
458
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
466class CourseTicketManageActionButton(ManageActionButton):
467    grok.order(1)
468    grok.context(ICourseTicket)
469    grok.view(CourseTicketDisplayFormPage)
470    grok.require('waeup.manageStudent')
471    text = _('Manage')
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):
483    grok.order(9) # This button should always be the last one.
484    grok.context(IStudentOnlinePayment)
485    grok.view(OnlinePaymentDisplayFormPage)
486    grok.require('waeup.viewStudent')
487    icon = 'actionicon_pdf.png'
488    text = _('Download payment slip')
489    target = 'payment_slip.pdf'
490
491    @property
492    def target_url(self):
493        #if self.context.p_state != 'paid':
494        #    return ''
495        return self.view.url(self.view.context, self.target)
496
497class ApprovePaymentActionButton(ManageActionButton):
498    grok.order(8)
499    grok.context(IStudentOnlinePayment)
500    grok.view(OnlinePaymentDisplayFormPage)
501    grok.require('waeup.managePortal')
502    icon = 'actionicon_accept.png'
503    text = _('Approve payment')
504    target = 'approve'
505
506    @property
507    def target_url(self):
508        if self.context.p_state == 'paid':
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'
518    text = _('Book accommodation')
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'
527    text = _('Download bed allocation slip')
528    target = 'bed_allocation_slip.pdf'
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'
536    text = _('Relocate student')
537    target = 'relocate'
538
539class StudentBaseActionButton(ManageActionButton):
540    grok.order(1)
541    grok.context(IStudent)
542    grok.view(StudentBaseDisplayFormPage)
543    grok.require('waeup.handleStudent')
544    text = _('Edit')
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'
553    text = _('Change password')
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'
562    text = _('Change portrait')
563    target = 'change_portrait'
564
565    @property
566    def target_url(self):
567        PWCHANGE_STATES = getUtility(IStudentsUtils).PWCHANGE_STATES
568        if self.context.state not in PWCHANGE_STATES:
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'
578    text = _('Start clearance')
579    target = 'start_clearance'
580
581    @property
582    def target_url(self):
583        if self.context.state != ADMITTED:
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')
592    text = _('Edit')
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
601class StartSessionActionButton(ManageActionButton):
602    grok.order(1)
603    grok.context(IStudentStudyCourse)
604    grok.view(StudyCourseDisplayFormPage)
605    grok.require('waeup.handleStudent')
606    icon = 'actionicon_start.gif'
607    text = _('Start new session')
608    target = 'start_session'
609
610    @property
611    def target_url(self):
612        if self.context.next_session_allowed and self.context.is_current:
613            return self.view.url(self.view.context, self.target)
614        return False
615
616class AddStudyLevelActionButton(AddActionButton):
617    grok.order(1)
618    grok.context(IStudentStudyCourse)
619    grok.view(StudyCourseDisplayFormPage)
620    grok.require('waeup.handleStudent')
621    text = _('Add course list')
622    target = 'add'
623
624    @property
625    def target_url(self):
626        student = self.view.context.student
627        condition1 = student.state != PAID
628        condition2 = str(student['studycourse'].current_level) in \
629            self.view.context.keys()
630        condition3 = not self.context.is_current
631        if condition1 or condition2 or condition3:
632            return ''
633        return self.view.url(self.view.context, self.target)
634
635class StudyLevelEditActionButton(ManageActionButton):
636    grok.order(2)
637    grok.context(IStudentStudyLevel)
638    grok.view(StudyLevelDisplayFormPage)
639    grok.require('waeup.editStudyLevel')
640    text = _('Edit course list')
641    target = 'edit'
642
643    @property
644    def target_url(self):
645        student = self.view.context.student
646        condition1 = student.state == PAID
647        condition2 = self.view.context.is_current_level
648        is_current = self.context.__parent__.is_current
649        if condition1 and condition2 and is_current:
650            return self.view.url(self.view.context, self.target)
651        return ''
652
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')
666    grok.name('addpreviouspaymentactionbutton')
667    text = _('Add previous session payment ticket')
668    target = 'addpp'
669
670    @property
671    def target_url(self):
672        student = self.view.context.student
673        if student.before_payment or not self.target:
674            return ''
675        return self.view.url(self.view.context, self.target)
676
677class AddBalancePaymentActionButton(AddActionButton):
678    grok.order(3)
679    grok.context(IStudentPaymentsContainer)
680    grok.view(PaymentsManageFormPage)
681    grok.require('waeup.manageStudent')
682    grok.name('addbalancepaymentactionbutton')
683    text = _('Add balance payment ticket')
684    target = 'addbp'
685
686    @property
687    def target_url(self):
688        if not self.target:
689            return ''
690        return self.view.url(self.view.context, self.target)
691
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
707class ProcessTranscriptRequestActionButton(ManageActionButton):
708    grok.order(9)
709    grok.context(IStudent)
710    grok.view(StudentBaseDisplayFormPage)
711    grok.require('waeup.viewTranscript')
712    text = _('Manage transcript request')
713    target = 'process_transcript_request'
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
722class StudentsTab(PrimaryNavTab):
723    """Students tab in primary navigation.
724    """
725
726    grok.context(IKofaObject)
727    grok.order(4)
728    grok.require('waeup.viewStudentsTab')
729    grok.name('studentstab')
730
731    pnav = 4
732    tab_title = _(u'Students')
733
734    @property
735    def link_target(self):
736        return self.view.application_url('students')
737
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()
747    grok.context(IKofaObject)
748    grok.viewletmanager(PrimaryStudentNavManager)
749    template = default_primary_nav_template
750    grok.order(1)
751    grok.require('waeup.Authenticated')
752    pnav = 0
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
766class MyStudentDataTab(PrimaryStudentNavTab):
767    """MyData dropdown tab in primary navigation.
768    """
769    grok.order(3)
770    grok.require('waeup.viewMyStudentDataTab')
771    grok.template('mydatadropdowntabs')
772    grok.name('mystudentdatatab')
773    pnav = 4
774    tab_title = _(u'My Data')
775
776    @property
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'
782
783    @property
784    def targets(self):
785        student = grok.getSite()['students'][self.request.principal.id]
786        student_url = self.view.url(student)
787        app_slip = getUtility(IExtFileStore).getFileByContext(
788            student, 'application_slip')
789        targets = []
790        if app_slip:
791            targets = [{'url':student_url + '/application_slip',
792                        'title':_('Application Slip')},]
793        targets += [
794            {'url':student_url, 'title':'Base Data'},
795            {'url':student_url + '/view_clearance',
796             'title':_('Clearance Data')},
797            {'url':student_url + '/view_personal', 'title':_('Personal Data')},
798            {'url':student_url + '/studycourse', 'title':_('Study Course')},
799            {'url':student_url + '/payments', 'title':_('Payments')},
800            {'url':student_url + '/accommodation',
801             'title':_('Accommodation Data')},
802            {'url':student_url + '/history', 'title':_('History')},
803            ]
804        return targets
805
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)
812    context.writeLogMessage(view, 'deleted: %s' % download_name)
813    view.flash(_('${a} deleted.', mapping = {'a':download_name}))
814    return
815
816def handle_file_upload(upload, context, view, max_size, download_name=None):
817    """Handle upload of student file.
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    """
824    # Check some file requirements first
825    size = file_size(upload)
826    if size > max_size:
827        view.flash(_('Uploaded file is too big.'), type="danger")
828        return False
829    upload.seek(0) # file pointer moved when determining size
830    dummy,ext = os.path.splitext(upload.filename)
831    # fp files are expected to be fingerprint scans, file
832    # format is not yet checked
833    if ext == '.fp':
834        file_format = 'fp'
835    else:
836        file_format = get_fileformat(None, upload.read(512))
837        upload.seek(0) # same here
838    if file_format is None:
839        view.flash(_('Could not determine file type.'), type="danger")
840        return False
841    basename, expected_ext = os.path.splitext(download_name)
842    if expected_ext:
843        if '.' + file_format != expected_ext:
844            view.flash(_('${a} file extension expected.',
845                mapping = {'a':expected_ext[1:]}), type="danger")
846            return False
847    else:
848        if not file_format in ALLOWED_FILE_EXTENSIONS:
849            view.flash(
850                _('Only the following extensions are allowed: ${a}',
851                mapping = {'a':', '.join(ALLOWED_FILE_EXTENSIONS)}),
852                type="danger")
853            return False
854        download_name += '.' + file_format
855    store = getUtility(IExtFileStore)
856    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
857    store.createFile(file_id, upload)
858    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
859        download_name,upload.filename))
860    view.flash(_('File ${a} uploaded.', mapping = {'a':download_name}))
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()
872    grok.context(IStudent)
873    grok.viewletmanager(FileManager)
874    grok.view(StudentClearanceDisplayFormPage)
875    template = default_filedisplay_template
876    grok.order(1)
877    grok.require('waeup.viewStudent')
878    label = _(u'File')
879    title = _(u'Scan')
880    download_name = u'filename.jpg'
881
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
891class FileUpload(FileDisplay):
892    """Base upload viewlet.
893    """
894    grok.baseclass()
895    grok.context(IStudent)
896    grok.viewletmanager(FileManager)
897    grok.view(StudentClearanceManageFormPage)
898    template = default_fileupload_template
899    grok.require('waeup.uploadStudentFile')
900    tab_redirect = '#tab2-top'
901    mus = 1024 * 150
902    upload_button =_('Upload selected file')
903    delete_button = _('Delete')
904
905    @property
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
913    def input_name(self):
914        return "%s" % self.__name__
915
916    def update(self):
917        self.max_upload_size = string_from_bytes(self.mus)
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)
922        if delete_button:
923            handle_file_delete(
924                context=self.context, view=self.view,
925                download_name=self.download_name)
926            self.view.redirect(
927                self.view.url(
928                    self.context, self.view.__name__) + self.tab_redirect)
929            return
930        if upload_button:
931            upload = self.request.form.get(self.input_name, None)
932            if upload:
933                # We got a fresh upload
934                handle_file_upload(upload,
935                    self.context, self.view, self.mus, self.download_name)
936                self.view.redirect(
937                    self.view.url(
938                        self.context, self.view.__name__) + self.tab_redirect)
939            else:
940                self.view.flash(_('No local file selected.'), type="danger")
941                self.view.redirect(
942                    self.view.url(
943                        self.context, self.view.__name__) + self.tab_redirect)
944        return
945
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')
954    label = _(u'Passport Picture')
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)
963    grok.require('waeup.manageStudent')
964    grok.template('imageupload')
965    label = _(u'Passport Picture (jpg only)')
966    mus = 1024 * 50
967    download_name = u'passport.jpg'
968    tab_redirect = '#tab2'
969
970class PassportUploadEdit(PassportUploadManage):
971    """Passport upload viewlet for students.
972    """
973    grok.view(StudentFilesUploadPage)
974    grok.require('waeup.uploadStudentFile')
975
976class BirthCertificateDisplay(FileDisplay):
977    """Birth Certificate display viewlet.
978    """
979    grok.order(1)
980    label = _(u'Birth Certificate')
981    title = _(u'Birth Certificate Scan')
982    download_name = u'birth_certificate'
983
984class BirthCertificateSlip(BirthCertificateDisplay):
985    grok.view(ExportPDFClearanceSlipPage)
986
987class BirthCertificateUpload(FileUpload):
988    """Birth Certificate upload viewlet.
989    """
990    grok.order(1)
991    label = _(u'Birth Certificate')
992    title = _(u'Birth Certificate Scan')
993    mus = 1024 * 150
994    download_name = u'birth_certificate'
995    tab_redirect = '#tab2-top'
996
997class Image(grok.View):
998    """Renders images for students.
999    """
1000    grok.baseclass()
1001    grok.name('none.jpg')
1002    grok.context(IStudent)
1003    grok.require('waeup.viewStudent')
1004    download_name = u'none.jpg'
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(
1010            self.context, attr=self.download_name)
1011        if image is None:
1012            # show placeholder image
1013            self.response.setHeader('Content-Type', 'image/jpeg')
1014            return open(DEFAULT_IMAGE_PATH, 'rb').read()
1015        dummy,ext = os.path.splitext(image.name)
1016        if ext == '.jpg':
1017            self.response.setHeader('Content-Type', 'image/jpeg')
1018        elif ext == '.fp':
1019            self.response.setHeader('Content-Type', 'application/binary')
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')
1026        return image
1027
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
1035class ApplicationSlipImage(Image):
1036    """Renders application slip scan.
1037    """
1038    grok.name('application_slip')
1039    download_name = u'application_slip'
1040
1041class BirthCertificateImage(Image):
1042    """Renders birth certificate scan.
1043    """
1044    grok.name('birth_certificate')
1045    download_name = u'birth_certificate'
Note: See TracBrowser for help on using the repository browser.