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

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

Convert waeup.local.ApplicationsOfficer? to waeup.ApplicationsOfficer?.
Change and simplify permission settings in application section.
Assign application section permissions to Portal Manager.

Actually we need a Campus Manager (Academics Section Manager) too. Then manageUniversity should be renamed to manageAcademics.

File size: 18.2 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 (
[6196]56    IApplicant, IApplicantPrincipal,IApplicantEdit,
[6081]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)
[6198]91    grok.require('waeup.manageApplications')
[6069]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
[6198]101    grok.require('waeup.manageApplications')
[6069]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)
[6198]166    grok.require('waeup.manageApplications')
[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)
[6198]238    grok.require('waeup.manageApplications')
[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)
[6198]273    grok.require('waeup.manageApplications')
[6070]274    text = 'Manage applicants container'
[5832]275
276
[6107]277class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
[5837]278    grok.context(IApplicantsContainer)
[5850]279    grok.name('manage')
[6107]280    grok.template('applicantscontainermanagepage')
[6105]281    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
282    taboneactions = ['Save','Cancel']
283    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
[6184]284    tabthreeactions1 = ['Remove selected local roles']
285    tabthreeactions2 = ['Add local role']
[5844]286    # Use friendlier date widget...
[6054]287    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
288    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6198]289    grok.require('waeup.manageApplications')
[5850]290
291    @property
292    def title(self):
[6087]293        return "Applicants Container: %s" % self.context.title
[6078]294
[5850]295    @property
296    def label(self):
[6087]297        return 'Manage applicants container'
[5850]298
[5845]299    pnav = 3
[5837]300
301    def update(self):
[5850]302        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]303        tabs.need()
[6015]304        datatable.need()  # Enable jQurey datatables for contents listing
[6107]305        return super(ApplicantsContainerManageFormPage, self).update()
[5837]306
[6184]307    def getLocalRoles(self):
308        roles = ILocalRolesAssignable(self.context)
309        return roles()
310
311    def getUsers(self):
312        """Get a list of all users.
313        """
314        for key, val in grok.getSite()['users'].items():
315            url = self.url(val)
316            yield(dict(url=url, name=key, val=val))
317
318    def getUsersWithLocalRoles(self):
319        return get_users_with_local_roles(self.context)
320
[5850]321    @grok.action('Save')
[5837]322    def apply(self, **data):
323        self.applyData(self.context, **data)
324        self.flash('Data saved.')
325        return
[6078]326
[6105]327    # ToDo: Show warning message before deletion
328    @grok.action('Remove selected')
329    def delApplicant(self, **data):
[6189]330        form = self.request.form
331        if form.has_key('val_id'):
332            child_id = form['val_id']
333        else:
334            self.flash('No applicant selected!')
335            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
336            return
337        if not isinstance(child_id, list):
338            child_id = [child_id]
339        deleted = []
340        for id in child_id:
341            try:
342                del self.context[id]
343                deleted.append(id)
344            except:
345                self.flash('Could not delete %s: %s: %s' % (
346                        id, sys.exc_info()[0], sys.exc_info()[1]))
347        if len(deleted):
348            self.flash('Successfully removed: %s' % ', '.join(deleted))
349        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
350        return
[6105]351
352    @grok.action('Add applicant', validator=NullValidator)
353    def addApplicant(self, **data):
354        return self.flash('Manual addition of applicants not yet implemented!')
355
356    @grok.action('Cancel', validator=NullValidator)
[5837]357    def cancel(self, **data):
358        self.redirect(self.url(self.context))
359        return
[5886]360
[6184]361    @grok.action('Add local role', validator=NullValidator)
362    def addLocalRole(self, **data):
363        return add_local_role(self,3, **data)
[6105]364
[6184]365    @grok.action('Remove selected local roles')
366    def delLocalRoles(self, **data):
367        return del_local_roles(self,3,**data)
368
369
[5886]370class LoginApplicant(WAeUPPage):
371    grok.context(IApplicantsContainer)
372    grok.name('login')
[6153]373    grok.require('waeup.Public')
[5886]374
[6110]375    @property
376    def title(self):
377        return u"Applicant Login: %s" % self.context.title
[6078]378
[5886]379    @property
380    def label(self):
[6110]381        return u'Login for applicants only'
[5886]382
383    pnav = 3
[6110]384   
385    @property
386    def ac_prefix(self):
387        return self.context.ac_prefix
[6078]388
[5896]389    def update(self, SUBMIT=None):
390        self.ac_series = self.request.form.get('form.ac_series', None)
391        self.ac_number = self.request.form.get('form.ac_number', None)
[5886]392        if SUBMIT is None:
393            return
394
[5894]395        if self.request.principal.id == 'zope.anybody':
[6105]396            self.flash('Entered credentials are invalid.')
[5886]397            return
[5894]398
399        if not IApplicantPrincipal.providedBy(self.request.principal):
400            # Don't care if user is already authenticated as non-applicant
401            return
402
[5905]403        pin = self.request.principal.access_code
404        if pin not in self.context.keys():
405            # Create applicant record
406            applicant = Applicant()
407            applicant.access_code = pin
408            self.context[pin] = applicant
[6078]409
[5937]410        # Assign current principal the owner role on created applicant
411        # record
[6184]412        role_manager = IPrincipalRoleManager(self.context[pin])
[5937]413        role_manager.assignRoleToPrincipal(
[6043]414            'waeup.local.ApplicationOwner', self.request.principal.id)
[5937]415        self.redirect(self.url(self.context[pin], 'edit'))
[5886]416        return
[6153]417       
418class AccessCodeLink(LeftSidebarLink):
419    grok.order(1)
420    grok.require('waeup.Public')
[5886]421
[6153]422    def render(self):
423        if not IApplicantPrincipal.providedBy(self.request.principal):
424            return ''
425        access_code = getattr(self.request.principal,'access_code',None)
426        if access_code:
427            applicant_object = get_applicant_data(access_code)
428            url = absoluteURL(applicant_object, self.request)
[6198]429            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
[6153]430                url,access_code)
431        return ''
432
[5273]433class DisplayApplicant(WAeUPDisplayFormPage):
434    grok.context(IApplicant)
435    grok.name('index')
[6198]436    grok.require('waeup.handleApplication')
[5941]437    form_fields = grok.AutoFields(IApplicant).omit('locked')
[6196]438    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]439    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]440    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]441    label = 'Applicant'
[5843]442    pnav = 3
[5273]443
[6196]444    @property
445    def title(self):
446        return '%s' % self.context.access_code
447
448    @property
449    def label(self):
450        container_title = self.context.__parent__.title
451        return '%s Application Record' % container_title
452
[6198]453class ApplicantsManageActionButton(ManageActionButton):
454    grok.context(IApplicant)
455    grok.view(DisplayApplicant)
456    grok.require('waeup.manageApplications')
457    text = 'Edit application record'
458    target = 'edit_full'
459
[6196]460class EditApplicantFull(WAeUPEditFormPage):
461    """A full edit view for applicant data.
462    """
463    grok.context(IApplicant)
464    grok.name('edit_full')
[6198]465    grok.require('waeup.manageApplications')
[6196]466    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
467    form_fields['passport'].custom_widget = EncodingImageFileWidget
468    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
469    grok.template('form_edit')
470    pnav = 3
471
472    def update(self):
473        datepicker.need() # Enable jQuery datepicker in date fields.
474        super(EditApplicantFull, self).update()
475        return
476
477    @property
478    def title(self):
479        return self.context.access_code
480
481    @property
482    def label(self):
483        container_title = self.context.__parent__.title
484        return '%s Application Form' % container_title
485
486    @grok.action('Save')
487    def save(self, **data):
488        self.applyData(self.context, **data)
489        self.context._p_changed = True
490        self.flash('Form has been saved.')
491        return
492
493class EditApplicantStudent(EditApplicantFull):
[5982]494    """An applicant-centered edit view for applicant data.
495    """
[6196]496    grok.context(IApplicantEdit)
[5273]497    grok.name('edit')
[6198]498    grok.require('waeup.handleApplication')
[6196]499    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
[5686]500    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]501    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6196]502    grok.template('form_edit')
[5484]503
[5941]504    def emitLockMessage(self):
[6105]505        self.flash('The requested form is locked (read-only).')
[5941]506        self.redirect(self.url(self.context))
507        return
[6078]508
[5686]509    def update(self):
[5941]510        if self.context.locked:
[6198]511            self.redirect(self.url(self.context))
[5941]512            return
[6040]513        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]514        super(EditApplicantStudent, self).update()
[5686]515        return
[5952]516
[6196]517    def dataNotComplete(self):
518        if self.context.confirm_passport is not True:
519            return 'Passport confirmation box not ticked.'
520        if len(self.errors) > 0:
521            return 'Form has errors.'
522        return False
[5952]523
[5273]524    @grok.action('Save')
525    def save(self, **data):
[5941]526        if self.context.locked:
527            self.emitLockMessage()
528            return
[5273]529        self.applyData(self.context, **data)
530        self.context._p_changed = True
[6196]531        self.flash('Form has been saved.')
[5273]532        return
533
[5484]534    @grok.action('Final Submit')
535    def finalsubmit(self, **data):
[5941]536        if self.context.locked:
537            self.emitLockMessage()
538            return
[5273]539        self.applyData(self.context, **data)
[5484]540        self.context._p_changed = True
[6196]541        if self.dataNotComplete():
542            self.flash(self.dataNotComplete())
[5941]543            return
544        self.context.locked = True
[6196]545        self.flash('Form has been submitted.')
546        self.redirect(self.url(self.context))
[5273]547        return
[5941]548
Note: See TracBrowser for help on using the repository browser.