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

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

Include permission waeup.uploadStudentFile to handle file uploads.

Fix pagetemplate filesuploadpage.pt.

Rearrange fileupload.pt

  • Property svn:keywords set to Id
File size: 12.5 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')
[7097]288    mus = 1024 * 150
289
[7117]290    @property
291    def input_name(self):
292        return "%s" % self.__name__
293
[7097]294    def update(self):
295        self.max_upload_size = string_from_bytes(self.mus)
[7117]296        delete_button = self.request.form.get(
297            'delete_%s' % self.input_name, None)
298        upload_button = self.request.form.get(
299            'upload_%s' % self.input_name, None)
[7108]300        if delete_button:
[7107]301            handle_file_delete(
302                context=self.context, view=self.view,
303                download_name=self.download_name)
304            self.view.redirect(
305                self.view.url(self.context, self.view.__name__))
306            return
[7108]307        if upload_button:
308            upload = self.request.form.get(self.input_name, None)
309            if upload:
310                # We got a fresh upload
[7111]311                handle_file_upload(upload,
312                    self.context, self.view, self.mus, self.download_name)
313                self.view.redirect(
314                    self.view.url(self.context, self.view.__name__))
[7117]315            else:
316                self.view.flash('No local file selected.')
317                self.view.redirect(
318                    self.view.url(self.context, self.view.__name__))
[7097]319        return
320
[7112]321class PassportDisplay(FileDisplay):
322    """Passport display viewlet.
323    """
324    grok.order(1)
325    grok.context(IStudent)
326    grok.view(StudentBaseDisplayFormPage)
327    grok.require('waeup.viewStudent')
328    grok.template('imagedisplay')
329    label = u'Passport Picture:'
330    download_name = u'passport.jpg'
331
332class PassportUploadManage(FileUpload):
333    """Passport upload viewlet for officers.
334    """
335    grok.order(1)
336    grok.context(IStudent)
337    grok.view(StudentBaseManageFormPage)
338    grok.require('waeup.manageStudents')
339    grok.template('imageupload')
340    label = u'Passport Picture (jpg only):'
341    mus = 1024 * 50
342    download_name = u'passport.jpg'
343
344class PassportUploadEdit(PassportUploadManage):
345    """Passport upload viewlet for students.
346    """
[7114]347    grok.view(StudentFilesUploadPage)
[7127]348    grok.require('waeup.uploadStudentFile')
[7112]349
[7097]350class BirthCertificateDisplay(FileDisplay):
[7112]351    """Birth Certificate display viewlet.
[7097]352    """
353    grok.order(1)
354    label = u'Birth Certificate:'
[7127]355    title = u'Birth Certificate Scan'
[7123]356    download_name = u'birth_certificate'
[7097]357
[7127]358class BirthCertificateUpload(FileUpload):
[7097]359    """Birth Certificate upload viewlet.
360    """
361    grok.order(1)
[7123]362    label = u'Birth Certificate:'
[7127]363    title = u'Birth Certificate Scan'
[7097]364    mus = 1024 * 150
[7123]365    download_name = u'birth_certificate'
[7097]366
[7111]367class AcceptanceLetterDisplay(FileDisplay):
[7112]368    """Acceptance Letter display viewlet.
[7111]369    """
370    grok.order(1)
371    label = u'Acceptance Letter:'
[7127]372    title = u'Acceptance Letter Scan'
[7123]373    download_name = u'acceptance_letter'
[7111]374
[7127]375class AcceptanceLetterUpload(FileUpload):
[7111]376    """AcceptanceLetter upload viewlet.
377    """
[7112]378    grok.order(2)
[7123]379    label = u'Acceptance Letter:'
[7127]380    title = u'Acceptance Letter Scan'
[7111]381    mus = 1024 * 150
[7123]382    download_name = u'acceptance_letter'
[7111]383
[7097]384class Image(grok.View):
[7106]385    """Renders jpeg images for students.
[7097]386    """
[7106]387    grok.baseclass()
[7097]388    grok.name('none.jpg')
[7112]389    grok.context(IStudentClearance)
[7097]390    grok.require('waeup.viewStudent')
[7106]391    download_name = u'none.jpg'
[7097]392
393    def render(self):
394        # A filename chooser turns a context into a filename suitable
395        # for file storage.
396        image = getUtility(IExtFileStore).getFileByContext(
[7106]397            self.context, attr=self.download_name)
[7097]398        if image is None:
399            # show placeholder image
[7123]400            self.response.setHeader('Content-Type', 'image/jpeg')
[7097]401            return open(DEFAULT_IMAGE_PATH, 'rb').read()
[7123]402        dummy,ext = os.path.splitext(image.name)
403        if ext == '.jpg':
404            self.response.setHeader('Content-Type', 'image/jpeg')
405        elif ext == '.png':
406            self.response.setHeader('Content-Type', 'image/png')
407        elif ext == '.pdf':
408            self.response.setHeader('Content-Type', 'application/pdf')
409        elif ext == '.tif':
410            self.response.setHeader('Content-Type', 'image/tiff')
[7097]411        return image
412
[7112]413class Passport(Image):
414    """Renders jpeg passport picture.
415    """
416    grok.name('passport.jpg')
417    download_name = u'passport.jpg'
418    grok.context(IStudent)
419
[7097]420class BirthCertificateImage(Image):
[7111]421    """Renders birth certificate jpeg scan.
[7097]422    """
[7123]423    grok.name('birth_certificate')
424    download_name = u'birth_certificate'
[7111]425
426class AcceptanceLetterImage(Image):
427    """Renders acceptance letter jpeg scan.
428    """
[7123]429    grok.name('acceptance_letter')
430    download_name = u'acceptance_letter'
Note: See TracBrowser for help on using the repository browser.