source: main/waeup.kofa/branches/uli-stud-utils-cleanup/src/waeup/kofa/students/viewlets.py @ 13606

Last change on this file since 13606 was 11772, checked in by Henrik Bettermann, 10 years ago

Provide customizable method for disabling clearance by clearance officers.

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