source: main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/viewlets.py @ 10009

Last change on this file since 10009 was 9208, checked in by uli, 12 years ago

Merge changes from trunk r9171:9207.

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