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

Last change on this file since 12012 was 11976, checked in by Henrik Bettermann, 10 years ago

Fix comments.

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