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

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

Add view permission for applicants to render view and edit buttons correctly.

Reenable transtion 'start' after first login.

File size: 27.0 KB
Line 
1##
2## browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet & Henrik Bettermann
5## $Id$
6##
7## Copyright (C) 2010 Uli Fouquet & Henrik Bettermann
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.
12##
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.
17##
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##
22"""UI components for basic applicants and related components.
23"""
24import sys
25import grok
26
27from datetime import datetime
28from zope.formlib.widget import CustomWidgetFactory
29from zope.formlib.form import setUpEditWidgets
30from zope.securitypolicy.interfaces import IPrincipalRoleManager
31from zope.traversing.browser import absoluteURL
32
33from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
34from reportlab.pdfgen import canvas
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
42
43from waeup.sirp.accesscodes import invalidate_accesscode
44from waeup.sirp.browser import (
45    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
46from waeup.sirp.browser.breadcrumbs import Breadcrumb
47from waeup.sirp.browser.layout import NullValidator
48from waeup.sirp.browser.pages import add_local_role, del_local_roles
49from waeup.sirp.browser.resources import datepicker, tabs, datatable
50from waeup.sirp.browser.viewlets import (
51    ManageActionButton, PrimaryNavTab, LeftSidebarLink
52    )
53from waeup.sirp.image.browser.widget import (
54    ThumbnailWidget, EncodingImageFileWidget,
55    )
56from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
57from waeup.sirp.permissions import get_users_with_local_roles
58from waeup.sirp.university.interfaces import ICertificate
59from waeup.sirp.widgets.datewidget import (
60    FriendlyDateWidget, FriendlyDateDisplayWidget)
61from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
62from waeup.sirp.widgets.objectwidget import (
63    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
64from waeup.sirp.widgets.multilistwidget import (
65    MultiListWidget, MultiListDisplayWidget)
66from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
67from waeup.sirp.applicants.interfaces import (
68    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
69    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
70    )
71from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
72
73results_widget = CustomWidgetFactory(
74    WAeUPObjectWidget, ResultEntry)
75
76results_display_widget = CustomWidgetFactory(
77    WAeUPObjectDisplayWidget, ResultEntry)
78
79list_results_widget = CustomWidgetFactory(
80    MultiListWidget, subwidget=results_widget)
81
82list_results_display_widget = CustomWidgetFactory(
83    MultiListDisplayWidget, subwidget=results_display_widget)
84
85#TRANSITION_OBJECTS = create_workflow()
86
87#TRANSITION_DICT = dict([
88#    (transition_object.transition_id,transition_object.title)
89#    for transition_object in TRANSITION_OBJECTS])
90
91class ApplicantsRootPage(WAeUPPage):
92    grok.context(IApplicantsRoot)
93    grok.name('index')
94    grok.require('waeup.Public')
95    title = 'Applicants'
96    label = 'Application Section'
97    pnav = 3
98
99    def update(self):
100        super(ApplicantsRootPage, self).update()
101        datatable.need()
102        return
103
104class ManageApplicantsRootActionButton(ManageActionButton):
105    grok.context(IApplicantsRoot)
106    grok.view(ApplicantsRootPage)
107    grok.require('waeup.manageApplications')
108    text = 'Manage application section'
109
110class ApplicantsRootManageFormPage(WAeUPEditFormPage):
111    grok.context(IApplicantsRoot)
112    grok.name('manage')
113    grok.template('applicantsrootmanagepage')
114    title = 'Applicants'
115    label = 'Manage application section'
116    pnav = 3
117    grok.require('waeup.manageApplications')
118    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
119    tabtwoactions1 = ['Remove selected local roles']
120    tabtwoactions2 = ['Add local role']
121    subunits = 'Applicants Containers'
122
123    def update(self):
124        tabs.need()
125        datatable.need()
126        return super(ApplicantsRootManageFormPage, self).update()
127
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
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))
159        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
160        return
161
162    @grok.action('Add applicants container', validator=NullValidator)
163    def addApplicantsContainer(self, **data):
164        self.redirect(self.url(self.context, '@@add'))
165        return
166
167    @grok.action('Cancel', validator=NullValidator)
168    def cancel(self, **data):
169        self.redirect(self.url(self.context))
170        return
171
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
180class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
181    grok.context(IApplicantsRoot)
182    grok.require('waeup.manageApplications')
183    grok.name('add')
184    grok.template('applicantscontaineraddpage')
185    title = 'Applicants'
186    label = 'Add applicants container'
187    pnav = 3
188
189    form_fields = grok.AutoFields(
190        IApplicantsContainerAdd).omit('code').omit('title')
191    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
192    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
193
194    def update(self):
195        datepicker.need() # Enable jQuery datepicker in date fields.
196        return super(ApplicantsContainerAddFormPage, self).update()
197
198    @grok.action('Add applicants container')
199    def addApplicantsContainer(self, **data):
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)
204        if code in self.context.keys():
205            self.flash(
206                'An applicants container for the same application '
207                'type and entrance year exists already in the database.')
208            return
209        # Add new applicants container...
210        provider = data['provider'][1]
211        container = provider.factory()
212        self.applyData(container, **data)
213        container.code = code
214        container.title = title
215        self.context[code] = container
216        self.flash('Added: "%s".' % code)
217        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
218        return
219
220    @grok.action('Cancel', validator=NullValidator)
221    def cancel(self, **data):
222        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
223
224class ApplicantsRootBreadcrumb(Breadcrumb):
225    """A breadcrumb for applicantsroot.
226    """
227    grok.context(IApplicantsRoot)
228    title = u'Application Section'
229
230class ApplicantsContainerBreadcrumb(Breadcrumb):
231    """A breadcrumb for applicantscontainers.
232    """
233    grok.context(IApplicantsContainer)
234
235class ApplicantBreadcrumb(Breadcrumb):
236    """A breadcrumb for applicants.
237    """
238    grok.context(IApplicant)
239
240    @property
241    def title(self):
242        """Get a title for a context.
243        """
244        return self.context.access_code
245
246class ApplicantsTab(PrimaryNavTab):
247    """Applicants tab in primary navigation.
248    """
249
250    grok.context(IWAeUPObject)
251    grok.order(3)
252    grok.require('waeup.Public')
253    grok.template('primarynavtab')
254
255    pnav = 3
256    tab_title = u'Applicants'
257
258    @property
259    def link_target(self):
260        return self.view.application_url('applicants')
261
262class ApplicantsContainerPage(WAeUPDisplayFormPage):
263    """The standard view for regular applicant containers.
264    """
265    grok.context(IApplicantsContainer)
266    grok.name('index')
267    grok.require('waeup.Public')
268    grok.template('applicantscontainerpage')
269    pnav = 3
270
271    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
272    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
273    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
274    form_fields['description'].custom_widget = ReSTDisplayWidget
275
276    @property
277    def title(self):
278        return "Applicants Container: %s" % self.context.title
279
280    @property
281    def label(self):
282        return self.context.title
283
284class ApplicantsContainerManageActionButton(ManageActionButton):
285    grok.order(1)
286    grok.context(IApplicantsContainer)
287    grok.view(ApplicantsContainerPage)
288    grok.require('waeup.manageApplications')
289    text = 'Manage applicants container'
290
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'
299
300class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
301    grok.context(IApplicantsContainer)
302    grok.name('manage')
303    grok.template('applicantscontainermanagepage')
304    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
305    taboneactions = ['Save','Cancel']
306    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
307    tabthreeactions1 = ['Remove selected local roles']
308    tabthreeactions2 = ['Add local role']
309    # Use friendlier date widget...
310    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
311    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
312    grok.require('waeup.manageApplications')
313
314    @property
315    def title(self):
316        return "Applicants Container: %s" % self.context.title
317
318    @property
319    def label(self):
320        return 'Manage applicants container'
321
322    pnav = 3
323
324    def update(self):
325        datepicker.need() # Enable jQuery datepicker in date fields.
326        tabs.need()
327        datatable.need()  # Enable jQurey datatables for contents listing
328        return super(ApplicantsContainerManageFormPage, self).update()
329
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
344    @grok.action('Save')
345    def apply(self, **data):
346        self.applyData(self.context, **data)
347        self.flash('Data saved.')
348        return
349
350    # ToDo: Show warning message before deletion
351    @grok.action('Remove selected')
352    def delApplicant(self, **data):
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
374
375    @grok.action('Add applicant', validator=NullValidator)
376    def addApplicant(self, **data):
377        self.redirect(self.url(self.context, 'addapplicant'))
378        return
379
380    @grok.action('Cancel', validator=NullValidator)
381    def cancel(self, **data):
382        self.redirect(self.url(self.context))
383        return
384
385    @grok.action('Add local role', validator=NullValidator)
386    def addLocalRole(self, **data):
387        return add_local_role(self,3, **data)
388
389    @grok.action('Remove selected local roles')
390    def delLocalRoles(self, **data):
391        return del_local_roles(self,3,**data)
392
393class LoginApplicant(WAeUPPage):
394    grok.context(IApplicantsContainer)
395    grok.name('login')
396    grok.require('waeup.Public')
397
398    @property
399    def title(self):
400        return u"Applicant Login: %s" % self.context.title
401
402    @property
403    def label(self):
404        return u'Login for applicants only'
405
406    pnav = 3
407
408    @property
409    def ac_prefix(self):
410        return self.context.ac_prefix
411
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)
415        if SUBMIT is None:
416            return
417        if self.request.principal.id == 'zope.anybody':
418            self.flash('Entered credentials are invalid.')
419            return
420        if not IApplicantPrincipal.providedBy(self.request.principal):
421            # Don't care if user is already authenticated as non-applicant
422            return
423        pin = self.request.principal.access_code
424        state = IWorkflowState(self.context[pin]).getState()
425        if state == INITIALIZED:
426            IWorkflowInfo(self.context[pin]).fireTransition('start')
427        if pin in self.context.keys():
428            self.redirect(self.url(self.context[pin], 'edit'))
429            return
430
431        # Mark pin as used
432        invalidate_accesscode(pin)
433
434        # Create applicant record
435        applicant = Applicant()
436        applicant.access_code = pin
437        self.context[pin] = applicant
438
439        # Assign current principal the owner role on created applicant
440        # record
441        role_manager = IPrincipalRoleManager(self.context[pin])
442        role_manager.assignRoleToPrincipal(
443            'waeup.local.ApplicationOwner', self.request.principal.id)
444        # Assign current principal the PortalUser role
445        role_manager = IPrincipalRoleManager(grok.getSite()['faculties'])
446        role_manager.assignRoleToPrincipal(
447            'waeup.PortalUser', self.request.principal.id)
448        self.redirect(self.url(self.context[pin], 'edit'))
449        return
450
451class ApplicantAddFormPage(WAeUPAddFormPage):
452    """Add-form to add certificate to a department.
453    """
454    grok.context(IApplicantsContainer)
455    grok.require('waeup.manageApplications')
456    grok.name('addapplicant')
457    grok.template('applicantaddpage')
458    title = 'Applicants'
459    label = 'Add applicant'
460    pnav = 3
461
462    @property
463    def title(self):
464        return "Applicants Container: %s" % self.context.title
465
466    @property
467    def ac_prefix(self):
468        return self.context.ac_prefix
469
470    @grok.action('Create application record')
471    def addApplicant(self, **data):
472        ac_series = self.request.form.get('form.ac_series', None)
473        ac_number = self.request.form.get('form.ac_number', None)
474        pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number)
475        if pin not in self.context.keys():
476            # Create applicant record
477            applicant = Applicant()
478            applicant.access_code = pin
479            self.context[pin] = applicant
480        self.redirect(self.url(self.context[pin], 'edit'))
481        return
482
483class AccessCodeLink(LeftSidebarLink):
484    grok.order(1)
485    grok.require('waeup.Public')
486
487    def render(self):
488        if not IApplicantPrincipal.providedBy(self.request.principal):
489            return ''
490        access_code = getattr(self.request.principal,'access_code',None)
491        if access_code:
492            applicant_object = get_applicant_data(access_code)
493            url = absoluteURL(applicant_object, self.request)
494            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
495                url,access_code)
496        return ''
497
498class DisplayApplicant(WAeUPDisplayFormPage):
499    grok.context(IApplicant)
500    grok.name('index')
501    grok.require('waeup.handleApplication')
502    form_fields = grok.AutoFields(IApplicant).omit(
503        'locked').omit('course_admitted')
504    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
505    form_fields['passport'].custom_widget = ThumbnailWidget
506    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
507    label = 'Applicant'
508    grok.template('form_display')
509    pnav = 3
510
511    @property
512    def title(self):
513        return '%s' % self.context.access_code
514
515    @property
516    def label(self):
517        container_title = self.context.__parent__.title
518        return '%s Application Record' % container_title
519
520    def getCourseAdmitted(self):
521        """Return link, title and code in html format to the certificate
522           admitted.
523        """
524        course_admitted = self.context.course_admitted
525        if ICertificate.providedBy(course_admitted):
526            url = self.url(course_admitted)
527            title = course_admitted.title
528            code = course_admitted.code
529            return '<a href="%s">%s - %s</a>' %(url,code,title)
530        return 'not yet admitted'
531
532class PDFActionButton(ManageActionButton):
533    grok.context(IApplicant)
534    grok.view(DisplayApplicant)
535    grok.require('waeup.handleApplication')
536    icon = 'actionicon_pdf.png'
537    text = 'Download application slip'
538    target = 'application_slip.pdf'
539
540class ExportPDFPage(grok.View):
541    """Deliver a PDF slip of the context.
542    """
543    grok.context(IApplicant)
544    grok.name('application_slip.pdf')
545    grok.require('waeup.handleApplication')
546    form_fields = grok.AutoFields(IApplicant).omit(
547        'locked').omit('course_admitted')
548    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
549    prefix = 'form'
550
551    @property
552    def label(self):
553        container_title = self.context.__parent__.title
554        return '%s Application Record' % container_title
555
556    def getCourseAdmitted(self):
557        """Return title and code in html format to the certificate
558           admitted.
559        """
560        course_admitted = self.context.course_admitted
561        if ICertificate.providedBy(course_admitted):
562            title = course_admitted.title
563            code = course_admitted.code
564            return '%s - %s' %(code,title)
565        return 'not yet admitted'
566
567    def setUpWidgets(self, ignore_request=False):
568        self.adapters = {}
569        self.widgets = setUpEditWidgets(
570            self.form_fields, self.prefix, self.context, self.request,
571            adapters=self.adapters, for_display=True,
572            ignore_request=ignore_request
573            )
574
575    def render(self):
576        # (0,0),(-1,-1) = whole table
577        # (0,0),(0,-1) = first column
578        # (-1,0),(-1,-1) = last column
579        # (0,0),(-1,0) = first row
580        # (0,-1),(-1,-1) = last row
581        SLIP_STYLE = TableStyle(
582            [('VALIGN',(0,0),(-1,-1),'TOP')]
583            )
584
585        pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
586        pdf.setTitle(self.label)
587        pdf.setSubject('Application')
588        pdf.setAuthor('%s (%s)' % (self.request.principal.title,
589            self.request.principal.id))
590        pdf.setCreator('WAeUP SIRP')
591        width, height = A4
592        style = getSampleStyleSheet()
593        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
594
595        story = []
596        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
597        header_title = getattr(grok.getSite(), 'name', u'Sample University')
598        story.append(Paragraph(header_title, style["Heading1"]))
599        #import pdb; pdb.set_trace()
600        frame_header.addFromList(story,pdf)
601
602        story = []
603        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
604        story.append(Paragraph(self.label, style["Heading2"]))
605        #story.append(HRFlowable())
606        story.append(Spacer(1, 18))
607        for msg in self.context.history.messages:
608            f_msg = '<font face="Courier" size=10>%s</font>' % msg
609            story.append(Paragraph(f_msg, style["Normal"]))
610        story.append(Spacer(1, 24))
611        self.setUpWidgets()
612        data = []
613        for widget in self.widgets:
614            f_label = '<font size=12>%s</font>' % widget.label.strip()
615            f_label = Paragraph(f_label, style["Normal"])
616            if widget.name != 'form.passport':
617                f_text = '<font size=12>%s</font>' % widget()
618                f_text = Paragraph(f_text, style["Normal"])
619                data.append([f_label,f_text])
620            else:
621                filename = widget._data.file.name
622                im = Image(filename,width=4*cm, height=3*cm,kind='bound')
623                data.append([f_label,im])
624        f_label = '<font size=12>Admitted Course of Study</font>'
625        f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
626        f_label = Paragraph(f_label, style["Normal"])
627        f_text = Paragraph(f_text, style["Normal"])
628        data.append([f_label,f_text])
629        table = Table(data,style=SLIP_STYLE)
630        story.append(table)
631        frame_body.addFromList(story,pdf)
632
633        story = []
634        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
635        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
636        f_text = '<font size=10>%s</font>' % timestamp
637        story.append(Paragraph(f_text, style["Normal"]))
638        frame_footer.addFromList(story,pdf)
639
640        self.response.setHeader(
641            'Content-Type', 'application/pdf')
642        return pdf.getpdfdata()
643
644class ApplicantsManageActionButton(ManageActionButton):
645    grok.context(IApplicant)
646    grok.view(DisplayApplicant)
647    grok.require('waeup.manageApplications')
648    text = 'Edit application record'
649    target = 'edit_full'
650
651class ApplicantEditActionButton(ManageActionButton):
652    grok.context(IApplicant)
653    grok.view(DisplayApplicant)
654    grok.require('waeup.handleApplication')
655    text = 'Edit application record'
656    target = 'edit'
657
658class EditApplicantFull(WAeUPEditFormPage):
659    """A full edit view for applicant data.
660    """
661    grok.context(IApplicant)
662    grok.name('edit_full')
663    grok.require('waeup.manageApplications')
664    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
665    form_fields['passport'].custom_widget = EncodingImageFileWidget
666    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
667    grok.template('form_edit')
668    manage_applications = True
669    pnav = 3
670
671    def update(self):
672        datepicker.need() # Enable jQuery datepicker in date fields.
673        super(EditApplicantFull, self).update()
674        self.wf_info = IWorkflowInfo(self.context)
675        return
676
677    @property
678    def title(self):
679        return self.context.access_code
680
681    @property
682    def label(self):
683        container_title = self.context.__parent__.title
684        return '%s Application Form' % container_title
685
686    def getTransitions(self):
687        """Return a list of dicts of allowed transition ids and titles.
688
689        Each list entry provides keys ``name`` and ``title`` for
690        internal name and (human readable) title of a single
691        transition.
692        """
693        allowed_transitions = self.wf_info.getManualTransitions()
694        return [dict(name='', title='No transition')] +[
695            dict(name=x, title=y) for x, y in allowed_transitions]
696
697    @grok.action('Save')
698    def save(self, **data):
699        self.applyData(self.context, **data)
700        self.context._p_changed = True
701        form = self.request.form
702        if form.has_key('transition') and form['transition']:
703            transition_id = form['transition']
704            self.wf_info.fireTransition(transition_id)
705        self.flash('Form has been saved.')
706        self.context.getApplicantsRootLogger().info('Saved')
707        return
708
709class EditApplicantStudent(EditApplicantFull):
710    """An applicant-centered edit view for applicant data.
711    """
712    grok.context(IApplicantEdit)
713    grok.name('edit')
714    grok.require('waeup.handleApplication')
715    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
716    form_fields['passport'].custom_widget = EncodingImageFileWidget
717    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
718    grok.template('form_edit')
719    manage_applications = False
720
721
722    def emitLockMessage(self):
723        self.flash('The requested form is locked (read-only).')
724        self.redirect(self.url(self.context))
725        return
726
727    def update(self):
728        if self.context.locked:
729            self.emitLockMessage()
730            return
731        datepicker.need() # Enable jQuery datepicker in date fields.
732        super(EditApplicantStudent, self).update()
733        return
734
735    def dataNotComplete(self):
736        if not self.request.form.get('confirm_passport', False):
737            return 'Passport confirmation box not ticked.'
738        if len(self.errors) > 0:
739            return 'Form has errors.'
740        return False
741
742    @grok.action('Save')
743    def save(self, **data):
744        if self.context.locked:
745            self.emitLockMessage()
746            return
747        self.applyData(self.context, **data)
748        self.context._p_changed = True
749        self.flash('Form has been saved.')
750        return
751
752    @grok.action('Final Submit')
753    def finalsubmit(self, **data):
754        if self.context.locked:
755            self.emitLockMessage()
756            return
757        self.applyData(self.context, **data)
758        self.context._p_changed = True
759        if self.dataNotComplete():
760            self.flash(self.dataNotComplete())
761            return
762        state = IWorkflowState(self.context).getState()
763        # This shouldn't happen, but the application officer
764        # might have forgotten to lock the form after changing the state
765        if state != STARTED:
766            self.flash('This form cannot be submitted. Wrong state!')
767            return
768        IWorkflowInfo(self.context).fireTransition('submit')
769        self.context.locked = True
770        self.flash('Form has been submitted.')
771        self.redirect(self.url(self.context))
772        return
773
774class ApplicantViewActionButton(ManageActionButton):
775    grok.context(IApplicant)
776    grok.view(EditApplicantStudent)
777    grok.require('waeup.handleApplication')
778    text = 'View application record'
779    target = 'index'
Note: See TracBrowser for help on using the repository browser.