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

Last change on this file since 6395 was 6394, checked in by uli, 14 years ago

* empty log message *

File size: 27.1 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
[6153]487class AccessCodeLink(LeftSidebarLink):
488    grok.order(1)
489    grok.require('waeup.Public')
[5886]490
[6153]491    def render(self):
492        if not IApplicantPrincipal.providedBy(self.request.principal):
493            return ''
494        access_code = getattr(self.request.principal,'access_code',None)
495        if access_code:
496            applicant_object = get_applicant_data(access_code)
497            url = absoluteURL(applicant_object, self.request)
[6198]498            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
[6153]499                url,access_code)
500        return ''
501
[5273]502class DisplayApplicant(WAeUPDisplayFormPage):
503    grok.context(IApplicant)
504    grok.name('index')
[6198]505    grok.require('waeup.handleApplication')
[6320]506    form_fields = grok.AutoFields(IApplicant).omit(
507        'locked').omit('course_admitted')
[6196]508    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]509    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]510    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]511    label = 'Applicant'
[6254]512    grok.template('form_display')
[5843]513    pnav = 3
[5273]514
[6196]515    @property
516    def title(self):
517        return '%s' % self.context.access_code
518
519    @property
520    def label(self):
521        container_title = self.context.__parent__.title
522        return '%s Application Record' % container_title
523
[6254]524    def getCourseAdmitted(self):
[6355]525        """Return link, title and code in html format to the certificate
526           admitted.
[6351]527        """
[6254]528        course_admitted = self.context.course_admitted
529        if ICertificate.providedBy(course_admitted):
530            url = self.url(course_admitted)
531            title = course_admitted.title
532            code = course_admitted.code
[6366]533            return '<a href="%s">%s - %s</a>' %(url,code,title)
[6254]534        return 'not yet admitted'
535
[6358]536class PDFActionButton(ManageActionButton):
537    grok.context(IApplicant)
538    grok.view(DisplayApplicant)
539    grok.require('waeup.handleApplication')
540    icon = 'actionicon_pdf.png'
[6367]541    text = 'Download application slip'
[6358]542    target = 'application_slip.pdf'
543
544class ExportPDFPage(grok.View):
545    """Deliver a PDF slip of the context.
546    """
547    grok.context(IApplicant)
548    grok.name('application_slip.pdf')
549    grok.require('waeup.handleApplication')
550    form_fields = grok.AutoFields(IApplicant).omit(
551        'locked').omit('course_admitted')
552    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
553    prefix = 'form'
554
[6363]555    @property
556    def label(self):
557        container_title = self.context.__parent__.title
558        return '%s Application Record' % container_title
559
[6358]560    def getCourseAdmitted(self):
561        """Return title and code in html format to the certificate
562           admitted.
563        """
564        course_admitted = self.context.course_admitted
565        if ICertificate.providedBy(course_admitted):
566            title = course_admitted.title
567            code = course_admitted.code
[6365]568            return '%s - %s' %(code,title)
[6358]569        return 'not yet admitted'
570
571    def setUpWidgets(self, ignore_request=False):
572        self.adapters = {}
573        self.widgets = setUpEditWidgets(
574            self.form_fields, self.prefix, self.context, self.request,
575            adapters=self.adapters, for_display=True,
576            ignore_request=ignore_request
577            )
578
579    def render(self):
[6364]580        # (0,0),(-1,-1) = whole table
581        # (0,0),(0,-1) = first column
582        # (-1,0),(-1,-1) = last column
583        # (0,0),(-1,0) = first row
584        # (0,-1),(-1,-1) = last row
585        SLIP_STYLE = TableStyle(
586            [('VALIGN',(0,0),(-1,-1),'TOP')]
587            )
[6358]588
589        pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
[6364]590        pdf.setTitle(self.label)
591        pdf.setSubject('Application')
592        pdf.setAuthor('%s (%s)' % (self.request.principal.title,
593            self.request.principal.id))
594        pdf.setCreator('WAeUP SIRP')
[6358]595        width, height = A4
596        style = getSampleStyleSheet()
[6365]597        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
[6363]598
[6358]599        story = []
[6365]600        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
[6363]601        header_title = getattr(grok.getSite(), 'name', u'Sample University')
602        story.append(Paragraph(header_title, style["Heading1"]))
603        #import pdb; pdb.set_trace()
604        frame_header.addFromList(story,pdf)
605
606        story = []
[6365]607        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
[6364]608        story.append(Paragraph(self.label, style["Heading2"]))
609        #story.append(HRFlowable())
610        story.append(Spacer(1, 18))
611        for msg in self.context.history.messages:
612            f_msg = '<font face="Courier" size=10>%s</font>' % msg
613            story.append(Paragraph(f_msg, style["Normal"]))
[6363]614        story.append(Spacer(1, 24))
[6358]615        self.setUpWidgets()
[6363]616        data = []
[6358]617        for widget in self.widgets:
[6363]618            f_label = '<font size=12>%s</font>' % widget.label.strip()
619            f_label = Paragraph(f_label, style["Normal"])
[6358]620            if widget.name != 'form.passport':
[6363]621                f_text = '<font size=12>%s</font>' % widget()
622                f_text = Paragraph(f_text, style["Normal"])
623                data.append([f_label,f_text])
[6360]624            else:
625                filename = widget._data.file.name
[6364]626                im = Image(filename,width=4*cm, height=3*cm,kind='bound')
[6363]627                data.append([f_label,im])
628        f_label = '<font size=12>Admitted Course of Study</font>'
629        f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
630        f_label = Paragraph(f_label, style["Normal"])
631        f_text = Paragraph(f_text, style["Normal"])
632        data.append([f_label,f_text])
[6364]633        table = Table(data,style=SLIP_STYLE)
[6363]634        story.append(table)
635        frame_body.addFromList(story,pdf)
636
[6364]637        story = []
[6365]638        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
[6364]639        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
640        f_text = '<font size=10>%s</font>' % timestamp
641        story.append(Paragraph(f_text, style["Normal"]))
642        frame_footer.addFromList(story,pdf)
643
[6358]644        self.response.setHeader(
645            'Content-Type', 'application/pdf')
646        return pdf.getpdfdata()
647
[6383]648class ApplicantManageActionButton(ManageActionButton):
[6198]649    grok.context(IApplicant)
650    grok.view(DisplayApplicant)
651    grok.require('waeup.manageApplications')
[6383]652    text = 'Manage application record'
[6198]653    target = 'edit_full'
654
[6367]655class ApplicantEditActionButton(ManageActionButton):
656    grok.context(IApplicant)
657    grok.view(DisplayApplicant)
658    grok.require('waeup.handleApplication')
659    text = 'Edit application record'
660    target = 'edit'
661
[6196]662class EditApplicantFull(WAeUPEditFormPage):
663    """A full edit view for applicant data.
664    """
665    grok.context(IApplicant)
666    grok.name('edit_full')
[6198]667    grok.require('waeup.manageApplications')
[6196]668    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
669    form_fields['passport'].custom_widget = EncodingImageFileWidget
670    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
671    grok.template('form_edit')
[6322]672    manage_applications = True
[6196]673    pnav = 3
674
675    def update(self):
676        datepicker.need() # Enable jQuery datepicker in date fields.
677        super(EditApplicantFull, self).update()
[6353]678        self.wf_info = IWorkflowInfo(self.context)
[6196]679        return
680
681    @property
682    def title(self):
683        return self.context.access_code
684
685    @property
686    def label(self):
687        container_title = self.context.__parent__.title
688        return '%s Application Form' % container_title
689
[6303]690    def getTransitions(self):
[6351]691        """Return a list of dicts of allowed transition ids and titles.
[6353]692
693        Each list entry provides keys ``name`` and ``title`` for
694        internal name and (human readable) title of a single
695        transition.
[6349]696        """
[6353]697        allowed_transitions = self.wf_info.getManualTransitions()
[6355]698        return [dict(name='', title='No transition')] +[
699            dict(name=x, title=y) for x, y in allowed_transitions]
[6303]700
[6196]701    @grok.action('Save')
702    def save(self, **data):
703        self.applyData(self.context, **data)
704        self.context._p_changed = True
[6303]705        form = self.request.form
706        if form.has_key('transition') and form['transition']:
[6305]707            transition_id = form['transition']
[6353]708            self.wf_info.fireTransition(transition_id)
[6196]709        self.flash('Form has been saved.')
[6348]710        self.context.getApplicantsRootLogger().info('Saved')
[6196]711        return
712
713class EditApplicantStudent(EditApplicantFull):
[5982]714    """An applicant-centered edit view for applicant data.
715    """
[6196]716    grok.context(IApplicantEdit)
[5273]717    grok.name('edit')
[6198]718    grok.require('waeup.handleApplication')
[6196]719    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
[5686]720    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]721    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6196]722    grok.template('form_edit')
[6322]723    manage_applications = False
[5484]724
[6322]725
[5941]726    def emitLockMessage(self):
[6105]727        self.flash('The requested form is locked (read-only).')
[5941]728        self.redirect(self.url(self.context))
729        return
[6078]730
[5686]731    def update(self):
[5941]732        if self.context.locked:
[6367]733            self.emitLockMessage()
[5941]734            return
[6040]735        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]736        super(EditApplicantStudent, self).update()
[5686]737        return
[5952]738
[6196]739    def dataNotComplete(self):
[6322]740        if not self.request.form.get('confirm_passport', False):
[6196]741            return 'Passport confirmation box not ticked.'
742        if len(self.errors) > 0:
743            return 'Form has errors.'
744        return False
[5952]745
[5273]746    @grok.action('Save')
747    def save(self, **data):
[5941]748        if self.context.locked:
749            self.emitLockMessage()
750            return
[5273]751        self.applyData(self.context, **data)
752        self.context._p_changed = True
[6196]753        self.flash('Form has been saved.')
[5273]754        return
755
[5484]756    @grok.action('Final Submit')
757    def finalsubmit(self, **data):
[5941]758        if self.context.locked:
759            self.emitLockMessage()
760            return
[5273]761        self.applyData(self.context, **data)
[5484]762        self.context._p_changed = True
[6196]763        if self.dataNotComplete():
764            self.flash(self.dataNotComplete())
[5941]765            return
[6303]766        state = IWorkflowState(self.context).getState()
[6322]767        # This shouldn't happen, but the application officer
768        # might have forgotten to lock the form after changing the state
[6303]769        if state != STARTED:
[6322]770            self.flash('This form cannot be submitted. Wrong state!')
[6303]771            return
772        IWorkflowInfo(self.context).fireTransition('submit')
[5941]773        self.context.locked = True
[6196]774        self.flash('Form has been submitted.')
775        self.redirect(self.url(self.context))
[5273]776        return
[5941]777
[6367]778class ApplicantViewActionButton(ManageActionButton):
779    grok.context(IApplicant)
[6383]780    grok.view(EditApplicantFull)
781    icon = 'actionicon_view.png'
[6367]782    text = 'View application record'
783    target = 'index'
Note: See TracBrowser for help on using the repository browser.