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

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

Add clearance_enabled field to ISessionConfiguration. Clearance can now be disabled for certain sessions via this switch.

  • Property svn:keywords set to Id
File size: 29.4 KB
Line 
13## $Id: viewlets.py 9814 2012-12-21 08:51:44Z 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,
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 StudyLevelManageActionButton(ManageActionButton):
347    grok.order(1)
348    grok.context(IStudentStudyLevel)
349    grok.view(StudyLevelDisplayFormPage)
350    grok.require('waeup.manageStudent')
351    text = _('Manage')
352    target = 'manage'
353
354    @property
355    def target_url(self):
356        is_current = self.context.__parent__.is_current
357        if not is_current:
358            return ''
359        return self.view.url(self.view.context, self.target)
360
361class StudentValidateCoursesActionButton(ManageActionButton):
362    grok.order(3)
363    grok.context(IStudentStudyLevel)
364    grok.view(StudyLevelDisplayFormPage)
365    grok.require('waeup.validateStudent')
366    text = _('Validate courses')
367    target = 'validate_courses'
368    icon = 'actionicon_accept.png'
369
370    @property
371    def target_url(self):
372        is_current = self.context.__parent__.is_current
373        if self.context.student.state != REGISTERED or \
374            str(self.context.__parent__.current_level) != self.context.__name__ or\
375            not is_current:
376            return ''
377        return self.view.url(self.view.context, self.target)
378
379class StudentRejectCoursesActionButton(ManageActionButton):
380    grok.order(4)
381    grok.context(IStudentStudyLevel)
382    grok.view(StudyLevelDisplayFormPage)
383    grok.require('waeup.validateStudent')
384    text = _('Reject courses')
385    target = 'reject_courses'
386    icon = 'actionicon_reject.png'
387
388    @property
389    def target_url(self):
390        is_current = self.context.__parent__.is_current
391        if self.context.student.state not in (VALIDATED, REGISTERED) or \
392            str(self.context.__parent__.current_level) != self.context.__name__ or\
393            not is_current:
394            return ''
395        return self.view.url(self.view.context, self.target)
396
397class CourseRegistrationSlipActionButton(ManageActionButton):
398    grok.order(5)
399    grok.context(IStudentStudyLevel)
400    grok.view(StudyLevelDisplayFormPage)
401    grok.require('waeup.viewStudent')
402    icon = 'actionicon_pdf.png'
403    text = _('Download course registration slip')
404    target = 'course_registration_slip.pdf'
405
406    @property
407    def target_url(self):
408        is_current = self.context.__parent__.is_current
409        if not is_current:
410            return ''
411        return self.view.url(self.view.context, self.target)
412
413class CourseTicketManageActionButton(ManageActionButton):
414    grok.order(1)
415    grok.context(ICourseTicket)
416    grok.view(CourseTicketDisplayFormPage)
417    grok.require('waeup.manageStudent')
418    text = _('Manage')
419    target = 'manage'
420
421#class OnlinePaymentManageActionButton(ManageActionButton):
422#    grok.order(1)
423#    grok.context(IStudentPaymentsContainer)
424#    grok.view(PaymentsDisplayFormPage)
425#    grok.require('waeup.manageStudent')
426#    text = 'Manage payments'
427#    target = 'manage'
428
429class PaymentReceiptActionButton(ManageActionButton):
430    grok.order(9) # This button should always be the last one.
431    grok.context(IStudentOnlinePayment)
432    grok.view(OnlinePaymentDisplayFormPage)
433    grok.require('waeup.viewStudent')
434    icon = 'actionicon_pdf.png'
435    text = _('Download payment slip')
436    target = 'payment_slip.pdf'
437
438    @property
439    def target_url(self):
440        #if self.context.p_state != 'paid':
441        #    return ''
442        return self.view.url(self.view.context, self.target)
443
444class ApprovePaymentActionButton(ManageActionButton):
445    grok.order(8)
446    grok.context(IStudentOnlinePayment)
447    grok.view(OnlinePaymentDisplayFormPage)
448    grok.require('waeup.managePortal')
449    icon = 'actionicon_accept.png'
450    text = _('Approve payment')
451    target = 'approve'
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 AddBedTicketActionButton(ManageActionButton):
460    grok.order(1)
461    grok.context(IStudentAccommodation)
462    grok.view(AccommodationManageFormPage)
463    grok.require('waeup.handleAccommodation')
464    icon = 'actionicon_home.png'
465    text = _('Book accommodation')
466    target = 'add'
467
468class BedTicketSlipActionButton(ManageActionButton):
469    grok.order(1)
470    grok.context(IBedTicket)
471    grok.view(BedTicketDisplayFormPage)
472    grok.require('waeup.handleAccommodation')
473    icon = 'actionicon_pdf.png'
474    text = _('Download bed allocation slip')
475    target = 'bed_allocation_slip.pdf'
476
477class RelocateStudentActionButton(ManageActionButton):
478    grok.order(2)
479    grok.context(IBedTicket)
480    grok.view(BedTicketDisplayFormPage)
481    grok.require('waeup.manageHostels')
482    icon = 'actionicon_reload.png'
483    text = _('Relocate student')
484    target = 'relocate'
485
486class StudentBaseActionButton(ManageActionButton):
487    grok.order(1)
488    grok.context(IStudent)
489    grok.view(StudentBaseDisplayFormPage)
490    grok.require('waeup.handleStudent')
491    text = _('Edit')
492    target = 'edit_base'
493
494class StudentPasswordActionButton(ManageActionButton):
495    grok.order(2)
496    grok.context(IStudent)
497    grok.view(StudentBaseDisplayFormPage)
498    grok.require('waeup.handleStudent')
499    icon = 'actionicon_key.png'
500    text = _('Change password')
501    target = 'change_password'
502
503class StudentPassportActionButton(ManageActionButton):
504    grok.order(3)
505    grok.context(IStudent)
506    grok.view(StudentBaseDisplayFormPage)
507    grok.require('waeup.handleStudent')
508    icon = 'actionicon_portrait.png'
509    text = _('Change portrait')
510    target = 'change_portrait'
511
512    @property
513    def target_url(self):
514        if self.context.state != ADMITTED:
515            return ''
516        return self.view.url(self.view.context, self.target)
517
518class StudentClearanceStartActionButton(ManageActionButton):
519    grok.order(1)
520    grok.context(IStudent)
521    grok.view(StudentClearanceDisplayFormPage)
522    grok.require('waeup.handleStudent')
523    icon = 'actionicon_start.gif'
524    text = _('Start clearance')
525    target = 'start_clearance'
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 StudentClearanceEditActionButton(ManageActionButton):
534    grok.order(1)
535    grok.context(IStudent)
536    grok.view(StudentClearanceDisplayFormPage)
537    grok.require('waeup.handleStudent')
538    text = _('Edit')
539    target = 'cedit'
540
541    @property
542    def target_url(self):
543        if self.context.clearance_locked:
544            return ''
545        return self.view.url(self.view.context, self.target)
546
547class StartSessionActionButton(ManageActionButton):
548    grok.order(1)
549    grok.context(IStudentStudyCourse)
550    grok.view(StudyCourseDisplayFormPage)
551    grok.require('waeup.handleStudent')
552    icon = 'actionicon_start.gif'
553    text = _('Start new session')
554    target = 'start_session'
555
556    @property
557    def target_url(self):
558        if self.context.next_session_allowed and self.context.is_current:
559            return self.view.url(self.view.context, self.target)
560        return False
561
562class AddStudyLevelActionButton(AddActionButton):
563    grok.order(1)
564    grok.context(IStudentStudyCourse)
565    grok.view(StudyCourseDisplayFormPage)
566    grok.require('waeup.handleStudent')
567    text = _('Add course list')
568    target = 'add'
569
570    @property
571    def target_url(self):
572        student = self.view.context.student
573        condition1 = student.state != PAID
574        condition2 = str(student['studycourse'].current_level) in \
575            self.view.context.keys()
576        condition3 = not self.context.is_current
577        if condition1 or condition2 or condition3:
578            return ''
579        return self.view.url(self.view.context, self.target)
580
581class StudyLevelEditActionButton(ManageActionButton):
582    grok.order(2)
583    grok.context(IStudentStudyLevel)
584    grok.view(StudyLevelDisplayFormPage)
585    grok.require('waeup.handleStudent')
586    text = _('Edit course list')
587    target = 'edit'
588
589    @property
590    def target_url(self):
591        student = self.view.context.student
592        condition1 = student.state == PAID
593        condition2 = self.view.context.is_current_level
594        is_current = self.context.__parent__.is_current
595        if condition1 and condition2 and is_current:
596            return self.view.url(self.view.context, self.target)
597        return ''
598
599class AddPaymentActionButton(AddActionButton):
600    grok.order(1)
601    grok.context(IStudentPaymentsContainer)
602    grok.view(PaymentsManageFormPage)
603    grok.require('waeup.payStudent')
604    text = _('Add current session payment ticket')
605    target = 'addop'
606
607class AddPreviousPaymentActionButton(AddActionButton):
608    grok.order(2)
609    grok.context(IStudentPaymentsContainer)
610    grok.view(PaymentsManageFormPage)
611    grok.require('waeup.payStudent')
612    text = _('Add previous session payment ticket')
613    target = 'addpp'
614
615    @property
616    def target_url(self):
617        student = self.view.context.student
618        if student.before_payment:
619            return ''
620        return self.view.url(self.view.context, self.target)
621
622class StudentsTab(PrimaryNavTab):
623    """Students tab in primary navigation.
624    """
625
626    grok.context(IKofaObject)
627    grok.order(4)
628    grok.require('waeup.viewStudentsTab')
629
630    pnav = 4
631    tab_title = _(u'Students')
632
633    @property
634    def link_target(self):
635        return self.view.application_url('students')
636
637class PrimaryStudentNavManager(grok.ViewletManager):
638    """Viewlet manager for the primary navigation tab.
639    """
640    grok.name('primary_nav_student')
641
642class PrimaryStudentNavTab(grok.Viewlet):
643    """Base for primary student nav tabs.
644    """
645    grok.baseclass()
646    grok.viewletmanager(PrimaryStudentNavManager)
647    template = default_primary_nav_template
648    grok.order(1)
649    grok.require('waeup.Authenticated')
650    pnav = 0
651    tab_title = u'Some Text'
652
653    @property
654    def link_target(self):
655        return self.view.application_url()
656
657    @property
658    def active(self):
659        view_pnav = getattr(self.view, 'pnav', 0)
660        if view_pnav == self.pnav:
661            return 'active'
662        return ''
663
664class MyStudentDataTab(PrimaryStudentNavTab):
665    """MyData dropdown tab in primary navigation.
666    """
667    grok.order(3)
668    grok.require('waeup.viewMyStudentDataTab')
669    grok.template('mydatadropdowntabs')
670    pnav = 4
671    tab_title = _(u'My Data')
672
673    @property
674    def active(self):
675        view_pnav = getattr(self.view, 'pnav', 0)
676        if view_pnav == self.pnav:
677            return 'active dropdown'
678        return 'dropdown'
679
680    @property
681    def targets(self):
682        student = grok.getSite()['students'][self.request.principal.id]
683        student_url = self.view.url(student)
684        app_slip = getUtility(IExtFileStore).getFileByContext(
685            student, 'application_slip')
686        targets = []
687        if app_slip:
688            targets = [{'url':student_url + '/application_slip', 'title':'Application Slip'},]
689        targets += [
690            {'url':student_url, 'title':'Base Data'},
691            {'url':student_url + '/view_clearance',
692             'title':_('Clearance Data')},
693            {'url':student_url + '/view_personal', 'title':_('Personal Data')},
694            {'url':student_url + '/studycourse', 'title':_('Study Course')},
695            {'url':student_url + '/payments', 'title':_('Payments')},
696            {'url':student_url + '/accommodation',
697             'title':_('Accommodation Data')},
698            {'url':student_url + '/history', 'title':_('History')},
699            ]
700        return targets
701
702def handle_file_delete(context, view, download_name):
703    """Handle deletion of student file.
704
705    """
706    store = getUtility(IExtFileStore)
707    store.deleteFileByContext(context, attr=download_name)
708    context.writeLogMessage(view, 'deleted: %s' % download_name)
709    view.flash(_('${a} deleted.', mapping = {'a':download_name}))
710    return
711
712def handle_file_upload(upload, context, view, max_size, download_name=None):
713    """Handle upload of student file.
714
715    Returns `True` in case of success or `False`.
716
717    Please note that file pointer passed in (`upload`) most probably
718    points to end of file when leaving this function.
719    """
720    # Check some file requirements first
721    size = file_size(upload)
722    if size > max_size:
723        view.flash(_('Uploaded file is too big.'))
724        return False
725    upload.seek(0) # file pointer moved when determining size
726    file_format = get_fileformat(None, upload.read(512))
727    upload.seek(0) # same here
728    if file_format is None:
729        view.flash(_('Could not determine file type.'))
730        return False
731    basename, expected_ext = os.path.splitext(download_name)
732    if expected_ext:
733        if '.' + file_format != expected_ext:
734            view.flash(_('${a} file extension expected.',
735                mapping = {'a':expected_ext[1:]}))
736            return False
737    else:
738        if not file_format in ALLOWED_FILE_EXTENSIONS:
739            view.flash(
740                _('Only the following extensions are allowed: ${a}',
741                mapping = {'a':', '.join(ALLOWED_FILE_EXTENSIONS)}))
742            return False
743        download_name += '.' + file_format
744    store = getUtility(IExtFileStore)
745    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
746    store.createFile(file_id, upload)
747    context.writeLogMessage(view, 'uploaded: %s (%s)' % (
748        download_name,upload.filename))
749    view.flash(_('File ${a} uploaded.', mapping = {'a':download_name}))
750    return True
751
752class FileManager(grok.ViewletManager):
753    """Viewlet manager for uploading files, preferably scanned images.
754    """
755    grok.name('files')
756
757class FileDisplay(grok.Viewlet):
758    """Base file display viewlet.
759    """
760    grok.baseclass()
761    grok.context(IStudent)
762    grok.viewletmanager(FileManager)
763    grok.view(StudentClearanceDisplayFormPage)
764    template = default_filedisplay_template
765    grok.order(1)
766    grok.require('waeup.viewStudent')
767    label = _(u'File')
768    title = _(u'Scan')
769    download_name = u'filename.jpg'
770
771    @property
772    def file_exists(self):
773        image = getUtility(IExtFileStore).getFileByContext(
774            self.context, attr=self.download_name)
775        if image:
776            return True
777        else:
778            return False
779
780class FileUpload(FileDisplay):
781    """Base upload viewlet.
782    """
783    grok.baseclass()
784    grok.context(IStudent)
785    grok.viewletmanager(FileManager)
786    grok.view(StudentClearanceManageFormPage)
787    template = default_fileupload_template
788    grok.require('waeup.uploadStudentFile')
789    tab_redirect = '?tab2'
790    mus = 1024 * 150
791    upload_button =_('Upload new file')
792    delete_button = _('Delete attachment')
793    show_viewlet = True
794
795    @property
796    def input_name(self):
797        return "%s" % self.__name__
798
799    def update(self):
800        self.max_upload_size = string_from_bytes(self.mus)
801        delete_button = self.request.form.get(
802            'delete_%s' % self.input_name, None)
803        upload_button = self.request.form.get(
804            'upload_%s' % self.input_name, None)
805        if delete_button:
806            handle_file_delete(
807                context=self.context, view=self.view,
808                download_name=self.download_name)
809            self.view.redirect(
810                self.view.url(
811                    self.context, self.view.__name__) + self.tab_redirect)
812            return
813        if upload_button:
814            upload = self.request.form.get(self.input_name, None)
815            if upload:
816                # We got a fresh upload
817                handle_file_upload(upload,
818                    self.context, self.view, self.mus, self.download_name)
819                self.view.redirect(
820                    self.view.url(
821                        self.context, self.view.__name__) + self.tab_redirect)
822            else:
823                self.view.flash(_('No local file selected.'))
824                self.view.redirect(
825                    self.view.url(
826                        self.context, self.view.__name__) + self.tab_redirect)
827        return
828
829class PassportDisplay(FileDisplay):
830    """Passport display viewlet.
831    """
832    grok.order(1)
833    grok.context(IStudent)
834    grok.view(StudentBaseDisplayFormPage)
835    grok.require('waeup.viewStudent')
836    grok.template('imagedisplay')
837    label = _(u'Passport Picture')
838    download_name = u'passport.jpg'
839
840class PassportUploadManage(FileUpload):
841    """Passport upload viewlet for officers.
842    """
843    grok.order(1)
844    grok.context(IStudent)
845    grok.view(StudentBaseManageFormPage)
846    grok.require('waeup.manageStudent')
847    grok.template('imageupload')
848    label = _(u'Passport Picture (jpg only)')
849    mus = 1024 * 50
850    download_name = u'passport.jpg'
851    tab_redirect = '?tab2'
852
853class PassportUploadEdit(PassportUploadManage):
854    """Passport upload viewlet for students.
855    """
856    grok.view(StudentFilesUploadPage)
857    grok.require('waeup.uploadStudentFile')
858
859class BirthCertificateDisplay(FileDisplay):
860    """Birth Certificate display viewlet.
861    """
862    grok.order(1)
863    label = _(u'Birth Certificate')
864    title = _(u'Birth Certificate Scan')
865    download_name = u'birth_certificate'
866
867class BirthCertificateSlip(BirthCertificateDisplay):
868    grok.view(ExportPDFClearanceSlipPage)
869
870class BirthCertificateUpload(FileUpload):
871    """Birth Certificate upload viewlet.
872    """
873    grok.order(1)
874    label = _(u'Birth Certificate')
875    title = _(u'Birth Certificate Scan')
876    mus = 1024 * 150
877    download_name = u'birth_certificate'
878    tab_redirect = '?tab2'
879
880class Image(grok.View):
881    """Renders images for students.
882    """
883    grok.baseclass()
884    grok.name('none.jpg')
885    grok.context(IStudent)
886    grok.require('waeup.viewStudent')
887    download_name = u'none.jpg'
888
889    def render(self):
890        # A filename chooser turns a context into a filename suitable
891        # for file storage.
892        image = getUtility(IExtFileStore).getFileByContext(
893            self.context, attr=self.download_name)
894        if image is None:
895            # show placeholder image
896            self.response.setHeader('Content-Type', 'image/jpeg')
897            return open(DEFAULT_IMAGE_PATH, 'rb').read()
898        dummy,ext = os.path.splitext(image.name)
899        if ext == '.jpg':
900            self.response.setHeader('Content-Type', 'image/jpeg')
901        elif ext == '.png':
902            self.response.setHeader('Content-Type', 'image/png')
903        elif ext == '.pdf':
904            self.response.setHeader('Content-Type', 'application/pdf')
905        elif ext == '.tif':
906            self.response.setHeader('Content-Type', 'image/tiff')
907        return image
908
909class Passport(Image):
910    """Renders jpeg passport picture.
911    """
912    grok.name('passport.jpg')
913    download_name = u'passport.jpg'
914    grok.context(IStudent)
915
916class ApplicationSlipImage(Image):
917    """Renders application slip scan.
918    """
919    grok.name('application_slip')
920    download_name = u'application_slip'
921
922class BirthCertificateImage(Image):
923    """Renders birth certificate scan.
924    """
925    grok.name('birth_certificate')
926    download_name = u'birth_certificate'
Note: See TracBrowser for help on using the repository browser.