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
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, ILocalRolesAssignable
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    )
60from waeup.sirp.browser.pages import add_local_role, del_local_roles
61from waeup.sirp.permissions import get_users_with_local_roles, getRoles
62
63results_widget = CustomWidgetFactory(
64    WAeUPObjectWidget, ResultEntry)
65
66results_display_widget = CustomWidgetFactory(
67    WAeUPObjectDisplayWidget, ResultEntry)
68
69list_results_widget = CustomWidgetFactory(
70    MultiListWidget, subwidget=results_widget)
71
72list_results_display_widget = CustomWidgetFactory(
73    MultiListDisplayWidget, subwidget=results_display_widget)
74
75class ApplicantsRootPage(WAeUPPage):
76    grok.context(IApplicantsRoot)
77    grok.name('index')
78    grok.require('waeup.Public')
79    title = 'Applicants'
80    label = 'Application Section'
81    pnav = 3
82
83    def update(self):
84        super(ApplicantsRootPage, self).update()
85        datatable.need()
86        return
87
88class ManageApplicantsRootActionButton(ManageActionButton):
89    grok.context(IApplicantsRoot)
90    grok.view(ApplicantsRootPage)
91    grok.require('waeup.manageUniversity')
92    text = 'Manage application section'
93
94class ApplicantsRootManageFormPage(WAeUPEditFormPage):
95    grok.context(IApplicantsRoot)
96    grok.name('manage')
97    grok.template('applicantsrootmanagepage')
98    title = 'Applicants'
99    label = 'Manage application section'
100    pnav = 3
101    grok.require('waeup.manageUniversity')
102    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
103    tabtwoactions1 = ['Remove selected local roles']
104    tabtwoactions2 = ['Add local role']
105    subunits = 'Applicants Containers'
106
107    def update(self):
108        tabs.need()
109        datatable.need()
110        return super(ApplicantsRootManageFormPage, self).update()
111
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
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))
143        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
144        return
145
146    @grok.action('Add applicants container', validator=NullValidator)
147    def addApplicantsContainer(self, **data):
148        self.redirect(self.url(self.context, '@@add'))
149        return
150
151    @grok.action('Cancel', validator=NullValidator)
152    def cancel(self, **data):
153        self.redirect(self.url(self.context))
154        return
155
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
164class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
165    grok.context(IApplicantsRoot)
166    grok.require('waeup.manageUniversity')
167    grok.name('add')
168    grok.template('applicantscontaineraddpage')
169    title = 'Applicants'
170    label = 'Add applicants container'
171    pnav = 3
172
173    form_fields = grok.AutoFields(
174        IApplicantsContainerAdd).omit('code').omit('title')
175    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
176    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
177
178    def update(self):
179        datepicker.need() # Enable jQuery datepicker in date fields.
180        #from waeup.sirp.browser.resources import jqueryui
181        #jqueryui.need()
182        return super(ApplicantsContainerAddFormPage, self).update()
183
184    @grok.action('Add applicants container')
185    def addApplicantsContainer(self, **data):
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)
190        if code in self.context.keys():
191            self.flash(
192                'An applicants container for the same application '
193                'type and entrance year exists already in the database.')
194            return
195        # Add new applicants container...
196        provider = data['provider'][1]
197        container = provider.factory()
198        self.applyData(container, **data)
199        container.code = code
200        container.title = title
201        self.context[code] = container
202        self.flash('Added: "%s".' % code)
203        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
204        return
205
206    @grok.action('Cancel', validator=NullValidator)
207    def cancel(self, **data):
208        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
209
210class ApplicantsRootBreadcrumb(Breadcrumb):
211    """A breadcrumb for applicantsroot.
212    """
213    grok.context(IApplicantsRoot)
214    title = u'Application Section'
215
216class ApplicantsContainerBreadcrumb(Breadcrumb):
217    """A breadcrumb for applicantscontainers.
218    """
219    grok.context(IApplicantsContainer)
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
231
232class ApplicantsTab(PrimaryNavTab):
233    """Applicants tab in primary navigation.
234    """
235
236    grok.context(IWAeUPObject)
237    grok.order(3)
238    grok.require('waeup.manageUniversity')
239    grok.template('primarynavtab')
240
241    pnav = 3
242    tab_title = u'Applicants'
243
244    @property
245    def link_target(self):
246        return self.view.application_url('applicants')
247
248class ApplicantsContainerPage(WAeUPDisplayFormPage):
249    """The standard view for regular applicant containers.
250    """
251    grok.context(IApplicantsContainer)
252    grok.name('index')
253    grok.require('waeup.Public')
254    grok.template('applicantscontainerpage')
255    pnav = 3
256
257    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
258    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
259    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
260    form_fields['description'].custom_widget = ReSTDisplayWidget
261
262    @property
263    def title(self):
264        return "Applicants Container: %s" % self.context.title
265
266    @property
267    def label(self):
268        return self.context.title
269
270class ApplicantsContainerManageActionButton(ManageActionButton):
271    grok.context(IApplicantsContainer)
272    grok.view(ApplicantsContainerPage)
273    text = 'Manage applicants container'
274
275
276class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
277    grok.context(IApplicantsContainer)
278    grok.name('manage')
279    grok.template('applicantscontainermanagepage')
280    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
281    taboneactions = ['Save','Cancel']
282    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
283    tabthreeactions1 = ['Remove selected local roles']
284    tabthreeactions2 = ['Add local role']
285    # Use friendlier date widget...
286    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
287    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
288    grok.require('waeup.manageUniversity')
289
290    @property
291    def title(self):
292        return "Applicants Container: %s" % self.context.title
293
294    @property
295    def label(self):
296        return 'Manage applicants container'
297
298    pnav = 3
299
300    def update(self):
301        datepicker.need() # Enable jQuery datepicker in date fields.
302        tabs.need()
303        datatable.need()  # Enable jQurey datatables for contents listing
304        return super(ApplicantsContainerManageFormPage, self).update()
305
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
320    @grok.action('Save')
321    def apply(self, **data):
322        self.applyData(self.context, **data)
323        self.flash('Data saved.')
324        return
325
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)
336    def cancel(self, **data):
337        self.redirect(self.url(self.context))
338        return
339
340    @grok.action('Add local role', validator=NullValidator)
341    def addLocalRole(self, **data):
342        return add_local_role(self,3, **data)
343
344    @grok.action('Remove selected local roles')
345    def delLocalRoles(self, **data):
346        return del_local_roles(self,3,**data)
347
348
349class LoginApplicant(WAeUPPage):
350    grok.context(IApplicantsContainer)
351    grok.name('login')
352    grok.require('waeup.Public')
353
354    @property
355    def title(self):
356        return u"Applicant Login: %s" % self.context.title
357
358    @property
359    def label(self):
360        return u'Login for applicants only'
361
362    pnav = 3
363   
364    @property
365    def ac_prefix(self):
366        return self.context.ac_prefix
367
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)
371        if SUBMIT is None:
372            return
373
374        if self.request.principal.id == 'zope.anybody':
375            self.flash('Entered credentials are invalid.')
376            return
377
378        if not IApplicantPrincipal.providedBy(self.request.principal):
379            # Don't care if user is already authenticated as non-applicant
380            return
381
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
388
389        # Assign current principal the owner role on created applicant
390        # record
391        role_manager = IPrincipalRoleManager(self.context[pin])
392        role_manager.assignRoleToPrincipal(
393            'waeup.local.ApplicationOwner', self.request.principal.id)
394        self.redirect(self.url(self.context[pin], 'edit'))
395        return
396       
397class AccessCodeLink(LeftSidebarLink):
398    grok.order(1)
399    grok.require('waeup.Public')
400
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
412class DisplayApplicant(WAeUPDisplayFormPage):
413    grok.context(IApplicant)
414    grok.name('index')
415    grok.require('waeup.viewApplication')
416    form_fields = grok.AutoFields(IApplicant).omit('locked')
417    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
418    form_fields['passport'].custom_widget = ThumbnailWidget
419    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
420    label = 'Applicant'
421    title = 'Applicant'
422    pnav = 3
423
424class EditApplicantStudent(WAeUPEditFormPage):
425    """An applicant-centered edit view for applicant data.
426    """
427    grok.context(IApplicant)
428    grok.name('edit')
429    grok.require('waeup.editApplication')
430    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
431    form_fields['passport'].custom_widget = EncodingImageFileWidget
432    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
433    grok.template('form_edit_pde')
434
435    def emitLockMessage(self):
436        self.flash('The requested form is locked (read-only).')
437        self.redirect(self.url(self.context))
438        return
439
440    def update(self):
441        if self.context.locked:
442            self.emitLockMessage()
443            return
444        datepicker.need() # Enable jQuery datepicker in date fields.
445        super(EditApplicantStudent, self).update()
446        return
447
448    def filteredWidgets(self):
449        for widget in self.widgets:
450            if widget.name == 'form.confirm_passport':
451                continue
452            yield widget
453
454    @property
455    def label(self):
456        # XXX: Use current/upcoming session
457        return 'Apply for Post UDE Screening Test (2009/2010)'
458    title = 'Edit Application'
459    pnav = 3
460
461    @grok.action('Save')
462    def save(self, **data):
463        if self.context.locked:
464            self.emitLockMessage()
465            return
466        self.applyData(self.context, **data)
467        self.context._p_changed = True
468        return
469
470    @grok.action('Final Submit')
471    def finalsubmit(self, **data):
472        if self.context.locked:
473            self.emitLockMessage()
474            return
475        self.applyData(self.context, **data)
476        self.context._p_changed = True
477        if not self.dataComplete():
478            self.flash('Data yet not complete.')
479            return
480        self.context.locked = True
481        return
482
483    def dataComplete(self):
484        if self.context.confirm_passport is not True:
485            return False
486        if len(self.errors) > 0:
487            return False
488        return True
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
500    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
501    grok.template('form_edit_full')
502
503    def update(self):
504        if self.context.locked:
505            self.emitLockMessage()
506            return
507        datepicker.need() # Enable jQuery datepicker in date fields.
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.