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

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

Adjust pdf slip to display view.

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