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

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

accesscodes.invalidate_accesscode(): return false if ac doesn't exist

Use this in browser.py to flash message in addApplicant if ac doesn't exist.

Use a view icon for view tabs.

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