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

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

Add browser components to facilitate transfer reversion.

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