source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py @ 6292

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

Render Admitted Course of Study seperately and provide URL to certificate.

File size: 18.8 KB
Line 
1##
2## browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet & Henrik Bettermann
5## $Id$
6##
7## Copyright (C) 2010 Uli Fouquet & Henrik Bettermann
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""UI components for basic applicants and related components.
23"""
24import sys
25import grok
26
27from zope.component import getUtility
28from zope.formlib.widget import CustomWidgetFactory
29from zope.interface import Invalid
30from zope.securitypolicy.interfaces import IPrincipalRoleManager
31from zope.traversing.browser import absoluteURL
32
33from waeup.sirp.browser import (
34    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage,
35    WAeUPDisplayFormPage, NullValidator)
36from waeup.sirp.browser.breadcrumbs import Breadcrumb
37from waeup.sirp.browser.layout import NullValidator
38from waeup.sirp.browser.resources import datepicker, tabs, datatable
39from waeup.sirp.browser.viewlets import (
40    ManageActionButton, PrimaryNavTab, LeftSidebarLink
41    )
42from waeup.sirp.image.browser.widget import (
43    ThumbnailWidget, EncodingImageFileWidget,
44    )
45from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
46from waeup.sirp.university.interfaces import ICertificate
47from waeup.sirp.widgets.datewidget import (
48    FriendlyDateWidget, FriendlyDateDisplayWidget)
49from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
50from waeup.sirp.widgets.objectwidget import (
51    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
52from waeup.sirp.widgets.multilistwidget import (
53    MultiListWidget, MultiListDisplayWidget)
54
55from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
56from waeup.sirp.applicants.interfaces import (
57    IApplicant, IApplicantPrincipal,IApplicantEdit,
58    IApplicantsRoot, IApplicantsContainer, IApplicantsContainerProvider,
59    IApplicantsContainerAdd, application_types_vocab
60    )
61from waeup.sirp.browser.pages import add_local_role, del_local_roles
62from waeup.sirp.permissions import get_users_with_local_roles, getRoles
63
64results_widget = CustomWidgetFactory(
65    WAeUPObjectWidget, ResultEntry)
66
67results_display_widget = CustomWidgetFactory(
68    WAeUPObjectDisplayWidget, ResultEntry)
69
70list_results_widget = CustomWidgetFactory(
71    MultiListWidget, subwidget=results_widget)
72
73list_results_display_widget = CustomWidgetFactory(
74    MultiListDisplayWidget, subwidget=results_display_widget)
75
76class ApplicantsRootPage(WAeUPPage):
77    grok.context(IApplicantsRoot)
78    grok.name('index')
79    grok.require('waeup.Public')
80    title = 'Applicants'
81    label = 'Application Section'
82    pnav = 3
83
84    def update(self):
85        super(ApplicantsRootPage, self).update()
86        datatable.need()
87        return
88
89class ManageApplicantsRootActionButton(ManageActionButton):
90    grok.context(IApplicantsRoot)
91    grok.view(ApplicantsRootPage)
92    grok.require('waeup.manageApplications')
93    text = 'Manage application section'
94
95class ApplicantsRootManageFormPage(WAeUPEditFormPage):
96    grok.context(IApplicantsRoot)
97    grok.name('manage')
98    grok.template('applicantsrootmanagepage')
99    title = 'Applicants'
100    label = 'Manage application section'
101    pnav = 3
102    grok.require('waeup.manageApplications')
103    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
104    tabtwoactions1 = ['Remove selected local roles']
105    tabtwoactions2 = ['Add local role']
106    subunits = 'Applicants Containers'
107
108    def update(self):
109        tabs.need()
110        datatable.need()
111        return super(ApplicantsRootManageFormPage, self).update()
112
113    def getLocalRoles(self):
114        roles = ILocalRolesAssignable(self.context)
115        return roles()
116
117    def getUsers(self):
118        """Get a list of all users.
119        """
120        for key, val in grok.getSite()['users'].items():
121            url = self.url(val)
122            yield(dict(url=url, name=key, val=val))
123
124    def getUsersWithLocalRoles(self):
125        return get_users_with_local_roles(self.context)
126
127    # ToDo: Show warning message before deletion
128    @grok.action('Remove selected')
129    def delApplicantsContainers(self, **data):
130        form = self.request.form
131        child_id = form['val_id']
132        if not isinstance(child_id, list):
133            child_id = [child_id]
134        deleted = []
135        for id in child_id:
136            try:
137                del self.context[id]
138                deleted.append(id)
139            except:
140                self.flash('Could not delete %s: %s: %s' % (
141                        id, sys.exc_info()[0], sys.exc_info()[1]))
142        if len(deleted):
143            self.flash('Successfully removed: %s' % ', '.join(deleted))
144        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
145        return
146
147    @grok.action('Add applicants container', validator=NullValidator)
148    def addApplicantsContainer(self, **data):
149        self.redirect(self.url(self.context, '@@add'))
150        return
151
152    @grok.action('Cancel', validator=NullValidator)
153    def cancel(self, **data):
154        self.redirect(self.url(self.context))
155        return
156
157    @grok.action('Add local role', validator=NullValidator)
158    def addLocalRole(self, **data):
159        return add_local_role(self,2, **data)
160
161    @grok.action('Remove selected local roles')
162    def delLocalRoles(self, **data):
163        return del_local_roles(self,2,**data)
164
165class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
166    grok.context(IApplicantsRoot)
167    grok.require('waeup.manageApplications')
168    grok.name('add')
169    grok.template('applicantscontaineraddpage')
170    title = 'Applicants'
171    label = 'Add applicants container'
172    pnav = 3
173
174    form_fields = grok.AutoFields(
175        IApplicantsContainerAdd).omit('code').omit('title')
176    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
177    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
178
179    def update(self):
180        datepicker.need() # Enable jQuery datepicker in date fields.
181        #from waeup.sirp.browser.resources import jqueryui
182        #jqueryui.need()
183        return super(ApplicantsContainerAddFormPage, self).update()
184
185    @grok.action('Add applicants container')
186    def addApplicantsContainer(self, **data):
187        year = data['year']
188        code = u'%s%s' % (data['prefix'], year)
189        prefix = application_types_vocab.getTerm(data['prefix'])
190        title = u'%s %s/%s' % (prefix.title, year, year + 1)
191        if code in self.context.keys():
192            self.flash(
193                'An applicants container for the same application '
194                'type and entrance year exists already in the database.')
195            return
196        # Add new applicants container...
197        provider = data['provider'][1]
198        container = provider.factory()
199        self.applyData(container, **data)
200        container.code = code
201        container.title = title
202        self.context[code] = container
203        self.flash('Added: "%s".' % code)
204        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
205        return
206
207    @grok.action('Cancel', validator=NullValidator)
208    def cancel(self, **data):
209        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
210
211class ApplicantsRootBreadcrumb(Breadcrumb):
212    """A breadcrumb for applicantsroot.
213    """
214    grok.context(IApplicantsRoot)
215    title = u'Application Section'
216
217class ApplicantsContainerBreadcrumb(Breadcrumb):
218    """A breadcrumb for applicantscontainers.
219    """
220    grok.context(IApplicantsContainer)
221   
222class ApplicantBreadcrumb(Breadcrumb):
223    """A breadcrumb for applicants.
224    """
225    grok.context(IApplicant)
226   
227    @property
228    def title(self):
229        """Get a title for a context.
230        """
231        return self.context.access_code
232
233class ApplicantsTab(PrimaryNavTab):
234    """Applicants tab in primary navigation.
235    """
236
237    grok.context(IWAeUPObject)
238    grok.order(3)
239    grok.require('waeup.manageApplications')
240    grok.template('primarynavtab')
241
242    pnav = 3
243    tab_title = u'Applicants'
244
245    @property
246    def link_target(self):
247        return self.view.application_url('applicants')
248
249class ApplicantsContainerPage(WAeUPDisplayFormPage):
250    """The standard view for regular applicant containers.
251    """
252    grok.context(IApplicantsContainer)
253    grok.name('index')
254    grok.require('waeup.Public')
255    grok.template('applicantscontainerpage')
256    pnav = 3
257
258    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
259    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
260    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
261    form_fields['description'].custom_widget = ReSTDisplayWidget
262
263    @property
264    def title(self):
265        return "Applicants Container: %s" % self.context.title
266
267    @property
268    def label(self):
269        return self.context.title
270
271class ApplicantsContainerManageActionButton(ManageActionButton):
272    grok.context(IApplicantsContainer)
273    grok.view(ApplicantsContainerPage)
274    grok.require('waeup.manageApplications')
275    text = 'Manage applicants container'
276
277
278class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
279    grok.context(IApplicantsContainer)
280    grok.name('manage')
281    grok.template('applicantscontainermanagepage')
282    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
283    taboneactions = ['Save','Cancel']
284    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
285    tabthreeactions1 = ['Remove selected local roles']
286    tabthreeactions2 = ['Add local role']
287    # Use friendlier date widget...
288    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
289    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
290    grok.require('waeup.manageApplications')
291
292    @property
293    def title(self):
294        return "Applicants Container: %s" % self.context.title
295
296    @property
297    def label(self):
298        return 'Manage applicants container'
299
300    pnav = 3
301
302    def update(self):
303        datepicker.need() # Enable jQuery datepicker in date fields.
304        tabs.need()
305        datatable.need()  # Enable jQurey datatables for contents listing
306        return super(ApplicantsContainerManageFormPage, self).update()
307
308    def getLocalRoles(self):
309        roles = ILocalRolesAssignable(self.context)
310        return roles()
311
312    def getUsers(self):
313        """Get a list of all users.
314        """
315        for key, val in grok.getSite()['users'].items():
316            url = self.url(val)
317            yield(dict(url=url, name=key, val=val))
318
319    def getUsersWithLocalRoles(self):
320        return get_users_with_local_roles(self.context)
321
322    @grok.action('Save')
323    def apply(self, **data):
324        self.applyData(self.context, **data)
325        self.flash('Data saved.')
326        return
327
328    # ToDo: Show warning message before deletion
329    @grok.action('Remove selected')
330    def delApplicant(self, **data):
331        form = self.request.form
332        if form.has_key('val_id'):
333            child_id = form['val_id']
334        else:
335            self.flash('No applicant selected!')
336            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
337            return
338        if not isinstance(child_id, list):
339            child_id = [child_id]
340        deleted = []
341        for id in child_id:
342            try:
343                del self.context[id]
344                deleted.append(id)
345            except:
346                self.flash('Could not delete %s: %s: %s' % (
347                        id, sys.exc_info()[0], sys.exc_info()[1]))
348        if len(deleted):
349            self.flash('Successfully removed: %s' % ', '.join(deleted))
350        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
351        return
352
353    @grok.action('Add applicant', validator=NullValidator)
354    def addApplicant(self, **data):
355        return self.flash('Manual addition of applicants not yet implemented!')
356
357    @grok.action('Cancel', validator=NullValidator)
358    def cancel(self, **data):
359        self.redirect(self.url(self.context))
360        return
361
362    @grok.action('Add local role', validator=NullValidator)
363    def addLocalRole(self, **data):
364        return add_local_role(self,3, **data)
365
366    @grok.action('Remove selected local roles')
367    def delLocalRoles(self, **data):
368        return del_local_roles(self,3,**data)
369
370
371class LoginApplicant(WAeUPPage):
372    grok.context(IApplicantsContainer)
373    grok.name('login')
374    grok.require('waeup.Public')
375
376    @property
377    def title(self):
378        return u"Applicant Login: %s" % self.context.title
379
380    @property
381    def label(self):
382        return u'Login for applicants only'
383
384    pnav = 3
385   
386    @property
387    def ac_prefix(self):
388        return self.context.ac_prefix
389
390    def update(self, SUBMIT=None):
391        self.ac_series = self.request.form.get('form.ac_series', None)
392        self.ac_number = self.request.form.get('form.ac_number', None)
393        if SUBMIT is None:
394            return
395
396        if self.request.principal.id == 'zope.anybody':
397            self.flash('Entered credentials are invalid.')
398            return
399
400        if not IApplicantPrincipal.providedBy(self.request.principal):
401            # Don't care if user is already authenticated as non-applicant
402            return
403
404        pin = self.request.principal.access_code
405        if pin not in self.context.keys():
406            # Create applicant record
407            applicant = Applicant()
408            applicant.access_code = pin
409            self.context[pin] = applicant
410
411        # Assign current principal the owner role on created applicant
412        # record
413        role_manager = IPrincipalRoleManager(self.context[pin])
414        role_manager.assignRoleToPrincipal(
415            'waeup.local.ApplicationOwner', self.request.principal.id)
416        self.redirect(self.url(self.context[pin], 'edit'))
417        return
418       
419class AccessCodeLink(LeftSidebarLink):
420    grok.order(1)
421    grok.require('waeup.Public')
422
423    def render(self):
424        if not IApplicantPrincipal.providedBy(self.request.principal):
425            return ''
426        access_code = getattr(self.request.principal,'access_code',None)
427        if access_code:
428            applicant_object = get_applicant_data(access_code)
429            url = absoluteURL(applicant_object, self.request)
430            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
431                url,access_code)
432        return ''
433
434class DisplayApplicant(WAeUPDisplayFormPage):
435    grok.context(IApplicant)
436    grok.name('index')
437    grok.require('waeup.handleApplication')
438    form_fields = grok.AutoFields(IApplicant).omit('locked').omit('course_admitted')
439    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
440    form_fields['passport'].custom_widget = ThumbnailWidget
441    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
442    label = 'Applicant'
443    grok.template('form_display')
444    pnav = 3
445
446    @property
447    def title(self):
448        return '%s' % self.context.access_code
449
450    @property
451    def label(self):
452        container_title = self.context.__parent__.title
453        return '%s Application Record' % container_title
454
455    @property
456    def getCourseAdmitted(self):
457        course_admitted = self.context.course_admitted
458        #import pdb; pdb.set_trace()
459        if ICertificate.providedBy(course_admitted):
460            url = self.url(course_admitted)
461            title = course_admitted.title
462            code = course_admitted.code
463            return '<a href="%s">%s (%s)</a>' %(url,title,code)
464        return 'not yet admitted'
465
466class ApplicantsManageActionButton(ManageActionButton):
467    grok.context(IApplicant)
468    grok.view(DisplayApplicant)
469    grok.require('waeup.manageApplications')
470    text = 'Edit application record'
471    target = 'edit_full'
472
473class EditApplicantFull(WAeUPEditFormPage):
474    """A full edit view for applicant data.
475    """
476    grok.context(IApplicant)
477    grok.name('edit_full')
478    grok.require('waeup.manageApplications')
479    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
480    form_fields['passport'].custom_widget = EncodingImageFileWidget
481    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
482    grok.template('form_edit')
483    pnav = 3
484
485    def update(self):
486        datepicker.need() # Enable jQuery datepicker in date fields.
487        super(EditApplicantFull, self).update()
488        return
489
490    @property
491    def title(self):
492        return self.context.access_code
493
494    @property
495    def label(self):
496        container_title = self.context.__parent__.title
497        return '%s Application Form' % container_title
498
499    @grok.action('Save')
500    def save(self, **data):
501        self.applyData(self.context, **data)
502        self.context._p_changed = True
503        self.flash('Form has been saved.')
504        return
505
506class EditApplicantStudent(EditApplicantFull):
507    """An applicant-centered edit view for applicant data.
508    """
509    grok.context(IApplicantEdit)
510    grok.name('edit')
511    grok.require('waeup.handleApplication')
512    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
513    form_fields['passport'].custom_widget = EncodingImageFileWidget
514    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
515    grok.template('form_edit')
516
517    def emitLockMessage(self):
518        self.flash('The requested form is locked (read-only).')
519        self.redirect(self.url(self.context))
520        return
521
522    def update(self):
523        if self.context.locked:
524            self.redirect(self.url(self.context))
525            return
526        datepicker.need() # Enable jQuery datepicker in date fields.
527        super(EditApplicantStudent, self).update()
528        return
529
530    def dataNotComplete(self):
531        if self.context.confirm_passport is not True:
532            return 'Passport confirmation box not ticked.'
533        if len(self.errors) > 0:
534            return 'Form has errors.'
535        return False
536
537    @grok.action('Save')
538    def save(self, **data):
539        if self.context.locked:
540            self.emitLockMessage()
541            return
542        self.applyData(self.context, **data)
543        self.context._p_changed = True
544        self.flash('Form has been saved.')
545        return
546
547    @grok.action('Final Submit')
548    def finalsubmit(self, **data):
549        if self.context.locked:
550            self.emitLockMessage()
551            return
552        self.applyData(self.context, **data)
553        self.context._p_changed = True
554        if self.dataNotComplete():
555            self.flash(self.dataNotComplete())
556            return
557        self.context.locked = True
558        self.flash('Form has been submitted.')
559        self.redirect(self.url(self.context))
560        return
561
Note: See TracBrowser for help on using the repository browser.