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

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

Workaround for freshly uploaded images not being displayed in
otherwise erraneous applicants forms.

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