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

Last change on this file since 6235 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
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,IApplicantEdit,
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.manageApplications')
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.manageApplications')
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.manageApplications')
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.manageApplications')
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    grok.require('waeup.manageApplications')
274    text = 'Manage applicants container'
275
276
277class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
278    grok.context(IApplicantsContainer)
279    grok.name('manage')
280    grok.template('applicantscontainermanagepage')
281    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
282    taboneactions = ['Save','Cancel']
283    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
284    tabthreeactions1 = ['Remove selected local roles']
285    tabthreeactions2 = ['Add local role']
286    # Use friendlier date widget...
287    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
288    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
289    grok.require('waeup.manageApplications')
290
291    @property
292    def title(self):
293        return "Applicants Container: %s" % self.context.title
294
295    @property
296    def label(self):
297        return 'Manage applicants container'
298
299    pnav = 3
300
301    def update(self):
302        datepicker.need() # Enable jQuery datepicker in date fields.
303        tabs.need()
304        datatable.need()  # Enable jQurey datatables for contents listing
305        return super(ApplicantsContainerManageFormPage, self).update()
306
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
321    @grok.action('Save')
322    def apply(self, **data):
323        self.applyData(self.context, **data)
324        self.flash('Data saved.')
325        return
326
327    # ToDo: Show warning message before deletion
328    @grok.action('Remove selected')
329    def delApplicant(self, **data):
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
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)
357    def cancel(self, **data):
358        self.redirect(self.url(self.context))
359        return
360
361    @grok.action('Add local role', validator=NullValidator)
362    def addLocalRole(self, **data):
363        return add_local_role(self,3, **data)
364
365    @grok.action('Remove selected local roles')
366    def delLocalRoles(self, **data):
367        return del_local_roles(self,3,**data)
368
369
370class LoginApplicant(WAeUPPage):
371    grok.context(IApplicantsContainer)
372    grok.name('login')
373    grok.require('waeup.Public')
374
375    @property
376    def title(self):
377        return u"Applicant Login: %s" % self.context.title
378
379    @property
380    def label(self):
381        return u'Login for applicants only'
382
383    pnav = 3
384   
385    @property
386    def ac_prefix(self):
387        return self.context.ac_prefix
388
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)
392        if SUBMIT is None:
393            return
394
395        if self.request.principal.id == 'zope.anybody':
396            self.flash('Entered credentials are invalid.')
397            return
398
399        if not IApplicantPrincipal.providedBy(self.request.principal):
400            # Don't care if user is already authenticated as non-applicant
401            return
402
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
409
410        # Assign current principal the owner role on created applicant
411        # record
412        role_manager = IPrincipalRoleManager(self.context[pin])
413        role_manager.assignRoleToPrincipal(
414            'waeup.local.ApplicationOwner', self.request.principal.id)
415        self.redirect(self.url(self.context[pin], 'edit'))
416        return
417       
418class AccessCodeLink(LeftSidebarLink):
419    grok.order(1)
420    grok.require('waeup.Public')
421
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)
429            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
430                url,access_code)
431        return ''
432
433class DisplayApplicant(WAeUPDisplayFormPage):
434    grok.context(IApplicant)
435    grok.name('index')
436    grok.require('waeup.handleApplication')
437    form_fields = grok.AutoFields(IApplicant).omit('locked')
438    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
439    form_fields['passport'].custom_widget = ThumbnailWidget
440    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
441    label = 'Applicant'
442    pnav = 3
443
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
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
460class EditApplicantFull(WAeUPEditFormPage):
461    """A full edit view for applicant data.
462    """
463    grok.context(IApplicant)
464    grok.name('edit_full')
465    grok.require('waeup.manageApplications')
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):
494    """An applicant-centered edit view for applicant data.
495    """
496    grok.context(IApplicantEdit)
497    grok.name('edit')
498    grok.require('waeup.handleApplication')
499    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
500    form_fields['passport'].custom_widget = EncodingImageFileWidget
501    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
502    grok.template('form_edit')
503
504    def emitLockMessage(self):
505        self.flash('The requested form is locked (read-only).')
506        self.redirect(self.url(self.context))
507        return
508
509    def update(self):
510        if self.context.locked:
511            self.redirect(self.url(self.context))
512            return
513        datepicker.need() # Enable jQuery datepicker in date fields.
514        super(EditApplicantStudent, self).update()
515        return
516
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
523
524    @grok.action('Save')
525    def save(self, **data):
526        if self.context.locked:
527            self.emitLockMessage()
528            return
529        self.applyData(self.context, **data)
530        self.context._p_changed = True
531        self.flash('Form has been saved.')
532        return
533
534    @grok.action('Final Submit')
535    def finalsubmit(self, **data):
536        if self.context.locked:
537            self.emitLockMessage()
538            return
539        self.applyData(self.context, **data)
540        self.context._p_changed = True
541        if self.dataNotComplete():
542            self.flash(self.dataNotComplete())
543            return
544        self.context.locked = True
545        self.flash('Form has been submitted.')
546        self.redirect(self.url(self.context))
547        return
548
Note: See TracBrowser for help on using the repository browser.