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

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

Add student base data edit page including interfaces and page template. This page will be only used for changing the password. All other data will be readonly for students.

The browser tests fail because students are being logged out after having changed the password. This shouldn't be.

  • Property svn:keywords set to Id
File size: 16.1 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,
66    IStudentPersonal, IStudentBase, IStudentStudyCourse,
67    IStudentPayments, IStudentAccommodation, IStudentNavigation,
68    IStudentBaseEdit,
69    )
70from waeup.sirp.students.student import Student
71from waeup.sirp.students.catalog import search
72
73class StudentsTab(PrimaryNavTab):
74    """Students tab in primary navigation.
75    """
76
77    grok.context(IWAeUPObject)
78    grok.order(3)
79    grok.require('waeup.viewStudent')
80    grok.template('primarynavtab')
81
82    pnav = 4
83    tab_title = u'Students'
84
85    @property
86    def link_target(self):
87        return self.view.application_url('students')
88
89class StudentsBreadcrumb(Breadcrumb):
90    """A breadcrumb for the students container.
91    """
92    grok.context(IStudentsContainer)
93    title = u'Students'
94
95class SudyCourseBreadcrumb(Breadcrumb):
96    """A breadcrumb for the student study course.
97    """
98    grok.context(IStudentStudyCourse)
99    title = u'Study Course'
100
101class PaymentsBreadcrumb(Breadcrumb):
102    """A breadcrumb for the student payments folder.
103    """
104    grok.context(IStudentPayments)
105    title = u'Payments'
106
107class AccommodationBreadcrumb(Breadcrumb):
108    """A breadcrumb for the student accommodation folder.
109    """
110    grok.context(IStudentAccommodation)
111    title = u'Accommodation'
112
113class StudentsContainerPage(WAeUPPage):
114    """The standard view for student containers.
115    """
116    grok.context(IStudentsContainer)
117    grok.name('index')
118    grok.require('waeup.viewStudent')
119    grok.template('studentscontainerpage')
120    label = 'Student Section'
121    title = 'Students'
122    pnav = 4
123
124    def update(self, *args, **kw):
125        datatable.need()
126        form = self.request.form
127        self.hitlist = []
128        if 'searchterm' in form and form['searchterm']:
129            self.searchterm = form['searchterm']
130            self.searchtype = form['searchtype']
131        elif 'old_searchterm' in form:
132            self.searchterm = form['old_searchterm']
133            self.searchtype = form['old_searchtype']
134        else:
135            if 'search' in form:
136                self.flash('Empty search string.')
137            return
138        self.hitlist = search(query=self.searchterm,
139            searchtype=self.searchtype, view=self)
140        if not self.hitlist:
141            self.flash('No student found.')
142        return
143
144class StudentsContainerManageActionButton(ManageActionButton):
145    grok.order(1)
146    grok.context(IStudentsContainer)
147    grok.view(StudentsContainerPage)
148    grok.require('waeup.manageStudents')
149    text = 'Manage student section'
150
151
152class StudentsContainerManagePage(WAeUPPage):
153    """The manage page for student containers.
154    """
155    grok.context(IStudentsContainer)
156    grok.name('manage')
157    grok.require('waeup.manageStudents')
158    grok.template('studentscontainermanagepage')
159    pnav = 4
160    title = 'Manage student section'
161
162    @property
163    def label(self):
164        return self.title
165
166    def update(self, *args, **kw):
167        datatable.need()
168        form = self.request.form
169        self.hitlist = []
170        if 'searchterm' in form and form['searchterm']:
171            self.searchterm = form['searchterm']
172            self.searchtype = form['searchtype']
173        elif 'old_searchterm' in form:
174            self.searchterm = form['old_searchterm']
175            self.searchtype = form['old_searchtype']
176        else:
177            if 'search' in form:
178                self.flash('Empty search string.')
179            return
180        if not 'entries' in form:
181            self.hitlist = search(query=self.searchterm,
182                searchtype=self.searchtype, view=self)
183            if not self.hitlist:
184                self.flash('No student found.')
185            return
186        entries = form['entries']
187        if isinstance(entries, basestring):
188            entries = [entries]
189        deleted = []
190        for entry in entries:
191            if 'remove' in form:
192                del self.context[entry]
193                deleted.append(entry)
194        self.hitlist = search(query=self.searchterm,
195            searchtype=self.searchtype, view=self)
196        if len(deleted):
197            self.flash('Successfully removed: %s' % ', '.join(deleted))
198        return
199
200class StudentsContainerAddActionButton(AddActionButton):
201    grok.order(1)
202    grok.context(IStudentsContainer)
203    grok.view(StudentsContainerManagePage)
204    grok.require('waeup.manageStudents')
205    text = 'Add student'
206    target = 'addstudent'
207
208class StudentAddFormPage(WAeUPAddFormPage):
209    """Add-form to add a student.
210    """
211    grok.context(IStudentsContainer)
212    grok.require('waeup.manageStudents')
213    grok.name('addstudent')
214    grok.template('studentaddpage')
215    form_fields = grok.AutoFields(IStudent)
216    title = 'Students'
217    label = 'Add student'
218    pnav = 4
219
220    @grok.action('Create student record')
221    def addStudent(self, **data):
222        student = createObject(u'waeup.Student')
223        self.applyData(student, **data)
224        self.context.addStudent(student)
225        self.flash('Student record created.')
226        self.redirect(self.url(self.context[student.student_id], 'index'))
227        return
228
229class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
230    """ Page to display student base data
231    """
232    grok.context(IStudent)
233    grok.name('index')
234    grok.require('waeup.viewStudent')
235    grok.template('studentpage')
236    form_fields = grok.AutoFields(IStudentBase).omit('password')
237    pnav = 4
238    title = 'Base Data'
239
240    @property
241    def label(self):
242        return '%s: Base Data' % self.context.name
243
244class StudentBaseManageActionButton(ManageActionButton):
245    grok.order(1)
246    grok.context(IStudent)
247    grok.view(StudentBaseDisplayFormPage)
248    grok.require('waeup.manageStudents')
249    text = 'Edit'
250    target = 'edit_base'
251
252class StudentBaseManageFormPage(WAeUPEditFormPage):
253    """ View to edit student base data
254    """
255    grok.context(IStudent)
256    grok.name('edit_base')
257    grok.require('waeup.manageStudents')
258    form_fields = grok.AutoFields(IStudentBase).omit('student_id')
259    grok.template('studentbasemanagepage')
260    label = 'Edit base data'
261    title = 'Base Data'
262    pnav = 4
263
264    def update(self):
265        datepicker.need() # Enable jQuery datepicker in date fields.
266        super(StudentBaseManageFormPage, self).update()
267        self.wf_info = IWorkflowInfo(self.context)
268        return
269
270    def getTransitions(self):
271        """Return a list of dicts of allowed transition ids and titles.
272
273        Each list entry provides keys ``name`` and ``title`` for
274        internal name and (human readable) title of a single
275        transition.
276        """
277        allowed_transitions = self.wf_info.getManualTransitions()
278        return [dict(name='', title='No transition')] +[
279            dict(name=x, title=y) for x, y in allowed_transitions]
280
281    @grok.action('Save')
282    def save(self, **data):
283        changed_fields = self.applyData(self.context, **data)
284        changed_fields = changed_fields.values()
285        fields_string = '+'.join(' + '.join(str(i) for i in b) for b in changed_fields)
286        self.context._p_changed = True
287        form = self.request.form
288        if form.has_key('transition') and form['transition']:
289            transition_id = form['transition']
290            self.wf_info.fireTransition(transition_id)
291        self.flash('Form has been saved.')
292        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
293        if fields_string:
294            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
295        if 'password' in fields_string:
296            IUserAccount(self.context).setPassword(form['form.password'])
297        return
298
299class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
300    """ Page to display student clearance data
301    """
302    grok.context(IStudent)
303    grok.name('view_clearance')
304    grok.require('waeup.viewStudent')
305    form_fields = grok.AutoFields(IStudentClearance)
306    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
307    title = 'Clearance Data'
308    pnav = 4
309
310    @property
311    def label(self):
312        return '%s: Clearance Data' % self.context.name
313
314class StudentClearanceManageActionButton(ManageActionButton):
315    grok.order(1)
316    grok.context(IStudent)
317    grok.view(StudentClearanceDisplayFormPage)
318    grok.require('waeup.manageStudents')
319    text = 'Edit'
320    target = 'edit_clearance'
321
322class StudentClearanceManageFormPage(WAeUPEditFormPage):
323    """ Page to edit student clearance data
324    """
325    grok.context(IStudent)
326    grok.name('edit_clearance')
327    grok.require('waeup.manageStudents')
328    form_fields = grok.AutoFields(IStudentClearance)
329    label = 'Edit clearance data'
330    title = 'Clearance Data'
331    pnav = 4
332
333    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
334
335    def update(self):
336        datepicker.need() # Enable jQuery datepicker in date fields.
337        return super(StudentClearanceManageFormPage, self).update()
338
339class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
340    """ Page to display student personal data
341    """
342    grok.context(IStudent)
343    grok.name('view_personal')
344    grok.require('waeup.viewStudent')
345    form_fields = grok.AutoFields(IStudentPersonal)
346    title = 'Personal Data'
347    pnav = 4
348
349    @property
350    def label(self):
351        return '%s: Personal Data' % self.context.name
352
353class StudentPersonalManageActionButton(ManageActionButton):
354    grok.order(1)
355    grok.context(IStudent)
356    grok.view(StudentPersonalDisplayFormPage)
357    grok.require('waeup.manageStudents')
358    text = 'Edit'
359    target = 'edit_personal'
360
361class StudentPersonalManageFormPage(WAeUPEditFormPage):
362    """ Page to edit student clearance data
363    """
364    grok.context(IStudent)
365    grok.name('edit_personal')
366    grok.require('waeup.viewStudent')
367    form_fields = grok.AutoFields(IStudentPersonal)
368    label = 'Edit personal data'
369    title = 'Personal Data'
370    pnav = 4
371
372class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
373    """ Page to display the student study course data
374    """
375    grok.context(IStudentStudyCourse)
376    grok.name('index')
377    grok.require('waeup.viewStudent')
378    form_fields = grok.AutoFields(IStudentStudyCourse)
379    #grok.template('studycoursepage')
380    title = 'Study Course'
381    pnav = 4
382
383    @property
384    def label(self):
385        return '%s: Study Course' % self.context.__parent__.name
386
387class StudyCourseManageActionButton(ManageActionButton):
388    grok.order(1)
389    grok.context(IStudentStudyCourse)
390    grok.view(StudyCourseDisplayFormPage)
391    grok.require('waeup.manageStudents')
392    text = 'Edit'
393    target = 'edit'
394
395class StudyCourseManageFormPage(WAeUPEditFormPage):
396    """ Page to edit the student study course data
397    """
398    grok.context(IStudentStudyCourse)
399    grok.name('edit')
400    grok.require('waeup.manageStudents')
401    form_fields = grok.AutoFields(IStudentStudyCourse)
402    label = 'Edit clearance data'
403    title = 'Study Course'
404    label = 'Edit study course'
405    pnav = 4
406
407class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
408    """ Page to display the student payments
409    """
410    grok.context(IStudentPayments)
411    grok.name('index')
412    grok.require('waeup.viewStudent')
413    form_fields = grok.AutoFields(IStudentPayments)
414    #grok.template('paymentspage')
415    title = 'Payments'
416    pnav = 4
417
418    @property
419    def label(self):
420        return '%s: Payments' % self.context.__parent__.name
421
422class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
423    """ Page to display the student accommodation data
424    """
425    grok.context(IStudentAccommodation)
426    grok.name('index')
427    grok.require('waeup.viewStudent')
428    form_fields = grok.AutoFields(IStudentAccommodation)
429    #grok.template('accommodationpage')
430    title = 'Accommodation'
431    pnav = 4
432
433    @property
434    def label(self):
435        return '%s: Accommodation Data' % self.context.__parent__.name
436
437class StudentHistoryPage(WAeUPPage):
438    """ Page to display student clearance data
439    """
440    grok.context(IStudent)
441    grok.name('history')
442    grok.require('waeup.viewStudent')
443    grok.template('studenthistory')
444    title = 'History'
445    pnav = 4
446
447    @property
448    def label(self):
449        return '%s: History' % self.context.name
450
451# Pages for students only
452
453class StudentBaseEditActionButton(ManageActionButton):
454    grok.order(1)
455    grok.context(IStudent)
456    grok.view(StudentBaseDisplayFormPage)
457    grok.require('waeup.handleStudent')
458    text = 'Change password'
459    target = 'bedit'
460
461class StudentBaseEditFormPage(WAeUPEditFormPage):
462    """ View to edit student base data by student
463    """
464    grok.context(IStudent)
465    grok.name('bedit')
466    grok.require('waeup.handleStudent')
467    form_fields = grok.AutoFields(IStudentBaseEdit).omit('student_id')
468    grok.template('studentbaseeditpage')
469    label = 'Change password'
470    title = 'Base Data'
471    pnav = 4
472
473    def update(self):
474        datepicker.need() # Enable jQuery datepicker in date fields.
475        super(StudentBaseEditFormPage, self).update()
476        self.wf_info = IWorkflowInfo(self.context)
477        return
478
479    @grok.action('Save')
480    def save(self, **data):
481        changed_fields = self.applyData(self.context, **data)
482        changed_fields = changed_fields.values()
483        fields_string = '+'.join(' + '.join(str(i) for i in b) for b in changed_fields)
484        self.context._p_changed = True
485        self.flash('Form has been saved.')
486        form = self.request.form
487        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
488        if fields_string:
489            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
490        if 'password' in fields_string:
491            IUserAccount(self.context).setPassword(form['form.password'])
492        return
Note: See TracBrowser for help on using the repository browser.