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

Last change on this file since 6381 was 6377, checked in by uli, 14 years ago

Reenable application start workflow on login.

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