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

Last change on this file since 6185 was 6184, checked in by Henrik Bettermann, 14 years ago

Implement local role assignment and removal in application section.

browser.py line 341: Use correct context for the IPrincipalRoleManager adapter.

File size: 17.4 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):
329        return self.flash('Removal of applicants is not yet implemented!')
330
331    @grok.action('Add applicant', validator=NullValidator)
332    def addApplicant(self, **data):
333        return self.flash('Manual addition of applicants not yet implemented!')
334
335    @grok.action('Cancel', validator=NullValidator)
[5837]336    def cancel(self, **data):
337        self.redirect(self.url(self.context))
338        return
[5886]339
[6184]340    @grok.action('Add local role', validator=NullValidator)
341    def addLocalRole(self, **data):
342        return add_local_role(self,3, **data)
[6105]343
[6184]344    @grok.action('Remove selected local roles')
345    def delLocalRoles(self, **data):
346        return del_local_roles(self,3,**data)
347
348
[5886]349class LoginApplicant(WAeUPPage):
350    grok.context(IApplicantsContainer)
351    grok.name('login')
[6153]352    grok.require('waeup.Public')
[5886]353
[6110]354    @property
355    def title(self):
356        return u"Applicant Login: %s" % self.context.title
[6078]357
[5886]358    @property
359    def label(self):
[6110]360        return u'Login for applicants only'
[5886]361
362    pnav = 3
[6110]363   
364    @property
365    def ac_prefix(self):
366        return self.context.ac_prefix
[6078]367
[5896]368    def update(self, SUBMIT=None):
369        self.ac_series = self.request.form.get('form.ac_series', None)
370        self.ac_number = self.request.form.get('form.ac_number', None)
[5886]371        if SUBMIT is None:
372            return
373
[5894]374        if self.request.principal.id == 'zope.anybody':
[6105]375            self.flash('Entered credentials are invalid.')
[5886]376            return
[5894]377
378        if not IApplicantPrincipal.providedBy(self.request.principal):
379            # Don't care if user is already authenticated as non-applicant
380            return
381
[5905]382        pin = self.request.principal.access_code
383        if pin not in self.context.keys():
384            # Create applicant record
385            applicant = Applicant()
386            applicant.access_code = pin
387            self.context[pin] = applicant
[6078]388
[5937]389        # Assign current principal the owner role on created applicant
390        # record
[6184]391        role_manager = IPrincipalRoleManager(self.context[pin])
[5937]392        role_manager.assignRoleToPrincipal(
[6043]393            'waeup.local.ApplicationOwner', self.request.principal.id)
[5937]394        self.redirect(self.url(self.context[pin], 'edit'))
[5886]395        return
[6153]396       
397class AccessCodeLink(LeftSidebarLink):
398    grok.order(1)
399    grok.require('waeup.Public')
[5886]400
[6153]401    def render(self):
402        if not IApplicantPrincipal.providedBy(self.request.principal):
403            return ''
404        access_code = getattr(self.request.principal,'access_code',None)
405        if access_code:
406            applicant_object = get_applicant_data(access_code)
407            url = absoluteURL(applicant_object, self.request)
408            return u'<div class="portlet"><a href="%s">%s</a></div>' % (
409                url,access_code)
410        return ''
411
[5273]412class DisplayApplicant(WAeUPDisplayFormPage):
413    grok.context(IApplicant)
414    grok.name('index')
[5937]415    grok.require('waeup.viewApplication')
[5941]416    form_fields = grok.AutoFields(IApplicant).omit('locked')
[5273]417    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]418    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]419    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]420    label = 'Applicant'
421    title = 'Applicant'
[5843]422    pnav = 3
[5273]423
[5982]424class EditApplicantStudent(WAeUPEditFormPage):
425    """An applicant-centered edit view for applicant data.
426    """
[5273]427    grok.context(IApplicant)
428    grok.name('edit')
[5937]429    grok.require('waeup.editApplication')
[5941]430    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
[5686]431    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]432    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5488]433    grok.template('form_edit_pde')
[5484]434
[5941]435    def emitLockMessage(self):
[6105]436        self.flash('The requested form is locked (read-only).')
[5941]437        self.redirect(self.url(self.context))
438        return
[6078]439
[5686]440    def update(self):
[5941]441        if self.context.locked:
442            self.emitLockMessage()
443            return
[6040]444        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]445        super(EditApplicantStudent, self).update()
[5686]446        return
[5952]447
448    def filteredWidgets(self):
449        for widget in self.widgets:
450            if widget.name == 'form.confirm_passport':
451                continue
452            yield widget
453
[5484]454    @property
455    def label(self):
456        # XXX: Use current/upcoming session
457        return 'Apply for Post UDE Screening Test (2009/2010)'
[5273]458    title = 'Edit Application'
[5845]459    pnav = 3
[5273]460
461    @grok.action('Save')
462    def save(self, **data):
[5941]463        if self.context.locked:
464            self.emitLockMessage()
465            return
[5273]466        self.applyData(self.context, **data)
467        self.context._p_changed = True
468        return
469
[5484]470    @grok.action('Final Submit')
471    def finalsubmit(self, **data):
[5941]472        if self.context.locked:
473            self.emitLockMessage()
474            return
[5273]475        self.applyData(self.context, **data)
[5484]476        self.context._p_changed = True
[5941]477        if not self.dataComplete():
478            self.flash('Data yet not complete.')
479            return
480        self.context.locked = True
[5273]481        return
[5941]482
483    def dataComplete(self):
[6082]484        if self.context.confirm_passport is not True:
[5941]485            return False
486        if len(self.errors) > 0:
487            return False
488        return True
[5982]489
490class EditApplicantFull(WAeUPEditFormPage):
491    """A full edit view for applicant data.
492
493    This one is meant to be used by officers only.
494    """
495    grok.context(IApplicant)
496    grok.name('edit_full')
497    grok.require('waeup.editFullApplication')
498    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
499    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]500    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5982]501    grok.template('form_edit_full')
502
503    def update(self):
504        if self.context.locked:
505            self.emitLockMessage()
506            return
[6041]507        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]508        super(EditApplicantFull, self).update()
509        return
510
511    def filteredWidgets(self):
512        for widget in self.widgets:
513            if widget.name == 'form.confirm_passport':
514                continue
515            yield widget
516
517    @property
518    def label(self):
519        return 'Application for %s' % self.context.access_code
520    title = 'Edit Application'
521    pnav = 3
522
523    @grok.action('Save')
524    def save(self, **data):
525        self.applyData(self.context, **data)
526        self.context._p_changed = True
527        return
Note: See TracBrowser for help on using the repository browser.