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

Last change on this file since 6591 was 6582, checked in by Henrik Bettermann, 13 years ago

Fix log string.

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