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

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

Select application_category for applicants containers.

Implement removal of applicants.

File size: 18.1 KB
RevLine 
[5273]1##
2## browser.py
3## Login : <uli@pu.smp.net>
[6153]4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet & Henrik Bettermann
[5273]5## $Id$
[6078]6##
[6063]7## Copyright (C) 2010 Uli Fouquet & Henrik Bettermann
[5273]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.
[6078]12##
[5273]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.
[6078]17##
[5273]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##
[5824]22"""UI components for basic applicants and related components.
[5273]23"""
[6082]24import sys
[5273]25import grok
26
[6082]27from zope.component import getUtility
[6081]28from zope.formlib.widget import CustomWidgetFactory
[6082]29from zope.interface import Invalid
[5937]30from zope.securitypolicy.interfaces import IPrincipalRoleManager
[6153]31from zope.traversing.browser import absoluteURL
[6081]32
[5273]33from waeup.sirp.browser import (
34    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage,
35    WAeUPDisplayFormPage, NullValidator)
[6081]36from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6103]37from waeup.sirp.browser.layout import NullValidator
[6013]38from waeup.sirp.browser.resources import datepicker, tabs, datatable
[6153]39from waeup.sirp.browser.viewlets import (
40    ManageActionButton, PrimaryNavTab, LeftSidebarLink
41    )
[6081]42from waeup.sirp.image.browser.widget import (
43    ThumbnailWidget, EncodingImageFileWidget,
[5822]44    )
[6184]45from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
[6054]46from waeup.sirp.widgets.datewidget import (
47    FriendlyDateWidget, FriendlyDateDisplayWidget)
[6084]48from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
[5303]49from waeup.sirp.widgets.objectwidget import (
[5301]50    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
[5303]51from waeup.sirp.widgets.multilistwidget import (
[5273]52    MultiListWidget, MultiListDisplayWidget)
[6081]53
[6153]54from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
[6081]55from waeup.sirp.applicants.interfaces import (
56    IApplicant, IApplicantPrincipal, IApplicantPDEEditData,
57    IApplicantsRoot, IApplicantsContainer, IApplicantsContainerProvider,
[6087]58    IApplicantsContainerAdd, application_types_vocab
[5686]59    )
[6184]60from waeup.sirp.browser.pages import add_local_role, del_local_roles
61from waeup.sirp.permissions import get_users_with_local_roles, getRoles
[5320]62
[5273]63results_widget = CustomWidgetFactory(
[5301]64    WAeUPObjectWidget, ResultEntry)
[5273]65
66results_display_widget = CustomWidgetFactory(
[5301]67    WAeUPObjectDisplayWidget, ResultEntry)
[5273]68
69list_results_widget = CustomWidgetFactory(
70    MultiListWidget, subwidget=results_widget)
71
72list_results_display_widget = CustomWidgetFactory(
73    MultiListDisplayWidget, subwidget=results_display_widget)
74
[6067]75class ApplicantsRootPage(WAeUPPage):
[5822]76    grok.context(IApplicantsRoot)
77    grok.name('index')
[6153]78    grok.require('waeup.Public')
[5822]79    title = 'Applicants'
[6069]80    label = 'Application Section'
[5843]81    pnav = 3
[6012]82
83    def update(self):
[6067]84        super(ApplicantsRootPage, self).update()
[6012]85        datatable.need()
86        return
87
[5828]88class ManageApplicantsRootActionButton(ManageActionButton):
89    grok.context(IApplicantsRoot)
[6067]90    grok.view(ApplicantsRootPage)
[6069]91    grok.require('waeup.manageUniversity')
92    text = 'Manage application section'
[5828]93
[6069]94class ApplicantsRootManageFormPage(WAeUPEditFormPage):
[5828]95    grok.context(IApplicantsRoot)
96    grok.name('manage')
[6107]97    grok.template('applicantsrootmanagepage')
[6069]98    title = 'Applicants'
99    label = 'Manage application section'
[5843]100    pnav = 3
[6069]101    grok.require('waeup.manageUniversity')
102    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
[6184]103    tabtwoactions1 = ['Remove selected local roles']
104    tabtwoactions2 = ['Add local role']
[6069]105    subunits = 'Applicants Containers'
[6078]106
[6069]107    def update(self):
108        tabs.need()
[6108]109        datatable.need()
[6069]110        return super(ApplicantsRootManageFormPage, self).update()
[5828]111
[6184]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
[6069]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))
[6078]143        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
144        return
[5828]145
[6069]146    @grok.action('Add applicants container', validator=NullValidator)
147    def addApplicantsContainer(self, **data):
148        self.redirect(self.url(self.context, '@@add'))
[6078]149        return
150
[6069]151    @grok.action('Cancel', validator=NullValidator)
152    def cancel(self, **data):
153        self.redirect(self.url(self.context))
[6078]154        return
155
[6184]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
[6069]164class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
[5822]165    grok.context(IApplicantsRoot)
[6069]166    grok.require('waeup.manageUniversity')
[5822]167    grok.name('add')
[6107]168    grok.template('applicantscontaineraddpage')
[6069]169    title = 'Applicants'
170    label = 'Add applicants container'
[5843]171    pnav = 3
[6078]172
[6103]173    form_fields = grok.AutoFields(
174        IApplicantsContainerAdd).omit('code').omit('title')
[6083]175    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
176    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6078]177
[6083]178    def update(self):
179        datepicker.need() # Enable jQuery datepicker in date fields.
[6110]180        #from waeup.sirp.browser.resources import jqueryui
181        #jqueryui.need()
[6083]182        return super(ApplicantsContainerAddFormPage, self).update()
183
[6069]184    @grok.action('Add applicants container')
185    def addApplicantsContainer(self, **data):
[6103]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)
[6087]190        if code in self.context.keys():
[6105]191            self.flash(
192                'An applicants container for the same application '
193                'type and entrance year exists already in the database.')
[5822]194            return
195        # Add new applicants container...
[6083]196        provider = data['provider'][1]
[5822]197        container = provider.factory()
[6069]198        self.applyData(container, **data)
[6087]199        container.code = code
200        container.title = title
201        self.context[code] = container
[6105]202        self.flash('Added: "%s".' % code)
[6069]203        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
[5822]204        return
[6078]205
[6103]206    @grok.action('Cancel', validator=NullValidator)
[6069]207    def cancel(self, **data):
[6103]208        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
[6078]209
[5845]210class ApplicantsRootBreadcrumb(Breadcrumb):
211    """A breadcrumb for applicantsroot.
212    """
213    grok.context(IApplicantsRoot)
[6153]214    title = u'Application Section'
[6078]215
[5845]216class ApplicantsContainerBreadcrumb(Breadcrumb):
217    """A breadcrumb for applicantscontainers.
218    """
219    grok.context(IApplicantsContainer)
[6153]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
[5828]231
232class ApplicantsTab(PrimaryNavTab):
[6153]233    """Applicants tab in primary navigation.
[5828]234    """
[6078]235
[5828]236    grok.context(IWAeUPObject)
237    grok.order(3)
[6069]238    grok.require('waeup.manageUniversity')
[5828]239    grok.template('primarynavtab')
240
[5843]241    pnav = 3
[5828]242    tab_title = u'Applicants'
243
244    @property
245    def link_target(self):
246        return self.view.application_url('applicants')
247
[6029]248class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]249    """The standard view for regular applicant containers.
250    """
251    grok.context(IApplicantsContainer)
252    grok.name('index')
[6153]253    grok.require('waeup.Public')
[6029]254    grok.template('applicantscontainerpage')
[5850]255    pnav = 3
[6053]256
[6105]257    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
[6054]258    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
259    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
[6084]260    form_fields['description'].custom_widget = ReSTDisplayWidget
[6053]261
[5837]262    @property
263    def title(self):
[6087]264        return "Applicants Container: %s" % self.context.title
[5837]265
266    @property
267    def label(self):
[6087]268        return self.context.title
[5830]269
[6107]270class ApplicantsContainerManageActionButton(ManageActionButton):
[5832]271    grok.context(IApplicantsContainer)
272    grok.view(ApplicantsContainerPage)
[6070]273    text = 'Manage applicants container'
[5832]274
275
[6107]276class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
[5837]277    grok.context(IApplicantsContainer)
[5850]278    grok.name('manage')
[6107]279    grok.template('applicantscontainermanagepage')
[6105]280    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
281    taboneactions = ['Save','Cancel']
282    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
[6184]283    tabthreeactions1 = ['Remove selected local roles']
284    tabthreeactions2 = ['Add local role']
[5844]285    # Use friendlier date widget...
[6054]286    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
287    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6153]288    grok.require('waeup.manageUniversity')
[5850]289
290    @property
291    def title(self):
[6087]292        return "Applicants Container: %s" % self.context.title
[6078]293
[5850]294    @property
295    def label(self):
[6087]296        return 'Manage applicants container'
[5850]297
[5845]298    pnav = 3
[5837]299
300    def update(self):
[5850]301        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]302        tabs.need()
[6015]303        datatable.need()  # Enable jQurey datatables for contents listing
[6107]304        return super(ApplicantsContainerManageFormPage, self).update()
[5837]305
[6184]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
[5850]320    @grok.action('Save')
[5837]321    def apply(self, **data):
322        self.applyData(self.context, **data)
323        self.flash('Data saved.')
324        return
[6078]325
[6105]326    # ToDo: Show warning message before deletion
327    @grok.action('Remove selected')
328    def delApplicant(self, **data):
[6189]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
[6105]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)
[5837]356    def cancel(self, **data):
357        self.redirect(self.url(self.context))
358        return
[5886]359
[6184]360    @grok.action('Add local role', validator=NullValidator)
361    def addLocalRole(self, **data):
362        return add_local_role(self,3, **data)
[6105]363
[6184]364    @grok.action('Remove selected local roles')
365    def delLocalRoles(self, **data):
366        return del_local_roles(self,3,**data)
367
368
[5886]369class LoginApplicant(WAeUPPage):
370    grok.context(IApplicantsContainer)
371    grok.name('login')
[6153]372    grok.require('waeup.Public')
[5886]373
[6110]374    @property
375    def title(self):
376        return u"Applicant Login: %s" % self.context.title
[6078]377
[5886]378    @property
379    def label(self):
[6110]380        return u'Login for applicants only'
[5886]381
382    pnav = 3
[6110]383   
384    @property
385    def ac_prefix(self):
386        return self.context.ac_prefix
[6078]387
[5896]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)
[5886]391        if SUBMIT is None:
392            return
393
[5894]394        if self.request.principal.id == 'zope.anybody':
[6105]395            self.flash('Entered credentials are invalid.')
[5886]396            return
[5894]397
398        if not IApplicantPrincipal.providedBy(self.request.principal):
399            # Don't care if user is already authenticated as non-applicant
400            return
401
[5905]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
[6078]408
[5937]409        # Assign current principal the owner role on created applicant
410        # record
[6184]411        role_manager = IPrincipalRoleManager(self.context[pin])
[5937]412        role_manager.assignRoleToPrincipal(
[6043]413            'waeup.local.ApplicationOwner', self.request.principal.id)
[5937]414        self.redirect(self.url(self.context[pin], 'edit'))
[5886]415        return
[6153]416       
417class AccessCodeLink(LeftSidebarLink):
418    grok.order(1)
419    grok.require('waeup.Public')
[5886]420
[6153]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
[5273]432class DisplayApplicant(WAeUPDisplayFormPage):
433    grok.context(IApplicant)
434    grok.name('index')
[5937]435    grok.require('waeup.viewApplication')
[5941]436    form_fields = grok.AutoFields(IApplicant).omit('locked')
[5273]437    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]438    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]439    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]440    label = 'Applicant'
441    title = 'Applicant'
[5843]442    pnav = 3
[5273]443
[5982]444class EditApplicantStudent(WAeUPEditFormPage):
445    """An applicant-centered edit view for applicant data.
446    """
[5273]447    grok.context(IApplicant)
448    grok.name('edit')
[5937]449    grok.require('waeup.editApplication')
[5941]450    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
[5686]451    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]452    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5488]453    grok.template('form_edit_pde')
[5484]454
[5941]455    def emitLockMessage(self):
[6105]456        self.flash('The requested form is locked (read-only).')
[5941]457        self.redirect(self.url(self.context))
458        return
[6078]459
[5686]460    def update(self):
[5941]461        if self.context.locked:
462            self.emitLockMessage()
463            return
[6040]464        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]465        super(EditApplicantStudent, self).update()
[5686]466        return
[5952]467
468    def filteredWidgets(self):
469        for widget in self.widgets:
470            if widget.name == 'form.confirm_passport':
471                continue
472            yield widget
473
[5484]474    @property
475    def label(self):
476        # XXX: Use current/upcoming session
477        return 'Apply for Post UDE Screening Test (2009/2010)'
[5273]478    title = 'Edit Application'
[5845]479    pnav = 3
[5273]480
481    @grok.action('Save')
482    def save(self, **data):
[5941]483        if self.context.locked:
484            self.emitLockMessage()
485            return
[5273]486        self.applyData(self.context, **data)
487        self.context._p_changed = True
488        return
489
[5484]490    @grok.action('Final Submit')
491    def finalsubmit(self, **data):
[5941]492        if self.context.locked:
493            self.emitLockMessage()
494            return
[5273]495        self.applyData(self.context, **data)
[5484]496        self.context._p_changed = True
[5941]497        if not self.dataComplete():
498            self.flash('Data yet not complete.')
499            return
500        self.context.locked = True
[5273]501        return
[5941]502
503    def dataComplete(self):
[6082]504        if self.context.confirm_passport is not True:
[5941]505            return False
506        if len(self.errors) > 0:
507            return False
508        return True
[5982]509
510class EditApplicantFull(WAeUPEditFormPage):
511    """A full edit view for applicant data.
512
513    This one is meant to be used by officers only.
514    """
515    grok.context(IApplicant)
516    grok.name('edit_full')
517    grok.require('waeup.editFullApplication')
518    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
519    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]520    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5982]521    grok.template('form_edit_full')
522
523    def update(self):
524        if self.context.locked:
525            self.emitLockMessage()
526            return
[6041]527        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]528        super(EditApplicantFull, self).update()
529        return
530
531    def filteredWidgets(self):
532        for widget in self.widgets:
533            if widget.name == 'form.confirm_passport':
534                continue
535            yield widget
536
537    @property
538    def label(self):
539        return 'Application for %s' % self.context.access_code
540    title = 'Edit Application'
541    pnav = 3
542
543    @grok.action('Save')
544    def save(self, **data):
545        self.applyData(self.context, **data)
546        self.context._p_changed = True
547        return
Note: See TracBrowser for help on using the repository browser.