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

Last change on this file since 10707 was 10706, checked in by Henrik Bettermann, 11 years ago

We need to customize the states in which students can change their passport picture (portrait).

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