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

Last change on this file since 6391 was 6390, checked in by uli, 14 years ago

pyflakes happy.

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