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

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

Remove unused import.

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