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

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

Ad breadcrumb and leftsidbar link for applicants.

File size: 15.7 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    )
[6081]45from waeup.sirp.interfaces import IWAeUPObject
[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    )
[5320]60
[5273]61results_widget = CustomWidgetFactory(
[5301]62    WAeUPObjectWidget, ResultEntry)
[5273]63
64results_display_widget = CustomWidgetFactory(
[5301]65    WAeUPObjectDisplayWidget, ResultEntry)
[5273]66
67list_results_widget = CustomWidgetFactory(
68    MultiListWidget, subwidget=results_widget)
69
70list_results_display_widget = CustomWidgetFactory(
71    MultiListDisplayWidget, subwidget=results_display_widget)
72
[6067]73class ApplicantsRootPage(WAeUPPage):
[5822]74    grok.context(IApplicantsRoot)
75    grok.name('index')
[6153]76    grok.require('waeup.Public')
[5822]77    title = 'Applicants'
[6069]78    label = 'Application Section'
[5843]79    pnav = 3
[6012]80
81    def update(self):
[6067]82        super(ApplicantsRootPage, self).update()
[6012]83        datatable.need()
84        return
85
[5828]86class ManageApplicantsRootActionButton(ManageActionButton):
87    grok.context(IApplicantsRoot)
[6067]88    grok.view(ApplicantsRootPage)
[6069]89    grok.require('waeup.manageUniversity')
90    text = 'Manage application section'
[5828]91
[6069]92class ApplicantsRootManageFormPage(WAeUPEditFormPage):
[5828]93    grok.context(IApplicantsRoot)
94    grok.name('manage')
[6107]95    grok.template('applicantsrootmanagepage')
[6069]96    title = 'Applicants'
97    label = 'Manage application section'
[5843]98    pnav = 3
[6069]99    grok.require('waeup.manageUniversity')
100    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
101    subunits = 'Applicants Containers'
[6078]102
[6069]103    def update(self):
104        tabs.need()
[6108]105        datatable.need()
[6069]106        return super(ApplicantsRootManageFormPage, self).update()
[5828]107
[6069]108    # ToDo: Show warning message before deletion
109    @grok.action('Remove selected')
110    def delApplicantsContainers(self, **data):
111        form = self.request.form
112        child_id = form['val_id']
113        if not isinstance(child_id, list):
114            child_id = [child_id]
115        deleted = []
116        for id in child_id:
117            try:
118                del self.context[id]
119                deleted.append(id)
120            except:
121                self.flash('Could not delete %s: %s: %s' % (
122                        id, sys.exc_info()[0], sys.exc_info()[1]))
123        if len(deleted):
124            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6078]125        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
126        return
[5828]127
[6069]128    @grok.action('Add applicants container', validator=NullValidator)
129    def addApplicantsContainer(self, **data):
130        self.redirect(self.url(self.context, '@@add'))
[6078]131        return
132
[6069]133    @grok.action('Cancel', validator=NullValidator)
134    def cancel(self, **data):
135        self.redirect(self.url(self.context))
[6078]136        return
137
[6069]138class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
[5822]139    grok.context(IApplicantsRoot)
[6069]140    grok.require('waeup.manageUniversity')
[5822]141    grok.name('add')
[6107]142    grok.template('applicantscontaineraddpage')
[6069]143    title = 'Applicants'
144    label = 'Add applicants container'
[5843]145    pnav = 3
[6078]146
[6103]147    form_fields = grok.AutoFields(
148        IApplicantsContainerAdd).omit('code').omit('title')
[6083]149    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
150    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6078]151
[6083]152    def update(self):
153        datepicker.need() # Enable jQuery datepicker in date fields.
[6110]154        #from waeup.sirp.browser.resources import jqueryui
155        #jqueryui.need()
[6083]156        return super(ApplicantsContainerAddFormPage, self).update()
157
[6069]158    @grok.action('Add applicants container')
159    def addApplicantsContainer(self, **data):
[6103]160        year = data['year']
161        code = u'%s%s' % (data['prefix'], year)
162        prefix = application_types_vocab.getTerm(data['prefix'])
163        title = u'%s %s/%s' % (prefix.title, year, year + 1)
[6087]164        if code in self.context.keys():
[6105]165            self.flash(
166                'An applicants container for the same application '
167                'type and entrance year exists already in the database.')
[5822]168            return
169        # Add new applicants container...
[6083]170        provider = data['provider'][1]
[5822]171        container = provider.factory()
[6069]172        self.applyData(container, **data)
[6087]173        container.code = code
174        container.title = title
175        self.context[code] = container
[6105]176        self.flash('Added: "%s".' % code)
[6069]177        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
[5822]178        return
[6078]179
[6103]180    @grok.action('Cancel', validator=NullValidator)
[6069]181    def cancel(self, **data):
[6103]182        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
[6078]183
[5845]184class ApplicantsRootBreadcrumb(Breadcrumb):
185    """A breadcrumb for applicantsroot.
186    """
187    grok.context(IApplicantsRoot)
[6153]188    title = u'Application Section'
[6078]189
[5845]190class ApplicantsContainerBreadcrumb(Breadcrumb):
191    """A breadcrumb for applicantscontainers.
192    """
193    grok.context(IApplicantsContainer)
[6153]194   
195class ApplicantBreadcrumb(Breadcrumb):
196    """A breadcrumb for applicants.
197    """
198    grok.context(IApplicant)
199   
200    @property
201    def title(self):
202        """Get a title for a context.
203        """
204        return self.context.access_code
[5828]205
206class ApplicantsTab(PrimaryNavTab):
[6153]207    """Applicants tab in primary navigation.
[5828]208    """
[6078]209
[5828]210    grok.context(IWAeUPObject)
211    grok.order(3)
[6069]212    grok.require('waeup.manageUniversity')
[5828]213    grok.template('primarynavtab')
214
[5843]215    pnav = 3
[5828]216    tab_title = u'Applicants'
217
218    @property
219    def link_target(self):
220        return self.view.application_url('applicants')
221
[6029]222class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]223    """The standard view for regular applicant containers.
224    """
225    grok.context(IApplicantsContainer)
226    grok.name('index')
[6153]227    grok.require('waeup.Public')
[6029]228    grok.template('applicantscontainerpage')
[5850]229    pnav = 3
[6053]230
[6105]231    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
[6054]232    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
233    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
[6084]234    form_fields['description'].custom_widget = ReSTDisplayWidget
[6053]235
[5837]236    @property
237    def title(self):
[6087]238        return "Applicants Container: %s" % self.context.title
[5837]239
240    @property
241    def label(self):
[6087]242        return self.context.title
[5830]243
[6107]244class ApplicantsContainerManageActionButton(ManageActionButton):
[5832]245    grok.context(IApplicantsContainer)
246    grok.view(ApplicantsContainerPage)
[6070]247    text = 'Manage applicants container'
[5832]248
249
[6107]250class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
[5837]251    grok.context(IApplicantsContainer)
[5850]252    grok.name('manage')
[6107]253    grok.template('applicantscontainermanagepage')
[6105]254    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
255    taboneactions = ['Save','Cancel']
256    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
[5844]257    # Use friendlier date widget...
[6054]258    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
259    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6153]260    grok.require('waeup.manageUniversity')
[5850]261
262    @property
263    def title(self):
[6087]264        return "Applicants Container: %s" % self.context.title
[6078]265
[5850]266    @property
267    def label(self):
[6087]268        return 'Manage applicants container'
[5850]269
[5845]270    pnav = 3
[5837]271
272    def update(self):
[5850]273        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]274        tabs.need()
[6015]275        datatable.need()  # Enable jQurey datatables for contents listing
[6107]276        return super(ApplicantsContainerManageFormPage, self).update()
[5837]277
[5850]278    @grok.action('Save')
[5837]279    def apply(self, **data):
280        self.applyData(self.context, **data)
281        self.flash('Data saved.')
282        return
[6078]283
[6105]284    # ToDo: Show warning message before deletion
285    @grok.action('Remove selected')
286    def delApplicant(self, **data):
287        return self.flash('Removal of applicants is not yet implemented!')
288
289    @grok.action('Add applicant', validator=NullValidator)
290    def addApplicant(self, **data):
291        return self.flash('Manual addition of applicants not yet implemented!')
292
293    @grok.action('Cancel', validator=NullValidator)
[5837]294    def cancel(self, **data):
295        self.redirect(self.url(self.context))
296        return
[5886]297
[6105]298
[5886]299class LoginApplicant(WAeUPPage):
300    grok.context(IApplicantsContainer)
301    grok.name('login')
[6153]302    grok.require('waeup.Public')
[5886]303
[6110]304    @property
305    def title(self):
306        return u"Applicant Login: %s" % self.context.title
[6078]307
[5886]308    @property
309    def label(self):
[6110]310        return u'Login for applicants only'
[5886]311
312    pnav = 3
[6110]313   
314    @property
315    def ac_prefix(self):
316        return self.context.ac_prefix
[6078]317
[5896]318    def update(self, SUBMIT=None):
319        self.ac_series = self.request.form.get('form.ac_series', None)
320        self.ac_number = self.request.form.get('form.ac_number', None)
[5886]321        if SUBMIT is None:
322            return
323
[5894]324        if self.request.principal.id == 'zope.anybody':
[6105]325            self.flash('Entered credentials are invalid.')
[5886]326            return
[5894]327
328        if not IApplicantPrincipal.providedBy(self.request.principal):
329            # Don't care if user is already authenticated as non-applicant
330            return
331
[5905]332        pin = self.request.principal.access_code
333        if pin not in self.context.keys():
334            # Create applicant record
335            applicant = Applicant()
336            applicant.access_code = pin
337            self.context[pin] = applicant
[6078]338
[5937]339        # Assign current principal the owner role on created applicant
340        # record
341        role_manager = IPrincipalRoleManager(self.context)
342        role_manager.assignRoleToPrincipal(
[6043]343            'waeup.local.ApplicationOwner', self.request.principal.id)
[5937]344        self.redirect(self.url(self.context[pin], 'edit'))
[5886]345        return
[6153]346       
347class AccessCodeLink(LeftSidebarLink):
348    grok.order(1)
349    grok.require('waeup.Public')
[5886]350
[6153]351    def render(self):
352        if not IApplicantPrincipal.providedBy(self.request.principal):
353            return ''
354        access_code = getattr(self.request.principal,'access_code',None)
355        if access_code:
356            applicant_object = get_applicant_data(access_code)
357            url = absoluteURL(applicant_object, self.request)
358            return u'<div class="portlet"><a href="%s">%s</a></div>' % (
359                url,access_code)
360        return ''
361
[5273]362class DisplayApplicant(WAeUPDisplayFormPage):
363    grok.context(IApplicant)
364    grok.name('index')
[5937]365    grok.require('waeup.viewApplication')
[5941]366    form_fields = grok.AutoFields(IApplicant).omit('locked')
[5273]367    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]368    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]369    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]370    label = 'Applicant'
371    title = 'Applicant'
[5843]372    pnav = 3
[5273]373
[5982]374class EditApplicantStudent(WAeUPEditFormPage):
375    """An applicant-centered edit view for applicant data.
376    """
[5273]377    grok.context(IApplicant)
378    grok.name('edit')
[5937]379    grok.require('waeup.editApplication')
[5941]380    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
[5686]381    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]382    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5488]383    grok.template('form_edit_pde')
[5484]384
[5941]385    def emitLockMessage(self):
[6105]386        self.flash('The requested form is locked (read-only).')
[5941]387        self.redirect(self.url(self.context))
388        return
[6078]389
[5686]390    def update(self):
[5941]391        if self.context.locked:
392            self.emitLockMessage()
393            return
[6040]394        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]395        super(EditApplicantStudent, self).update()
[5686]396        return
[5952]397
398    def filteredWidgets(self):
399        for widget in self.widgets:
400            if widget.name == 'form.confirm_passport':
401                continue
402            yield widget
403
[5484]404    @property
405    def label(self):
406        # XXX: Use current/upcoming session
407        return 'Apply for Post UDE Screening Test (2009/2010)'
[5273]408    title = 'Edit Application'
[5845]409    pnav = 3
[5273]410
411    @grok.action('Save')
412    def save(self, **data):
[5941]413        if self.context.locked:
414            self.emitLockMessage()
415            return
[5273]416        self.applyData(self.context, **data)
417        self.context._p_changed = True
418        return
419
[5484]420    @grok.action('Final Submit')
421    def finalsubmit(self, **data):
[5941]422        if self.context.locked:
423            self.emitLockMessage()
424            return
[5273]425        self.applyData(self.context, **data)
[5484]426        self.context._p_changed = True
[5941]427        if not self.dataComplete():
428            self.flash('Data yet not complete.')
429            return
430        self.context.locked = True
[5273]431        return
[5941]432
433    def dataComplete(self):
[6082]434        if self.context.confirm_passport is not True:
[5941]435            return False
436        if len(self.errors) > 0:
437            return False
438        return True
[5982]439
440class EditApplicantFull(WAeUPEditFormPage):
441    """A full edit view for applicant data.
442
443    This one is meant to be used by officers only.
444    """
445    grok.context(IApplicant)
446    grok.name('edit_full')
447    grok.require('waeup.editFullApplication')
448    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
449    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]450    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5982]451    grok.template('form_edit_full')
452
453    def update(self):
454        if self.context.locked:
455            self.emitLockMessage()
456            return
[6041]457        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]458        super(EditApplicantFull, self).update()
459        return
460
461    def filteredWidgets(self):
462        for widget in self.widgets:
463            if widget.name == 'form.confirm_passport':
464                continue
465            yield widget
466
467    @property
468    def label(self):
469        return 'Application for %s' % self.context.access_code
470    title = 'Edit Application'
471    pnav = 3
472
473    @grok.action('Save')
474    def save(self, **data):
475        self.applyData(self.context, **data)
476        self.context._p_changed = True
477        return
Note: See TracBrowser for help on using the repository browser.