## $Id: browser.py 7192 2011-11-25 07:15:50Z henrik $
##
## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
"""UI components for basic applicants and related components.
"""
import os
import sys
import grok
from datetime import datetime, date
from zope.authentication.interfaces import ILogout, IAuthentication
from zope.component import getUtility
from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.form import setUpEditWidgets
from zope.securitypolicy.interfaces import IPrincipalRoleManager
from zope.traversing.browser import absoluteURL
from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import (Frame, Paragraph, Image,
Table, Spacer)
from reportlab.platypus.tables import TableStyle
from waeup.sirp.accesscodes import invalidate_accesscode, get_access_code
from waeup.sirp.accesscodes.workflow import USED
from waeup.sirp.browser import (
WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
from waeup.sirp.browser.breadcrumbs import Breadcrumb
from waeup.sirp.browser.layout import NullValidator
from waeup.sirp.browser.pages import add_local_role, del_local_roles
from waeup.sirp.browser.resources import datepicker, tabs, datatable
from waeup.sirp.browser.viewlets import (
ManageActionButton, PrimaryNavTab, LeftSidebarLink
)
from waeup.sirp.interfaces import (
IWAeUPObject, ILocalRolesAssignable, IExtFileStore, IFileStoreNameChooser)
from waeup.sirp.permissions import get_users_with_local_roles
from waeup.sirp.browser import DEFAULT_PASSPORT_IMAGE_PATH
from waeup.sirp.university.interfaces import ICertificate
from waeup.sirp.utils.helpers import string_from_bytes, file_size
from waeup.sirp.widgets.datewidget import (
FriendlyDateWidget, FriendlyDateDisplayWidget)
from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
from waeup.sirp.widgets.objectwidget import (
WAeUPObjectWidget, WAeUPObjectDisplayWidget)
from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
from waeup.sirp.applicants.interfaces import (
IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
MAX_UPLOAD_SIZE,
)
from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
results_widget = CustomWidgetFactory(
WAeUPObjectWidget, ResultEntry)
results_display_widget = CustomWidgetFactory(
WAeUPObjectDisplayWidget, ResultEntry)
class ApplicantsRootPage(WAeUPPage):
grok.context(IApplicantsRoot)
grok.name('index')
grok.require('waeup.Public')
title = 'Applicants'
label = 'Application Section'
pnav = 3
def update(self):
super(ApplicantsRootPage, self).update()
datatable.need()
return
class ManageApplicantsRootActionButton(ManageActionButton):
grok.context(IApplicantsRoot)
grok.view(ApplicantsRootPage)
grok.require('waeup.manageApplication')
text = 'Manage application section'
class ApplicantsRootManageFormPage(WAeUPEditFormPage):
grok.context(IApplicantsRoot)
grok.name('manage')
grok.template('applicantsrootmanagepage')
title = 'Applicants'
label = 'Manage application section'
pnav = 3
grok.require('waeup.manageApplication')
taboneactions = ['Add applicants container', 'Remove selected','Cancel']
tabtwoactions1 = ['Remove selected local roles']
tabtwoactions2 = ['Add local role']
subunits = 'Applicants Containers'
def update(self):
tabs.need()
datatable.need()
return super(ApplicantsRootManageFormPage, self).update()
def getLocalRoles(self):
roles = ILocalRolesAssignable(self.context)
return roles()
def getUsers(self):
"""Get a list of all users.
"""
for key, val in grok.getSite()['users'].items():
url = self.url(val)
yield(dict(url=url, name=key, val=val))
def getUsersWithLocalRoles(self):
return get_users_with_local_roles(self.context)
# ToDo: Show warning message before deletion
@grok.action('Remove selected')
def delApplicantsContainers(self, **data):
form = self.request.form
child_id = form['val_id']
if not isinstance(child_id, list):
child_id = [child_id]
deleted = []
for id in child_id:
try:
del self.context[id]
deleted.append(id)
except:
self.flash('Could not delete %s: %s: %s' % (
id, sys.exc_info()[0], sys.exc_info()[1]))
if len(deleted):
self.flash('Successfully removed: %s' % ', '.join(deleted))
self.redirect(self.url(self.context, '@@manage')+'#tab-1')
return
@grok.action('Add applicants container', validator=NullValidator)
def addApplicantsContainer(self, **data):
self.redirect(self.url(self.context, '@@add'))
return
@grok.action('Cancel', validator=NullValidator)
def cancel(self, **data):
self.redirect(self.url(self.context))
return
@grok.action('Add local role', validator=NullValidator)
def addLocalRole(self, **data):
return add_local_role(self,2, **data)
@grok.action('Remove selected local roles')
def delLocalRoles(self, **data):
return del_local_roles(self,2,**data)
class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
grok.context(IApplicantsRoot)
grok.require('waeup.manageApplication')
grok.name('add')
grok.template('applicantscontaineraddpage')
title = 'Applicants'
label = 'Add applicants container'
pnav = 3
form_fields = grok.AutoFields(
IApplicantsContainerAdd).omit('code').omit('title')
form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
def update(self):
datepicker.need() # Enable jQuery datepicker in date fields.
return super(ApplicantsContainerAddFormPage, self).update()
@grok.action('Add applicants container')
def addApplicantsContainer(self, **data):
year = data['year']
code = u'%s%s' % (data['prefix'], year)
prefix = application_types_vocab.getTerm(data['prefix'])
title = u'%s %s/%s' % (prefix.title, year, year + 1)
if code in self.context.keys():
self.flash(
'An applicants container for the same application '
'type and entrance year exists already in the database.')
return
# Add new applicants container...
provider = data['provider'][1]
container = provider.factory()
self.applyData(container, **data)
container.code = code
container.title = title
self.context[code] = container
self.flash('Added: "%s".' % code)
self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
return
@grok.action('Cancel', validator=NullValidator)
def cancel(self, **data):
self.redirect(self.url(self.context, '@@manage') + '#tab-1')
class ApplicantsRootBreadcrumb(Breadcrumb):
"""A breadcrumb for applicantsroot.
"""
grok.context(IApplicantsRoot)
title = u'Applicants'
class ApplicantsContainerBreadcrumb(Breadcrumb):
"""A breadcrumb for applicantscontainers.
"""
grok.context(IApplicantsContainer)
class ApplicantBreadcrumb(Breadcrumb):
"""A breadcrumb for applicants.
"""
grok.context(IApplicant)
@property
def title(self):
"""Get a title for a context.
"""
return self.context.access_code
class ApplicantsAuthTab(PrimaryNavTab):
"""Applicants tab in primary navigation.
"""
grok.context(IWAeUPObject)
grok.order(3)
grok.require('waeup.viewApplication')
grok.template('primarynavtab')
pnav = 3
tab_title = u'Applicants'
@property
def link_target(self):
return self.view.application_url('applicants')
class ApplicantsAnonTab(ApplicantsAuthTab):
"""Applicants tab in primary navigation.
Display tab only for anonymous. Authenticated users can call the
form from the user navigation bar.
"""
grok.require('waeup.Anonymous')
tab_title = u'Application'
# Also zope.manager has role Anonymous.
# To avoid displaying this tab, uncomment the following.
#def tab_title(self):
# userid = self.request.principal.id
# if userid != 'zope.anybody':
# tt = u''
# else:
# tt = u'Application'
# return tt
class ApplicantsContainerPage(WAeUPDisplayFormPage):
"""The standard view for regular applicant containers.
"""
grok.context(IApplicantsContainer)
grok.name('index')
grok.require('waeup.Public')
grok.template('applicantscontainerpage')
pnav = 3
form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
form_fields['description'].custom_widget = ReSTDisplayWidget
@property
def title(self):
return "Applicants Container: %s" % self.context.title
@property
def label(self):
return self.context.title
class ApplicantsContainerManageActionButton(ManageActionButton):
grok.order(1)
grok.context(IApplicantsContainer)
grok.view(ApplicantsContainerPage)
grok.require('waeup.manageApplication')
text = 'Manage applicants container'
class LoginApplicantActionButton(ManageActionButton):
grok.order(2)
grok.context(IApplicantsContainer)
grok.view(ApplicantsContainerPage)
grok.require('waeup.Anonymous')
icon = 'login.png'
text = 'Login for applicants'
target = 'login'
class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
grok.context(IApplicantsContainer)
grok.name('manage')
grok.template('applicantscontainermanagepage')
form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
taboneactions = ['Save','Cancel']
tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
tabthreeactions1 = ['Remove selected local roles']
tabthreeactions2 = ['Add local role']
# Use friendlier date widget...
form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
grok.require('waeup.manageApplication')
@property
def title(self):
return "Applicants Container: %s" % self.context.title
@property
def label(self):
return 'Manage applicants container'
pnav = 3
def update(self):
datepicker.need() # Enable jQuery datepicker in date fields.
tabs.need()
datatable.need() # Enable jQurey datatables for contents listing
return super(ApplicantsContainerManageFormPage, self).update()
def getLocalRoles(self):
roles = ILocalRolesAssignable(self.context)
return roles()
def getUsers(self):
"""Get a list of all users.
"""
for key, val in grok.getSite()['users'].items():
url = self.url(val)
yield(dict(url=url, name=key, val=val))
def getUsersWithLocalRoles(self):
return get_users_with_local_roles(self.context)
@grok.action('Save')
def apply(self, **data):
self.applyData(self.context, **data)
self.flash('Data saved.')
return
# ToDo: Show warning message before deletion
@grok.action('Remove selected')
def delApplicant(self, **data):
form = self.request.form
if form.has_key('val_id'):
child_id = form['val_id']
else:
self.flash('No applicant selected!')
self.redirect(self.url(self.context, '@@manage')+'#tab-2')
return
if not isinstance(child_id, list):
child_id = [child_id]
deleted = []
for id in child_id:
try:
del self.context[id]
deleted.append(id)
except:
self.flash('Could not delete %s: %s: %s' % (
id, sys.exc_info()[0], sys.exc_info()[1]))
if len(deleted):
self.flash('Successfully removed: %s' % ', '.join(deleted))
self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
return
@grok.action('Add applicant', validator=NullValidator)
def addApplicant(self, **data):
self.redirect(self.url(self.context, 'addapplicant'))
return
@grok.action('Cancel', validator=NullValidator)
def cancel(self, **data):
self.redirect(self.url(self.context))
return
@grok.action('Add local role', validator=NullValidator)
def addLocalRole(self, **data):
return add_local_role(self,3, **data)
@grok.action('Remove selected local roles')
def delLocalRoles(self, **data):
return del_local_roles(self,3,**data)
class LoginApplicant(WAeUPPage):
grok.context(IApplicantsContainer)
grok.name('login')
grok.require('waeup.Public')
@property
def title(self):
return u"Applicant Login: %s" % self.context.title
@property
def label(self):
return u"Login for '%s' applicants only" % self.context.title
pnav = 3
@property
def ac_prefix(self):
return self.context.ac_prefix
def update(self, SUBMIT=None):
self.ac_series = self.request.form.get('form.ac_series', None)
self.ac_number = self.request.form.get('form.ac_number', None)
if SUBMIT is None:
return
if self.request.principal.id == 'zope.anybody':
self.flash('Entered credentials are invalid.')
return
if not IApplicantPrincipal.providedBy(self.request.principal):
# Don't care if user is already authenticated as non-applicant
return
# From here we handle an applicant (not an officer browsing)
pin = self.request.principal.access_code
# If application has not yet started,
# logout without marking AC as used
if self.context.startdate > date.today():
self.flash('Application has not yet started.')
auth = getUtility(IAuthentication)
ILogout(auth).logout(self.request)
self.redirect(self.url(self.context, 'login'))
return
# If application has ended and applicant record doesn't exist,
# logout without marking AC as used
if not pin in self.context.keys() and (
self.context.enddate < date.today()):
self.flash('Application has ended.')
auth = getUtility(IAuthentication)
ILogout(auth).logout(self.request)
self.redirect(self.url(self.context, 'login'))
return
# Mark AC as used (this also fires a pin related transition)
if get_access_code(pin).state != USED:
comment = u"AC invalidated"
# Here we know that the ac is in state initialized so we do not
# expect an exception
invalidate_accesscode(pin,comment)
if not pin in self.context.keys():
# Create applicant record
applicant = Applicant()
applicant.access_code = pin
self.context[pin] = applicant
role_manager = IPrincipalRoleManager(self.context[pin])
role_manager.assignRoleToPrincipal(
'waeup.local.ApplicationOwner', self.request.principal.id)
# Assign current principal the global Applicant role
role_manager = IPrincipalRoleManager(grok.getSite())
role_manager.assignRoleToPrincipal(
'waeup.Applicant', self.request.principal.id)
# Mark application as started
if IWorkflowState(self.context[pin]).getState() is INITIALIZED:
IWorkflowInfo(self.context[pin]).fireTransition('start')
self.redirect(self.url(self.context[pin], 'edit'))
return
class ApplicantAddFormPage(WAeUPAddFormPage):
"""Add-form to add an applicant.
"""
grok.context(IApplicantsContainer)
grok.require('waeup.manageApplication')
grok.name('addapplicant')
grok.template('applicantaddpage')
title = 'Applicants'
label = 'Add applicant'
pnav = 3
@property
def title(self):
return "Applicants Container: %s" % self.context.title
@property
def ac_prefix(self):
return self.context.ac_prefix
@grok.action('Create application record')
def addApplicant(self, **data):
ac_series = self.request.form.get('form.ac_series', None)
ac_number = self.request.form.get('form.ac_number', None)
pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number)
if not invalidate_accesscode(pin, comment=u"Invalidated by system"):
self.flash('%s is not a valid access code.' % pin)
self.redirect(self.url(self.context, '@@manage')+'#tab-2')
return
else:
# Create applicant record
applicant = Applicant()
applicant.access_code = pin
self.context[pin] = applicant
self.redirect(self.url(self.context[pin], 'edit'))
return
class AccessCodeViewLink(LeftSidebarLink):
grok.order(1)
grok.require('waeup.Public')
icon = 'actionicon_view.png'
title = 'View Record'
target = '/@@index'
@property
def url(self):
if not IApplicantPrincipal.providedBy(self.request.principal):
return ''
access_code = getattr(self.request.principal,'access_code',None)
if access_code:
applicant_object = get_applicant_data(access_code)
return absoluteURL(applicant_object, self.request) + self.target
return ''
class AccessCodeEditLink(AccessCodeViewLink):
grok.order(2)
grok.require('waeup.Public')
icon = 'actionicon_modify.png'
title = 'Edit Record'
target = '/@@edit'
@property
def url(self):
if not IApplicantPrincipal.providedBy(self.request.principal):
return ''
access_code = getattr(self.request.principal,'access_code',None)
if access_code:
applicant_object = get_applicant_data(access_code)
if applicant_object.locked:
return ''
return absoluteURL(applicant_object, self.request) + self.target
return ''
class AccessCodeSlipLink(AccessCodeViewLink):
grok.order(3)
grok.require('waeup.Public')
icon = 'actionicon_pdf.png'
title = 'Download Slip'
target = '/application_slip.pdf'
class DisplayApplicant(WAeUPDisplayFormPage):
grok.context(IApplicant)
grok.name('index')
grok.require('waeup.viewApplication')
form_fields = grok.AutoFields(IApplicant).omit(
'locked').omit('course_admitted')
form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
label = 'Applicant'
grok.template('form_display')
pnav = 3
def update(self):
self.passport_url = self.url(self.context, 'passport.jpg')
return
@property
def title(self):
if self.request.principal.title == 'Applicant':
return u'Your Application Record'
return '%s' % self.context.access_code
@property
def label(self):
container_title = self.context.__parent__.title
return '%s Application Record' % container_title
def getCourseAdmitted(self):
"""Return link, title and code in html format to the certificate
admitted.
"""
course_admitted = self.context.course_admitted
if ICertificate.providedBy(course_admitted):
url = self.url(course_admitted)
title = course_admitted.title
code = course_admitted.code
return '%s - %s' %(url,code,title)
return ''
class PDFActionButton(ManageActionButton):
grok.context(IApplicant)
grok.require('waeup.viewApplication')
icon = 'actionicon_pdf.png'
text = 'Download application slip'
target = 'application_slip.pdf'
class ExportPDFPage(grok.View):
"""Deliver a PDF slip of the context.
"""
grok.context(IApplicant)
grok.name('application_slip.pdf')
grok.require('waeup.viewApplication')
form_fields = grok.AutoFields(IApplicant).omit(
'locked').omit('course_admitted')
form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
prefix = 'form'
@property
def label(self):
container_title = self.context.__parent__.title
return '%s Application Record' % container_title
def getCourseAdmitted(self):
"""Return title and code in html format to the certificate
admitted.
"""
course_admitted = self.context.course_admitted
if ICertificate.providedBy(course_admitted):
title = course_admitted.title
code = course_admitted.code
return '%s - %s' %(code,title)
return ''
def setUpWidgets(self, ignore_request=False):
self.adapters = {}
self.widgets = setUpEditWidgets(
self.form_fields, self.prefix, self.context, self.request,
adapters=self.adapters, for_display=True,
ignore_request=ignore_request
)
def render(self):
SLIP_STYLE = TableStyle(
[('VALIGN',(0,0),(-1,-1),'TOP')]
)
pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
pdf.setTitle(self.label)
pdf.setSubject('Application')
pdf.setAuthor('%s (%s)' % (self.request.principal.title,
self.request.principal.id))
pdf.setCreator('WAeUP SIRP')
width, height = A4
style = getSampleStyleSheet()
pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
story = []
frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
header_title = getattr(grok.getSite(), 'name', u'Sample University')
story.append(Paragraph(header_title, style["Heading1"]))
frame_header.addFromList(story,pdf)
story = []
frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
story.append(Paragraph(self.label, style["Heading2"]))
story.append(Spacer(1, 18))
for msg in self.context.history.messages:
f_msg = '%s' % msg
story.append(Paragraph(f_msg, style["Normal"]))
story.append(Spacer(1, 24))
# insert passport photograph
img = getUtility(IExtFileStore).getFileByContext(self.context)
if img is None:
img = open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb')
doc_img = Image(img.name, width=4*cm, height=3*cm, kind='bound')
story.append(doc_img)
story.append(Spacer(1, 18))
# render widget fields
data = []
self.setUpWidgets()
for widget in self.widgets:
f_label = '%s:' % widget.label.strip()
f_label = Paragraph(f_label, style["Normal"])
f_text = '%s' % widget()
f_text = Paragraph(f_text, style["Normal"])
data.append([f_label,f_text])
f_label = 'Admitted Course of Study:'
f_text = '%s' % self.getCourseAdmitted()
f_label = Paragraph(f_label, style["Normal"])
f_text = Paragraph(f_text, style["Normal"])
data.append([f_label,f_text])
table = Table(data,style=SLIP_STYLE)
story.append(table)
frame_body.addFromList(story,pdf)
story = []
frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
f_text = '%s' % timestamp
story.append(Paragraph(f_text, style["Normal"]))
frame_footer.addFromList(story,pdf)
self.response.setHeader(
'Content-Type', 'application/pdf')
return pdf.getpdfdata()
class ApplicantManageActionButton(ManageActionButton):
grok.context(IApplicant)
grok.view(DisplayApplicant)
grok.require('waeup.manageApplication')
text = 'Manage application record'
target = 'edit_full'
def handle_img_upload(upload, context, view):
"""Handle upload of applicant image.
Returns `True` in case of success or `False`.
Please note that file pointer passed in (`upload`) most probably
points to end of file when leaving this function.
"""
size = file_size(upload)
if size > MAX_UPLOAD_SIZE:
view.flash('Uploaded image is too big!')
return False
upload.seek(0) # file pointer moved when determining size
store = getUtility(IExtFileStore)
file_id = IFileStoreNameChooser(context).chooseName()
store.createFile(file_id, upload)
return True
class EditApplicantFull(WAeUPEditFormPage):
"""A full edit view for applicant data.
"""
grok.context(IApplicant)
grok.name('edit_full')
grok.require('waeup.manageApplication')
form_fields = grok.AutoFields(IApplicant)
form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
grok.template('form_edit')
manage_applications = True
pnav = 3
def update(self):
datepicker.need() # Enable jQuery datepicker in date fields.
super(EditApplicantFull, self).update()
self.wf_info = IWorkflowInfo(self.context)
self.max_upload_size = string_from_bytes(MAX_UPLOAD_SIZE)
self.passport_changed = None
upload = self.request.form.get('form.passport', None)
if upload:
# We got a fresh upload
self.passport_changed = handle_img_upload(
upload, self.context, self)
return
@property
def title(self):
return self.context.access_code
@property
def label(self):
container_title = self.context.__parent__.title
return '%s Application Form' % container_title
def getTransitions(self):
"""Return a list of dicts of allowed transition ids and titles.
Each list entry provides keys ``name`` and ``title`` for
internal name and (human readable) title of a single
transition.
"""
allowed_transitions = self.wf_info.getManualTransitions()
return [dict(name='', title='No transition')] +[
dict(name=x, title=y) for x, y in allowed_transitions]
@grok.action('Save')
def save(self, **data):
if self.passport_changed is False: # False is not None!
return # error during image upload. Ignore other values
changed_fields = self.applyData(self.context, **data)
changed_fields = changed_fields.values()
fields_string = '+'.join(
' + '.join(str(i) for i in b) for b in changed_fields)
if self.passport_changed:
fields_string += ' + passport'
#self.context._p_changed = True
form = self.request.form
trans_id = form.get('transition', None)
if trans_id:
self.wf_info.fireTransition(trans_id)
self.flash('Form has been saved.')
ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
if fields_string:
self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
return
class EditApplicantStudent(EditApplicantFull):
"""An applicant-centered edit view for applicant data.
"""
grok.context(IApplicantEdit)
grok.name('edit')
grok.require('waeup.handleApplication')
form_fields = grok.AutoFields(IApplicantEdit).omit(
'locked', 'course_admitted', 'student_id',
'screening_score',
)
form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
grok.template('form_edit')
manage_applications = False
title = u'Your Application Form'
def emit_lock_message(self):
self.flash('The requested form is locked (read-only).')
self.redirect(self.url(self.context))
return
def update(self):
if self.context.locked:
self.emit_lock_message()
return
datepicker.need() # Enable jQuery datepicker in date fields.
super(EditApplicantStudent, self).update()
return
def dataNotComplete(self):
if not self.request.form.get('confirm_passport', False):
return 'Passport confirmation box not ticked.'
return False
@grok.action('Save')
def save(self, **data):
if self.passport_changed is False: # False is not None!
return # error during image upload. Ignore other values
self.applyData(self.context, **data)
#self.context._p_changed = True
self.flash('Form has been saved.')
return
@grok.action('Final Submit')
def finalsubmit(self, **data):
if self.passport_changed is False: # False is not None!
return # error during image upload. Ignore other values
self.applyData(self.context, **data)
self.context._p_changed = True
if self.dataNotComplete():
self.flash(self.dataNotComplete())
return
state = IWorkflowState(self.context).getState()
# This shouldn't happen, but the application officer
# might have forgotten to lock the form after changing the state
if state != STARTED:
self.flash('This form cannot be submitted. Wrong state!')
return
IWorkflowInfo(self.context).fireTransition('submit')
self.context.application_date = datetime.now()
self.context.locked = True
self.flash('Form has been submitted.')
self.redirect(self.url(self.context))
return
class ApplicantViewActionButton(ManageActionButton):
grok.context(IApplicant)
grok.view(EditApplicantFull)
grok.require('waeup.manageApplication')
icon = 'actionicon_view.png'
text = 'View application record'
target = 'index'
class PassportImage(grok.View):
"""Renders the passport image for applicants.
"""
grok.name('passport.jpg')
grok.context(IApplicant)
grok.require('waeup.viewApplication')
def render(self):
# A filename chooser turns a context into a filename suitable
# for file storage.
image = getUtility(IExtFileStore).getFileByContext(self.context)
self.response.setHeader(
'Content-Type', 'image/jpeg')
if image is None:
# show placeholder image
return open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb').read()
return image