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

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

Now exports passport picture into pdf. libjpeg62-dev must be installed!

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