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
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
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, IApplicantPDEEditData,
57    IApplicantsRoot, IApplicantsContainer, IApplicantsContainerProvider,
58    IApplicantsContainerAdd, application_types_vocab
59    )
60
61results_widget = CustomWidgetFactory(
62    WAeUPObjectWidget, ResultEntry)
63
64results_display_widget = CustomWidgetFactory(
65    WAeUPObjectDisplayWidget, ResultEntry)
66
67list_results_widget = CustomWidgetFactory(
68    MultiListWidget, subwidget=results_widget)
69
70list_results_display_widget = CustomWidgetFactory(
71    MultiListDisplayWidget, subwidget=results_display_widget)
72
73class ApplicantsRootPage(WAeUPPage):
74    grok.context(IApplicantsRoot)
75    grok.name('index')
76    grok.require('waeup.Public')
77    title = 'Applicants'
78    label = 'Application Section'
79    pnav = 3
80
81    def update(self):
82        super(ApplicantsRootPage, self).update()
83        datatable.need()
84        return
85
86class ManageApplicantsRootActionButton(ManageActionButton):
87    grok.context(IApplicantsRoot)
88    grok.view(ApplicantsRootPage)
89    grok.require('waeup.manageUniversity')
90    text = 'Manage application section'
91
92class ApplicantsRootManageFormPage(WAeUPEditFormPage):
93    grok.context(IApplicantsRoot)
94    grok.name('manage')
95    grok.template('applicantsrootmanagepage')
96    title = 'Applicants'
97    label = 'Manage application section'
98    pnav = 3
99    grok.require('waeup.manageUniversity')
100    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
101    subunits = 'Applicants Containers'
102
103    def update(self):
104        tabs.need()
105        datatable.need()
106        return super(ApplicantsRootManageFormPage, self).update()
107
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))
125        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
126        return
127
128    @grok.action('Add applicants container', validator=NullValidator)
129    def addApplicantsContainer(self, **data):
130        self.redirect(self.url(self.context, '@@add'))
131        return
132
133    @grok.action('Cancel', validator=NullValidator)
134    def cancel(self, **data):
135        self.redirect(self.url(self.context))
136        return
137
138class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
139    grok.context(IApplicantsRoot)
140    grok.require('waeup.manageUniversity')
141    grok.name('add')
142    grok.template('applicantscontaineraddpage')
143    title = 'Applicants'
144    label = 'Add applicants container'
145    pnav = 3
146
147    form_fields = grok.AutoFields(
148        IApplicantsContainerAdd).omit('code').omit('title')
149    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
150    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
151
152    def update(self):
153        datepicker.need() # Enable jQuery datepicker in date fields.
154        #from waeup.sirp.browser.resources import jqueryui
155        #jqueryui.need()
156        return super(ApplicantsContainerAddFormPage, self).update()
157
158    @grok.action('Add applicants container')
159    def addApplicantsContainer(self, **data):
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)
164        if code in self.context.keys():
165            self.flash(
166                'An applicants container for the same application '
167                'type and entrance year exists already in the database.')
168            return
169        # Add new applicants container...
170        provider = data['provider'][1]
171        container = provider.factory()
172        self.applyData(container, **data)
173        container.code = code
174        container.title = title
175        self.context[code] = container
176        self.flash('Added: "%s".' % code)
177        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
178        return
179
180    @grok.action('Cancel', validator=NullValidator)
181    def cancel(self, **data):
182        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
183
184class ApplicantsRootBreadcrumb(Breadcrumb):
185    """A breadcrumb for applicantsroot.
186    """
187    grok.context(IApplicantsRoot)
188    title = u'Application Section'
189
190class ApplicantsContainerBreadcrumb(Breadcrumb):
191    """A breadcrumb for applicantscontainers.
192    """
193    grok.context(IApplicantsContainer)
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
205
206class ApplicantsTab(PrimaryNavTab):
207    """Applicants tab in primary navigation.
208    """
209
210    grok.context(IWAeUPObject)
211    grok.order(3)
212    grok.require('waeup.manageUniversity')
213    grok.template('primarynavtab')
214
215    pnav = 3
216    tab_title = u'Applicants'
217
218    @property
219    def link_target(self):
220        return self.view.application_url('applicants')
221
222class ApplicantsContainerPage(WAeUPDisplayFormPage):
223    """The standard view for regular applicant containers.
224    """
225    grok.context(IApplicantsContainer)
226    grok.name('index')
227    grok.require('waeup.Public')
228    grok.template('applicantscontainerpage')
229    pnav = 3
230
231    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
232    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
233    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
234    form_fields['description'].custom_widget = ReSTDisplayWidget
235
236    @property
237    def title(self):
238        return "Applicants Container: %s" % self.context.title
239
240    @property
241    def label(self):
242        return self.context.title
243
244class ApplicantsContainerManageActionButton(ManageActionButton):
245    grok.context(IApplicantsContainer)
246    grok.view(ApplicantsContainerPage)
247    text = 'Manage applicants container'
248
249
250class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
251    grok.context(IApplicantsContainer)
252    grok.name('manage')
253    grok.template('applicantscontainermanagepage')
254    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
255    taboneactions = ['Save','Cancel']
256    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
257    # Use friendlier date widget...
258    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
259    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
260    grok.require('waeup.manageUniversity')
261
262    @property
263    def title(self):
264        return "Applicants Container: %s" % self.context.title
265
266    @property
267    def label(self):
268        return 'Manage applicants container'
269
270    pnav = 3
271
272    def update(self):
273        datepicker.need() # Enable jQuery datepicker in date fields.
274        tabs.need()
275        datatable.need()  # Enable jQurey datatables for contents listing
276        return super(ApplicantsContainerManageFormPage, self).update()
277
278    @grok.action('Save')
279    def apply(self, **data):
280        self.applyData(self.context, **data)
281        self.flash('Data saved.')
282        return
283
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)
294    def cancel(self, **data):
295        self.redirect(self.url(self.context))
296        return
297
298
299class LoginApplicant(WAeUPPage):
300    grok.context(IApplicantsContainer)
301    grok.name('login')
302    grok.require('waeup.Public')
303
304    @property
305    def title(self):
306        return u"Applicant Login: %s" % self.context.title
307
308    @property
309    def label(self):
310        return u'Login for applicants only'
311
312    pnav = 3
313   
314    @property
315    def ac_prefix(self):
316        return self.context.ac_prefix
317
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)
321        if SUBMIT is None:
322            return
323
324        if self.request.principal.id == 'zope.anybody':
325            self.flash('Entered credentials are invalid.')
326            return
327
328        if not IApplicantPrincipal.providedBy(self.request.principal):
329            # Don't care if user is already authenticated as non-applicant
330            return
331
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
338
339        # Assign current principal the owner role on created applicant
340        # record
341        role_manager = IPrincipalRoleManager(self.context)
342        role_manager.assignRoleToPrincipal(
343            'waeup.local.ApplicationOwner', self.request.principal.id)
344        self.redirect(self.url(self.context[pin], 'edit'))
345        return
346       
347class AccessCodeLink(LeftSidebarLink):
348    grok.order(1)
349    grok.require('waeup.Public')
350
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
362class DisplayApplicant(WAeUPDisplayFormPage):
363    grok.context(IApplicant)
364    grok.name('index')
365    grok.require('waeup.viewApplication')
366    form_fields = grok.AutoFields(IApplicant).omit('locked')
367    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
368    form_fields['passport'].custom_widget = ThumbnailWidget
369    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
370    label = 'Applicant'
371    title = 'Applicant'
372    pnav = 3
373
374class EditApplicantStudent(WAeUPEditFormPage):
375    """An applicant-centered edit view for applicant data.
376    """
377    grok.context(IApplicant)
378    grok.name('edit')
379    grok.require('waeup.editApplication')
380    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
381    form_fields['passport'].custom_widget = EncodingImageFileWidget
382    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
383    grok.template('form_edit_pde')
384
385    def emitLockMessage(self):
386        self.flash('The requested form is locked (read-only).')
387        self.redirect(self.url(self.context))
388        return
389
390    def update(self):
391        if self.context.locked:
392            self.emitLockMessage()
393            return
394        datepicker.need() # Enable jQuery datepicker in date fields.
395        super(EditApplicantStudent, self).update()
396        return
397
398    def filteredWidgets(self):
399        for widget in self.widgets:
400            if widget.name == 'form.confirm_passport':
401                continue
402            yield widget
403
404    @property
405    def label(self):
406        # XXX: Use current/upcoming session
407        return 'Apply for Post UDE Screening Test (2009/2010)'
408    title = 'Edit Application'
409    pnav = 3
410
411    @grok.action('Save')
412    def save(self, **data):
413        if self.context.locked:
414            self.emitLockMessage()
415            return
416        self.applyData(self.context, **data)
417        self.context._p_changed = True
418        return
419
420    @grok.action('Final Submit')
421    def finalsubmit(self, **data):
422        if self.context.locked:
423            self.emitLockMessage()
424            return
425        self.applyData(self.context, **data)
426        self.context._p_changed = True
427        if not self.dataComplete():
428            self.flash('Data yet not complete.')
429            return
430        self.context.locked = True
431        return
432
433    def dataComplete(self):
434        if self.context.confirm_passport is not True:
435            return False
436        if len(self.errors) > 0:
437            return False
438        return True
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
450    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
451    grok.template('form_edit_full')
452
453    def update(self):
454        if self.context.locked:
455            self.emitLockMessage()
456            return
457        datepicker.need() # Enable jQuery datepicker in date fields.
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.