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

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

Assign applicant the PortalUser? role at site level otherwise s/he won't see the Academics navigation tab.

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
[6397]440        role_manager = IPrincipalRoleManager(grok.getSite())
[6394]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.