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

Last change on this file since 6389 was 6383, checked in by Henrik Bettermann, 14 years ago

accesscodes.invalidate_accesscode(): return false if ac doesn't exist

Use this in browser.py to flash message in addApplicant if ac doesn't exist.

Use a view icon for view tabs.

File size: 27.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 datetime import datetime
28from zope.formlib.widget import CustomWidgetFactory
29from zope.formlib.form import setUpEditWidgets
30from zope.securitypolicy.interfaces import IPrincipalRoleManager
31from zope.traversing.browser import absoluteURL
32
33from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
34from reportlab.pdfgen import canvas
35from reportlab.lib.units import cm
36from reportlab.lib.pagesizes import A4, landscape
37from reportlab.lib.styles import getSampleStyleSheet
38from reportlab.platypus import (Frame, Paragraph, Image,
39    Table, Spacer)
40from reportlab.platypus.flowables import HRFlowable
41from reportlab.platypus.tables import TableStyle
42
43from waeup.sirp.accesscodes import invalidate_accesscode
44from waeup.sirp.browser import (
45    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
46from waeup.sirp.browser.breadcrumbs import Breadcrumb
47from waeup.sirp.browser.layout import NullValidator
48from waeup.sirp.browser.pages import add_local_role, del_local_roles
49from waeup.sirp.browser.resources import datepicker, tabs, datatable
50from waeup.sirp.browser.viewlets import (
51    ManageActionButton, PrimaryNavTab, LeftSidebarLink
52    )
53from waeup.sirp.image.browser.widget import (
54    ThumbnailWidget, EncodingImageFileWidget,
55    )
56from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
57from waeup.sirp.permissions import get_users_with_local_roles
58from waeup.sirp.university.interfaces import ICertificate
59from waeup.sirp.widgets.datewidget import (
60    FriendlyDateWidget, FriendlyDateDisplayWidget)
61from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
62from waeup.sirp.widgets.objectwidget import (
63    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
64from waeup.sirp.widgets.multilistwidget import (
65    MultiListWidget, MultiListDisplayWidget)
66from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
67from waeup.sirp.applicants.interfaces import (
68    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
69    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
70    )
71from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
72
73results_widget = CustomWidgetFactory(
74    WAeUPObjectWidget, ResultEntry)
75
76results_display_widget = CustomWidgetFactory(
77    WAeUPObjectDisplayWidget, ResultEntry)
78
79list_results_widget = CustomWidgetFactory(
80    MultiListWidget, subwidget=results_widget)
81
82list_results_display_widget = CustomWidgetFactory(
83    MultiListDisplayWidget, subwidget=results_display_widget)
84
85#TRANSITION_OBJECTS = create_workflow()
86
87#TRANSITION_DICT = dict([
88#    (transition_object.transition_id,transition_object.title)
89#    for transition_object in TRANSITION_OBJECTS])
90
91class ApplicantsRootPage(WAeUPPage):
92    grok.context(IApplicantsRoot)
93    grok.name('index')
94    grok.require('waeup.Public')
95    title = 'Applicants'
96    label = 'Application Section'
97    pnav = 3
98
99    def update(self):
100        super(ApplicantsRootPage, self).update()
101        datatable.need()
102        return
103
104class ManageApplicantsRootActionButton(ManageActionButton):
105    grok.context(IApplicantsRoot)
106    grok.view(ApplicantsRootPage)
107    grok.require('waeup.manageApplications')
108    text = 'Manage application section'
109
110class ApplicantsRootManageFormPage(WAeUPEditFormPage):
111    grok.context(IApplicantsRoot)
112    grok.name('manage')
113    grok.template('applicantsrootmanagepage')
114    title = 'Applicants'
115    label = 'Manage application section'
116    pnav = 3
117    grok.require('waeup.manageApplications')
118    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
119    tabtwoactions1 = ['Remove selected local roles']
120    tabtwoactions2 = ['Add local role']
121    subunits = 'Applicants Containers'
122
123    def update(self):
124        tabs.need()
125        datatable.need()
126        return super(ApplicantsRootManageFormPage, self).update()
127
128    def getLocalRoles(self):
129        roles = ILocalRolesAssignable(self.context)
130        return roles()
131
132    def getUsers(self):
133        """Get a list of all users.
134        """
135        for key, val in grok.getSite()['users'].items():
136            url = self.url(val)
137            yield(dict(url=url, name=key, val=val))
138
139    def getUsersWithLocalRoles(self):
140        return get_users_with_local_roles(self.context)
141
142    # ToDo: Show warning message before deletion
143    @grok.action('Remove selected')
144    def delApplicantsContainers(self, **data):
145        form = self.request.form
146        child_id = form['val_id']
147        if not isinstance(child_id, list):
148            child_id = [child_id]
149        deleted = []
150        for id in child_id:
151            try:
152                del self.context[id]
153                deleted.append(id)
154            except:
155                self.flash('Could not delete %s: %s: %s' % (
156                        id, sys.exc_info()[0], sys.exc_info()[1]))
157        if len(deleted):
158            self.flash('Successfully removed: %s' % ', '.join(deleted))
159        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
160        return
161
162    @grok.action('Add applicants container', validator=NullValidator)
163    def addApplicantsContainer(self, **data):
164        self.redirect(self.url(self.context, '@@add'))
165        return
166
167    @grok.action('Cancel', validator=NullValidator)
168    def cancel(self, **data):
169        self.redirect(self.url(self.context))
170        return
171
172    @grok.action('Add local role', validator=NullValidator)
173    def addLocalRole(self, **data):
174        return add_local_role(self,2, **data)
175
176    @grok.action('Remove selected local roles')
177    def delLocalRoles(self, **data):
178        return del_local_roles(self,2,**data)
179
180class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
181    grok.context(IApplicantsRoot)
182    grok.require('waeup.manageApplications')
183    grok.name('add')
184    grok.template('applicantscontaineraddpage')
185    title = 'Applicants'
186    label = 'Add applicants container'
187    pnav = 3
188
189    form_fields = grok.AutoFields(
190        IApplicantsContainerAdd).omit('code').omit('title')
191    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
192    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
193
194    def update(self):
195        datepicker.need() # Enable jQuery datepicker in date fields.
196        return super(ApplicantsContainerAddFormPage, self).update()
197
198    @grok.action('Add applicants container')
199    def addApplicantsContainer(self, **data):
200        year = data['year']
201        code = u'%s%s' % (data['prefix'], year)
202        prefix = application_types_vocab.getTerm(data['prefix'])
203        title = u'%s %s/%s' % (prefix.title, year, year + 1)
204        if code in self.context.keys():
205            self.flash(
206                'An applicants container for the same application '
207                'type and entrance year exists already in the database.')
208            return
209        # Add new applicants container...
210        provider = data['provider'][1]
211        container = provider.factory()
212        self.applyData(container, **data)
213        container.code = code
214        container.title = title
215        self.context[code] = container
216        self.flash('Added: "%s".' % code)
217        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
218        return
219
220    @grok.action('Cancel', validator=NullValidator)
221    def cancel(self, **data):
222        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
223
224class ApplicantsRootBreadcrumb(Breadcrumb):
225    """A breadcrumb for applicantsroot.
226    """
227    grok.context(IApplicantsRoot)
228    title = u'Application Section'
229
230class ApplicantsContainerBreadcrumb(Breadcrumb):
231    """A breadcrumb for applicantscontainers.
232    """
233    grok.context(IApplicantsContainer)
234
235class ApplicantBreadcrumb(Breadcrumb):
236    """A breadcrumb for applicants.
237    """
238    grok.context(IApplicant)
239
240    @property
241    def title(self):
242        """Get a title for a context.
243        """
244        return self.context.access_code
245
246class ApplicantsTab(PrimaryNavTab):
247    """Applicants tab in primary navigation.
248    """
249
250    grok.context(IWAeUPObject)
251    grok.order(3)
252    grok.require('waeup.Public')
253    grok.template('primarynavtab')
254
255    pnav = 3
256    tab_title = u'Applicants'
257
258    @property
259    def link_target(self):
260        return self.view.application_url('applicants')
261
262class ApplicantsContainerPage(WAeUPDisplayFormPage):
263    """The standard view for regular applicant containers.
264    """
265    grok.context(IApplicantsContainer)
266    grok.name('index')
267    grok.require('waeup.Public')
268    grok.template('applicantscontainerpage')
269    pnav = 3
270
271    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
272    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
273    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
274    form_fields['description'].custom_widget = ReSTDisplayWidget
275
276    @property
277    def title(self):
278        return "Applicants Container: %s" % self.context.title
279
280    @property
281    def label(self):
282        return self.context.title
283
284class ApplicantsContainerManageActionButton(ManageActionButton):
285    grok.order(1)
286    grok.context(IApplicantsContainer)
287    grok.view(ApplicantsContainerPage)
288    grok.require('waeup.manageApplications')
289    text = 'Manage applicants container'
290
291class LoginApplicantActionButton(ManageActionButton):
292    grok.order(2)
293    grok.context(IApplicantsContainer)
294    grok.view(ApplicantsContainerPage)
295    grok.require('waeup.Anonymous')
296    icon = 'login.png'
297    text = 'Login for applicants'
298    target = 'login'
299
300class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
301    grok.context(IApplicantsContainer)
302    grok.name('manage')
303    grok.template('applicantscontainermanagepage')
304    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
305    taboneactions = ['Save','Cancel']
306    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
307    tabthreeactions1 = ['Remove selected local roles']
308    tabthreeactions2 = ['Add local role']
309    # Use friendlier date widget...
310    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
311    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
312    grok.require('waeup.manageApplications')
313
314    @property
315    def title(self):
316        return "Applicants Container: %s" % self.context.title
317
318    @property
319    def label(self):
320        return 'Manage applicants container'
321
322    pnav = 3
323
324    def update(self):
325        datepicker.need() # Enable jQuery datepicker in date fields.
326        tabs.need()
327        datatable.need()  # Enable jQurey datatables for contents listing
328        return super(ApplicantsContainerManageFormPage, self).update()
329
330    def getLocalRoles(self):
331        roles = ILocalRolesAssignable(self.context)
332        return roles()
333
334    def getUsers(self):
335        """Get a list of all users.
336        """
337        for key, val in grok.getSite()['users'].items():
338            url = self.url(val)
339            yield(dict(url=url, name=key, val=val))
340
341    def getUsersWithLocalRoles(self):
342        return get_users_with_local_roles(self.context)
343
344    @grok.action('Save')
345    def apply(self, **data):
346        self.applyData(self.context, **data)
347        self.flash('Data saved.')
348        return
349
350    # ToDo: Show warning message before deletion
351    @grok.action('Remove selected')
352    def delApplicant(self, **data):
353        form = self.request.form
354        if form.has_key('val_id'):
355            child_id = form['val_id']
356        else:
357            self.flash('No applicant selected!')
358            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
359            return
360        if not isinstance(child_id, list):
361            child_id = [child_id]
362        deleted = []
363        for id in child_id:
364            try:
365                del self.context[id]
366                deleted.append(id)
367            except:
368                self.flash('Could not delete %s: %s: %s' % (
369                        id, sys.exc_info()[0], sys.exc_info()[1]))
370        if len(deleted):
371            self.flash('Successfully removed: %s' % ', '.join(deleted))
372        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
373        return
374
375    @grok.action('Add applicant', validator=NullValidator)
376    def addApplicant(self, **data):
377        self.redirect(self.url(self.context, 'addapplicant'))
378        return
379
380    @grok.action('Cancel', validator=NullValidator)
381    def cancel(self, **data):
382        self.redirect(self.url(self.context))
383        return
384
385    @grok.action('Add local role', validator=NullValidator)
386    def addLocalRole(self, **data):
387        return add_local_role(self,3, **data)
388
389    @grok.action('Remove selected local roles')
390    def delLocalRoles(self, **data):
391        return del_local_roles(self,3,**data)
392
393class LoginApplicant(WAeUPPage):
394    grok.context(IApplicantsContainer)
395    grok.name('login')
396    grok.require('waeup.Public')
397
398    @property
399    def title(self):
400        return u"Applicant Login: %s" % self.context.title
401
402    @property
403    def label(self):
404        return u'Login for applicants only'
405
406    pnav = 3
407
408    @property
409    def ac_prefix(self):
410        return self.context.ac_prefix
411
412    def update(self, SUBMIT=None):
413        self.ac_series = self.request.form.get('form.ac_series', None)
414        self.ac_number = self.request.form.get('form.ac_number', None)
415        if SUBMIT is None:
416            return
417        if self.request.principal.id == 'zope.anybody':
418            self.flash('Entered credentials are invalid.')
419            return
420        if not IApplicantPrincipal.providedBy(self.request.principal):
421            # Don't care if user is already authenticated as non-applicant
422            return
423
424        # From here we handle an applicant (not an officer browsing)
425        pin = self.request.principal.access_code
426
427        # Mark pin as used (this also fires a pin related transition)
428        invalidate_accesscode(pin)
429
430        if not pin in self.context.keys():
431            # Create applicant record
432            applicant = Applicant()
433            applicant.access_code = pin
434            self.context[pin] = applicant
435
436        role_manager = IPrincipalRoleManager(self.context[pin])
437        roles = role_manager.getRolesForPrincipal(self.request.principal.id)
438        if not 'waeup.local.ApplicationOwner' in map(lambda x, y: x, roles):
439            # Assign current principal the owner role on created applicant
440            # record
441            role_manager.assignRoleToPrincipal(
442                'waeup.local.ApplicationOwner', self.request.principal.id)
443
444            # Assign current principal the PortalUser role
445            role_manager = IPrincipalRoleManager(grok.getSite()['faculties'])
446            role_manager.assignRoleToPrincipal(
447                'waeup.PortalUser', self.request.principal.id)
448
449        # Mark application as started
450        if IWorkflowState(self.context[pin]).getState() is INITIALIZED:
451            IWorkflowInfo(self.context[pin]).fireTransition('start')
452
453        self.redirect(self.url(self.context[pin], 'edit'))
454        return
455
456class ApplicantAddFormPage(WAeUPAddFormPage):
457    """Add-form to add certificate to a department.
458    """
459    grok.context(IApplicantsContainer)
460    grok.require('waeup.manageApplications')
461    grok.name('addapplicant')
462    grok.template('applicantaddpage')
463    title = 'Applicants'
464    label = 'Add applicant'
465    pnav = 3
466
467    @property
468    def title(self):
469        return "Applicants Container: %s" % self.context.title
470
471    @property
472    def ac_prefix(self):
473        return self.context.ac_prefix
474
475    @grok.action('Create application record')
476    def addApplicant(self, **data):
477        ac_series = self.request.form.get('form.ac_series', None)
478        ac_number = self.request.form.get('form.ac_number', None)
479        pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number)
480        if not invalidate_accesscode(pin):
481            self.flash('%s is not a valid access code.' % pin)
482            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
483            return
484        else:
485            # Create applicant record
486            applicant = Applicant()
487            applicant.access_code = pin
488            self.context[pin] = applicant
489        self.redirect(self.url(self.context[pin], 'edit'))
490        return
491
492class AccessCodeLink(LeftSidebarLink):
493    grok.order(1)
494    grok.require('waeup.Public')
495
496    def render(self):
497        if not IApplicantPrincipal.providedBy(self.request.principal):
498            return ''
499        access_code = getattr(self.request.principal,'access_code',None)
500        if access_code:
501            applicant_object = get_applicant_data(access_code)
502            url = absoluteURL(applicant_object, self.request)
503            return u'<div class="portlet"><a href="%s/edit">%s</a></div>' % (
504                url,access_code)
505        return ''
506
507class DisplayApplicant(WAeUPDisplayFormPage):
508    grok.context(IApplicant)
509    grok.name('index')
510    grok.require('waeup.handleApplication')
511    form_fields = grok.AutoFields(IApplicant).omit(
512        'locked').omit('course_admitted')
513    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
514    form_fields['passport'].custom_widget = ThumbnailWidget
515    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
516    label = 'Applicant'
517    grok.template('form_display')
518    pnav = 3
519
520    @property
521    def title(self):
522        return '%s' % self.context.access_code
523
524    @property
525    def label(self):
526        container_title = self.context.__parent__.title
527        return '%s Application Record' % container_title
528
529    def getCourseAdmitted(self):
530        """Return link, title and code in html format to the certificate
531           admitted.
532        """
533        course_admitted = self.context.course_admitted
534        if ICertificate.providedBy(course_admitted):
535            url = self.url(course_admitted)
536            title = course_admitted.title
537            code = course_admitted.code
538            return '<a href="%s">%s - %s</a>' %(url,code,title)
539        return 'not yet admitted'
540
541class PDFActionButton(ManageActionButton):
542    grok.context(IApplicant)
543    grok.view(DisplayApplicant)
544    grok.require('waeup.handleApplication')
545    icon = 'actionicon_pdf.png'
546    text = 'Download application slip'
547    target = 'application_slip.pdf'
548
549class ExportPDFPage(grok.View):
550    """Deliver a PDF slip of the context.
551    """
552    grok.context(IApplicant)
553    grok.name('application_slip.pdf')
554    grok.require('waeup.handleApplication')
555    form_fields = grok.AutoFields(IApplicant).omit(
556        'locked').omit('course_admitted')
557    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
558    prefix = 'form'
559
560    @property
561    def label(self):
562        container_title = self.context.__parent__.title
563        return '%s Application Record' % container_title
564
565    def getCourseAdmitted(self):
566        """Return title and code in html format to the certificate
567           admitted.
568        """
569        course_admitted = self.context.course_admitted
570        if ICertificate.providedBy(course_admitted):
571            title = course_admitted.title
572            code = course_admitted.code
573            return '%s - %s' %(code,title)
574        return 'not yet admitted'
575
576    def setUpWidgets(self, ignore_request=False):
577        self.adapters = {}
578        self.widgets = setUpEditWidgets(
579            self.form_fields, self.prefix, self.context, self.request,
580            adapters=self.adapters, for_display=True,
581            ignore_request=ignore_request
582            )
583
584    def render(self):
585        # (0,0),(-1,-1) = whole table
586        # (0,0),(0,-1) = first column
587        # (-1,0),(-1,-1) = last column
588        # (0,0),(-1,0) = first row
589        # (0,-1),(-1,-1) = last row
590        SLIP_STYLE = TableStyle(
591            [('VALIGN',(0,0),(-1,-1),'TOP')]
592            )
593
594        pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
595        pdf.setTitle(self.label)
596        pdf.setSubject('Application')
597        pdf.setAuthor('%s (%s)' % (self.request.principal.title,
598            self.request.principal.id))
599        pdf.setCreator('WAeUP SIRP')
600        width, height = A4
601        style = getSampleStyleSheet()
602        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
603
604        story = []
605        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
606        header_title = getattr(grok.getSite(), 'name', u'Sample University')
607        story.append(Paragraph(header_title, style["Heading1"]))
608        #import pdb; pdb.set_trace()
609        frame_header.addFromList(story,pdf)
610
611        story = []
612        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
613        story.append(Paragraph(self.label, style["Heading2"]))
614        #story.append(HRFlowable())
615        story.append(Spacer(1, 18))
616        for msg in self.context.history.messages:
617            f_msg = '<font face="Courier" size=10>%s</font>' % msg
618            story.append(Paragraph(f_msg, style["Normal"]))
619        story.append(Spacer(1, 24))
620        self.setUpWidgets()
621        data = []
622        for widget in self.widgets:
623            f_label = '<font size=12>%s</font>' % widget.label.strip()
624            f_label = Paragraph(f_label, style["Normal"])
625            if widget.name != 'form.passport':
626                f_text = '<font size=12>%s</font>' % widget()
627                f_text = Paragraph(f_text, style["Normal"])
628                data.append([f_label,f_text])
629            else:
630                filename = widget._data.file.name
631                im = Image(filename,width=4*cm, height=3*cm,kind='bound')
632                data.append([f_label,im])
633        f_label = '<font size=12>Admitted Course of Study</font>'
634        f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
635        f_label = Paragraph(f_label, style["Normal"])
636        f_text = Paragraph(f_text, style["Normal"])
637        data.append([f_label,f_text])
638        table = Table(data,style=SLIP_STYLE)
639        story.append(table)
640        frame_body.addFromList(story,pdf)
641
642        story = []
643        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
644        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
645        f_text = '<font size=10>%s</font>' % timestamp
646        story.append(Paragraph(f_text, style["Normal"]))
647        frame_footer.addFromList(story,pdf)
648
649        self.response.setHeader(
650            'Content-Type', 'application/pdf')
651        return pdf.getpdfdata()
652
653class ApplicantManageActionButton(ManageActionButton):
654    grok.context(IApplicant)
655    grok.view(DisplayApplicant)
656    grok.require('waeup.manageApplications')
657    text = 'Manage application record'
658    target = 'edit_full'
659
660class ApplicantEditActionButton(ManageActionButton):
661    grok.context(IApplicant)
662    grok.view(DisplayApplicant)
663    grok.require('waeup.handleApplication')
664    text = 'Edit application record'
665    target = 'edit'
666
667class EditApplicantFull(WAeUPEditFormPage):
668    """A full edit view for applicant data.
669    """
670    grok.context(IApplicant)
671    grok.name('edit_full')
672    grok.require('waeup.manageApplications')
673    form_fields = grok.AutoFields(IApplicant)   #.omit('locked')
674    form_fields['passport'].custom_widget = EncodingImageFileWidget
675    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
676    grok.template('form_edit')
677    manage_applications = True
678    pnav = 3
679
680    def update(self):
681        datepicker.need() # Enable jQuery datepicker in date fields.
682        super(EditApplicantFull, self).update()
683        self.wf_info = IWorkflowInfo(self.context)
684        return
685
686    @property
687    def title(self):
688        return self.context.access_code
689
690    @property
691    def label(self):
692        container_title = self.context.__parent__.title
693        return '%s Application Form' % container_title
694
695    def getTransitions(self):
696        """Return a list of dicts of allowed transition ids and titles.
697
698        Each list entry provides keys ``name`` and ``title`` for
699        internal name and (human readable) title of a single
700        transition.
701        """
702        allowed_transitions = self.wf_info.getManualTransitions()
703        return [dict(name='', title='No transition')] +[
704            dict(name=x, title=y) for x, y in allowed_transitions]
705
706    @grok.action('Save')
707    def save(self, **data):
708        self.applyData(self.context, **data)
709        self.context._p_changed = True
710        form = self.request.form
711        if form.has_key('transition') and form['transition']:
712            transition_id = form['transition']
713            self.wf_info.fireTransition(transition_id)
714        self.flash('Form has been saved.')
715        self.context.getApplicantsRootLogger().info('Saved')
716        return
717
718class EditApplicantStudent(EditApplicantFull):
719    """An applicant-centered edit view for applicant data.
720    """
721    grok.context(IApplicantEdit)
722    grok.name('edit')
723    grok.require('waeup.handleApplication')
724    form_fields = grok.AutoFields(IApplicantEdit).omit('locked')
725    form_fields['passport'].custom_widget = EncodingImageFileWidget
726    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
727    grok.template('form_edit')
728    manage_applications = False
729
730
731    def emitLockMessage(self):
732        self.flash('The requested form is locked (read-only).')
733        self.redirect(self.url(self.context))
734        return
735
736    def update(self):
737        if self.context.locked:
738            self.emitLockMessage()
739            return
740        datepicker.need() # Enable jQuery datepicker in date fields.
741        super(EditApplicantStudent, self).update()
742        return
743
744    def dataNotComplete(self):
745        if not self.request.form.get('confirm_passport', False):
746            return 'Passport confirmation box not ticked.'
747        if len(self.errors) > 0:
748            return 'Form has errors.'
749        return False
750
751    @grok.action('Save')
752    def save(self, **data):
753        if self.context.locked:
754            self.emitLockMessage()
755            return
756        self.applyData(self.context, **data)
757        self.context._p_changed = True
758        self.flash('Form has been saved.')
759        return
760
761    @grok.action('Final Submit')
762    def finalsubmit(self, **data):
763        if self.context.locked:
764            self.emitLockMessage()
765            return
766        self.applyData(self.context, **data)
767        self.context._p_changed = True
768        if self.dataNotComplete():
769            self.flash(self.dataNotComplete())
770            return
771        state = IWorkflowState(self.context).getState()
772        # This shouldn't happen, but the application officer
773        # might have forgotten to lock the form after changing the state
774        if state != STARTED:
775            self.flash('This form cannot be submitted. Wrong state!')
776            return
777        IWorkflowInfo(self.context).fireTransition('submit')
778        self.context.locked = True
779        self.flash('Form has been submitted.')
780        self.redirect(self.url(self.context))
781        return
782
783class ApplicantViewActionButton(ManageActionButton):
784    grok.context(IApplicant)
785    grok.view(EditApplicantFull)
786    icon = 'actionicon_view.png'
787    text = 'View application record'
788    target = 'index'
Note: See TracBrowser for help on using the repository browser.