source: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py @ 6771

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

Turn list of lists into single list with Uli's technique.

Do not log changes made by students. During clearance this would be too much.

  • Property svn:keywords set to Id
File size: 24.3 KB
Line 
1## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
2## This program is free software; you can redistribute it and/or modify
3## it under the terms of the GNU General Public License as published by
4## the Free Software Foundation; either version 2 of the License, or
5## (at your option) any later version.
6##
7## This program is distributed in the hope that it will be useful,
8## but WITHOUT ANY WARRANTY; without even the implied warranty of
9## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10## GNU General Public License for more details.
11##
12## You should have received a copy of the GNU General Public License
13## along with this program; if not, write to the Free Software
14## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15##
16"""UI components for students and related components.
17"""
18import grok
19
20from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
21from zope.component import createObject
22from waeup.sirp.accesscodes import invalidate_accesscode, get_access_code
23from waeup.sirp.accesscodes.workflow import USED
24from waeup.sirp.browser import (
25    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
26from waeup.sirp.browser.breadcrumbs import Breadcrumb
27from waeup.sirp.browser.resources import datepicker, datatable
28from waeup.sirp.browser.viewlets import (
29    ManageActionButton, PrimaryNavTab, AddActionButton)
30from waeup.sirp.interfaces import IWAeUPObject, IUserAccount
31from waeup.sirp.widgets.datewidget import (
32    FriendlyDateWidget, FriendlyDateDisplayWidget)
33from waeup.sirp.students.interfaces import (
34    IStudentsContainer, IStudent, IStudentClearance, IStudentPasswordSetting,
35    IStudentPersonal, IStudentBase, IStudentStudyCourse, IStudentPayments,
36    IStudentAccommodation, IStudentClearanceEdit,
37    )
38from waeup.sirp.students.catalog import search
39from waeup.sirp.students.workflow import CLEARANCE
40
41# Save function used for save methods in manager pages
42def msave(view, **data):
43    form = view.request.form
44    ob_class = view.__implemented__.__name__.replace('waeup.sirp.','')
45    changed_fields = view.applyData(view.context, **data)
46    # Turn list of lists into single list
47    if changed_fields:
48        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
49    fields_string = ' + '.join(changed_fields)
50    view.context._p_changed = True
51    view.flash('Form has been saved.')
52    if fields_string:
53        try:
54            view.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
55        except AttributeError:
56            view.context.__parent__.loggerInfo(ob_class, 'saved: % s' % fields_string)
57    return
58
59class StudentsTab(PrimaryNavTab):
60    """Students tab in primary navigation.
61    """
62
63    grok.context(IWAeUPObject)
64    grok.order(3)
65    grok.require('waeup.viewStudent')
66    grok.template('primarynavtab')
67
68    pnav = 4
69    tab_title = u'Students'
70
71    @property
72    def link_target(self):
73        return self.view.application_url('students')
74
75class StudentsBreadcrumb(Breadcrumb):
76    """A breadcrumb for the students container.
77    """
78    grok.context(IStudentsContainer)
79    title = u'Students'
80
81class SudyCourseBreadcrumb(Breadcrumb):
82    """A breadcrumb for the student study course.
83    """
84    grok.context(IStudentStudyCourse)
85    title = u'Study Course'
86
87class PaymentsBreadcrumb(Breadcrumb):
88    """A breadcrumb for the student payments folder.
89    """
90    grok.context(IStudentPayments)
91    title = u'Payments'
92
93class AccommodationBreadcrumb(Breadcrumb):
94    """A breadcrumb for the student accommodation folder.
95    """
96    grok.context(IStudentAccommodation)
97    title = u'Accommodation'
98
99class StudentsContainerPage(WAeUPPage):
100    """The standard view for student containers.
101    """
102    grok.context(IStudentsContainer)
103    grok.name('index')
104    grok.require('waeup.viewStudent')
105    grok.template('containerpage')
106    label = 'Student Section'
107    title = 'Students'
108    pnav = 4
109
110    def update(self, *args, **kw):
111        datatable.need()
112        form = self.request.form
113        self.hitlist = []
114        if 'searchterm' in form and form['searchterm']:
115            self.searchterm = form['searchterm']
116            self.searchtype = form['searchtype']
117        elif 'old_searchterm' in form:
118            self.searchterm = form['old_searchterm']
119            self.searchtype = form['old_searchtype']
120        else:
121            if 'search' in form:
122                self.flash('Empty search string.')
123            return
124        self.hitlist = search(query=self.searchterm,
125            searchtype=self.searchtype, view=self)
126        if not self.hitlist:
127            self.flash('No student found.')
128        return
129
130class SetPassword(WAeUPPage):
131    grok.context(IWAeUPObject)
132    grok.name('setpassword')
133    grok.require('waeup.Public')
134    title = ''
135    label = 'Set password for first-time login'
136    ac_prefix = 'PWD'
137    pnav = 0
138
139    def update(self, SUBMIT=None):
140        self.reg_number = self.request.form.get('reg_number', None)
141        self.ac_series = self.request.form.get('ac_series', None)
142        self.ac_number = self.request.form.get('ac_number', None)
143
144        if SUBMIT is None:
145            return
146        hitlist = search(query=self.reg_number,
147            searchtype='reg_number', view=self)
148        if not hitlist:
149            self.flash('No student found.')
150            return
151        if len(hitlist) != 1:   # Cannot happen but anyway
152            self.flash('More than one student found.')
153            return
154        student = hitlist[0].context
155        self.student_id = student.student_id
156        student_pw = student.password
157        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
158        code = get_access_code(pin)
159        if not code:
160            self.flash('Access code is invalid.')
161            return
162        if student_pw and pin == student.adm_code:
163            self.flash('Password has already been set. Your Student Id is %s'
164                % self.student_id)
165            return
166        elif student_pw:
167            self.flash('Password has already been set.')
168            return
169        # Mark pin as used (this also fires a pin related transition)
170        # and set student password
171        if code.state == USED:
172            self.flash('Access code has already been used.')
173            return
174        else:
175            comment = u"AC invalidated for %s" % self.student_id
176            # Here we know that the ac is in state initialized so we do not
177            # expect an exception
178            #import pdb; pdb.set_trace()
179            invalidate_accesscode(pin,comment)
180            IUserAccount(student).setPassword(self.ac_number)
181            student.adm_code = pin
182        self.flash('Password has been set. Your Student Id is %s'
183            % self.student_id)
184        return
185
186class StudentsContainerManageActionButton(ManageActionButton):
187    grok.order(1)
188    grok.context(IStudentsContainer)
189    grok.view(StudentsContainerPage)
190    grok.require('waeup.manageStudents')
191    text = 'Manage student section'
192
193
194class StudentsContainerManagePage(WAeUPPage):
195    """The manage page for student containers.
196    """
197    grok.context(IStudentsContainer)
198    grok.name('manage')
199    grok.require('waeup.manageStudents')
200    grok.template('containermanagepage')
201    pnav = 4
202    title = 'Manage student section'
203
204    @property
205    def label(self):
206        return self.title
207
208    def update(self, *args, **kw):
209        datatable.need()
210        form = self.request.form
211        self.hitlist = []
212        if 'searchterm' in form and form['searchterm']:
213            self.searchterm = form['searchterm']
214            self.searchtype = form['searchtype']
215        elif 'old_searchterm' in form:
216            self.searchterm = form['old_searchterm']
217            self.searchtype = form['old_searchtype']
218        else:
219            if 'search' in form:
220                self.flash('Empty search string.')
221            return
222        if not 'entries' in form:
223            self.hitlist = search(query=self.searchterm,
224                searchtype=self.searchtype, view=self)
225            if not self.hitlist:
226                self.flash('No student found.')
227            return
228        entries = form['entries']
229        if isinstance(entries, basestring):
230            entries = [entries]
231        deleted = []
232        for entry in entries:
233            if 'remove' in form:
234                del self.context[entry]
235                deleted.append(entry)
236        self.hitlist = search(query=self.searchterm,
237            searchtype=self.searchtype, view=self)
238        if len(deleted):
239            self.flash('Successfully removed: %s' % ', '.join(deleted))
240        return
241
242class StudentsContainerAddActionButton(AddActionButton):
243    grok.order(1)
244    grok.context(IStudentsContainer)
245    grok.view(StudentsContainerManagePage)
246    grok.require('waeup.manageStudents')
247    text = 'Add student'
248    target = 'addstudent'
249
250class StudentAddFormPage(WAeUPAddFormPage):
251    """Add-form to add a student.
252    """
253    grok.context(IStudentsContainer)
254    grok.require('waeup.manageStudents')
255    grok.name('addstudent')
256    grok.template('studentaddpage')
257    form_fields = grok.AutoFields(IStudent)
258    title = 'Students'
259    label = 'Add student'
260    pnav = 4
261
262    @grok.action('Create student record')
263    def addStudent(self, **data):
264        student = createObject(u'waeup.Student')
265        self.applyData(student, **data)
266        self.context.addStudent(student)
267        self.flash('Student record created.')
268        self.redirect(self.url(self.context[student.student_id], 'index'))
269        return
270
271class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
272    """ Page to display student base data
273    """
274    grok.context(IStudent)
275    grok.name('index')
276    grok.require('waeup.viewStudent')
277    grok.template('basepage')
278    form_fields = grok.AutoFields(IStudentBase).omit('password')
279    pnav = 4
280    title = 'Base Data'
281
282    @property
283    def label(self):
284        return '%s: Base Data' % self.context.name
285
286    @property
287    def hasPassword(self):
288        if self.context.password:
289            return 'set'
290        return 'unset'
291
292class StudentBaseManageActionButton(ManageActionButton):
293    grok.order(1)
294    grok.context(IStudent)
295    grok.view(StudentBaseDisplayFormPage)
296    grok.require('waeup.manageStudents')
297    text = 'Manage'
298    target = 'edit_base'
299
300class StudentBaseManageFormPage(WAeUPEditFormPage):
301    """ View to edit student base data
302    """
303    grok.context(IStudent)
304    grok.name('edit_base')
305    grok.require('waeup.manageStudents')
306    form_fields = grok.AutoFields(IStudentBase).omit('student_id')
307    grok.template('basemanagepage')
308    label = 'Manage base data'
309    title = 'Base Data'
310    pnav = 4
311
312    def update(self):
313        datepicker.need() # Enable jQuery datepicker in date fields.
314        super(StudentBaseManageFormPage, self).update()
315        self.wf_info = IWorkflowInfo(self.context)
316        return
317
318    def getTransitions(self):
319        """Return a list of dicts of allowed transition ids and titles.
320
321        Each list entry provides keys ``name`` and ``title`` for
322        internal name and (human readable) title of a single
323        transition.
324        """
325        allowed_transitions = self.wf_info.getManualTransitions()
326        return [dict(name='', title='No transition')] +[
327            dict(name=x, title=y) for x, y in allowed_transitions]
328
329    @grok.action('Save')
330    def save(self, **data):
331        form = self.request.form
332        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
333        if form.has_key('password') and form['password']:
334            if form['password'] != form['control_password']:
335                self.flash('Passwords do not match.')
336                return
337            IUserAccount(self.context).setPassword(form['password'])
338            self.context.loggerInfo(ob_class, 'password changed')
339        # Turn list of lists into single list
340        changed_fields = self.applyData(self.context, **data)
341        if changed_fields:
342            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
343            changed_fields = [x for x in changed_fields
344                              if not x.startswith('password')]
345        fields_string = ' + '.join(changed_fields)
346        self.context._p_changed = True
347        if form.has_key('transition') and form['transition']:
348            transition_id = form['transition']
349            self.wf_info.fireTransition(transition_id)
350        self.flash('Form has been saved.')
351        if fields_string:
352            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
353        return
354
355class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
356    """ Page to display student clearance data
357    """
358    grok.context(IStudent)
359    grok.name('view_clearance')
360    grok.require('waeup.viewStudent')
361    form_fields = grok.AutoFields(IStudentClearance).omit('clearance_locked')
362    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
363    title = 'Clearance Data'
364    pnav = 4
365
366    @property
367    def label(self):
368        return '%s: Clearance Data' % self.context.name
369
370class StudentClearanceManageActionButton(ManageActionButton):
371    grok.order(1)
372    grok.context(IStudent)
373    grok.view(StudentClearanceDisplayFormPage)
374    grok.require('waeup.manageStudents')
375    text = 'Manage'
376    target = 'edit_clearance'
377
378class StudentClearanceManageFormPage(WAeUPEditFormPage):
379    """ Page to edit student clearance data
380    """
381    grok.context(IStudent)
382    grok.name('edit_clearance')
383    grok.require('waeup.manageStudents')
384    form_fields = grok.AutoFields(IStudentClearance)
385    label = 'Manage clearance data'
386    title = 'Clearance Data'
387    pnav = 4
388
389    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
390
391    def update(self):
392        datepicker.need() # Enable jQuery datepicker in date fields.
393        return super(StudentClearanceManageFormPage, self).update()
394
395    @grok.action('Save')
396    def save(self, **data):
397        msave(self, **data)
398        return
399
400class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
401    """ Page to display student personal data
402    """
403    grok.context(IStudent)
404    grok.name('view_personal')
405    grok.require('waeup.viewStudent')
406    form_fields = grok.AutoFields(IStudentPersonal)
407    title = 'Personal Data'
408    pnav = 4
409
410    @property
411    def label(self):
412        return '%s: Personal Data' % self.context.name
413
414class StudentPersonalManageActionButton(ManageActionButton):
415    grok.order(1)
416    grok.context(IStudent)
417    grok.view(StudentPersonalDisplayFormPage)
418    grok.require('waeup.manageStudents')
419    text = 'Manage'
420    target = 'edit_personal'
421
422class StudentPersonalManageFormPage(WAeUPEditFormPage):
423    """ Page to edit student clearance data
424    """
425    grok.context(IStudent)
426    grok.name('edit_personal')
427    grok.require('waeup.viewStudent')
428    form_fields = grok.AutoFields(IStudentPersonal)
429    label = 'Manage personal data'
430    title = 'Personal Data'
431    pnav = 4
432
433    @grok.action('Save')
434    def save(self, **data):
435        msave(self, **data)
436        return
437
438class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
439    """ Page to display the student study course data
440    """
441    grok.context(IStudentStudyCourse)
442    grok.name('index')
443    grok.require('waeup.viewStudent')
444    form_fields = grok.AutoFields(IStudentStudyCourse)
445    #grok.template('studycoursepage')
446    title = 'Study Course'
447    pnav = 4
448
449    @property
450    def label(self):
451        return '%s: Study Course' % self.context.__parent__.name
452
453class StudyCourseManageActionButton(ManageActionButton):
454    grok.order(1)
455    grok.context(IStudentStudyCourse)
456    grok.view(StudyCourseDisplayFormPage)
457    grok.require('waeup.manageStudents')
458    text = 'Manage'
459    target = 'edit'
460
461class StudyCourseManageFormPage(WAeUPEditFormPage):
462    """ Page to edit the student study course data
463    """
464    grok.context(IStudentStudyCourse)
465    grok.name('edit')
466    grok.require('waeup.manageStudents')
467    form_fields = grok.AutoFields(IStudentStudyCourse)
468    title = 'Study Course'
469    label = 'Manage study course'
470    pnav = 4
471
472    @grok.action('Save')
473    def save(self, **data):
474        msave(self, **data)
475        return
476
477class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
478    """ Page to display the student payments
479    """
480    grok.context(IStudentPayments)
481    grok.name('index')
482    grok.require('waeup.viewStudent')
483    form_fields = grok.AutoFields(IStudentPayments)
484    #grok.template('paymentspage')
485    title = 'Payments'
486    pnav = 4
487
488    @property
489    def label(self):
490        return '%s: Payments' % self.context.__parent__.name
491
492class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
493    """ Page to display the student accommodation data
494    """
495    grok.context(IStudentAccommodation)
496    grok.name('index')
497    grok.require('waeup.viewStudent')
498    form_fields = grok.AutoFields(IStudentAccommodation)
499    #grok.template('accommodationpage')
500    title = 'Accommodation'
501    pnav = 4
502
503    @property
504    def label(self):
505        return '%s: Accommodation Data' % self.context.__parent__.name
506
507class StudentHistoryPage(WAeUPPage):
508    """ Page to display student clearance data
509    """
510    grok.context(IStudent)
511    grok.name('history')
512    grok.require('waeup.viewStudent')
513    grok.template('studenthistory')
514    title = 'History'
515    pnav = 4
516
517    @property
518    def label(self):
519        return '%s: History' % self.context.name
520
521# Pages for students only
522
523class StudentBaseEditActionButton(ManageActionButton):
524    grok.order(1)
525    grok.context(IStudent)
526    grok.view(StudentBaseDisplayFormPage)
527    grok.require('waeup.handleStudent')
528    text = 'Change password'
529    target = 'bedit'
530
531class StudentPasswordSetting(grok.Adapter):
532    """Adapt IStudent to data needed for password settings.
533
534    We provide password getters/setters for the attached context (an
535    IStudent object) that cooperate seamless with the usual
536    formlib/form techniques.
537    """
538    grok.context(IStudent)
539    grok.provides(IStudentPasswordSetting)
540
541    def __init__(self, context):
542        self.name = context.name
543        self.password_repeat = context.password
544        self.context = context
545        return
546
547    def getPassword(self):
548        return self.context.password
549
550    def setPassword(self, password):
551        IUserAccount(self.context).setPassword(password)
552        return
553
554    password = property(getPassword, setPassword)
555
556class StudentBaseEditFormPage(WAeUPEditFormPage):
557    """ View to edit student base data by student
558    """
559    grok.context(IStudent)
560    grok.name('bedit')
561    grok.require('waeup.handleStudent')
562    #form_fields = grok.AutoFields(IStudentBaseEdit).omit(
563    #    'student_id', 'reg_number', 'matric_number')
564    form_fields = grok.AutoFields(IStudentPasswordSetting)
565    grok.template('baseeditpage')
566    label = 'Change password'
567    title = 'Base Data'
568    pnav = 4
569
570    def update(self):
571        super(StudentBaseEditFormPage, self).update()
572        self.wf_info = IWorkflowInfo(self.context)
573        return
574
575    def onFailure(self, action, data, errors):
576        new_status = []
577        other_errors = False
578        for error in errors:
579            msg = getattr(error, 'message', '')
580            if isinstance(msg, basestring) and msg != '':
581                new_status.append(msg)
582            else:
583                other_errors = True
584        if other_errors:
585            if new_status:
586                new_status.append('see below for further errors')
587            else:
588                new_status.append('See below for details.')
589        if new_status:
590            self.status = u'There were errors: %s' % ', '.join(new_status)
591        return
592
593    @grok.action('Save', failure=onFailure)
594    def save(self, **data):
595        self.applyData(self.context, **data)
596        self.flash('Form has been saved.')
597        return
598
599class StudentClearanceStartActionButton(ManageActionButton):
600    grok.order(1)
601    grok.context(IStudent)
602    grok.view(StudentClearanceDisplayFormPage)
603    grok.require('waeup.handleStudent')
604    icon = 'actionicon_start.png'
605    text = 'Start clearance'
606    target = 'start_clearance'
607
608    @property
609    def target_url(self):
610        if self.context.state != 'admitted':
611            return ''
612        return self.view.url(self.view.context, self.target)
613
614class StartClearance(WAeUPPage):
615    grok.context(IStudent)
616    grok.name('start_clearance')
617    grok.require('waeup.handleStudent')
618    grok.template('enterpin')
619    title = 'Start clearance'
620    label = 'Start clearance'
621    ac_prefix = 'CLR'
622    notice = ''
623    pnav = 4
624    buttonname = 'Start clearance now'
625
626    def update(self, SUBMIT=None):
627        self.ac_series = self.request.form.get('ac_series', None)
628        self.ac_number = self.request.form.get('ac_number', None)
629
630        if SUBMIT is None:
631            return
632        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
633        code = get_access_code(pin)
634        if not code:
635            self.flash('Access code is invalid.')
636            return
637        # Mark pin as used (this also fires a pin related transition)
638        # and fire transition start_clearance
639        if code.state == USED:
640            self.flash('Access code has already been used.')
641            return
642        else:
643            comment = u"AC invalidated for %s" % self.context.student_id
644            # Here we know that the ac is in state initialized so we do not
645            # expect an exception
646            invalidate_accesscode(pin,comment)
647            self.context.clr_code = pin
648        IWorkflowInfo(self.context).fireTransition('start_clearance')
649        self.flash('Clearance process has been started.')
650        self.redirect(self.url(self.context,'cedit'))
651        return
652
653class StudentClearanceEditActionButton(ManageActionButton):
654    grok.order(1)
655    grok.context(IStudent)
656    grok.view(StudentClearanceDisplayFormPage)
657    grok.require('waeup.handleStudent')
658    text = 'Edit'
659    target = 'cedit'
660
661    @property
662    def target_url(self):
663        if self.context.clearance_locked:
664            return ''
665        return self.view.url(self.view.context, self.target)
666
667class StudentClearanceEditFormPage(StudentClearanceManageFormPage):
668    """ View to edit student clearance data by student
669    """
670    grok.context(IStudent)
671    grok.name('cedit')
672    grok.require('waeup.handleStudent')
673    form_fields = grok.AutoFields(
674        IStudentClearanceEdit).omit('clearance_locked')
675    label = 'Edit clearance data'
676    title = 'Clearance Data'
677    pnav = 4
678    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
679
680    def emitLockMessage(self):
681        self.flash('The requested form is locked (read-only).')
682        self.redirect(self.url(self.context))
683        return
684
685    def update(self):
686        if self.context.clearance_locked:
687            self.emitLockMessage()
688            return
689        datepicker.need()
690        return super(StudentClearanceEditFormPage, self).update()
691
692    @grok.action('Save')
693    def save(self, **data):
694        self.applyData(self.context, **data)
695        self.flash('Clearance form has been saved.')
696        return
697
698    @grok.action('Save and request clearance')
699    def requestclearance(self, **data):
700        self.applyData(self.context, **data)
701        self.context._p_changed = True
702        #if self.dataNotComplete():
703        #    self.flash(self.dataNotComplete())
704        #    return
705        self.flash('Clearance form has been saved.')
706        self.redirect(self.url(self.context,'request_clearance'))
707        return
708
709class RequestClearance(WAeUPPage):
710    grok.context(IStudent)
711    grok.name('request_clearance')
712    grok.require('waeup.handleStudent')
713    grok.template('enterpin')
714    title = 'Request clearance'
715    label = 'Request clearance'
716    notice = 'Enter the CLR access code used for starting clearance.'
717    ac_prefix = 'CLR'
718    pnav = 4
719    buttonname = 'Request clearance now'
720
721    def update(self, SUBMIT=None):
722        self.ac_series = self.request.form.get('ac_series', None)
723        self.ac_number = self.request.form.get('ac_number', None)
724        if SUBMIT is None:
725            return
726        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
727        if self.context.clr_code != pin:
728            self.flash("This isn't your CLR access code.")
729            return
730        state = IWorkflowState(self.context).getState()
731        # This shouldn't happen, but the application officer
732        # might have forgotten to lock the form after changing the state
733        if state != CLEARANCE:
734            self.flash('This form cannot be submitted. Wrong state!')
735            return
736        IWorkflowInfo(self.context).fireTransition('request_clearance')
737        self.flash('Clearance has been requested.')
738        self.redirect(self.url(self.context))
739        return
Note: See TracBrowser for help on using the repository browser.