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

Last change on this file since 6014 was 6013, checked in by uli, 14 years ago

Move import to the head.

File size: 15.0 KB
Line 
1##
2## browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2010 Uli Fouquet
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 grok
25
26from datetime import datetime
27from hurry.jquery import jquery
28from hurry.jqueryui import jqueryui
29from zope.component import getUtility, getAllUtilitiesRegisteredFor
30from zope.formlib.widgets import FileWidget, DateWidget
31from zope.securitypolicy.interfaces import IPrincipalRoleManager
32from waeup.sirp.browser import (
33    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage,
34    WAeUPDisplayFormPage, NullValidator)
35from waeup.sirp.browser.pages import LoginPage
36from waeup.sirp.interfaces import IWAeUPObject
37from waeup.sirp.browser.resources import datepicker, tabs, datatable
38from waeup.sirp.browser.viewlets import (
39    AddActionButton, ManageActionButton, PrimaryNavTab,
40    )
41from waeup.sirp.browser.breadcrumbs import Breadcrumb
42from waeup.sirp.applicants import get_applicant_data, ResultEntry, Applicant
43from waeup.sirp.applicants.interfaces import (
44    IApplicant, IApplicantPrincipal, IApplicantPDEEditData,
45    IApplicantsRoot, IApplicantsContainer, IApplicantsContainerProvider,
46    )
47from waeup.sirp.widgets.passportwidget import (
48    PassportWidget, PassportDisplayWidget
49    )
50#from zope.formlib.objectwidget import ObjectWidget
51from zope.formlib.sequencewidget import ListSequenceWidget, SequenceDisplayWidget
52from zope.formlib.widget import CustomWidgetFactory
53from waeup.sirp.utils.helpers import ReST2HTML
54from waeup.sirp.widgets.objectwidget import (
55    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
56from waeup.sirp.widgets.multilistwidget import (
57    MultiListWidget, MultiListDisplayWidget)
58from waeup.sirp.image.browser.widget import (
59    ThumbnailWidget, EncodingImageFileWidget,
60    )
61
62results_widget = CustomWidgetFactory(
63    WAeUPObjectWidget, ResultEntry)
64
65results_display_widget = CustomWidgetFactory(
66    WAeUPObjectDisplayWidget, ResultEntry)
67
68#list_results_widget = CustomWidgetFactory(
69#    ListSequenceWidget, subwidget=results_widget)
70
71list_results_widget = CustomWidgetFactory(
72    MultiListWidget, subwidget=results_widget)
73
74list_results_display_widget = CustomWidgetFactory(
75    MultiListDisplayWidget, subwidget=results_display_widget)
76
77#: A date widget that renders with CSS class `datepicker` thus
78#  enabling : the jQuery datepicker for this field if the form loaded
79#  the jQuery code.
80FriendlyDateWidget = CustomWidgetFactory(
81    DateWidget, cssClass='datepicker')
82
83class ApplicationsPage(WAeUPPage):
84    grok.context(IApplicantsRoot)
85    grok.name('index')
86    title = 'Applicants'
87    pnav = 3
88
89    def update(self):
90        super(ApplicationsPage, self).update()
91        datatable.need()
92        return
93
94    def getApplications(self):
95        """Get a list of all stored applicant containers.
96        """
97        for key, val in self.context.items():
98            url = self.url(val)
99            yield(dict(url=url, name=key, container=val))
100
101class ManageApplicantsRootActionButton(ManageActionButton):
102    grok.context(IApplicantsRoot)
103    grok.view(ApplicationsPage)
104    text = 'Manage applicant containers'
105
106class ApplicantsRootEditPage(WAeUPPage):
107    grok.context(IApplicantsRoot)
108    grok.name('manage')
109    grok.template('applicantsrooteditpage')
110    title = 'Edit applicants containers'
111    pnav = 3
112
113    def update(self, entries=None, DELETE=None, CANCEL=None):
114        if CANCEL is not None:
115            self.redirect(self.url(self.context))
116            return
117        if DELETE is None:
118            return
119        if entries is None:
120            return
121        if not isinstance(entries, list):
122            entries = [entries]
123        for name in entries:
124            del self.context[name]
125            self.flash('Deleted "%s"' % name)
126        return
127
128    def getApplications(self):
129        """Get a list of all stored applicant containers.
130        """
131        for key, val in self.context.items():
132            url = self.url(val)
133            yield(dict(url=url, name=key))
134
135class AddApplicantsContainerActionButton(AddActionButton):
136    grok.context(IApplicantsRoot)
137    grok.view(ApplicantsRootEditPage)
138    text = 'Add applicants container'
139
140class AddApplicantsContainer(WAeUPPage):
141    grok.context(IApplicantsRoot)
142    grok.name('add')
143    grok.template('addcontainer')
144    title = 'Add applicants container'
145    pnav = 3
146
147    def update(self, providername=None, name=None, title=None,
148               description=None, ADD=None, CANCEL=None):
149        if CANCEL is not None:
150            self.redirect(self.url(self.context))
151            return
152        if ADD is None:
153            return
154        if not name:
155            self.flash('Error: you must give a name')
156            return
157        if name in self.context.keys():
158            self.flash('A container of the given name already exists')
159            return
160        # Add new applicants container...
161        provider = getUtility(IApplicantsContainerProvider,
162                              name=providername)
163        container = provider.factory()
164        if title:
165            container.title = title
166        if description:
167            container.description = description
168        self.context[name] = container
169        self.flash('Added "%s".' % name)
170        self.redirect(self.url(self.context))
171        return
172       
173    def getContainerProviders(self):
174        """Get a list of applicants container providers.
175
176        Applicants container providers are named utilities that help
177        to create applicants containers of different types
178        (JAMB-based, non-JAMB-based, etc.).
179
180        The list returned contains dicts::
181
182          {'name': <utility_name>,
183           'provider': <provider instance>}
184
185        where `utility_name` is the name under which the respective
186        provider utility is registered and `provider` is the real
187        provider instance.
188
189        The `utility_name` can be used to lookup the utility anew (for
190        instance after submitting a form) and the `provider` instance
191        can be used to create new instances of the respective
192        applicants container type.
193        """
194        providers = getAllUtilitiesRegisteredFor(IApplicantsContainerProvider)
195        result = [
196            {'name': getattr(x, 'grokcore.component.directive.name'),
197             'provider': x}
198             for x in providers
199            ]
200        return result
201
202class ApplicantsRootBreadcrumb(Breadcrumb):
203    """A breadcrumb for applicantsroot.
204    """
205    grok.context(IApplicantsRoot)
206    title = u'Applicants'
207   
208class ApplicantsContainerBreadcrumb(Breadcrumb):
209    """A breadcrumb for applicantscontainers.
210    """
211    grok.context(IApplicantsContainer)
212
213class ApplicantsTab(PrimaryNavTab):
214    """Faculties-tab in primary navigation.
215    """
216    grok.context(IWAeUPObject)
217    grok.order(3)
218    grok.require('waeup.View')
219    grok.template('primarynavtab')
220
221    pnav = 3
222    tab_title = u'Applicants'
223
224    @property
225    def link_target(self):
226        return self.view.application_url('applicants')
227
228class ApplicantsContainerPage(WAeUPPage):
229    """The standard view for regular applicant containers.
230    """
231    grok.context(IApplicantsContainer)
232    grok.name('index')
233    pnav = 3
234   
235    @property
236    def title(self):
237        return "Applicants Container: %s" % getattr(
238            self.context, '__name__', 'unnamed')
239
240    @property
241    def label(self):
242        return self.title
243
244    def descriptionToHTML(self):
245        return ReST2HTML(self.context.description)
246
247class ManageApplicantsContainerActionButton(ManageActionButton):
248    grok.context(IApplicantsContainer)
249    grok.view(ApplicantsContainerPage)
250    text = 'Manage'
251
252
253class ManageApplicantsContainer(WAeUPEditFormPage):
254    grok.context(IApplicantsContainer)
255    grok.name('manage')
256    grok.template('form_manage_applicants_container')
257    form_fields = grok.AutoFields(IApplicantsContainer)
258    # Use friendlier date widget...
259    form_fields['startdate'].custom_widget = FriendlyDateWidget
260    form_fields['enddate'].custom_widget = FriendlyDateWidget
261
262    @property
263    def title(self):
264        return "Manage applicants container: %s" % getattr(
265            self.context, '__name__', 'unnamed')
266   
267    @property
268    def label(self):
269        return self.title
270
271    pnav = 3
272
273    def update(self):
274        datepicker.need() # Enable jQuery datepicker in date fields.
275        tabs.need()
276        return super(ManageApplicantsContainer, 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    @grok.action('Back')
285    def cancel(self, **data):
286        self.redirect(self.url(self.context))
287        return
288
289class LoginApplicant(WAeUPPage):
290    grok.context(IApplicantsContainer)
291    grok.name('login')
292    grok.require('zope.Public')
293
294    title = u'Login'
295   
296    @property
297    def label(self):
298        return self.title
299
300    pnav = 3
301    prefix = u'APP'
302   
303    def update(self, SUBMIT=None):
304        self.ac_series = self.request.form.get('form.ac_series', None)
305        self.ac_number = self.request.form.get('form.ac_number', None)
306        if SUBMIT is None:
307            return
308
309        if self.request.principal.id == 'zope.anybody':
310            self.flash('Entered credentials are invalid')
311            return
312
313        if not IApplicantPrincipal.providedBy(self.request.principal):
314            # Don't care if user is already authenticated as non-applicant
315            return
316
317        pin = self.request.principal.access_code
318        if pin not in self.context.keys():
319            # Create applicant record
320            applicant = Applicant()
321            applicant.access_code = pin
322            self.context[pin] = applicant
323           
324        # Assign current principal the owner role on created applicant
325        # record
326        role_manager = IPrincipalRoleManager(self.context)
327        role_manager.assignRoleToPrincipal(
328            'waeup.ApplicationOwner', self.request.principal.id)
329        self.redirect(self.url(self.context[pin], 'edit'))
330        return
331
332#class AddApplicant(WAeUPAddFormPage):
333#    grok.context(IApplicantsContainer)
334#    grok.name('add')
335#    form_fields = grok.AutoFields(IApplicant)
336#    form_fields['fst_sit_results'].custom_widget = list_results_widget
337#    form_fields['passport'].custom_widget = EncodingImageFileWidget
338#    label = 'Add Applicant'
339#    title = 'Add Applicant'
340#    pnav = 1
341#
342#    @grok.action('Add applicant')
343#    def addApplicant(self, **data):
344#        from waeup.sirp.jambtables.applicants import Applicant
345#        applicant = Applicant()
346#        self.applyData(applicant, **data)
347#        # XXX: temporarily disabled.
348#        #self.context[applicant.reg_no] = applicant
349#        try:
350#            self.context[applicant.access_code] = applicant
351#        except KeyError:
352#            self.flash('The given access code is already in use!')
353#            return
354#        self.redirect(self.url(self.context))
355
356class DisplayApplicant(WAeUPDisplayFormPage):
357    grok.context(IApplicant)
358    grok.name('index')
359    grok.require('waeup.viewApplication')
360    form_fields = grok.AutoFields(IApplicant).omit('locked')
361    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
362    #form_fields['passport'].custom_widget = PassportDisplayWidget
363    form_fields['passport'].custom_widget = ThumbnailWidget
364    #form_fields['passport'].custom_widget = EncodingImageFileWidget
365    label = 'Applicant'
366    title = 'Applicant'
367    pnav = 3
368
369class EditApplicantStudent(WAeUPEditFormPage):
370    """An applicant-centered edit view for applicant data.
371    """
372    grok.context(IApplicant)
373    grok.name('edit')
374    grok.require('waeup.editApplication')
375    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
376    #form_fields['passport'].custom_widget = FileWidget
377    #form_fields['passport'].custom_widget = PassportWidget
378    form_fields['passport'].custom_widget = EncodingImageFileWidget
379    grok.template('form_edit_pde')
380
381    def emitLockMessage(self):
382        self.flash('The requested form is locked (read-only)')
383        self.redirect(self.url(self.context))
384        return
385   
386    def update(self):
387        if self.context.locked:
388            self.emitLockMessage()
389            return
390        super(EditApplicantStudent, self).update()
391        return
392
393    def filteredWidgets(self):
394        for widget in self.widgets:
395            if widget.name == 'form.confirm_passport':
396                continue
397            yield widget
398
399    @property
400    def label(self):
401        # XXX: Use current/upcoming session
402        return 'Apply for Post UDE Screening Test (2009/2010)'
403    title = 'Edit Application'
404    pnav = 3
405
406    @grok.action('Save')
407    def save(self, **data):
408        if self.context.locked:
409            self.emitLockMessage()
410            return
411        self.applyData(self.context, **data)
412        self.context._p_changed = True
413        return
414
415    @grok.action('Final Submit')
416    def finalsubmit(self, **data):
417        if self.context.locked:
418            self.emitLockMessage()
419            return
420        self.applyData(self.context, **data)
421        self.context._p_changed = True
422        if not self.dataComplete():
423            self.flash('Data yet not complete.')
424            return
425        self.context.locked = True
426        return
427
428    def dataComplete(self):
429        if context.confirm_passport is not True:
430            return False
431        if len(self.errors) > 0:
432            return False
433        return True
434
435class EditApplicantFull(WAeUPEditFormPage):
436    """A full edit view for applicant data.
437
438    This one is meant to be used by officers only.
439    """
440    grok.context(IApplicant)
441    grok.name('edit_full')
442    grok.require('waeup.editFullApplication')
443    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
444    form_fields['passport'].custom_widget = EncodingImageFileWidget
445    grok.template('form_edit_full')
446
447    def update(self):
448        if self.context.locked:
449            self.emitLockMessage()
450            return
451        super(EditApplicantFull, self).update()
452        return
453
454    def filteredWidgets(self):
455        for widget in self.widgets:
456            if widget.name == 'form.confirm_passport':
457                continue
458            yield widget
459
460    @property
461    def label(self):
462        return 'Application for %s' % self.context.access_code
463    title = 'Edit Application'
464    pnav = 3
465
466    @grok.action('Save')
467    def save(self, **data):
468        self.applyData(self.context, **data)
469        self.context._p_changed = True
470        return
Note: See TracBrowser for help on using the repository browser.