source: main/waeup.sirp/trunk/src/waeup/sirp/browser/pages.py @ 6126

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

Add 'Local roles' tab to facultymanagepage.pt and implement assignment of local roles on faculty level. The removal facilities and tests will follow.

File size: 48.4 KB
Line 
1# -*- coding: utf-8 -*-
2""" Viewing components for WAeUP objects.
3"""
4import copy
5import csv
6import grok
7import os
8import re
9import sys
10import time
11import re
12import smtplib
13from email.mime.text import MIMEText
14from hurry.workflow.interfaces import NoTransitionAvailableError
15from zope import schema
16try:
17    from zope.authentication.interfaces import (
18        IAuthentication, IUnauthenticatedPrincipal, ILogout)
19except ImportError:
20    # BBB
21    from zope.app.security.interfaces import (
22        IAuthentication, IUnauthenticatedPrincipal, ILogout)
23from zope.securitypolicy.interfaces import IPrincipalRoleManager, IPrincipalRoleMap
24from zope.component import (
25    getUtility, createObject,getAllUtilitiesRegisteredFor)
26from zope.component.interfaces import Invalid
27from zope.exceptions import DuplicationError
28from zope.session.interfaces import ISession
29from waeup.sirp.browser.resources import warning, datepicker, tabs, datatable
30from waeup.sirp.browser import (
31    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage,
32    WAeUPDisplayFormPage, NullValidator)
33from waeup.sirp.browser.interfaces import (
34    IUniversity, IFacultyContainer, IFaculty, IFacultyAdd,
35    IDepartment, IDepartmentAdd, ICourse, ICourseAdd, ICertificate,
36    ICertificateAdd, ICertificateCourse, ICertificateCourseAdd)
37from waeup.sirp.catalog import search_context
38from waeup.sirp.interfaces import(
39    IWAeUPObject, IUserContainer, IUserAccount, IDataCenter,
40    IWAeUPXMLImporter, IWAeUPXMLExporter, IBatchProcessor)
41from waeup.sirp.university.catalog import search
42from waeup.sirp.university.faculty import local_roles as faculty_local_roles
43from waeup.sirp.university.vocabularies import course_levels
44from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
45
46grok.context(IWAeUPObject)
47grok.templatedir('templates')
48
49#
50# Login/logout pages...
51#
52
53class LoginPage(WAeUPPage):
54    """A login page, available for all objects.
55    """
56    grok.name('login')
57    grok.context(IWAeUPObject)
58    grok.require('waeup.Public')
59
60    title = u'Login'
61    camefrom = None
62
63    def update(self, SUBMIT=None, camefrom=None):
64        self.camefrom = camefrom
65        if SUBMIT is not None:
66            if self.request.principal.id != 'zope.anybody':
67                self.flash('You logged in.')
68                if not self.camefrom:
69                    # User might have entered the URL directly. Let's beam
70                    # him back to our context.
71                    self.redirect(self.url(self.context))
72                    return
73                self.redirect(self.camefrom)
74                return
75            self.flash('You entered wrong credentials!')
76
77
78class LoginStaffPage(LoginPage):
79    """A login page for staff members.
80    """
81    grok.name('loginstaff')
82    grok.template('loginstaffpage')
83    grok.require('waeup.Public')
84    title = u'Staff Login'
85
86
87class LoginStudentPage(LoginPage):
88    """A login page for students.
89    """
90    grok.name('loginstudent')
91    grok.template('loginstudentpage')
92    grok.require('waeup.Public')
93    title = u'Student Login'
94
95class LogoutPage(WAeUPPage):
96    """A logout page. Calling this page will log the current user out.
97    """
98    grok.context(IWAeUPObject)
99    grok.require('waeup.Public')
100    grok.name('logout')
101   
102    def update(self):
103        if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
104            auth = getUtility(IAuthentication)
105            ILogout(auth).logout(self.request)
106            self.flash("You have been logged out. Thanks for using WAeUP SIRP!")
107        self.redirect(self.url(self.context))
108
109
110#
111# University related pages...
112#
113
114class UniversityPage(WAeUPDisplayFormPage):
115    """ The main university page.
116    """
117    grok.require('waeup.Public')
118    grok.name('index')
119    grok.context(IUniversity)
120    pnav = 0
121   
122    form_fields = grok.AutoFields(IUniversity)
123    form_fields['frontpage'].custom_widget = ReSTDisplayWidget
124
125    @property
126    def title(self):
127        return "Welcome"
128
129    @property
130    def label(self):
131        return self.context.title
132
133class Manage(WAeUPEditFormPage):
134    """Manage the basic properties of a `University` instance.
135    """
136    form_fields = grok.AutoFields(IUniversity)
137    grok.context(IUniversity)
138    grok.require('waeup.manageUniversity')
139    title = u'Edit portal settings'
140    pnav = 0
141
142    @grok.action('Save')
143    def save(self, **data):
144        self.applyData(self.context, **data)
145        self.flash('Settings have been saved.')
146        return
147   
148    @grok.action('Save and return')
149    def saveAndReturn(self, **data):
150        self.applyData(self.context, **data)
151        self.redirect(self.url(self.context))
152        self.flash('Settings have been saved.')
153        return
154    @grok.action('Cancel', validator=NullValidator)
155    def cancel(self, **data):
156        self.flash('Action cancelled.')
157        self.redirect(self.url(self.context))
158        return
159
160    @grok.action('Update plugins', validator=NullValidator)
161    def updatePlugins(self, **data):
162        self.context.updatePlugins()
163        self.flash('Plugins were updated. See log file for details.')
164        self.redirect(self.url(self.context))
165        return
166       
167#class RemoveFormPage(WAeUPPage):
168#    grok.require('waeup.manageUniversity')
169#    grok.name('remove')
170#    grok.template('removesubobjectspage')
171#    pnav = 1
172#
173#    def update(self):
174#        warning.need()
175#        form = self.request.form
176#        if 'CANCEL' in form.keys():
177#            self.redirect(self.url(self.context))
178#        if not 'REMOVE' in form.keys():
179#            return
180#        child_id = form['val_id']
181#        if not isinstance(child_id, list):
182#            child_id = [child_id]
183#        deleted = []
184#        for id in child_id:
185#            try:
186#                del self.context[id]
187#                deleted.append(id)
188#            except:
189#                self.flash('Could not delete %s: %s: %s' % (
190#                        id, sys.exc_info()[0], sys.exc_info()[1]))
191#        if len(deleted):
192#            self.flash('Successfully removed: %s' % ', '.join(deleted))
193#        # We have to redirect to let flash messages appear immediately...
194#        self.redirect(self.url())
195#        return
196
197class AdministrationPage(WAeUPPage):
198    """ The administration overview page.
199    """
200    grok.name('administration')
201    grok.context(IUniversity)
202    grok.require('waeup.manageUniversity')
203    title = u'Administration'
204    pnav = 0
205
206class RSS20Feed(grok.View):
207    """An RSS 2.0 feed.
208    """
209    grok.name('feed.rss')
210    grok.context(IUniversity)
211    grok.require('waeup.Public')
212    grok.template('universityrss20feed')
213
214    name = 'General news feed'
215    description = 'waeup.sirp now supports RSS 2.0 feeds :-)'
216    language = None
217    date = None
218    buildDate = None
219    editor = None
220    webmaster = None
221
222    @property
223    def title(self):
224        return getattr(grok.getSite(), 'name', u'Sample University')
225
226    @property
227    def contexttitle(self):
228        return self.name
229
230    @property
231    def link(self):
232        return self.url(grok.getSite())
233
234    def update(self):
235        self.response.setHeader('Content-Type', 'text/xml; charset=UTF-8')
236
237    def entries(self):
238        return ()
239   
240#
241# User container pages...
242#
243   
244class UserContainerPage(WAeUPPage):
245    """Overview page for all local users.
246    """
247    grok.require('waeup.manageUsers')
248    grok.context(IUserContainer)
249    grok.name('index')
250    title = 'Portal Users'
251   
252    def update(self, userid=None, adduser=None, edit=None, delete=None):
253        if edit is not None and userid is not None:
254            self.redirect(self.url(userid))
255        if delete is not None and userid is not None:
256            self.context.delUser(userid)
257            self.flash('User %s successfully deleted.' % userid)
258
259class AddUserFormPage(WAeUPAddFormPage):
260    grok.require('waeup.manageUsers')
261    grok.context(IUserContainer)
262    grok.name('add')
263    form_fields = grok.AutoFields(IUserAccount)
264    label = ''
265    title = 'Add user'
266
267    @grok.action('Add user')
268    def addUser(self, **data):
269        name = data['name']
270        title = data['title']
271        description = data['description']
272        password = data['password']
273        roles = data['roles']
274        try:
275            self.context.addUser(name, password, title=title,
276                                 description=description, roles=roles)
277        except KeyError:
278            self.status = Invalid('The userid chosen already exists '
279                                  'in the database')
280            return
281        except DuplicationError:
282            self.status = Invalid('The userid chosen already exists '
283                                  'in the database')
284            return
285        self.redirect(self.url(self.context))
286
287class UserEditFormPage(WAeUPEditFormPage):
288    """Edit a user account.
289    """
290    grok.context(IUserAccount)
291    grok.name('index')
292   
293    form_fields = grok.AutoFields(IUserAccount)
294    grok.require('waeup.manageUsers')
295
296    title = "Edit user"
297
298    @grok.action('Save')
299    def save(self, **data):
300        self.applyData(self.context, **data)
301        self.flash('User settings have been saved.')
302        return
303   
304    @grok.action('Save and return')
305    def saveAndReturn(self, **data):
306        self.applyData(self.context, **data)
307        self.flash('User settings have been saved.')
308        self.redirect(self.url(self.context.__parent__))
309        return
310
311    @grok.action('Cancel', validator=NullValidator)
312    def cancel(self, **data):
313        self.redirect(self.url(self.context.__parent__))
314        return
315
316#
317# Search pages...
318#
319class SearchStudentPage(WAeUPPage):
320    grok.context(IUniversity)
321    grok.name('searchstudent')
322    grok.template('searchstudentpage')
323    grok.require('waeup.View')
324    title = u"Student Search"
325    pnav = 2
326
327    def update(self, query=None):
328        self.search_result = []
329        if query is not None:
330            self.search_result = search_context(query)
331            print "RESULT: ", list(self.search_result)
332
333
334class SearchPage(WAeUPPage):
335    grok.context(IUniversity)
336    grok.name('search')
337    grok.template('searchpage')
338    grok.require('waeup.View')
339    title = u"Site Search"
340    pnav = 2
341
342    def update(self, *args, **kw):
343        form = self.request.form
344        self.hitlist = []
345        self.query = ''
346        if not 'query' in form:
347            return
348        query = form['query']
349        self.query = query
350        self.hitlist = search(query=self.query, view=self)
351        return
352       
353#
354# Contact forms...
355#
356class ContactAdminForm(WAeUPPage):
357    grok.context(IUniversity)
358    grok.name('contactadmin')
359    grok.template('contactadminform')
360    grok.require('waeup.Public')
361    pnav = 2
362   
363    def title(self):
364        """Return True if the calling user is authenticated.
365        """
366        userid = self.request.principal.id
367        if userid != 'zope.anybody':
368            tt = u'Contact'
369        else:
370            tt = u'Enquiries'         
371        return tt     
372       
373    def update(self, *args, **kw):
374        form = self.request.form
375        if not ('fullname' in form and 'email' in form and 'descr' in form):
376            return
377        self.fullname = fullname = form['fullname']
378        email = form['email']
379        self.descr = descr = form['descr']
380        regno =form['regno']
381        if not (fullname and email and descr):
382            self.flash('Error: All fields must be filled!')
383            return       
384        if not re.match("^[a-zA-Z0-9._%-]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$",
385                        email):
386            self.flash('Error: %s is not a valid email address!' % email)
387            return
388        if regno == 'zope.anybody':
389            regno = 'Anonymous User'
390        text = """Fullname: %s
391Member ID: %s
392Description: %s
393"""
394        msg = MIMEText(text % (fullname,regno,descr))
395        msg['From'] = '%s <%s>' % (fullname,email)
396       
397        # These parameters should be part of the portal configuration.
398        msg['To'] = 'contact@waeup.org'
399        msg['Subject'] = 'WAeUP Contact'
400        #server = smtplib.SMTP('smtp_server')
401        # Also connection details (username, passwords, host, port,
402        # etc.) should be settable somewhere.
403        # server.login('user_name','secret_password')
404        server = smtplib.SMTP('localhost')
405        server.sendmail(email,'contact@waeup.org',msg.as_string())
406       
407        server.quit()
408        self.flash('Your message has been sent.')
409        return
410
411#
412# Datacenter pages...
413#
414
415class DatacenterPage(WAeUPPage):
416    grok.context(IDataCenter)
417    grok.name('index')
418    grok.require('waeup.manageUniversity')
419    title = u'Data Center'
420    pnav = 0
421
422class DatacenterUploadPage(WAeUPPage):
423    grok.context(IDataCenter)
424    grok.name('upload')
425    grok.require('waeup.manageUniversity')
426    title = u'Data Center Upload'
427    pnav = 0
428   
429    def update(self, uploadfile=None, CANCEL=None, SUBMIT=None):
430        if CANCEL is not None:
431            self.redirect(self.url(self.context))
432            return
433        if not uploadfile:
434            return
435        try:
436            filename = uploadfile.filename
437            target = os.path.join(self.context.storage,
438                                  self.getNormalizedFileName(filename))
439            open(target, 'wb').write(uploadfile.read())
440            logger = self.context.logger
441            logger.info('%s: Uploaded file "%s"' % (
442                self.request.principal.id, target))
443        except IOError:
444            self.flash('Error while uploading file. Please retry.')
445            self.flash('I/O error: %s' % sys.exc_info()[1])
446            return
447        self.redirect(self.url(self.context))
448
449    def getNormalizedFileName(self, filename):
450        """Build sane filename.
451
452        An uploaded file foo.csv will be stored as foo_USERNAME.csv
453        where username is the principal id of the currently logged in
454        user.
455
456        Spaces in filename are replaced by underscore.
457        """
458        username = self.request.principal.id
459        filename = filename.replace(' ', '_')
460        # Only accept typical filname chars...
461        filtered_username = ''.join(re.findall('[a-zA-Z0-9_\.\-]', username))
462        base, ext = os.path.splitext(filename)
463        return '%s_%s%s' % (base, filtered_username, ext.lower())
464
465class DatacenterImportStep1(WAeUPPage):
466    """Manual import step 1: choose file
467    """
468    grok.context(IDataCenter)
469    grok.name('import1')
470    grok.template('datacenterimport1page')
471    grok.require('waeup.manageUniversity')
472    title = u'Process CSV file'
473    pnav = 0
474
475    def getFiles(self):
476        files = self.context.getFiles(sort='date')
477        for file in files:
478            name = file.name
479            if not name.endswith('.csv') and not name.endswith('.pending'):
480                continue
481            yield file
482   
483    def update(self, filename=None, select=None, cancel=None):
484        if cancel is not None:
485            self.flash('Import aborted')
486            self.redirect(self.url(self.context))
487            return
488        if select is not None:
489            # A filename was selected
490            session = ISession(self.request)['waeup.sirp']
491            session['import_filename'] = select
492            self.redirect(self.url(self.context, '@@import2'))
493
494class DatacenterImportStep2(WAeUPPage):
495    """Manual import step 2: choose importer
496    """
497    grok.context(IDataCenter)
498    grok.name('import2')
499    grok.template('datacenterimport2page')
500    grok.require('waeup.manageUniversity')
501    title = u'Process CSV file'
502    pnav = 0
503
504    filename = None
505    mode = 'create'
506    importer = None
507    mode_locked = False
508
509    def getPreviewHeader(self):
510        """Get the header fields of attached CSV file.
511        """
512        reader = csv.reader(open(self.fullpath, 'rb'))
513        return reader.next()
514   
515    def getPreviewBody(self):
516        """Get the first 5 rows of attached CSV file.
517        """
518        result = []
519        num = 0
520        for row in self.reader:
521            if num > 4:
522                break
523            num += 1
524            row = row.items()
525            # Sort fields in headerfield order
526            row = sorted(row, key=lambda k: self.reader.fieldnames.index(k[0]))
527            row = [x[1] for x in row]
528            result.append(row)
529        result.append(len(result[0]) * ['...'])
530        return result
531
532    def getImporters(self):
533        importers = getAllUtilitiesRegisteredFor(IBatchProcessor)
534        importers = [
535            dict(title=x.name, name=x.util_name) for x in importers]
536        return importers
537
538    def getModeFromFilename(self, filename):
539        """Lookup filename or path and return included mode name or None.
540        """
541        if not filename.endswith('.pending.csv'):
542            return None
543        base = os.path.basename(filename)
544        parts = base.rsplit('.', 3)
545        if len(parts) != 4:
546            return None
547        if parts[1] not in ['create', 'update', 'remove']:
548            return None
549        return parts[1]
550
551    def update(self, mode=None, importer=None,
552               back1=None, cancel=None, proceed=None):
553        session = ISession(self.request)['waeup.sirp']
554        self.filename = session.get('import_filename', None)
555       
556        if self.filename is None or back1 is not None:
557            self.redirect(self.url(self.context, '@@import1'))
558            return
559        if cancel is not None:
560            self.flash('Import aborted')
561            self.redirect(self.url(self.context))
562            return
563        self.mode = mode or session.get('import_mode', self.mode)
564        filename_mode = self.getModeFromFilename(self.filename)
565        if filename_mode is not None:
566            self.mode = filename_mode
567            self.mode_locked = True
568        self.importer = importer or session.get('import_importer', None)
569        session['import_mode'] = self.mode
570        session['import_importer'] = self.importer
571        if proceed is not None:
572            self.redirect(self.url(self.context, '@@import3'))
573            return
574        self.fullpath = os.path.join(self.context.storage, self.filename)
575        self.reader = csv.DictReader(open(self.fullpath, 'rb'))
576
577class DatacenterImportStep3(WAeUPPage):
578    """Manual import step 3: modify header
579    """
580    grok.context(IDataCenter)
581    grok.name('import3')
582    grok.template('datacenterimport3page')
583    grok.require('waeup.manageUniversity')
584    title = u'Process CSV file'
585    pnav = 0
586
587    filename = None
588    mode = None
589    importername = None
590   
591    @property
592    def nextstep(self):
593        return self.url(self.context, '@@import4')
594
595    def getPreviewHeader(self):
596        """Get the header fields of attached CSV file.
597        """
598        reader = csv.reader(open(self.fullpath, 'rb'))
599        return reader.next()
600   
601    def getPreviewBody(self):
602        """Get the first 5 rows of attached CSV file.
603        """
604        result = []
605        num = 0
606        for row in self.reader:
607            if num > 4:
608                break
609            num += 1
610            row = row.items()
611            # Sort fields in headerfield order
612            row = sorted(row, key=lambda k: self.reader.fieldnames.index(k[0]))
613            row = [x[1] for x in row]
614            result.append(row)
615        result.append(len(result[0]) * ['...'])
616        return result
617
618    def getPossibleHeaders(self):
619        """Get the possible headers.
620
621        The headers are described as dicts {value:internal_name,
622        title:displayed_name}
623        """
624        result = [dict(title='<IGNORE COL>', value='--IGNORE--')]
625        headers = self.importer.getHeaders()
626        result.extend([dict(title=x, value=x) for x in headers])
627        return result
628
629    def getWarnings(self):
630        import sys
631        result = []
632        try:
633            self.importer.checkHeaders(self.headerfields, mode=self.mode)
634        except:
635            fatal = '%s' % sys.exc_info()[1]
636            result.append(fatal)
637        return result
638   
639    @property
640    def nextstep(self):
641        return self.url(self.context, '@@import4')
642
643    def update(self, headerfield=None, back2=None, cancel=None, proceed=None):
644        session = ISession(self.request)['waeup.sirp']
645        self.filename = session.get('import_filename', None)
646        self.mode = session.get('import_mode', None)
647        self.importername = session.get('import_importer', None)
648       
649        if None in (self.filename, self.mode, self.importername):
650            self.redirect(self.url(self.context, '@@import2'))
651            return
652        if back2 is not None:
653            self.redirect(self.url(self.context ,'@@import2'))
654            return
655        if cancel is not None:
656            self.flash('Import aborted.')
657            self.redirect(self.url(self.context))
658            return
659
660        self.fullpath = os.path.join(self.context.storage, self.filename)
661        self.headerfields = headerfield or self.getPreviewHeader()
662        session['import_headerfields'] = self.headerfields
663
664        if proceed is not None:
665            self.redirect(self.url(self.context, '@@import4'))
666            return
667       
668        self.importer = getUtility(IBatchProcessor, name=self.importername)
669        self.reader = csv.DictReader(open(self.fullpath, 'rb'))
670
671class DatacenterImportStep4(WAeUPPage):
672    """Manual import step 4: do actual import
673    """
674    grok.context(IDataCenter)
675    grok.name('import4')
676    grok.template('datacenterimport4page')
677    grok.require('waeup.manageUniversity')
678    title = u'Process CSV file'
679    pnav = 0
680
681    filename = None
682    mode = None
683    importername = None
684    headerfields = None
685    warnnum = None
686
687    def update(self, back=None, finish=None, showlog=None):
688        if finish is not None:
689            self.redirect(self.url(self.context))
690            return
691        if back is not None:
692            self.redirect(self.url(self.context, '@@import3'))
693            return
694        session = ISession(self.request)['waeup.sirp']
695        self.filename = session.get('import_filename', None)
696        self.mode = session.get('import_mode', None)
697        self.importername = session.get('import_importer', None)
698        self.headerfields = session.get('import_headerfields', None)
699       
700        if None in (self.filename, self.mode, self.importername,
701                    self.headerfields):
702            self.redirect(self.url(self.context, '@@import3'))
703            return
704
705        if showlog is not None:
706            logfilename = "datacenter.log"
707            session['logname'] = logfilename
708            self.redirect(self.url(self.context, '@@show'))
709            return
710           
711        self.fullpath = os.path.join(self.context.storage, self.filename)
712        self.importer = getUtility(IBatchProcessor, name=self.importername)
713
714        # Perform batch processing...
715        # XXX: This might be better placed in datacenter module.
716        (linenum, self.warn_num,
717         fin_path, pending_path) = self.importer.doImport(
718            self.fullpath, self.headerfields, self.mode,
719            self.request.principal.id, logger=self.context.logger)
720        # Put result files in desired locations...
721        self.context.distProcessedFiles(
722            self.warn_num == 0, self.fullpath, fin_path, pending_path,
723            self.mode)
724
725        if self.warn_num:
726            self.flash('Processing of %d rows failed!' % self.warn_num)
727        self.flash('Successfully processed %s rows' % (
728                linenum - (self.warn_num)))
729
730class DatacenterLogsOverview(WAeUPPage):
731    grok.context(IDataCenter)
732    grok.name('logs')
733    grok.template('datacenterlogspage')
734    grok.require('waeup.manageUniversity')
735    title = u'Data Center Logs'
736    pnav = 0
737
738    def update(self, show=None, logname=None, back=None):
739        session = ISession(self.request)['waeup.sirp']
740        if back is not None:
741            self.redirect(self.url(self.context))
742            return
743        if logname is not None:
744            session['logname'] = logname
745        if show is not None:
746            self.redirect(self.url(self.context, '@@show'))
747            return
748        self.files = self.context.getLogFiles()
749
750class DatacenterLogsFileview(WAeUPPage):
751    grok.context(IDataCenter)
752    grok.name('show')
753    grok.template('datacenterlogsshowfilepage')
754    grok.require('waeup.manageUniversity')
755    title = u'Show file'
756    pnav = 0
757
758    def update(self, show=None, remove=None, back=None):
759        session = ISession(self.request)['waeup.sirp']
760        logname = session.get('logname', None)
761        if back is not None or logname is None:
762            self.redirect(self.url(self.context, '@@logs'))
763            return
764        self.filename = logname
765        self.files = self.context.getLogFiles()
766        fullpath = os.path.join(self.context.storage, 'logs', logname)
767        self.filecontents = open(fullpath, 'rb').read()
768
769class DatacenterSettings(WAeUPPage):
770    grok.context(IDataCenter)
771    grok.name('manage')
772    grok.template('datacentermanagepage')
773    grok.require('waeup.manageUniversity')
774    title = u'Data Center Settings'
775    pnav = 0
776
777    def update(self, newpath=None, move=False, overwrite=False,
778               save=None, cancel=None):
779        if move:
780            move = True
781        if overwrite:
782            overwrite = True
783        if newpath is None:
784            return
785        if cancel is not None:
786            self.redirect(self.url(self.context))
787            return
788        try:
789            not_copied = self.context.setStoragePath(newpath, move=move)
790            for name in not_copied:
791                self.flash('File already existed (not copied): %s' % name)
792        except ValueError:
793            self.flash('Given storage path cannot be used.')
794            self.flash('Error: %s' %sys.exc_info()[1])
795            return
796        if newpath:
797            self.flash('New storage path succefully set.')
798            self.redirect(self.url(self.context))
799        return
800
801class ExportXMLPage(grok.View):
802    """Deliver an XML representation of the context.
803    """
804    grok.name('export.xml')
805    grok.require('waeup.manageUniversity')
806   
807    def render(self):
808        exporter = IWAeUPXMLExporter(self.context)
809        xml = exporter.export().read()
810        self.response.setHeader(
811            'Content-Type', 'text/xml; charset=UTF-8')
812        return xml
813
814class ImportXMLPage(WAeUPPage):
815    """Replace the context object by an object created from an XML
816       representation.
817
818       XXX: This does not work for ISite objects, i.e. for instance
819            for complete University objects.
820    """
821    grok.name('importxml')
822    grok.require('waeup.manageUniversity')
823
824    def update(self, xmlfile=None, CANCEL=None, SUBMIT=None):
825        if CANCEL is not None:
826            self.redirect(self.url(self.context))
827            return
828        if not xmlfile:
829            return
830        importer = IWAeUPXMLImporter(self.context)
831        obj = importer.doImport(xmlfile)
832        if type(obj) != type(self.context):
833            return
834        parent = self.context.__parent__
835        name = self.context.__name__
836        self.context = obj
837        if hasattr(parent, name):
838            setattr(parent, name, obj)
839        else:
840            del parent[name]
841            parent[name] = obj
842            pass
843        return
844
845
846
847#
848# FacultyContainer pages...
849#
850
851class FacultyContainerPage(WAeUPPage):
852    """ Index page for faculty containers.
853    """
854    grok.context(IFacultyContainer)
855    grok.require('waeup.View')
856    grok.name('index')
857    title = 'Academics'
858    label = 'Academic Section'
859    pnav = 1
860    grok.template('facultypage')
861
862    #def update(self, code=None, edit=None, delete=None):
863    #    warning.need()
864    #    if delete is not None and code is not None:
865    #        try:
866    #            del self.context[code]
867    #            self.flash('Faculty %s successfully removed.' % code)               
868    #        except:
869    #            self.flash('Could not delete %s.' % code)
870   
871   
872class FacultyContainerManageFormPage(WAeUPEditFormPage):
873    """Manage the basic properties of a `Faculty` instance.
874    """
875    grok.context(IFacultyContainer)
876    grok.name('manage')
877    grok.require('waeup.manageUniversity')
878    grok.template('facultycontainermanagepage')
879    pnav = 1
880    taboneactions = ['Add faculty', 'Remove selected','Cancel']
881    subunits = 'Faculties'
882    title = 'Academics'
883
884    @property
885    def label(self):
886        return 'Manage academic section'
887       
888    def update(self):
889        tabs.need()
890        #warning.need()
891        return super(FacultyContainerManageFormPage, self).update()
892
893    # ToDo: Show warning message before deletion
894    @grok.action('Remove selected')
895    def delFaculties(self, **data):
896        form = self.request.form
897        child_id = form['val_id']
898        if not isinstance(child_id, list):
899            child_id = [child_id]
900        deleted = []
901        for id in child_id:
902            try:
903                del self.context[id]
904                deleted.append(id)
905            except:
906                self.flash('Could not delete %s: %s: %s' % (
907                        id, sys.exc_info()[0], sys.exc_info()[1]))
908        if len(deleted):
909            self.flash('Successfully removed: %s' % ', '.join(deleted))
910        self.redirect(self.url(self.context, '@@manage')+'#tab-1')   
911        return       
912
913    @grok.action('Add faculty', validator=NullValidator)
914    def addFaculty(self, **data):
915        self.redirect(self.url(self.context, '@@add'))
916        return   
917       
918    @grok.action('Cancel', validator=NullValidator)
919    def cancel(self, **data):
920        self.redirect(self.url(self.context))
921        return
922   
923
924class FacultyAddFormPage(WAeUPAddFormPage):
925    """ Page form to add a new faculty to a faculty container.
926    """
927    grok.context(IFacultyContainer)
928    grok.require('waeup.manageUniversity')
929    grok.name('add')
930    label = 'Add faculty'
931    title = 'Academics'
932    form_fields = grok.AutoFields(IFacultyAdd)
933    pnav = 1
934
935    @grok.action('Add faculty')
936    def addFaculty(self, **data):
937        faculty = createObject(u'waeup.Faculty')
938        self.applyData(faculty, **data)
939        try:
940            self.context.addFaculty(faculty)
941        except KeyError:
942            self.status = Invalid('The name chosen already exists '
943                                  'in the database')
944            return
945        except DuplicationError:
946            self.status = Invalid('The name chosen already exists '
947                                  'in the database')
948            return
949        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
950       
951    @grok.action('Cancel')
952    def cancel(self, **data):
953        self.redirect(self.url(self.context))
954
955#class RemoveFacultyFormPage(RemoveFormPage):
956#   """ Remove Faculty Page
957#    """
958#    grok.context(IFacultyContainer)
959#    title = "Remove faculty"
960#    pnav = 1
961
962#
963# Faculty pages
964#
965class FacultyPage(WAeUPPage):
966    """Index page of faculties.
967    """
968    grok.context(IFaculty)
969    grok.require('waeup.View')
970    grok.name('index')
971    pnav = 1
972
973    @property
974    def title(self):
975        return self.context.longtitle()
976       
977    @property
978    def label(self):
979        return 'Departments'
980
981    #def update(self, code=None, edit=None, delete=None):
982    #    warning.need()
983    #    if delete is not None and code is not None:
984    #        try:
985    #            del self.context[code]
986    #            self.flash('Department %s successfully removed.' % code)               
987    #        except:
988    #            self.flash('Could not delete %s.' % code)
989   
990class FacultyManageFormPage(WAeUPEditFormPage):
991    """Manage the basic properties of a `Faculty` instance.
992    """
993    grok.context(IFaculty)
994    grok.name('manage')
995    grok.require('waeup.manageUniversity')
996    grok.template('facultymanagepage')
997    pnav = 1
998    taboneactions = ['Save','Cancel']
999    tabtwoactions = ['Add department', 'Remove selected','Cancel']
1000    tabthreeactions = ['Add local role']
1001    subunits = 'Departments'
1002
1003    form_fields = grok.AutoFields(IFaculty)
1004
1005    @property
1006    def label(self):
1007        return 'Manage faculty'
1008       
1009    @property
1010    def title(self):
1011        return self.context.longtitle()
1012
1013    def getLocalRoles(self):
1014        #import pdb; pdb.set_trace()
1015        return faculty_local_roles
1016       
1017    def getUsers(self):
1018        """Get a list of all users.
1019        """
1020        for key, val in grok.getSite()['users'].items():
1021            url = self.url(val)
1022            #import pdb; pdb.set_trace()
1023            yield(dict(url=url, name=key, val=val))
1024
1025    def update(self):
1026        tabs.need()
1027        #warning.need()
1028        return super(FacultyManageFormPage, self).update()
1029
1030    @grok.action('Save')
1031    def save(self, **data):
1032        self.applyData(self.context, **data)
1033        return
1034         
1035    # ToDo: Show warning message before deletion
1036    @grok.action('Remove selected')
1037    def delDepartments(self, **data):
1038        form = self.request.form
1039        child_id = form['val_id']
1040        if not isinstance(child_id, list):
1041            child_id = [child_id]
1042        deleted = []
1043        for id in child_id:
1044            try:
1045                del self.context[id]
1046                deleted.append(id)
1047            except:
1048                self.flash('Could not delete %s: %s: %s' % (
1049                        id, sys.exc_info()[0], sys.exc_info()[1]))
1050        if len(deleted):
1051            self.flash('Successfully removed: %s' % ', '.join(deleted))
1052        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')   
1053        return       
1054
1055    @grok.action('Add department', validator=NullValidator)
1056    def addDepartment(self, **data):
1057        self.redirect(self.url(self.context, '@@add'))
1058        return   
1059       
1060    @grok.action('Cancel', validator=NullValidator)
1061    def cancel(self, **data):
1062        self.redirect(self.url(self.context))
1063        return
1064       
1065    @grok.action('Add local role', validator=NullValidator)
1066    def addLocalRole(self, **data):
1067        form = self.request.form
1068        localrole = form['local_role']
1069        user = form['user']
1070        role_manager = IPrincipalRoleManager(self.context)
1071        role_map = IPrincipalRoleMap(self.context)
1072        #import pdb; pdb.set_trace()
1073        role_manager.assignRoleToPrincipal(localrole, user)
1074        self.redirect(self.url(self.context, u'@@manage')+'#tab-3')
1075        return
1076
1077class DepartmentAddFormPage(WAeUPAddFormPage):
1078    """Add a department to a faculty.
1079    """
1080    grok.context(IFaculty)
1081    grok.name('add')
1082    grok.require('waeup.manageUniversity')
1083    label = 'Add department'
1084    form_fields = grok.AutoFields(IDepartmentAdd)
1085    pnav = 1
1086
1087    @property
1088    def title(self):
1089        return self.context.longtitle()
1090   
1091    @grok.action('Add department')
1092    def addDepartment(self, **data):
1093        department = createObject(u'waeup.Department')
1094        self.applyData(department, **data)
1095        try:
1096            self.context.addDepartment(department)
1097        except KeyError:
1098            self.status = Invalid('The code chosen already exists '
1099                                  'in the database')
1100            return
1101        except DuplicationError:
1102            self.status = Invalid('The code chosen already exists '
1103                                  'in the database')
1104            return
1105        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
1106       
1107    @grok.action('Cancel')
1108    def cancel(self, **data):
1109        self.redirect(self.url(self.context))
1110
1111
1112
1113#class RemoveDepartmentFormPage(RemoveFormPage):
1114#    """ Remove Department Page
1115#    """
1116#    grok.context(IFaculty)
1117#    title = "Remove department"
1118#    pnav = 1
1119
1120
1121#
1122# Department pages
1123#
1124class DepartmentPage(WAeUPPage):
1125    """Department index page.
1126    """
1127    grok.context(IDepartment)
1128    grok.require('waeup.View')
1129    grok.name('index')
1130    pnav = 1
1131    label = 'Courses and Certificates'
1132
1133    @property
1134    def title(self):
1135        return self.context.longtitle()
1136   
1137    def update(self):
1138        tabs.need()
1139        datatable.need()
1140        super(DepartmentPage, self).update()
1141        return
1142       
1143    def getCourses(self):
1144        """Get a list of all stored courses.
1145        """
1146        for key, val in self.context.courses.items():
1147            url = self.url(val)
1148            yield(dict(url=url, name=key, container=val))
1149
1150    def getCertificates(self):
1151        """Get a list of all stored certificates.
1152        """
1153        for key, val in self.context.certificates.items():
1154            url = self.url(val)
1155            yield(dict(url=url, name=key, container=val))             
1156
1157class DepartmentManageFormPage(WAeUPEditFormPage):
1158    """Manage the basic properties of a `Department` instance.
1159    """
1160    grok.context(IDepartment)
1161    grok.name('manage')
1162    grok.require('waeup.manageUniversity')
1163    pnav = 1
1164    grok.template('departmentmanagepage')
1165    taboneactions = ['Save','Cancel']
1166    tabtwoactions = ['Add course', 'Remove selected courses','Cancel']
1167    tabthreeactions = ['Add certificate', 'Remove selected certificates', 'Cancel']
1168
1169    @property
1170    def label(self):
1171        return 'Manage department'
1172       
1173    @property
1174    def title(self):
1175        return self.context.longtitle()   
1176       
1177    def getCourses(self):
1178        """Get a list of all stored courses.
1179        """
1180        for key, val in self.context.courses.items():
1181            url = self.url(val)
1182            yield(dict(url=url, name=key, container=val))
1183
1184    def getCertificates(self):
1185        """Get a list of all stored certificates.
1186        """
1187        for key, val in self.context.certificates.items():
1188            url = self.url(val)
1189            yield(dict(url=url, name=key, container=val))                     
1190
1191    def update(self):
1192        tabs.need()
1193        datatable.need()
1194        super(DepartmentManageFormPage, self).update()
1195        return
1196
1197    form_fields = grok.AutoFields(IDepartment)
1198
1199    @grok.action('Save')
1200    def save(self, **data):
1201        self.applyData(self.context, **data)
1202        return
1203       
1204    # ToDo: Show warning message before deletion
1205    @grok.action('Remove selected courses')
1206    def delCourses(self, **data):
1207        form = self.request.form
1208        child_id = form['val_id']
1209        if not isinstance(child_id, list):
1210            child_id = [child_id]
1211        deleted = []
1212        for id in child_id:
1213            try:
1214                del self.context.courses[id]
1215                deleted.append(id)
1216            except:
1217                self.flash('Could not delete %s: %s: %s' % (
1218                        id, sys.exc_info()[0], sys.exc_info()[1]))
1219        if len(deleted):
1220            self.flash('Successfully removed: %s' % ', '.join(deleted))
1221        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')   
1222        return       
1223     
1224    @grok.action('Remove selected certificates')   
1225    def delCertificates(self, **data):
1226        form = self.request.form
1227        child_id = form['val_id']
1228        if not isinstance(child_id, list):
1229            child_id = [child_id]
1230        deleted = []
1231        for id in child_id:
1232            try:
1233                del self.context.certificates[id]
1234                deleted.append(id)
1235            except:
1236                self.flash('Could not delete %s: %s: %s' % (
1237                        id, sys.exc_info()[0], sys.exc_info()[1]))
1238        if len(deleted):
1239            self.flash('Successfully removed: %s' % ', '.join(deleted))
1240        self.redirect(self.url(self.context, u'@@manage')+'#tab-3')   
1241        return               
1242
1243    @grok.action('Add course', validator=NullValidator)
1244    def addCourse(self, **data):
1245        self.redirect(self.url(self.context, 'addcourse'))
1246        return   
1247       
1248    @grok.action('Add certificate', validator=NullValidator)
1249    def addCertificate(self, **data):
1250        self.redirect(self.url(self.context, 'addcertificate'))
1251        return                     
1252   
1253    @grok.action('Cancel', validator=NullValidator)
1254    def cancel(self, **data):
1255        self.redirect(self.url(self.context))
1256        return
1257
1258class CourseAddFormPage(WAeUPAddFormPage):
1259    """Add-form to add course to a department.
1260    """
1261    grok.context(IDepartment)
1262    grok.name('addcourse')
1263    grok.require('waeup.manageUniversity')
1264    label = u'Add course'
1265    form_fields = grok.AutoFields(ICourseAdd)
1266    pnav = 1
1267   
1268    @property
1269    def title(self):
1270        return self.context.longtitle()       
1271   
1272    @grok.action('Add course')
1273    def addCourse(self, **data):
1274        course = createObject(u'waeup.Course')
1275        self.applyData(course, **data)
1276        try:
1277            self.context.courses.addCourse(course)
1278        except KeyError:
1279            self.status = Invalid('The code chosen already exists '
1280                                  'in the database')
1281            return
1282        except DuplicationError:
1283            self.status = Invalid('The code chosen already exists '
1284                                  'in the database')
1285            return
1286        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')   
1287
1288    @grok.action('Cancel', validator=NullValidator)
1289    def cancel(self, **data):
1290        self.redirect(self.url(self.context))
1291        return
1292
1293class CertificateAddFormPage(WAeUPAddFormPage):
1294    """Add-form to add certificate to a department.
1295    """
1296    grok.context(IDepartment)
1297    grok.name('addcertificate')
1298    grok.require('waeup.manageUniversity')
1299    label = u'Add certificate'
1300    form_fields = grok.AutoFields(ICertificateAdd)
1301    pnav = 1
1302   
1303    @property
1304    def title(self):
1305        return self.context.longtitle()       
1306       
1307    @grok.action('Add certificate')
1308    def addCertificate(self, **data):
1309        certificate = createObject(u'waeup.Certificate')
1310        self.applyData(certificate, **data)
1311       
1312        #try:
1313        #    self.applyData(certificate, **data)
1314        #except NoTransitionAvailableError:
1315        #    self.status = Invalid('Review state cannot be changed to '
1316        #                          'requested state: no such transition '
1317        #                          'available in workflow.')
1318        #    return
1319       
1320        try:
1321            self.context.certificates.addCertificate(certificate)
1322        except KeyError:
1323            self.status = Invalid('The name chosen already exists '
1324                                  'in the database')
1325            return
1326        except DuplicationError:
1327            self.status = Invalid('The name chosen already exists '
1328                                  'in the database')
1329            return
1330        self.redirect(self.url(self.context, u'@@manage')+'#tab-3')   
1331
1332    @grok.action('Cancel', validator=NullValidator)
1333    def cancel(self): #, **data):
1334        self.redirect(self.url(self.context))
1335        return
1336
1337#
1338# Courses pages
1339#
1340class CoursePage(WAeUPPage):
1341    """Course index page.
1342    """
1343    grok.context(ICourse)
1344    grok.name('index')
1345    grok.require('waeup.View')
1346    pnav = 1
1347    form_fields = grok.AutoFields(ICourse)
1348
1349    @property
1350    def title(self):
1351        return 'Course: %s (%s)' % (self.context.title, self.context.code)
1352
1353class CourseManageFormPage(WAeUPEditFormPage):
1354    """Edit form page for courses.
1355    """
1356    grok.context(ICourse)
1357    grok.name('manage')
1358    grok.require('waeup.manageUniversity')
1359    label = u'Edit course'
1360    pnav = 1
1361
1362    form_fields = grok.AutoFields(ICourse)
1363   
1364    @property
1365    def title(self):
1366        return 'Course: %s (%s)' % (self.context.title, self.context.code)
1367
1368    @grok.action('Save')
1369    def save(self, **data):
1370        self.applyData(self.context, **data)
1371        return
1372   
1373    @grok.action('Save and return')
1374    def saveAndReturn(self, **data):
1375        self.applyData(self.context, **data)
1376        self.redirect(self.url(self.context))
1377        return
1378
1379    @grok.action('Cancel', validator=NullValidator)
1380    def cancel(self, **data):
1381        self.redirect(self.url(self.context))
1382        return
1383
1384#
1385# Certificate pages
1386#
1387class CertificatePage(WAeUPDisplayFormPage):
1388    """Index page for certificates.
1389    """
1390    grok.context(ICertificate)
1391    grok.name('index')
1392    grok.require('waeup.View')
1393    pnav = 1
1394    form_fields = grok.AutoFields(ICertificate)
1395    grok.template('certificatepage')
1396
1397    @property
1398    def title(self):
1399        return "Certificate: %s (%s)" % (self.context.title, self.context.code)
1400       
1401    def update(self):
1402        tabs.need()
1403        #warning.need()
1404        datatable.need()
1405        return super(CertificatePage, self).update()
1406
1407class CertificateManageFormPage(WAeUPEditFormPage):
1408    """Manage the properties of a `Certificate` instance.
1409    """
1410    grok.context(ICertificate)
1411    grok.name('manage')
1412    grok.require('waeup.manageUniversity')
1413    pnav = 1
1414    label = 'Edit certificate'
1415   
1416    form_fields = grok.AutoFields(ICertificate)
1417   
1418    pnav = 1
1419    grok.template('certificatemanagepage')
1420    taboneactions = ['Save','Cancel']
1421    tabtwoactions = ['Add course referrer', 'Remove selected course referrers','Cancel']
1422
1423    @property
1424    def label(self):
1425        return 'Manage certificate'
1426       
1427    @property
1428    def title(self):
1429        return "Certificate: %s (%s)" % (self.context.title, self.context.code)
1430
1431    def update(self):
1432        tabs.need()
1433        #warning.need()
1434        datatable.need()
1435        return super(CertificateManageFormPage, self).update()
1436       
1437    form_fields = grok.AutoFields(ICertificate)
1438
1439    @grok.action('Save')
1440    def save(self, **data):
1441        self.applyData(self.context, **data)
1442        return
1443       
1444    # ToDo: Show warning message before deletion
1445    @grok.action('Remove selected course referrers')
1446    def delCertificateCourses(self, **data):
1447        form = self.request.form
1448        child_id = form['val_id']
1449        if not isinstance(child_id, list):
1450            child_id = [child_id]
1451        deleted = []
1452        for id in child_id:
1453            try:
1454                del self.context[id]
1455                deleted.append(id)
1456            except:
1457                self.flash('Could not delete %s: %s: %s' % (
1458                        id, sys.exc_info()[0], sys.exc_info()[1]))
1459        if len(deleted):
1460            self.flash('Successfully removed: %s' % ', '.join(deleted))
1461        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')   
1462        return       
1463     
1464
1465    @grok.action('Add course referrer', validator=NullValidator)
1466    def addCertificateCourse(self, **data):
1467        self.redirect(self.url(self.context, 'addcertificatecourse'))
1468        return   
1469       
1470    @grok.action('Cancel', validator=NullValidator)
1471    def cancel(self, **data):
1472        self.redirect(self.url(self.context))
1473        return
1474   
1475   
1476class CertificateCourseAddFormPage(WAeUPAddFormPage):
1477    """Add-page to add a course ref to a certificate
1478    """
1479    grok.context(ICertificate)
1480    grok.name('addcertificatecourse')
1481    grok.require('waeup.manageUniversity')
1482    form_fields = grok.AutoFields(ICertificateCourseAdd)
1483    pnav = 1
1484    label = 'Add course referrer'
1485   
1486    @property
1487    def title(self):
1488        return "Certificate: %s (%s)" % (self.context.title, self.context.code)
1489
1490    @grok.action('Add course referrer')
1491    def addCertcourse(self, **data):
1492        try:
1493            self.context.addCourseRef(**data)
1494        except KeyError:
1495            self.status = Invalid('The chosen course referrer is already part of'
1496                                  'this certificate')
1497            return
1498        except DuplicationError:
1499            self.status = Invalid('The chosen course referrer is already part of'
1500                                  'this certificate')
1501            return
1502        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
1503
1504    @grok.action('Cancel', validator=NullValidator)
1505    def cancel(self, **data):
1506        self.redirect(self.url(self.context))
1507        return
1508
1509
1510#
1511# Certificate course pages...
1512#
1513class CertificateCoursePage(WAeUPPage):
1514    """CertificateCourse index page.
1515    """
1516    grok.context(ICertificateCourse)
1517    grok.name('index')
1518    grok.require('waeup.View')
1519    pnav = 1
1520    form_fields = grok.AutoFields(ICertificateCourse)
1521
1522    @property
1523    def title(self):
1524        return 'Course Referrer: %s' % (self.context.longtitle())
1525   
1526    @property   
1527    def leveltitle(self):
1528        return course_levels.getTerm(self.context.level).title
1529
1530class CertificateCourseManageFormPage(WAeUPEditFormPage):
1531    """Manage the basic properties of a `CertificateCourse` instance.
1532    """
1533    grok.context(ICertificateCourse)
1534    grok.name('manage')
1535    grok.require('waeup.manageUniversity')
1536    form_fields = grok.AutoFields(ICertificateCourse)
1537    label = 'Edit course referrer'
1538    pnav = 1
1539   
1540    @property
1541    def title(self):
1542        return 'Course Referrer: %s' % (self.context.__name__)
1543   
1544    @grok.action('Save and return')
1545    def saveAndReturn(self, **data):
1546        parent = self.context.__parent__
1547        if self.context.level == data['level']:
1548            self.applyData(self.context, **data)
1549        else:
1550            # The level changed. We have to create a new certcourse as
1551            # the key of the entry will change as well...
1552            old_level = self.context.level
1553            data['course'] = self.context.course
1554            parent.addCourseRef(**data)
1555            parent.delCourseRef(data['course'].code, level=old_level)
1556        self.redirect(self.url(parent))
1557        return
1558
1559    @grok.action('Cancel', validator=NullValidator)
1560    def cancel(self, **data):
1561        self.redirect(self.url(self.context))
1562        return
1563
Note: See TracBrowser for help on using the repository browser.