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

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

Some title, label and breadcrumb adjustments.

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)
[6654]229    title = u'Applicants'
[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):
[6622]460    """Add-form to add an applicant.
[6327]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.','')
[6644]764        if fields_string:
765            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
[6196]766        return
767
768class EditApplicantStudent(EditApplicantFull):
[5982]769    """An applicant-centered edit view for applicant data.
770    """
[6196]771    grok.context(IApplicantEdit)
[5273]772    grok.name('edit')
[6198]773    grok.require('waeup.handleApplication')
[6459]774    form_fields = grok.AutoFields(IApplicantEdit).omit(
[6476]775        'locked', 'course_admitted', 'student_id',
[6459]776        'screening_score',
777        )
[5686]778    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]779    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6196]780    grok.template('form_edit')
[6322]781    manage_applications = False
[6465]782    title = u'Your Application Form'
[5484]783
[6322]784
[5941]785    def emitLockMessage(self):
[6105]786        self.flash('The requested form is locked (read-only).')
[5941]787        self.redirect(self.url(self.context))
788        return
[6078]789
[5686]790    def update(self):
[5941]791        if self.context.locked:
[6367]792            self.emitLockMessage()
[5941]793            return
[6040]794        datepicker.need() # Enable jQuery datepicker in date fields.
[6598]795        upload = self.request.form.get('form.passport', None)
796        if upload:
797            # We got a fresh upload
798            image = createWAeUPImageFile(upload.filename, upload)
799            # This would normally be totally illegal. We set context
800            # data without the complete form data being
801            # verified. Normally data is set in methods like `save`
802            # only, which is only called when all form data is correct
803            # (and if not, the context is not touched). With images
804            # and their uploads the problem then is, that the uploaded
805            # image won't be shown in a partially erranous form
806            # because the new uploaded image is still not part of the
807            # context obeject and the image widget cannot work around
808            # that (it has no access to the new data). For this reason
809            # we set the image here and not in 'save()' or other
810            # methods.
811            self.context.passport = image
812            upload.seek(0)
813            self.passport_changed = True
[5982]814        super(EditApplicantStudent, self).update()
[5686]815        return
[5952]816
[6196]817    def dataNotComplete(self):
[6322]818        if not self.request.form.get('confirm_passport', False):
[6196]819            return 'Passport confirmation box not ticked.'
[6595]820        #if len(self.errors) > 0:
821        #    return 'Form has errors.'
[6196]822        return False
[5952]823
[5273]824    @grok.action('Save')
825    def save(self, **data):
[6595]826        #if self.context.locked:
827        #    self.emitLockMessage()
828        #    return
[5273]829        self.applyData(self.context, **data)
830        self.context._p_changed = True
[6196]831        self.flash('Form has been saved.')
[5273]832        return
833
[5484]834    @grok.action('Final Submit')
835    def finalsubmit(self, **data):
[6595]836        #if self.context.locked:
837        #    self.emitLockMessage()
838        #    return
[5273]839        self.applyData(self.context, **data)
[5484]840        self.context._p_changed = True
[6196]841        if self.dataNotComplete():
842            self.flash(self.dataNotComplete())
[5941]843            return
[6303]844        state = IWorkflowState(self.context).getState()
[6322]845        # This shouldn't happen, but the application officer
846        # might have forgotten to lock the form after changing the state
[6303]847        if state != STARTED:
[6322]848            self.flash('This form cannot be submitted. Wrong state!')
[6303]849            return
850        IWorkflowInfo(self.context).fireTransition('submit')
[6476]851        self.context.application_date = datetime.now()
[5941]852        self.context.locked = True
[6196]853        self.flash('Form has been submitted.')
854        self.redirect(self.url(self.context))
[5273]855        return
[5941]856
[6367]857class ApplicantViewActionButton(ManageActionButton):
858    grok.context(IApplicant)
[6383]859    grok.view(EditApplicantFull)
[6396]860    grok.require('waeup.manageApplications')
[6383]861    icon = 'actionicon_view.png'
[6367]862    text = 'View application record'
[6598]863    target = 'index'
Note: See TracBrowser for help on using the repository browser.