source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py @ 6396

Last change on this file since 6396 was 6396, checked in by Henrik Bettermann, 14 years ago

Provide left sidebar links for applicants instead of action buttons. Only officers see the action buttons.

The LeftSidebarLink? baseclass does now also provide an icon_url which will be needed in the students section.

File size: 27.8 KB
RevLine 
[5273]1##
2## browser.py
3## Login : <uli@pu.smp.net>
[6153]4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet & Henrik Bettermann
[5273]5## $Id$
[6078]6##
[6063]7## Copyright (C) 2010 Uli Fouquet & Henrik Bettermann
[5273]8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
[6078]12##
[5273]13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
[6078]17##
[5273]18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
[5824]22"""UI components for basic applicants and related components.
[5273]23"""
[6082]24import sys
[5273]25import grok
26
[6305]27from datetime import datetime
[6081]28from zope.formlib.widget import CustomWidgetFactory
[6358]29from zope.formlib.form import setUpEditWidgets
[5937]30from zope.securitypolicy.interfaces import IPrincipalRoleManager
[6153]31from zope.traversing.browser import absoluteURL
[6081]32
[6303]33from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
[6357]34from reportlab.pdfgen import canvas
[6364]35from reportlab.lib.units import cm
[6390]36from reportlab.lib.pagesizes import A4
[6364]37from reportlab.lib.styles import getSampleStyleSheet
38from reportlab.platypus import (Frame, Paragraph, Image,
39    Table, Spacer)
40from reportlab.platypus.tables import TableStyle
[6357]41
[6359]42from waeup.sirp.accesscodes import invalidate_accesscode
[5273]43from waeup.sirp.browser import (
[6321]44    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
[6081]45from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6103]46from waeup.sirp.browser.layout import NullValidator
[6321]47from waeup.sirp.browser.pages import add_local_role, del_local_roles
[6013]48from waeup.sirp.browser.resources import datepicker, tabs, datatable
[6153]49from waeup.sirp.browser.viewlets import (
50    ManageActionButton, PrimaryNavTab, LeftSidebarLink
51    )
[6081]52from waeup.sirp.image.browser.widget import (
53    ThumbnailWidget, EncodingImageFileWidget,
[5822]54    )
[6184]55from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
[6321]56from waeup.sirp.permissions import get_users_with_local_roles
[6254]57from waeup.sirp.university.interfaces import ICertificate
[6054]58from waeup.sirp.widgets.datewidget import (
59    FriendlyDateWidget, FriendlyDateDisplayWidget)
[6084]60from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
[5303]61from waeup.sirp.widgets.objectwidget import (
[5301]62    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
[5303]63from waeup.sirp.widgets.multilistwidget import (
[5273]64    MultiListWidget, MultiListDisplayWidget)
[6153]65from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
[6081]66from waeup.sirp.applicants.interfaces import (
[6321]67    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
68    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
[5686]69    )
[6353]70from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
[5320]71
[5273]72results_widget = CustomWidgetFactory(
[5301]73    WAeUPObjectWidget, ResultEntry)
[5273]74
75results_display_widget = CustomWidgetFactory(
[5301]76    WAeUPObjectDisplayWidget, ResultEntry)
[5273]77
78list_results_widget = CustomWidgetFactory(
79    MultiListWidget, subwidget=results_widget)
80
81list_results_display_widget = CustomWidgetFactory(
82    MultiListDisplayWidget, subwidget=results_display_widget)
83
[6353]84#TRANSITION_OBJECTS = create_workflow()
[6305]85
[6322]86#TRANSITION_DICT = dict([
87#    (transition_object.transition_id,transition_object.title)
88#    for transition_object in TRANSITION_OBJECTS])
[6305]89
[6067]90class ApplicantsRootPage(WAeUPPage):
[5822]91    grok.context(IApplicantsRoot)
92    grok.name('index')
[6153]93    grok.require('waeup.Public')
[5822]94    title = 'Applicants'
[6069]95    label = 'Application Section'
[5843]96    pnav = 3
[6012]97
98    def update(self):
[6067]99        super(ApplicantsRootPage, self).update()
[6012]100        datatable.need()
101        return
102
[5828]103class ManageApplicantsRootActionButton(ManageActionButton):
104    grok.context(IApplicantsRoot)
[6067]105    grok.view(ApplicantsRootPage)
[6198]106    grok.require('waeup.manageApplications')
[6069]107    text = 'Manage application section'
[5828]108
[6069]109class ApplicantsRootManageFormPage(WAeUPEditFormPage):
[5828]110    grok.context(IApplicantsRoot)
111    grok.name('manage')
[6107]112    grok.template('applicantsrootmanagepage')
[6069]113    title = 'Applicants'
114    label = 'Manage application section'
[5843]115    pnav = 3
[6198]116    grok.require('waeup.manageApplications')
[6069]117    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
[6184]118    tabtwoactions1 = ['Remove selected local roles']
119    tabtwoactions2 = ['Add local role']
[6069]120    subunits = 'Applicants Containers'
[6078]121
[6069]122    def update(self):
123        tabs.need()
[6108]124        datatable.need()
[6069]125        return super(ApplicantsRootManageFormPage, self).update()
[5828]126
[6184]127    def getLocalRoles(self):
128        roles = ILocalRolesAssignable(self.context)
129        return roles()
130
131    def getUsers(self):
132        """Get a list of all users.
133        """
134        for key, val in grok.getSite()['users'].items():
135            url = self.url(val)
136            yield(dict(url=url, name=key, val=val))
137
138    def getUsersWithLocalRoles(self):
139        return get_users_with_local_roles(self.context)
140
[6069]141    # ToDo: Show warning message before deletion
142    @grok.action('Remove selected')
143    def delApplicantsContainers(self, **data):
144        form = self.request.form
145        child_id = form['val_id']
146        if not isinstance(child_id, list):
147            child_id = [child_id]
148        deleted = []
149        for id in child_id:
150            try:
151                del self.context[id]
152                deleted.append(id)
153            except:
154                self.flash('Could not delete %s: %s: %s' % (
155                        id, sys.exc_info()[0], sys.exc_info()[1]))
156        if len(deleted):
157            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6078]158        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
159        return
[5828]160
[6069]161    @grok.action('Add applicants container', validator=NullValidator)
162    def addApplicantsContainer(self, **data):
163        self.redirect(self.url(self.context, '@@add'))
[6078]164        return
165
[6069]166    @grok.action('Cancel', validator=NullValidator)
167    def cancel(self, **data):
168        self.redirect(self.url(self.context))
[6078]169        return
170
[6184]171    @grok.action('Add local role', validator=NullValidator)
172    def addLocalRole(self, **data):
173        return add_local_role(self,2, **data)
174
175    @grok.action('Remove selected local roles')
176    def delLocalRoles(self, **data):
177        return del_local_roles(self,2,**data)
178
[6069]179class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
[5822]180    grok.context(IApplicantsRoot)
[6198]181    grok.require('waeup.manageApplications')
[5822]182    grok.name('add')
[6107]183    grok.template('applicantscontaineraddpage')
[6069]184    title = 'Applicants'
185    label = 'Add applicants container'
[5843]186    pnav = 3
[6078]187
[6103]188    form_fields = grok.AutoFields(
189        IApplicantsContainerAdd).omit('code').omit('title')
[6083]190    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
191    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6078]192
[6083]193    def update(self):
194        datepicker.need() # Enable jQuery datepicker in date fields.
195        return super(ApplicantsContainerAddFormPage, self).update()
196
[6069]197    @grok.action('Add applicants container')
198    def addApplicantsContainer(self, **data):
[6103]199        year = data['year']
200        code = u'%s%s' % (data['prefix'], year)
201        prefix = application_types_vocab.getTerm(data['prefix'])
202        title = u'%s %s/%s' % (prefix.title, year, year + 1)
[6087]203        if code in self.context.keys():
[6105]204            self.flash(
205                'An applicants container for the same application '
206                'type and entrance year exists already in the database.')
[5822]207            return
208        # Add new applicants container...
[6083]209        provider = data['provider'][1]
[5822]210        container = provider.factory()
[6069]211        self.applyData(container, **data)
[6087]212        container.code = code
213        container.title = title
214        self.context[code] = container
[6105]215        self.flash('Added: "%s".' % code)
[6069]216        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
[5822]217        return
[6078]218
[6103]219    @grok.action('Cancel', validator=NullValidator)
[6069]220    def cancel(self, **data):
[6103]221        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
[6078]222
[5845]223class ApplicantsRootBreadcrumb(Breadcrumb):
224    """A breadcrumb for applicantsroot.
225    """
226    grok.context(IApplicantsRoot)
[6153]227    title = u'Application Section'
[6078]228
[5845]229class ApplicantsContainerBreadcrumb(Breadcrumb):
230    """A breadcrumb for applicantscontainers.
231    """
232    grok.context(IApplicantsContainer)
[6319]233
[6153]234class ApplicantBreadcrumb(Breadcrumb):
235    """A breadcrumb for applicants.
236    """
237    grok.context(IApplicant)
[6319]238
[6153]239    @property
240    def title(self):
241        """Get a title for a context.
242        """
243        return self.context.access_code
[5828]244
245class ApplicantsTab(PrimaryNavTab):
[6153]246    """Applicants tab in primary navigation.
[5828]247    """
[6078]248
[5828]249    grok.context(IWAeUPObject)
250    grok.order(3)
[6336]251    grok.require('waeup.Public')
[5828]252    grok.template('primarynavtab')
253
[5843]254    pnav = 3
[5828]255    tab_title = u'Applicants'
256
257    @property
258    def link_target(self):
259        return self.view.application_url('applicants')
260
[6029]261class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]262    """The standard view for regular applicant containers.
263    """
264    grok.context(IApplicantsContainer)
265    grok.name('index')
[6153]266    grok.require('waeup.Public')
[6029]267    grok.template('applicantscontainerpage')
[5850]268    pnav = 3
[6053]269
[6105]270    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
[6054]271    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
272    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
[6084]273    form_fields['description'].custom_widget = ReSTDisplayWidget
[6053]274
[5837]275    @property
276    def title(self):
[6087]277        return "Applicants Container: %s" % self.context.title
[5837]278
279    @property
280    def label(self):
[6087]281        return self.context.title
[5830]282
[6107]283class ApplicantsContainerManageActionButton(ManageActionButton):
[6336]284    grok.order(1)
[5832]285    grok.context(IApplicantsContainer)
286    grok.view(ApplicantsContainerPage)
[6198]287    grok.require('waeup.manageApplications')
[6070]288    text = 'Manage applicants container'
[5832]289
[6336]290class LoginApplicantActionButton(ManageActionButton):
291    grok.order(2)
292    grok.context(IApplicantsContainer)
293    grok.view(ApplicantsContainerPage)
294    grok.require('waeup.Anonymous')
295    icon = 'login.png'
296    text = 'Login for applicants'
297    target = 'login'
[5832]298
[6107]299class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
[5837]300    grok.context(IApplicantsContainer)
[5850]301    grok.name('manage')
[6107]302    grok.template('applicantscontainermanagepage')
[6105]303    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
304    taboneactions = ['Save','Cancel']
305    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
[6184]306    tabthreeactions1 = ['Remove selected local roles']
307    tabthreeactions2 = ['Add local role']
[5844]308    # Use friendlier date widget...
[6054]309    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
310    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6198]311    grok.require('waeup.manageApplications')
[5850]312
313    @property
314    def title(self):
[6087]315        return "Applicants Container: %s" % self.context.title
[6078]316
[5850]317    @property
318    def label(self):
[6087]319        return 'Manage applicants container'
[5850]320
[5845]321    pnav = 3
[5837]322
323    def update(self):
[5850]324        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]325        tabs.need()
[6015]326        datatable.need()  # Enable jQurey datatables for contents listing
[6107]327        return super(ApplicantsContainerManageFormPage, self).update()
[5837]328
[6184]329    def getLocalRoles(self):
330        roles = ILocalRolesAssignable(self.context)
331        return roles()
332
333    def getUsers(self):
334        """Get a list of all users.
335        """
336        for key, val in grok.getSite()['users'].items():
337            url = self.url(val)
338            yield(dict(url=url, name=key, val=val))
339
340    def getUsersWithLocalRoles(self):
341        return get_users_with_local_roles(self.context)
342
[5850]343    @grok.action('Save')
[5837]344    def apply(self, **data):
345        self.applyData(self.context, **data)
346        self.flash('Data saved.')
347        return
[6078]348
[6105]349    # ToDo: Show warning message before deletion
350    @grok.action('Remove selected')
351    def delApplicant(self, **data):
[6189]352        form = self.request.form
353        if form.has_key('val_id'):
354            child_id = form['val_id']
355        else:
356            self.flash('No applicant selected!')
357            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
358            return
359        if not isinstance(child_id, list):
360            child_id = [child_id]
361        deleted = []
362        for id in child_id:
363            try:
364                del self.context[id]
365                deleted.append(id)
366            except:
367                self.flash('Could not delete %s: %s: %s' % (
368                        id, sys.exc_info()[0], sys.exc_info()[1]))
369        if len(deleted):
370            self.flash('Successfully removed: %s' % ', '.join(deleted))
371        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
372        return
[6105]373
374    @grok.action('Add applicant', validator=NullValidator)
375    def addApplicant(self, **data):
[6327]376        self.redirect(self.url(self.context, 'addapplicant'))
377        return
[6105]378
379    @grok.action('Cancel', validator=NullValidator)
[5837]380    def cancel(self, **data):
381        self.redirect(self.url(self.context))
382        return
[5886]383
[6184]384    @grok.action('Add local role', validator=NullValidator)
385    def addLocalRole(self, **data):
386        return add_local_role(self,3, **data)
[6105]387
[6184]388    @grok.action('Remove selected local roles')
389    def delLocalRoles(self, **data):
390        return del_local_roles(self,3,**data)
391
[5886]392class LoginApplicant(WAeUPPage):
393    grok.context(IApplicantsContainer)
394    grok.name('login')
[6153]395    grok.require('waeup.Public')
[5886]396
[6110]397    @property
398    def title(self):
399        return u"Applicant Login: %s" % self.context.title
[6078]400
[5886]401    @property
402    def label(self):
[6110]403        return u'Login for applicants only'
[5886]404
405    pnav = 3
[6319]406
[6110]407    @property
408    def ac_prefix(self):
409        return self.context.ac_prefix
[6078]410
[5896]411    def update(self, SUBMIT=None):
412        self.ac_series = self.request.form.get('form.ac_series', None)
413        self.ac_number = self.request.form.get('form.ac_number', None)
[5886]414        if SUBMIT is None:
415            return
[5894]416        if self.request.principal.id == 'zope.anybody':
[6105]417            self.flash('Entered credentials are invalid.')
[5886]418            return
[5894]419        if not IApplicantPrincipal.providedBy(self.request.principal):
420            # Don't care if user is already authenticated as non-applicant
421            return
[6377]422
423        # From here we handle an applicant (not an officer browsing)
[5905]424        pin = self.request.principal.access_code
[6375]425
426        # Mark pin as used (this also fires a pin related transition)
427        invalidate_accesscode(pin)
428
[6377]429        if not pin in self.context.keys():
430            # Create applicant record
431            applicant = Applicant()
432            applicant.access_code = pin
433            self.context[pin] = applicant
[6359]434
[6377]435        role_manager = IPrincipalRoleManager(self.context[pin])
[6394]436        role_manager.assignRoleToPrincipal(
437            'waeup.local.ApplicationOwner', self.request.principal.id)
[6359]438
[6394]439        # Assign current principal the PortalUser role
440        role_manager = IPrincipalRoleManager(grok.getSite()['faculties'])
441        role_manager.assignRoleToPrincipal(
442            'waeup.PortalUser', self.request.principal.id)
[6377]443
444        # Mark application as started
445        if IWorkflowState(self.context[pin]).getState() is INITIALIZED:
446            IWorkflowInfo(self.context[pin]).fireTransition('start')
447
[5937]448        self.redirect(self.url(self.context[pin], 'edit'))
[5886]449        return
[6319]450
[6327]451class ApplicantAddFormPage(WAeUPAddFormPage):
452    """Add-form to add certificate to a department.
453    """
454    grok.context(IApplicantsContainer)
455    grok.require('waeup.manageApplications')
456    grok.name('addapplicant')
457    grok.template('applicantaddpage')
458    title = 'Applicants'
459    label = 'Add applicant'
460    pnav = 3
461
462    @property
463    def title(self):
464        return "Applicants Container: %s" % self.context.title
465
466    @property
467    def ac_prefix(self):
468        return self.context.ac_prefix
469
470    @grok.action('Create application record')
471    def addApplicant(self, **data):
472        ac_series = self.request.form.get('form.ac_series', None)
473        ac_number = self.request.form.get('form.ac_number', None)
474        pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number)
[6383]475        if not invalidate_accesscode(pin):
476            self.flash('%s is not a valid access code.' % pin)
477            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
478            return
479        else:
[6327]480            # Create applicant record
481            applicant = Applicant()
482            applicant.access_code = pin
483            self.context[pin] = applicant
484        self.redirect(self.url(self.context[pin], 'edit'))
485        return
486
[6396]487class AccessCodeViewLink(LeftSidebarLink):
[6153]488    grok.order(1)
489    grok.require('waeup.Public')
[6396]490    icon = 'actionicon_view.png'
491    title = 'View Record'
492    target = '/@@index'
[5886]493
[6396]494    @property
495    def url(self):
[6153]496        if not IApplicantPrincipal.providedBy(self.request.principal):
497            return ''
498        access_code = getattr(self.request.principal,'access_code',None)
499        if access_code:
500            applicant_object = get_applicant_data(access_code)
[6396]501            return absoluteURL(applicant_object, self.request) + self.target
502        else:
503            return ''
[6153]504
[6396]505class AccessCodeEditLink(AccessCodeViewLink):
506    grok.order(2)
507    grok.require('waeup.Public')
508    icon = 'actionicon_modify.png'
509    title = 'Edit Record'
510    target = '/@@edit'
511
512    @property
513    def url(self):
514        if not IApplicantPrincipal.providedBy(self.request.principal):
515            return ''
516        access_code = getattr(self.request.principal,'access_code',None)
517        if access_code:
518            applicant_object = get_applicant_data(access_code)
519            if applicant_object.locked:
520                return ''
521            return absoluteURL(applicant_object, self.request) + self.target
522        else:
523            return ''
524
525class AccessCodeSlipLink(AccessCodeViewLink):
526    grok.order(3)
527    grok.require('waeup.Public')
528    icon = 'actionicon_pdf.png'
529    title = 'Download Slip'
530    target = '/application_slip.pdf'
531
[5273]532class DisplayApplicant(WAeUPDisplayFormPage):
533    grok.context(IApplicant)
534    grok.name('index')
[6198]535    grok.require('waeup.handleApplication')
[6320]536    form_fields = grok.AutoFields(IApplicant).omit(
537        'locked').omit('course_admitted')
[6196]538    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]539    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]540    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]541    label = 'Applicant'
[6254]542    grok.template('form_display')
[5843]543    pnav = 3
[5273]544
[6196]545    @property
546    def title(self):
547        return '%s' % self.context.access_code
548
549    @property
550    def label(self):
551        container_title = self.context.__parent__.title
552        return '%s Application Record' % container_title
553
[6254]554    def getCourseAdmitted(self):
[6355]555        """Return link, title and code in html format to the certificate
556           admitted.
[6351]557        """
[6254]558        course_admitted = self.context.course_admitted
559        if ICertificate.providedBy(course_admitted):
560            url = self.url(course_admitted)
561            title = course_admitted.title
562            code = course_admitted.code
[6366]563            return '<a href="%s">%s - %s</a>' %(url,code,title)
[6254]564        return 'not yet admitted'
565
[6358]566class PDFActionButton(ManageActionButton):
567    grok.context(IApplicant)
[6396]568    #grok.view(DisplayApplicant)
569    grok.require('waeup.manageApplications')
[6358]570    icon = 'actionicon_pdf.png'
[6367]571    text = 'Download application slip'
[6358]572    target = 'application_slip.pdf'
573
574class ExportPDFPage(grok.View):
575    """Deliver a PDF slip of the context.
576    """
577    grok.context(IApplicant)
578    grok.name('application_slip.pdf')
579    grok.require('waeup.handleApplication')
580    form_fields = grok.AutoFields(IApplicant).omit(
581        'locked').omit('course_admitted')
582    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
583    prefix = 'form'
584
[6363]585    @property
586    def label(self):
587        container_title = self.context.__parent__.title
588        return '%s Application Record' % container_title
589
[6358]590    def getCourseAdmitted(self):
591        """Return title and code in html format to the certificate
592           admitted.
593        """
594        course_admitted = self.context.course_admitted
595        if ICertificate.providedBy(course_admitted):
596            title = course_admitted.title
597            code = course_admitted.code
[6365]598            return '%s - %s' %(code,title)
[6358]599        return 'not yet admitted'
600
601    def setUpWidgets(self, ignore_request=False):
602        self.adapters = {}
603        self.widgets = setUpEditWidgets(
604            self.form_fields, self.prefix, self.context, self.request,
605            adapters=self.adapters, for_display=True,
606            ignore_request=ignore_request
607            )
608
609    def render(self):
[6364]610        # (0,0),(-1,-1) = whole table
611        # (0,0),(0,-1) = first column
612        # (-1,0),(-1,-1) = last column
613        # (0,0),(-1,0) = first row
614        # (0,-1),(-1,-1) = last row
615        SLIP_STYLE = TableStyle(
616            [('VALIGN',(0,0),(-1,-1),'TOP')]
617            )
[6358]618
619        pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
[6364]620        pdf.setTitle(self.label)
621        pdf.setSubject('Application')
622        pdf.setAuthor('%s (%s)' % (self.request.principal.title,
623            self.request.principal.id))
624        pdf.setCreator('WAeUP SIRP')
[6358]625        width, height = A4
626        style = getSampleStyleSheet()
[6365]627        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
[6363]628
[6358]629        story = []
[6365]630        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
[6363]631        header_title = getattr(grok.getSite(), 'name', u'Sample University')
632        story.append(Paragraph(header_title, style["Heading1"]))
633        #import pdb; pdb.set_trace()
634        frame_header.addFromList(story,pdf)
635
636        story = []
[6365]637        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
[6364]638        story.append(Paragraph(self.label, style["Heading2"]))
639        #story.append(HRFlowable())
640        story.append(Spacer(1, 18))
641        for msg in self.context.history.messages:
642            f_msg = '<font face="Courier" size=10>%s</font>' % msg
643            story.append(Paragraph(f_msg, style["Normal"]))
[6363]644        story.append(Spacer(1, 24))
[6358]645        self.setUpWidgets()
[6363]646        data = []
[6358]647        for widget in self.widgets:
[6363]648            f_label = '<font size=12>%s</font>' % widget.label.strip()
649            f_label = Paragraph(f_label, style["Normal"])
[6358]650            if widget.name != 'form.passport':
[6363]651                f_text = '<font size=12>%s</font>' % widget()
652                f_text = Paragraph(f_text, style["Normal"])
653                data.append([f_label,f_text])
[6360]654            else:
655                filename = widget._data.file.name
[6364]656                im = Image(filename,width=4*cm, height=3*cm,kind='bound')
[6363]657                data.append([f_label,im])
658        f_label = '<font size=12>Admitted Course of Study</font>'
659        f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
660        f_label = Paragraph(f_label, style["Normal"])
661        f_text = Paragraph(f_text, style["Normal"])
662        data.append([f_label,f_text])
[6364]663        table = Table(data,style=SLIP_STYLE)
[6363]664        story.append(table)
665        frame_body.addFromList(story,pdf)
666
[6364]667        story = []
[6365]668        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
[6364]669        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
670        f_text = '<font size=10>%s</font>' % timestamp
671        story.append(Paragraph(f_text, style["Normal"]))
672        frame_footer.addFromList(story,pdf)
673
[6358]674        self.response.setHeader(
675            'Content-Type', 'application/pdf')
676        return pdf.getpdfdata()
677
[6383]678class ApplicantManageActionButton(ManageActionButton):
[6198]679    grok.context(IApplicant)
680    grok.view(DisplayApplicant)
681    grok.require('waeup.manageApplications')
[6383]682    text = 'Manage application record'
[6198]683    target = 'edit_full'
684
[6196]685class EditApplicantFull(WAeUPEditFormPage):
686    """A full edit view for applicant data.
687    """
688    grok.context(IApplicant)
689    grok.name('edit_full')
[6198]690    grok.require('waeup.manageApplications')
[6196]691    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
692    form_fields['passport'].custom_widget = EncodingImageFileWidget
693    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
694    grok.template('form_edit')
[6322]695    manage_applications = True
[6196]696    pnav = 3
697
698    def update(self):
699        datepicker.need() # Enable jQuery datepicker in date fields.
700        super(EditApplicantFull, self).update()
[6353]701        self.wf_info = IWorkflowInfo(self.context)
[6196]702        return
703
704    @property
705    def title(self):
706        return self.context.access_code
707
708    @property
709    def label(self):
710        container_title = self.context.__parent__.title
711        return '%s Application Form' % container_title
712
[6303]713    def getTransitions(self):
[6351]714        """Return a list of dicts of allowed transition ids and titles.
[6353]715
716        Each list entry provides keys ``name`` and ``title`` for
717        internal name and (human readable) title of a single
718        transition.
[6349]719        """
[6353]720        allowed_transitions = self.wf_info.getManualTransitions()
[6355]721        return [dict(name='', title='No transition')] +[
722            dict(name=x, title=y) for x, y in allowed_transitions]
[6303]723
[6196]724    @grok.action('Save')
725    def save(self, **data):
726        self.applyData(self.context, **data)
727        self.context._p_changed = True
[6303]728        form = self.request.form
729        if form.has_key('transition') and form['transition']:
[6305]730            transition_id = form['transition']
[6353]731            self.wf_info.fireTransition(transition_id)
[6196]732        self.flash('Form has been saved.')
[6348]733        self.context.getApplicantsRootLogger().info('Saved')
[6196]734        return
735
736class EditApplicantStudent(EditApplicantFull):
[5982]737    """An applicant-centered edit view for applicant data.
738    """
[6196]739    grok.context(IApplicantEdit)
[5273]740    grok.name('edit')
[6198]741    grok.require('waeup.handleApplication')
[6196]742    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
[5686]743    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]744    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6196]745    grok.template('form_edit')
[6322]746    manage_applications = False
[5484]747
[6322]748
[5941]749    def emitLockMessage(self):
[6105]750        self.flash('The requested form is locked (read-only).')
[5941]751        self.redirect(self.url(self.context))
752        return
[6078]753
[5686]754    def update(self):
[5941]755        if self.context.locked:
[6367]756            self.emitLockMessage()
[5941]757            return
[6040]758        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]759        super(EditApplicantStudent, self).update()
[5686]760        return
[5952]761
[6196]762    def dataNotComplete(self):
[6322]763        if not self.request.form.get('confirm_passport', False):
[6196]764            return 'Passport confirmation box not ticked.'
765        if len(self.errors) > 0:
766            return 'Form has errors.'
767        return False
[5952]768
[5273]769    @grok.action('Save')
770    def save(self, **data):
[5941]771        if self.context.locked:
772            self.emitLockMessage()
773            return
[5273]774        self.applyData(self.context, **data)
775        self.context._p_changed = True
[6196]776        self.flash('Form has been saved.')
[5273]777        return
778
[5484]779    @grok.action('Final Submit')
780    def finalsubmit(self, **data):
[5941]781        if self.context.locked:
782            self.emitLockMessage()
783            return
[5273]784        self.applyData(self.context, **data)
[5484]785        self.context._p_changed = True
[6196]786        if self.dataNotComplete():
787            self.flash(self.dataNotComplete())
[5941]788            return
[6303]789        state = IWorkflowState(self.context).getState()
[6322]790        # This shouldn't happen, but the application officer
791        # might have forgotten to lock the form after changing the state
[6303]792        if state != STARTED:
[6322]793            self.flash('This form cannot be submitted. Wrong state!')
[6303]794            return
795        IWorkflowInfo(self.context).fireTransition('submit')
[5941]796        self.context.locked = True
[6196]797        self.flash('Form has been submitted.')
798        self.redirect(self.url(self.context))
[5273]799        return
[5941]800
[6367]801class ApplicantViewActionButton(ManageActionButton):
802    grok.context(IApplicant)
[6383]803    grok.view(EditApplicantFull)
[6396]804    grok.require('waeup.manageApplications')
[6383]805    icon = 'actionicon_view.png'
[6367]806    text = 'View application record'
[6396]807    target = 'index'
Note: See TracBrowser for help on using the repository browser.