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

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

Rename is_fresh method. Actually we need to filter students who have not yet paid school fee.

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