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

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

Disable reg_number and matric_number checks in browser module. I'm not
completely sure whether we should use the new way of validating these
fields over interfaces, but it looks like it could work.

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