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

Last change on this file since 11450 was 11254, checked in by uli, 11 years ago

Merge changes from uli-diazo-themed back into trunk. If this works, then a miracle happened.

  • Property svn:keywords set to Id
File size: 33.1 KB
Line 
13## $Id: viewlets.py 11254 2014-02-22 15:46:03Z uli $
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')
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    file_format = get_fileformat(None, upload.read(512))
831    upload.seek(0) # same here
832    if file_format is None:
833        view.flash(_('Could not determine file type.'), type="danger")
834        return False
835    basename, expected_ext = os.path.splitext(download_name)
836    if expected_ext:
837        if '.' + file_format != expected_ext:
838            view.flash(_('${a} file extension expected.',
839                mapping = {'a':expected_ext[1:]}), type="danger")
840            return False
841    else:
842        if not file_format in ALLOWED_FILE_EXTENSIONS:
843            view.flash(
844                _('Only the following extensions are allowed: ${a}',
845                mapping = {'a':', '.join(ALLOWED_FILE_EXTENSIONS)}),
846                type="danger")
847            return False
848        download_name += '.' + file_format
849    store = getUtility(IExtFileStore)
850    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
851    store.createFile(file_id, upload)
852    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
853        download_name,upload.filename))
854    view.flash(_('File ${a} uploaded.', mapping = {'a':download_name}))
855    return True
856
857class FileManager(grok.ViewletManager):
858    """Viewlet manager for uploading files, preferably scanned images.
859    """
860    grok.name('files')
861
862class FileDisplay(grok.Viewlet):
863    """Base file display viewlet.
864    """
865    grok.baseclass()
866    grok.context(IStudent)
867    grok.viewletmanager(FileManager)
868    grok.view(StudentClearanceDisplayFormPage)
869    template = default_filedisplay_template
870    grok.order(1)
871    grok.require('waeup.viewStudent')
872    label = _(u'File')
873    title = _(u'Scan')
874    download_name = u'filename.jpg'
875
876    @property
877    def file_exists(self):
878        image = getUtility(IExtFileStore).getFileByContext(
879            self.context, attr=self.download_name)
880        if image:
881            return True
882        else:
883            return False
884
885class FileUpload(FileDisplay):
886    """Base upload viewlet.
887    """
888    grok.baseclass()
889    grok.context(IStudent)
890    grok.viewletmanager(FileManager)
891    grok.view(StudentClearanceManageFormPage)
892    template = default_fileupload_template
893    grok.require('waeup.uploadStudentFile')
894    tab_redirect = '#tab2-top'
895    mus = 1024 * 150
896    upload_button =_('Upload new file')
897    delete_button = _('Delete attachment')
898
899    @property
900    def show_viewlet(self):
901        students_utils = getUtility(IStudentsUtils)
902        if self.__name__ in students_utils.SKIP_UPLOAD_VIEWLETS:
903            return False
904        return True
905
906    @property
907    def input_name(self):
908        return "%s" % self.__name__
909
910    def update(self):
911        self.max_upload_size = string_from_bytes(self.mus)
912        delete_button = self.request.form.get(
913            'delete_%s' % self.input_name, None)
914        upload_button = self.request.form.get(
915            'upload_%s' % self.input_name, None)
916        if delete_button:
917            handle_file_delete(
918                context=self.context, view=self.view,
919                download_name=self.download_name)
920            self.view.redirect(
921                self.view.url(
922                    self.context, self.view.__name__) + self.tab_redirect)
923            return
924        if upload_button:
925            upload = self.request.form.get(self.input_name, None)
926            if upload:
927                # We got a fresh upload
928                handle_file_upload(upload,
929                    self.context, self.view, self.mus, self.download_name)
930                self.view.redirect(
931                    self.view.url(
932                        self.context, self.view.__name__) + self.tab_redirect)
933            else:
934                self.view.flash(_('No local file selected.'), type="danger")
935                self.view.redirect(
936                    self.view.url(
937                        self.context, self.view.__name__) + self.tab_redirect)
938        return
939
940class PassportDisplay(FileDisplay):
941    """Passport display viewlet.
942    """
943    grok.order(1)
944    grok.context(IStudent)
945    grok.view(StudentBaseDisplayFormPage)
946    grok.require('waeup.viewStudent')
947    grok.template('imagedisplay')
948    label = _(u'Passport Picture')
949    download_name = u'passport.jpg'
950
951class PassportUploadManage(FileUpload):
952    """Passport upload viewlet for officers.
953    """
954    grok.order(1)
955    grok.context(IStudent)
956    grok.view(StudentBaseManageFormPage)
957    grok.require('waeup.manageStudent')
958    grok.template('imageupload')
959    label = _(u'Passport Picture (jpg only)')
960    mus = 1024 * 50
961    download_name = u'passport.jpg'
962    tab_redirect = '#tab2'
963
964class PassportUploadEdit(PassportUploadManage):
965    """Passport upload viewlet for students.
966    """
967    grok.view(StudentFilesUploadPage)
968    grok.require('waeup.uploadStudentFile')
969
970class BirthCertificateDisplay(FileDisplay):
971    """Birth Certificate display viewlet.
972    """
973    grok.order(1)
974    label = _(u'Birth Certificate')
975    title = _(u'Birth Certificate Scan')
976    download_name = u'birth_certificate'
977
978class BirthCertificateSlip(BirthCertificateDisplay):
979    grok.view(ExportPDFClearanceSlipPage)
980
981class BirthCertificateUpload(FileUpload):
982    """Birth Certificate upload viewlet.
983    """
984    grok.order(1)
985    label = _(u'Birth Certificate')
986    title = _(u'Birth Certificate Scan')
987    mus = 1024 * 150
988    download_name = u'birth_certificate'
989    tab_redirect = '#tab2-top'
990
991class Image(grok.View):
992    """Renders images for students.
993    """
994    grok.baseclass()
995    grok.name('none.jpg')
996    grok.context(IStudent)
997    grok.require('waeup.viewStudent')
998    download_name = u'none.jpg'
999
1000    def render(self):
1001        # A filename chooser turns a context into a filename suitable
1002        # for file storage.
1003        image = getUtility(IExtFileStore).getFileByContext(
1004            self.context, attr=self.download_name)
1005        if image is None:
1006            # show placeholder image
1007            self.response.setHeader('Content-Type', 'image/jpeg')
1008            return open(DEFAULT_IMAGE_PATH, 'rb').read()
1009        dummy,ext = os.path.splitext(image.name)
1010        if ext == '.jpg':
1011            self.response.setHeader('Content-Type', 'image/jpeg')
1012        elif ext == '.png':
1013            self.response.setHeader('Content-Type', 'image/png')
1014        elif ext == '.pdf':
1015            self.response.setHeader('Content-Type', 'application/pdf')
1016        elif ext == '.tif':
1017            self.response.setHeader('Content-Type', 'image/tiff')
1018        return image
1019
1020class Passport(Image):
1021    """Renders jpeg passport picture.
1022    """
1023    grok.name('passport.jpg')
1024    download_name = u'passport.jpg'
1025    grok.context(IStudent)
1026
1027class ApplicationSlipImage(Image):
1028    """Renders application slip scan.
1029    """
1030    grok.name('application_slip')
1031    download_name = u'application_slip'
1032
1033class BirthCertificateImage(Image):
1034    """Renders birth certificate scan.
1035    """
1036    grok.name('birth_certificate')
1037    download_name = u'birth_certificate'
Note: See TracBrowser for help on using the repository browser.