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

Last change on this file since 6356 was 6355, checked in by uli, 14 years ago
File size: 21.8 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
[5937]29from zope.securitypolicy.interfaces import IPrincipalRoleManager
[6153]30from zope.traversing.browser import absoluteURL
[6081]31
[6303]32from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
33
[5273]34from waeup.sirp.browser import (
[6321]35    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
[6081]36from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6103]37from waeup.sirp.browser.layout import NullValidator
[6321]38from waeup.sirp.browser.pages import add_local_role, del_local_roles
[6013]39from waeup.sirp.browser.resources import datepicker, tabs, datatable
[6153]40from waeup.sirp.browser.viewlets import (
41    ManageActionButton, PrimaryNavTab, LeftSidebarLink
42    )
[6081]43from waeup.sirp.image.browser.widget import (
44    ThumbnailWidget, EncodingImageFileWidget,
[5822]45    )
[6184]46from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
[6321]47from waeup.sirp.permissions import get_users_with_local_roles
[6254]48from waeup.sirp.university.interfaces import ICertificate
[6054]49from waeup.sirp.widgets.datewidget import (
50    FriendlyDateWidget, FriendlyDateDisplayWidget)
[6084]51from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
[5303]52from waeup.sirp.widgets.objectwidget import (
[5301]53    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
[5303]54from waeup.sirp.widgets.multilistwidget import (
[5273]55    MultiListWidget, MultiListDisplayWidget)
[6153]56from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
[6081]57from waeup.sirp.applicants.interfaces import (
[6321]58    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
59    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
[5686]60    )
[6353]61from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
[5320]62
[5273]63results_widget = CustomWidgetFactory(
[5301]64    WAeUPObjectWidget, ResultEntry)
[5273]65
66results_display_widget = CustomWidgetFactory(
[5301]67    WAeUPObjectDisplayWidget, ResultEntry)
[5273]68
69list_results_widget = CustomWidgetFactory(
70    MultiListWidget, subwidget=results_widget)
71
72list_results_display_widget = CustomWidgetFactory(
73    MultiListDisplayWidget, subwidget=results_display_widget)
74
[6353]75#TRANSITION_OBJECTS = create_workflow()
[6305]76
[6322]77#TRANSITION_DICT = dict([
78#    (transition_object.transition_id,transition_object.title)
79#    for transition_object in TRANSITION_OBJECTS])
[6305]80
[6067]81class ApplicantsRootPage(WAeUPPage):
[5822]82    grok.context(IApplicantsRoot)
83    grok.name('index')
[6153]84    grok.require('waeup.Public')
[5822]85    title = 'Applicants'
[6069]86    label = 'Application Section'
[5843]87    pnav = 3
[6012]88
89    def update(self):
[6067]90        super(ApplicantsRootPage, self).update()
[6012]91        datatable.need()
92        return
93
[5828]94class ManageApplicantsRootActionButton(ManageActionButton):
95    grok.context(IApplicantsRoot)
[6067]96    grok.view(ApplicantsRootPage)
[6198]97    grok.require('waeup.manageApplications')
[6069]98    text = 'Manage application section'
[5828]99
[6069]100class ApplicantsRootManageFormPage(WAeUPEditFormPage):
[5828]101    grok.context(IApplicantsRoot)
102    grok.name('manage')
[6107]103    grok.template('applicantsrootmanagepage')
[6069]104    title = 'Applicants'
105    label = 'Manage application section'
[5843]106    pnav = 3
[6198]107    grok.require('waeup.manageApplications')
[6069]108    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
[6184]109    tabtwoactions1 = ['Remove selected local roles']
110    tabtwoactions2 = ['Add local role']
[6069]111    subunits = 'Applicants Containers'
[6078]112
[6069]113    def update(self):
114        tabs.need()
[6108]115        datatable.need()
[6069]116        return super(ApplicantsRootManageFormPage, self).update()
[5828]117
[6184]118    def getLocalRoles(self):
119        roles = ILocalRolesAssignable(self.context)
120        return roles()
121
122    def getUsers(self):
123        """Get a list of all users.
124        """
125        for key, val in grok.getSite()['users'].items():
126            url = self.url(val)
127            yield(dict(url=url, name=key, val=val))
128
129    def getUsersWithLocalRoles(self):
130        return get_users_with_local_roles(self.context)
131
[6069]132    # ToDo: Show warning message before deletion
133    @grok.action('Remove selected')
134    def delApplicantsContainers(self, **data):
135        form = self.request.form
136        child_id = form['val_id']
137        if not isinstance(child_id, list):
138            child_id = [child_id]
139        deleted = []
140        for id in child_id:
141            try:
142                del self.context[id]
143                deleted.append(id)
144            except:
145                self.flash('Could not delete %s: %s: %s' % (
146                        id, sys.exc_info()[0], sys.exc_info()[1]))
147        if len(deleted):
148            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6078]149        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
150        return
[5828]151
[6069]152    @grok.action('Add applicants container', validator=NullValidator)
153    def addApplicantsContainer(self, **data):
154        self.redirect(self.url(self.context, '@@add'))
[6078]155        return
156
[6069]157    @grok.action('Cancel', validator=NullValidator)
158    def cancel(self, **data):
159        self.redirect(self.url(self.context))
[6078]160        return
161
[6184]162    @grok.action('Add local role', validator=NullValidator)
163    def addLocalRole(self, **data):
164        return add_local_role(self,2, **data)
165
166    @grok.action('Remove selected local roles')
167    def delLocalRoles(self, **data):
168        return del_local_roles(self,2,**data)
169
[6069]170class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
[5822]171    grok.context(IApplicantsRoot)
[6198]172    grok.require('waeup.manageApplications')
[5822]173    grok.name('add')
[6107]174    grok.template('applicantscontaineraddpage')
[6069]175    title = 'Applicants'
176    label = 'Add applicants container'
[5843]177    pnav = 3
[6078]178
[6103]179    form_fields = grok.AutoFields(
180        IApplicantsContainerAdd).omit('code').omit('title')
[6083]181    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
182    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6078]183
[6083]184    def update(self):
185        datepicker.need() # Enable jQuery datepicker in date fields.
186        return super(ApplicantsContainerAddFormPage, self).update()
187
[6069]188    @grok.action('Add applicants container')
189    def addApplicantsContainer(self, **data):
[6103]190        year = data['year']
191        code = u'%s%s' % (data['prefix'], year)
192        prefix = application_types_vocab.getTerm(data['prefix'])
193        title = u'%s %s/%s' % (prefix.title, year, year + 1)
[6087]194        if code in self.context.keys():
[6105]195            self.flash(
196                'An applicants container for the same application '
197                'type and entrance year exists already in the database.')
[5822]198            return
199        # Add new applicants container...
[6083]200        provider = data['provider'][1]
[5822]201        container = provider.factory()
[6069]202        self.applyData(container, **data)
[6087]203        container.code = code
204        container.title = title
205        self.context[code] = container
[6105]206        self.flash('Added: "%s".' % code)
[6069]207        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
[5822]208        return
[6078]209
[6103]210    @grok.action('Cancel', validator=NullValidator)
[6069]211    def cancel(self, **data):
[6103]212        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
[6078]213
[5845]214class ApplicantsRootBreadcrumb(Breadcrumb):
215    """A breadcrumb for applicantsroot.
216    """
217    grok.context(IApplicantsRoot)
[6153]218    title = u'Application Section'
[6078]219
[5845]220class ApplicantsContainerBreadcrumb(Breadcrumb):
221    """A breadcrumb for applicantscontainers.
222    """
223    grok.context(IApplicantsContainer)
[6319]224
[6153]225class ApplicantBreadcrumb(Breadcrumb):
226    """A breadcrumb for applicants.
227    """
228    grok.context(IApplicant)
[6319]229
[6153]230    @property
231    def title(self):
232        """Get a title for a context.
233        """
234        return self.context.access_code
[5828]235
236class ApplicantsTab(PrimaryNavTab):
[6153]237    """Applicants tab in primary navigation.
[5828]238    """
[6078]239
[5828]240    grok.context(IWAeUPObject)
241    grok.order(3)
[6336]242    grok.require('waeup.Public')
[5828]243    grok.template('primarynavtab')
244
[5843]245    pnav = 3
[5828]246    tab_title = u'Applicants'
247
248    @property
249    def link_target(self):
250        return self.view.application_url('applicants')
251
[6029]252class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]253    """The standard view for regular applicant containers.
254    """
255    grok.context(IApplicantsContainer)
256    grok.name('index')
[6153]257    grok.require('waeup.Public')
[6029]258    grok.template('applicantscontainerpage')
[5850]259    pnav = 3
[6053]260
[6105]261    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
[6054]262    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
263    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
[6084]264    form_fields['description'].custom_widget = ReSTDisplayWidget
[6053]265
[5837]266    @property
267    def title(self):
[6087]268        return "Applicants Container: %s" % self.context.title
[5837]269
270    @property
271    def label(self):
[6087]272        return self.context.title
[5830]273
[6107]274class ApplicantsContainerManageActionButton(ManageActionButton):
[6336]275    grok.order(1)
[5832]276    grok.context(IApplicantsContainer)
277    grok.view(ApplicantsContainerPage)
[6198]278    grok.require('waeup.manageApplications')
[6070]279    text = 'Manage applicants container'
[5832]280
[6336]281class LoginApplicantActionButton(ManageActionButton):
282    grok.order(2)
283    grok.context(IApplicantsContainer)
284    grok.view(ApplicantsContainerPage)
285    grok.require('waeup.Anonymous')
286    icon = 'login.png'
287    text = 'Login for applicants'
288    target = 'login'
[5832]289
[6107]290class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
[5837]291    grok.context(IApplicantsContainer)
[5850]292    grok.name('manage')
[6107]293    grok.template('applicantscontainermanagepage')
[6105]294    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
295    taboneactions = ['Save','Cancel']
296    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
[6184]297    tabthreeactions1 = ['Remove selected local roles']
298    tabthreeactions2 = ['Add local role']
[5844]299    # Use friendlier date widget...
[6054]300    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
301    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6198]302    grok.require('waeup.manageApplications')
[5850]303
304    @property
305    def title(self):
[6087]306        return "Applicants Container: %s" % self.context.title
[6078]307
[5850]308    @property
309    def label(self):
[6087]310        return 'Manage applicants container'
[5850]311
[5845]312    pnav = 3
[5837]313
314    def update(self):
[5850]315        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]316        tabs.need()
[6015]317        datatable.need()  # Enable jQurey datatables for contents listing
[6107]318        return super(ApplicantsContainerManageFormPage, self).update()
[5837]319
[6184]320    def getLocalRoles(self):
321        roles = ILocalRolesAssignable(self.context)
322        return roles()
323
324    def getUsers(self):
325        """Get a list of all users.
326        """
327        for key, val in grok.getSite()['users'].items():
328            url = self.url(val)
329            yield(dict(url=url, name=key, val=val))
330
331    def getUsersWithLocalRoles(self):
332        return get_users_with_local_roles(self.context)
333
[5850]334    @grok.action('Save')
[5837]335    def apply(self, **data):
336        self.applyData(self.context, **data)
337        self.flash('Data saved.')
338        return
[6078]339
[6105]340    # ToDo: Show warning message before deletion
341    @grok.action('Remove selected')
342    def delApplicant(self, **data):
[6189]343        form = self.request.form
344        if form.has_key('val_id'):
345            child_id = form['val_id']
346        else:
347            self.flash('No applicant selected!')
348            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
349            return
350        if not isinstance(child_id, list):
351            child_id = [child_id]
352        deleted = []
353        for id in child_id:
354            try:
355                del self.context[id]
356                deleted.append(id)
357            except:
358                self.flash('Could not delete %s: %s: %s' % (
359                        id, sys.exc_info()[0], sys.exc_info()[1]))
360        if len(deleted):
361            self.flash('Successfully removed: %s' % ', '.join(deleted))
362        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
363        return
[6105]364
365    @grok.action('Add applicant', validator=NullValidator)
366    def addApplicant(self, **data):
[6327]367        self.redirect(self.url(self.context, 'addapplicant'))
368        return
[6105]369
370    @grok.action('Cancel', validator=NullValidator)
[5837]371    def cancel(self, **data):
372        self.redirect(self.url(self.context))
373        return
[5886]374
[6184]375    @grok.action('Add local role', validator=NullValidator)
376    def addLocalRole(self, **data):
377        return add_local_role(self,3, **data)
[6105]378
[6184]379    @grok.action('Remove selected local roles')
380    def delLocalRoles(self, **data):
381        return del_local_roles(self,3,**data)
382
[5886]383class LoginApplicant(WAeUPPage):
384    grok.context(IApplicantsContainer)
385    grok.name('login')
[6153]386    grok.require('waeup.Public')
[5886]387
[6110]388    @property
389    def title(self):
390        return u"Applicant Login: %s" % self.context.title
[6078]391
[5886]392    @property
393    def label(self):
[6110]394        return u'Login for applicants only'
[5886]395
396    pnav = 3
[6319]397
[6110]398    @property
399    def ac_prefix(self):
400        return self.context.ac_prefix
[6078]401
[5896]402    def update(self, SUBMIT=None):
403        self.ac_series = self.request.form.get('form.ac_series', None)
404        self.ac_number = self.request.form.get('form.ac_number', None)
[5886]405        if SUBMIT is None:
406            return
[5894]407        if self.request.principal.id == 'zope.anybody':
[6105]408            self.flash('Entered credentials are invalid.')
[5886]409            return
[5894]410        if not IApplicantPrincipal.providedBy(self.request.principal):
411            # Don't care if user is already authenticated as non-applicant
412            return
[5905]413        pin = self.request.principal.access_code
414        if pin not in self.context.keys():
415            # Create applicant record
416            applicant = Applicant()
417            applicant.access_code = pin
418            self.context[pin] = applicant
[5937]419        # Assign current principal the owner role on created applicant
420        # record
[6184]421        role_manager = IPrincipalRoleManager(self.context[pin])
[5937]422        role_manager.assignRoleToPrincipal(
[6043]423            'waeup.local.ApplicationOwner', self.request.principal.id)
[6325]424        # Assign current principal the PortalUser role
425        role_manager = IPrincipalRoleManager(grok.getSite()['faculties'])
426        role_manager.assignRoleToPrincipal(
427            'waeup.PortalUser', self.request.principal.id)
[6349]428        # XXX: disable for now. Pins will get a different workflow.
429        #state = IWorkflowState(self.context[pin]).getState()
430        #if state == INITIALIZED:
431        #    IWorkflowInfo(self.context[pin]).fireTransition('start')
[5937]432        self.redirect(self.url(self.context[pin], 'edit'))
[5886]433        return
[6319]434
[6327]435class ApplicantAddFormPage(WAeUPAddFormPage):
436    """Add-form to add certificate to a department.
437    """
438    grok.context(IApplicantsContainer)
439    grok.require('waeup.manageApplications')
440    grok.name('addapplicant')
441    grok.template('applicantaddpage')
442    title = 'Applicants'
443    label = 'Add applicant'
444    pnav = 3
445
446    @property
447    def title(self):
448        return "Applicants Container: %s" % self.context.title
449
450    @property
451    def ac_prefix(self):
452        return self.context.ac_prefix
453
454    @grok.action('Create application record')
455    def addApplicant(self, **data):
456        ac_series = self.request.form.get('form.ac_series', None)
457        ac_number = self.request.form.get('form.ac_number', None)
458        pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number)
459        if pin not in self.context.keys():
460            # Create applicant record
461            applicant = Applicant()
462            applicant.access_code = pin
463            self.context[pin] = applicant
464        self.redirect(self.url(self.context[pin], 'edit'))
465        return
466
[6153]467class AccessCodeLink(LeftSidebarLink):
468    grok.order(1)
469    grok.require('waeup.Public')
[5886]470
[6153]471    def render(self):
472        if not IApplicantPrincipal.providedBy(self.request.principal):
473            return ''
474        access_code = getattr(self.request.principal,'access_code',None)
475        if access_code:
476            applicant_object = get_applicant_data(access_code)
477            url = absoluteURL(applicant_object, self.request)
[6198]478            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
[6153]479                url,access_code)
480        return ''
481
[5273]482class DisplayApplicant(WAeUPDisplayFormPage):
483    grok.context(IApplicant)
484    grok.name('index')
[6198]485    grok.require('waeup.handleApplication')
[6320]486    form_fields = grok.AutoFields(IApplicant).omit(
487        'locked').omit('course_admitted')
[6196]488    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]489    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]490    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]491    label = 'Applicant'
[6254]492    grok.template('form_display')
[5843]493    pnav = 3
[5273]494
[6196]495    @property
496    def title(self):
497        return '%s' % self.context.access_code
498
499    @property
500    def label(self):
501        container_title = self.context.__parent__.title
502        return '%s Application Record' % container_title
503
[6254]504    def getCourseAdmitted(self):
[6355]505        """Return link, title and code in html format to the certificate
506           admitted.
[6351]507        """
[6254]508        course_admitted = self.context.course_admitted
509        if ICertificate.providedBy(course_admitted):
510            url = self.url(course_admitted)
511            title = course_admitted.title
512            code = course_admitted.code
513            return '<a href="%s">%s (%s)</a>' %(url,title,code)
514        return 'not yet admitted'
515
[6198]516class ApplicantsManageActionButton(ManageActionButton):
517    grok.context(IApplicant)
518    grok.view(DisplayApplicant)
519    grok.require('waeup.manageApplications')
520    text = 'Edit application record'
521    target = 'edit_full'
522
[6196]523class EditApplicantFull(WAeUPEditFormPage):
524    """A full edit view for applicant data.
525    """
526    grok.context(IApplicant)
527    grok.name('edit_full')
[6198]528    grok.require('waeup.manageApplications')
[6196]529    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
530    form_fields['passport'].custom_widget = EncodingImageFileWidget
531    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
532    grok.template('form_edit')
[6322]533    manage_applications = True
[6196]534    pnav = 3
535
536    def update(self):
537        datepicker.need() # Enable jQuery datepicker in date fields.
538        super(EditApplicantFull, self).update()
[6353]539        self.wf_info = IWorkflowInfo(self.context)
[6196]540        return
541
542    @property
543    def title(self):
544        return self.context.access_code
545
546    @property
547    def label(self):
548        container_title = self.context.__parent__.title
549        return '%s Application Form' % container_title
550
[6303]551    def getTransitions(self):
[6351]552        """Return a list of dicts of allowed transition ids and titles.
[6353]553
554        Each list entry provides keys ``name`` and ``title`` for
555        internal name and (human readable) title of a single
556        transition.
[6349]557        """
[6353]558        allowed_transitions = self.wf_info.getManualTransitions()
[6355]559        return [dict(name='', title='No transition')] +[
560            dict(name=x, title=y) for x, y in allowed_transitions]
[6303]561
[6196]562    @grok.action('Save')
563    def save(self, **data):
564        self.applyData(self.context, **data)
565        self.context._p_changed = True
[6303]566        form = self.request.form
567        if form.has_key('transition') and form['transition']:
[6305]568            transition_id = form['transition']
[6353]569            self.wf_info.fireTransition(transition_id)
[6196]570        self.flash('Form has been saved.')
[6348]571        self.context.getApplicantsRootLogger().info('Saved')
[6196]572        return
573
574class EditApplicantStudent(EditApplicantFull):
[5982]575    """An applicant-centered edit view for applicant data.
576    """
[6196]577    grok.context(IApplicantEdit)
[5273]578    grok.name('edit')
[6198]579    grok.require('waeup.handleApplication')
[6196]580    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
[5686]581    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]582    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6196]583    grok.template('form_edit')
[6322]584    manage_applications = False
[5484]585
[6322]586
[5941]587    def emitLockMessage(self):
[6105]588        self.flash('The requested form is locked (read-only).')
[5941]589        self.redirect(self.url(self.context))
590        return
[6078]591
[5686]592    def update(self):
[5941]593        if self.context.locked:
[6198]594            self.redirect(self.url(self.context))
[5941]595            return
[6040]596        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]597        super(EditApplicantStudent, self).update()
[5686]598        return
[5952]599
[6196]600    def dataNotComplete(self):
[6322]601        if not self.request.form.get('confirm_passport', False):
[6196]602            return 'Passport confirmation box not ticked.'
603        if len(self.errors) > 0:
604            return 'Form has errors.'
605        return False
[5952]606
[5273]607    @grok.action('Save')
608    def save(self, **data):
[5941]609        if self.context.locked:
610            self.emitLockMessage()
611            return
[5273]612        self.applyData(self.context, **data)
613        self.context._p_changed = True
[6196]614        self.flash('Form has been saved.')
[5273]615        return
616
[5484]617    @grok.action('Final Submit')
618    def finalsubmit(self, **data):
[5941]619        if self.context.locked:
620            self.emitLockMessage()
621            return
[5273]622        self.applyData(self.context, **data)
[5484]623        self.context._p_changed = True
[6196]624        if self.dataNotComplete():
625            self.flash(self.dataNotComplete())
[5941]626            return
[6303]627        state = IWorkflowState(self.context).getState()
[6322]628        # This shouldn't happen, but the application officer
629        # might have forgotten to lock the form after changing the state
[6303]630        if state != STARTED:
[6322]631            self.flash('This form cannot be submitted. Wrong state!')
[6303]632            return
633        IWorkflowInfo(self.context).fireTransition('submit')
[5941]634        self.context.locked = True
[6196]635        self.flash('Form has been submitted.')
636        self.redirect(self.url(self.context))
[5273]637        return
[5941]638
Note: See TracBrowser for help on using the repository browser.