source: main/waeup.sirp/branches/henrik-bootstrap/src/waeup/sirp/students/viewlets.py @ 7441

Last change on this file since 7441 was 7366, checked in by Henrik Bettermann, 13 years ago

We do not need a second plainactionbutton pagetemplate.

  • Property svn:keywords set to Id
File size: 14.7 KB
Line 
1## $Id: viewlets.py 7366 2011-12-18 07:20:14Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import os
19import grok
20from zope.component import getUtility
21from zope.interface import Interface
22from waeup.sirp.interfaces import (
23    ISIRPObject, IExtFileStore, IFileStoreNameChooser)
24from waeup.sirp.utils.helpers import string_from_bytes, file_size
25from waeup.sirp.browser import DEFAULT_IMAGE_PATH
26from waeup.sirp.browser.viewlets import PrimaryNavTab, PlainActionButton
27from waeup.sirp.browser.layout import default_primary_nav_template
28from waeup.sirp.students.browser import (
29    StudentClearanceDisplayFormPage, StudentClearanceManageFormPage,
30    write_log_message, StudentBaseManageFormPage, StudentBaseDisplayFormPage,
31    StudentFilesUploadPage, ExportPDFClearanceSlipPage)
32from waeup.sirp.students.interfaces import IStudent, IStudentClearance
33
34grok.context(ISIRPObject) # Make ISIRPObject the default context
35grok.templatedir('browser_templates')
36
37ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif')
38
39class StudentManageSidebar(grok.ViewletManager):
40    grok.name('left_studentmanage')
41
42class StudentManageLink(grok.Viewlet):
43    """A link displayed in the student box which shows up for StudentNavigation
44    objects.
45
46    """
47    grok.baseclass()
48    grok.viewletmanager(StudentManageSidebar)
49    grok.context(ISIRPObject)
50    grok.view(Interface)
51    grok.order(5)
52    grok.require('waeup.viewStudent')
53
54    link = 'index'
55    text = u'Base Data'
56
57    def render(self):
58        url = self.view.url(self.context.getStudent(), self.link)
59        return u'<div class="portlet"><a href="%s">%s</a></div>' % (
60                url, self.text)
61
62class StudentManageBaseLink(StudentManageLink):
63    grok.order(1)
64    link = 'index'
65    text = u'Base Data'
66
67class StudentManageApplicationLink(StudentManageLink):
68    grok.order(2)
69    link = 'application_slip'
70    text = u'Application Slip'
71
72    def render(self):
73        slip = getUtility(IExtFileStore).getFileByContext(
74            self.context.getStudent(), attr=self.link)
75        if slip:
76            url = self.view.url(self.context,self.link)
77            return u'<div class="portlet"><a href="%s">%s</a></div>' % (
78                    url, self.text)
79        return ''
80
81class StudentManageClearanceLink(StudentManageLink):
82    grok.order(3)
83    link = 'view_clearance'
84    text = u'Clearance Data'
85
86class StudentManagePersonalLink(StudentManageLink):
87    grok.order(4)
88    link = 'view_personal'
89    text = u'Personal Data'
90
91class StudentManageStudyCourseLink(StudentManageLink):
92    grok.order(5)
93    link = 'studycourse'
94    text = u'Study Course'
95
96class StudentManagePaymentsLink(StudentManageLink):
97    grok.order(6)
98    grok.require('waeup.payStudent')
99    link = 'payments'
100    text = u'Payments'
101
102class StudentManageAccommodationLink(StudentManageLink):
103    grok.order(7)
104    grok.require('waeup.handleAccommodation')
105    link = 'accommodation'
106    text = u'Accommodation Data'
107
108class StudentManageHistoryLink(StudentManageLink):
109    grok.order(8)
110    link = 'history'
111    text = u'History'
112
113
114class StudentMenu(grok.ViewletManager):
115    grok.name('top_student')
116
117class StudentLink(PlainActionButton):
118    """A link displayed in the student box which shows up for StudentNavigation
119    objects.
120
121    """
122    grok.baseclass()
123    grok.viewletmanager(StudentMenu)
124    grok.context(ISIRPObject)
125    grok.view(Interface)
126    grok.order(5)
127    grok.require('waeup.viewStudent')
128
129    link = 'index'
130    text = u'Base Data'
131
132    @property
133    def target_url(self):
134        """Get a URL to the target...
135        """
136        return self.view.url(self.context.getStudent(), self.link)
137
138class StudentBaseLink(StudentLink):
139    grok.order(1)
140    link = 'index'
141    text = u'Base Data'
142
143class ApplicationSlipLink(StudentLink):
144    grok.order(2)
145    link = 'application_slip'
146    text = u'Application Slip'
147
148    @property
149    def target_url(self):
150        slip = getUtility(IExtFileStore).getFileByContext(
151            self.context.getStudent(), attr=self.link)
152        if slip:
153            return self.view.url(self.context,self.link)
154        return
155
156class StudentClearanceLink(StudentLink):
157    grok.order(3)
158    link = 'view_clearance'
159    text = u'Clearance Data'
160
161class StudentPersonalLink(StudentLink):
162    grok.order(4)
163    link = 'view_personal'
164    text = u'Personal Data'
165
166class StudentStudyCourseLink(StudentLink):
167    grok.order(5)
168    link = 'studycourse'
169    text = u'Study Course'
170
171class StudentPaymentsLink(StudentLink):
172    grok.order(6)
173    link = 'payments'
174    text = u'Payments'
175
176class StudentAccommodationLink(StudentLink):
177    grok.order(7)
178    link = 'accommodation'
179    text = u'Accommodation'
180
181class StudentHistoryLink(StudentLink):
182    grok.order(8)
183    link = 'history'
184    text = u'History'
185
186class StudentsTab(PrimaryNavTab):
187    """Students tab in primary navigation.
188    """
189
190    grok.context(ISIRPObject)
191    grok.order(4)
192    grok.require('waeup.viewStudentsTab')
193
194    pnav = 4
195    tab_title = u'Students'
196
197    @property
198    def link_target(self):
199        return self.view.application_url('students')
200
201class PrimaryStudentNavManager(grok.ViewletManager):
202    """Viewlet manager for the primary navigation tab.
203    """
204    grok.name('primary_nav_student')
205
206class PrimaryStudentNavTab(grok.Viewlet):
207    """Base for primary student nav tabs.
208    """
209    grok.baseclass()
210    grok.viewletmanager(PrimaryStudentNavManager)
211    template = default_primary_nav_template
212    grok.order(1)
213    grok.require('waeup.Authenticated')
214    pnav = 0
215    tab_title = u'Some Text'
216
217    @property
218    def link_target(self):
219        return self.view.application_url()
220
221    @property
222    def active(self):
223        view_pnav = getattr(self.view, 'pnav', 0)
224        if view_pnav == self.pnav:
225            return 'active'
226        return ''
227
228class MyStudentDataTab(PrimaryStudentNavTab):
229    """MyData-tab in primary navigation.
230    """
231    grok.order(3)
232    grok.require('waeup.viewMyStudentDataTab')
233    pnav = 4
234    tab_title = u'My Data'
235
236    @property
237    def link_target(self):
238        rel_link = '/students/%s' % self.request.principal.id
239        return self.view.application_url() + rel_link
240
241def handle_file_delete(context, view, download_name):
242    """Handle deletion of student file.
243
244    """
245    store = getUtility(IExtFileStore)
246    store.deleteFileByContext(context, attr=download_name)
247    write_log_message(view, 'deleted: %s' % download_name)
248    view.flash('%s deleted.' % download_name)
249    return
250
251def handle_file_upload(upload, context, view, max_size, download_name=None):
252    """Handle upload of student file.
253
254    Returns `True` in case of success or `False`.
255
256    Please note that file pointer passed in (`upload`) most probably
257    points to end of file when leaving this function.
258    """
259    # Check some file requirements first
260    if upload.filename.count('.') == 0:
261        view.flash('File name has no extension.')
262        return False
263    if upload.filename.count('.') > 1:
264        view.flash('File name contains more than one dot.')
265        return False
266    basename, expected_ext = os.path.splitext(download_name)
267    dummy, ext = os.path.splitext(upload.filename)
268    ext.lower()
269    if expected_ext:
270        if ext != expected_ext:
271            view.flash('%s file extension expected.' %
272                expected_ext.replace('.',''))
273            return False
274    else:
275        if not ext.replace('.','') in ALLOWED_FILE_EXTENSIONS:
276            view.flash(
277                'Only the following extension are allowed: %s' %
278                ', '.join(ALLOWED_FILE_EXTENSIONS))
279            return False
280        download_name += ext
281    size = file_size(upload)
282    if size > max_size:
283        view.flash('Uploaded file is too big.')
284        return False
285    upload.seek(0) # file pointer moved when determining size
286    store = getUtility(IExtFileStore)
287    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
288    store.createFile(file_id, upload)
289    write_log_message(view, 'uploaded: %s (%s)' % (download_name,upload.filename))
290    view.flash('File %s uploaded.' % download_name)
291    return True
292
293class FileManager(grok.ViewletManager):
294    """Viewlet manager for uploading files, preferably scanned images.
295    """
296    grok.name('files')
297
298class FileDisplay(grok.Viewlet):
299    """Base file display viewlet.
300    """
301    grok.baseclass()
302    grok.context(IStudentClearance)
303    grok.viewletmanager(FileManager)
304    grok.view(StudentClearanceDisplayFormPage)
305    grok.template('filedisplay')
306    grok.order(1)
307    grok.require('waeup.viewStudent')
308    label = u'File'
309    title = u'Scan'
310    download_name = u'filename.jpg'
311
312    @property
313    def file_exists(self):
314        image = getUtility(IExtFileStore).getFileByContext(
315            self.context, attr=self.download_name)
316        if image:
317            return True
318        else:
319            return False
320
321class FileUpload(FileDisplay):
322    """Base upload viewlet.
323    """
324    grok.baseclass()
325    grok.context(IStudentClearance)
326    grok.viewletmanager(FileManager)
327    grok.view(StudentClearanceManageFormPage)
328    grok.template('fileupload')
329    grok.require('waeup.uploadStudentFile')
330    tab_redirect = ''
331    mus = 1024 * 150
332
333    @property
334    def input_name(self):
335        return "%s" % self.__name__
336
337    def update(self):
338        self.max_upload_size = string_from_bytes(self.mus)
339        delete_button = self.request.form.get(
340            'delete_%s' % self.input_name, None)
341        upload_button = self.request.form.get(
342            'upload_%s' % self.input_name, None)
343        if delete_button:
344            handle_file_delete(
345                context=self.context, view=self.view,
346                download_name=self.download_name)
347            self.view.redirect(
348                self.view.url(
349                    self.context, self.view.__name__) + self.tab_redirect)
350            return
351        if upload_button:
352            upload = self.request.form.get(self.input_name, None)
353            if upload:
354                # We got a fresh upload
355                handle_file_upload(upload,
356                    self.context, self.view, self.mus, self.download_name)
357                self.view.redirect(
358                    self.view.url(
359                        self.context, self.view.__name__) + self.tab_redirect)
360            else:
361                self.view.flash('No local file selected.')
362                self.view.redirect(
363                    self.view.url(
364                        self.context, self.view.__name__) + self.tab_redirect)
365        return
366
367class PassportDisplay(FileDisplay):
368    """Passport display viewlet.
369    """
370    grok.order(1)
371    grok.context(IStudent)
372    grok.view(StudentBaseDisplayFormPage)
373    grok.require('waeup.viewStudent')
374    grok.template('imagedisplay')
375    label = u'Passport Picture'
376    download_name = u'passport.jpg'
377
378class PassportUploadManage(FileUpload):
379    """Passport upload viewlet for officers.
380    """
381    grok.order(1)
382    grok.context(IStudent)
383    grok.view(StudentBaseManageFormPage)
384    grok.require('waeup.manageStudent')
385    grok.template('imageupload')
386    label = u'Passport Picture (jpg only)'
387    mus = 1024 * 50
388    download_name = u'passport.jpg'
389    tab_redirect = '#tab-2'
390
391class PassportUploadEdit(PassportUploadManage):
392    """Passport upload viewlet for students.
393    """
394    grok.view(StudentFilesUploadPage)
395    grok.require('waeup.uploadStudentFile')
396
397class BirthCertificateDisplay(FileDisplay):
398    """Birth Certificate display viewlet.
399    """
400    grok.order(1)
401    label = u'Birth Certificate'
402    title = u'Birth Certificate Scan'
403    download_name = u'birth_certificate'
404
405class BirthCertificateSlip(BirthCertificateDisplay):
406    grok.view(ExportPDFClearanceSlipPage)
407
408class BirthCertificateUpload(FileUpload):
409    """Birth Certificate upload viewlet.
410    """
411    grok.order(1)
412    label = u'Birth Certificate'
413    title = u'Birth Certificate Scan'
414    mus = 1024 * 150
415    download_name = u'birth_certificate'
416    tab_redirect = '#tab-2'
417
418class AcceptanceLetterDisplay(FileDisplay):
419    """Acceptance Letter display viewlet.
420    """
421    grok.order(1)
422    label = u'Acceptance Letter'
423    title = u'Acceptance Letter Scan'
424    download_name = u'acceptance_letter'
425
426class AcceptanceLetterSlip(AcceptanceLetterDisplay):
427    grok.view(ExportPDFClearanceSlipPage)
428
429class AcceptanceLetterUpload(FileUpload):
430    """AcceptanceLetter upload viewlet.
431    """
432    grok.order(2)
433    label = u'Acceptance Letter'
434    title = u'Acceptance Letter Scan'
435    mus = 1024 * 150
436    download_name = u'acceptance_letter'
437    tab_redirect = '#tab-2'
438
439class Image(grok.View):
440    """Renders images for students.
441    """
442    grok.baseclass()
443    grok.name('none.jpg')
444    grok.context(IStudentClearance)
445    grok.require('waeup.viewStudent')
446    download_name = u'none.jpg'
447
448    def render(self):
449        # A filename chooser turns a context into a filename suitable
450        # for file storage.
451        image = getUtility(IExtFileStore).getFileByContext(
452            self.context, attr=self.download_name)
453        if image is None:
454            # show placeholder image
455            self.response.setHeader('Content-Type', 'image/jpeg')
456            return open(DEFAULT_IMAGE_PATH, 'rb').read()
457        dummy,ext = os.path.splitext(image.name)
458        if ext == '.jpg':
459            self.response.setHeader('Content-Type', 'image/jpeg')
460        elif ext == '.png':
461            self.response.setHeader('Content-Type', 'image/png')
462        elif ext == '.pdf':
463            self.response.setHeader('Content-Type', 'application/pdf')
464        elif ext == '.tif':
465            self.response.setHeader('Content-Type', 'image/tiff')
466        return image
467
468class Passport(Image):
469    """Renders jpeg passport picture.
470    """
471    grok.name('passport.jpg')
472    download_name = u'passport.jpg'
473    grok.context(IStudent)
474
475class ApplicationSlipImage(Image):
476    """Renders application slip scan.
477    """
478    grok.name('application_slip')
479    download_name = u'application_slip'
480
481class BirthCertificateImage(Image):
482    """Renders birth certificate scan.
483    """
484    grok.name('birth_certificate')
485    download_name = u'birth_certificate'
486
487class AcceptanceLetterImage(Image):
488    """Renders acceptance letter scan.
489    """
490    grok.name('acceptance_letter')
491    download_name = u'acceptance_letter'
Note: See TracBrowser for help on using the repository browser.