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

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

Rename permissions and add clearStudent permission.

  • Property svn:keywords set to Id
File size: 12.7 KB
RevLine 
[7106]1import os
[6642]2import grok
[7097]3from zope.component import getUtility
[6642]4from zope.interface import Interface
[7097]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 (
[7108]10    StudentClearanceDisplayFormPage, StudentClearanceManageFormPage,
[7112]11    write_log_message, StudentBaseManageFormPage, StudentBaseDisplayFormPage,
[7114]12    StudentFilesUploadPage)
[7112]13from waeup.sirp.students.interfaces import IStudent, IStudentClearance
[6642]14
15grok.context(IWAeUPObject) # Make IWAeUPObject the default context
[6687]16grok.templatedir('browser_templates')
[6642]17
[7123]18ALLOWED_FILE_EXTENSIONS = ('jpg', 'png', 'pdf', 'tif')
19
[6687]20class StudentManageSidebar(grok.ViewletManager):
21    grok.name('left_studentmanage')
[6642]22
[6687]23class StudentManageLink(grok.Viewlet):
[6660]24    """A link displayed in the student box which shows up for StudentNavigation
[6642]25    objects.
26
27    """
28    grok.baseclass()
[6687]29    grok.viewletmanager(StudentManageSidebar)
[6642]30    grok.context(IWAeUPObject)
31    grok.view(Interface)
32    grok.order(5)
[6660]33    grok.require('waeup.viewStudent')
[6642]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
[6687]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
[6642]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'
[6687]132    text = u'Accommodation'
[6642]133
134class StudentHistoryLink(StudentLink):
135    grok.order(6)
136    link = 'history'
137    text = u'History'
[6687]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
[7097]197        return self.view.application_url() + rel_link
198
[7107]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)
[7108]205    write_log_message(view, 'deleted: %s' % download_name)
[7123]206    view.flash('%s deleted.' % download_name)
[7107]207    return
208
[7106]209def handle_file_upload(upload, context, view, max_size, download_name=None):
210    """Handle upload of student file.
[7097]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    """
[7106]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()
[7123]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
[7097]239    size = file_size(upload)
240    if size > max_size:
[7106]241        view.flash('Uploaded file is too big.')
[7097]242        return False
243    upload.seek(0) # file pointer moved when determining size
244    store = getUtility(IExtFileStore)
[7106]245    file_id = IFileStoreNameChooser(context).chooseName(attr=download_name)
[7097]246    store.createFile(file_id, upload)
[7108]247    write_log_message(view, 'uploaded: %s (%s)' % (download_name,upload.filename))
248    view.flash('File %s uploaded.' % download_name)
[7097]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()
[7100]260    grok.context(IStudentClearance)
[7097]261    grok.viewletmanager(FileManager)
262    grok.view(StudentClearanceDisplayFormPage)
263    grok.template('filedisplay')
264    grok.order(1)
265    grok.require('waeup.viewStudent')
[7106]266    label = u'File:'
[7127]267    title = u'Scan'
[7106]268    download_name = u'filename.jpg'
[7097]269
[7107]270    @property
271    def file_exists(self):
272        image = getUtility(IExtFileStore).getFileByContext(
273            self.context, attr=self.download_name)
274        if image:
275            return True
276        else:
277            return False
278
[7097]279class FileUpload(FileDisplay):
280    """Base upload viewlet.
281    """
282    grok.baseclass()
[7100]283    grok.context(IStudentClearance)
[7097]284    grok.viewletmanager(FileManager)
285    grok.view(StudentClearanceManageFormPage)
286    grok.template('fileupload')
[7127]287    grok.require('waeup.uploadStudentFile')
[7134]288    tab_redirect = ''
[7097]289    mus = 1024 * 150
290
[7117]291    @property
292    def input_name(self):
293        return "%s" % self.__name__
294
[7097]295    def update(self):
296        self.max_upload_size = string_from_bytes(self.mus)
[7117]297        delete_button = self.request.form.get(
298            'delete_%s' % self.input_name, None)
299        upload_button = self.request.form.get(
300            'upload_%s' % self.input_name, None)
[7108]301        if delete_button:
[7107]302            handle_file_delete(
303                context=self.context, view=self.view,
304                download_name=self.download_name)
305            self.view.redirect(
[7134]306                self.view.url(
307                    self.context, self.view.__name__) + self.tab_redirect)
[7107]308            return
[7108]309        if upload_button:
310            upload = self.request.form.get(self.input_name, None)
311            if upload:
312                # We got a fresh upload
[7111]313                handle_file_upload(upload,
314                    self.context, self.view, self.mus, self.download_name)
315                self.view.redirect(
[7134]316                    self.view.url(
317                        self.context, self.view.__name__) + self.tab_redirect)
[7117]318            else:
319                self.view.flash('No local file selected.')
320                self.view.redirect(
[7134]321                    self.view.url(
322                        self.context, self.view.__name__) + self.tab_redirect)
[7097]323        return
324
[7112]325class PassportDisplay(FileDisplay):
326    """Passport display viewlet.
327    """
328    grok.order(1)
329    grok.context(IStudent)
330    grok.view(StudentBaseDisplayFormPage)
331    grok.require('waeup.viewStudent')
332    grok.template('imagedisplay')
333    label = u'Passport Picture:'
334    download_name = u'passport.jpg'
335
336class PassportUploadManage(FileUpload):
337    """Passport upload viewlet for officers.
338    """
339    grok.order(1)
340    grok.context(IStudent)
341    grok.view(StudentBaseManageFormPage)
[7136]342    grok.require('waeup.manageStudent')
[7112]343    grok.template('imageupload')
344    label = u'Passport Picture (jpg only):'
345    mus = 1024 * 50
346    download_name = u'passport.jpg'
[7134]347    tab_redirect = '#tab-2'
[7112]348
349class PassportUploadEdit(PassportUploadManage):
350    """Passport upload viewlet for students.
351    """
[7114]352    grok.view(StudentFilesUploadPage)
[7127]353    grok.require('waeup.uploadStudentFile')
[7112]354
[7097]355class BirthCertificateDisplay(FileDisplay):
[7112]356    """Birth Certificate display viewlet.
[7097]357    """
358    grok.order(1)
359    label = u'Birth Certificate:'
[7127]360    title = u'Birth Certificate Scan'
[7123]361    download_name = u'birth_certificate'
[7097]362
[7127]363class BirthCertificateUpload(FileUpload):
[7097]364    """Birth Certificate upload viewlet.
365    """
366    grok.order(1)
[7123]367    label = u'Birth Certificate:'
[7127]368    title = u'Birth Certificate Scan'
[7097]369    mus = 1024 * 150
[7123]370    download_name = u'birth_certificate'
[7134]371    tab_redirect = '#tab-2'
[7097]372
[7111]373class AcceptanceLetterDisplay(FileDisplay):
[7112]374    """Acceptance Letter display viewlet.
[7111]375    """
376    grok.order(1)
377    label = u'Acceptance Letter:'
[7127]378    title = u'Acceptance Letter Scan'
[7123]379    download_name = u'acceptance_letter'
[7111]380
[7127]381class AcceptanceLetterUpload(FileUpload):
[7111]382    """AcceptanceLetter upload viewlet.
383    """
[7112]384    grok.order(2)
[7123]385    label = u'Acceptance Letter:'
[7127]386    title = u'Acceptance Letter Scan'
[7111]387    mus = 1024 * 150
[7123]388    download_name = u'acceptance_letter'
[7134]389    tab_redirect = '#tab-2'
[7111]390
[7097]391class Image(grok.View):
[7106]392    """Renders jpeg images for students.
[7097]393    """
[7106]394    grok.baseclass()
[7097]395    grok.name('none.jpg')
[7112]396    grok.context(IStudentClearance)
[7097]397    grok.require('waeup.viewStudent')
[7106]398    download_name = u'none.jpg'
[7097]399
400    def render(self):
401        # A filename chooser turns a context into a filename suitable
402        # for file storage.
403        image = getUtility(IExtFileStore).getFileByContext(
[7106]404            self.context, attr=self.download_name)
[7097]405        if image is None:
406            # show placeholder image
[7123]407            self.response.setHeader('Content-Type', 'image/jpeg')
[7097]408            return open(DEFAULT_IMAGE_PATH, 'rb').read()
[7123]409        dummy,ext = os.path.splitext(image.name)
410        if ext == '.jpg':
411            self.response.setHeader('Content-Type', 'image/jpeg')
412        elif ext == '.png':
413            self.response.setHeader('Content-Type', 'image/png')
414        elif ext == '.pdf':
415            self.response.setHeader('Content-Type', 'application/pdf')
416        elif ext == '.tif':
417            self.response.setHeader('Content-Type', 'image/tiff')
[7097]418        return image
419
[7112]420class Passport(Image):
421    """Renders jpeg passport picture.
422    """
423    grok.name('passport.jpg')
424    download_name = u'passport.jpg'
425    grok.context(IStudent)
426
[7097]427class BirthCertificateImage(Image):
[7111]428    """Renders birth certificate jpeg scan.
[7097]429    """
[7123]430    grok.name('birth_certificate')
431    download_name = u'birth_certificate'
[7111]432
433class AcceptanceLetterImage(Image):
434    """Renders acceptance letter jpeg scan.
435    """
[7123]436    grok.name('acceptance_letter')
437    download_name = u'acceptance_letter'
Note: See TracBrowser for help on using the repository browser.