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

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

Reorganise permissions:

The navigation viewlets now manage the permission requirements themselves. No need to do this in pagetemplates.

Rename permission waeup.View to waeup.viewAcademics because it only refers to the academic section.

Add permission waeup.Authenticated (which is used in students). The StudentRecordOwner? explicitly needs this permission. Otherwise the MyData? tab disappears when changing the password.

Roles do not need to get the waeup.Public permission. This is already guaranteed in site.zcml.

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