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

Last change on this file since 6758 was 6758, checked in by uli, 13 years ago

Use more simple form names, fix nesting errors, etc.

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