source: main/waeup.sirp/trunk/src/waeup/sirp/students/viewlets.py @ 7240

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

Rebuild applicants package (1st part). Applicants now have an applicant_id and a password and can use the regular login page to enter the portal.

Add user_type attribute to SIRPPrincipal objects.

Add some permissions in students package.

Some tests are still missing and will be re-added soon.

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