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

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

Reorganize views for applicants and application officers. We don't need a special treatment for the Uniben passport confirmation check box.

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