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

Last change on this file since 6375 was 6375, checked in by uli, 13 years ago

Remove the faulty transition trigger inserted just before and replace
it by correct one. Please remember that there is no 'start' trigger
for pins (AccessCodes?) and even if there would be one, it could only
be fired for AccessCode? instances and not for strings like
'APP-bla...'. Furthermore it us much shorter to use
invalidate_accesscode().

The removed code even failed tests.

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