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

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

Allow upload of files with various extensions as defined in ALLOWED_FILE_EXTENSIONS. Check file extension in handle_file_upload, not in createFile.

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