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

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

Copy all data from the course and its course referrer into course tickets.

A course ticket contains a copy of the original course and
course referrer data. If the courses and/or their referrers are removed, the
corresponding tickets remain unchanged. So we do not need any event
triggered actions on course ticket.

  • 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           
355            if hitlist and hitlist[0].student_id != self.context.student_id:
356                self.flash('Registration number exists.')
357                return
358        self.matric_number = form.get('form.matric_number', None)
359        if self.matric_number:
360            hitlist = search(query=self.matric_number,
361                searchtype='matric_number', view=self)
362            if hitlist and hitlist[0].student_id != self.context.student_id:
363                self.flash('Matriculation number exists.')
364                return
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.