# -*- coding: utf-8 # $Id$ # # Copyright 2001, 2002 by IVA Team and contributors # # This file is part of IVA. # # IVA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # IVA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with IVA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Contains class Course, which represents one course, which has one or more CourseContexts, which in turn contain knowledge building conversations.""" __version__ = '$Revision$'[11:-2] import time, strptime import string, types import OFS, Globals from Globals import Persistent, Acquisition import AccessControl from AccessControl import ClassSecurityInfo import copy import re import Kodutoo import Blog try: from Products.ZWiki.ZWikiPage import ZWikiPage USE_ZWIKI = 1 except: USE_ZWIKI = 0 from Jamming import Jamming from TraversableWrapper import Traversable from common import intersect_bool, make_action, get_roles, get_local_roles, translate from input_checks import strip_all, is_valid_title from CourseContext import CourseContext #from ThinkingTypeSetManager import ThinkingTypeSetManager as TTSM from Thread import Thread from Cruft import Cruft from TempObjectManager import TempObjectManager from input_checks import render, normal_entry_tags_and_link from common import perm_view, perm_edit, perm_manage, perm_add_lo from Products.ZCatalog.CatalogPathAwareness import CatalogAware import WebtopTrash import Subgroups import Kalender import WordMap from QuizManager import QuizManager # Each Course object contains (usually) one or more CourseContexts, which # represent different aspects of the course. # A Course contains information and services on one course implementation. class Course( Persistent, Traversable, Cruft, OFS.Folder.Folder, AccessControl.Role.RoleManager, OFS.SimpleItem.Item, Thread, CatalogAware, ): """Course, contained within CourseManager, represents one course.""" meta_type = 'Course' security = ClassSecurityInfo() security.declareObjectPublic() # Call courses/course_html (This way we avoid copying index_html # to each instance of Course class.) # ATTENTION: We don't exactly know how this works (for example, # do we really need REQUEST ?) # #TODO: This could be done with a URL pointing to the course_html script. #But if we need this default implementation, we could use #restrictedTraverse('course_html') to activate Zope acquisition. #FIX:bla? security.declareProtected('View', 'index_html') def index_html(self, REQUEST=None): """Default script""" return REQUEST.RESPONSE.redirect('course_html') # Parameters: # #- parent: should be a CourseManager # #- name: name of the Course # #- tts: list of ThinkingTypeSets (copies are made in manage_afterAdd) # #- teachers: list of users (of class UserInfo?) that are granted #Teacher privileges # #- etc: other textual information def __init__( self, parent, # Whatever you do, dont bind this to self. name, teachers, description='', organisation='', methods='', starting_date='', ending_date='', uniq_id='' ): """Constructor of the course.""" # Overriding all_meta_types is not not beautiful...but, hey!, it works! #self.all_meta_types = ( # {'name': 'ThinkingTypeSet', # 'action': 'get_id'},) # Cache active members. self.active_memb_cache = [] # Remove all HTML tags from parameters name = strip_all(name) description = strip_all(description) methods = strip_all(methods) organisation = strip_all(organisation) Thread.__init__(self, parent) # Takes care of id and title. if teachers != ('',): for teacher in teachers: self.set_roles(teacher, ('Teacher',)) self.__name = name # name of the course self.__organisation = organisation self.__description = description self.__methods = methods self.__starting_date = starting_date self.__ending_date = ending_date self.__credits = '' self.__courseID = '' self.__quote = '' self.course_category = 0 self.course_locked = 0 self.default_catalog = 'courses_zcatalog' # course should have uniq id since self.id tends to change self.setUniqId(uniq_id) # This is for group folder path listings - show path up to course. self.toplevel = 1 security.declarePrivate('manage_afterAdd') #Each course should have its own copy of the ThinkingTypeSets, #as the course administrator (teacher) should be able to edit them, #create new ones and so forth. These changes must not propagate to #other courses: hence the copying of the set. def manage_afterAdd(self, item, container): """foo""" from common import roles_student, roles_tutor, roles_teacher from common import roles_admin # add subgroups manager sg = Subgroups.SubgroupManager() self._setObject(sg.id, sg) # add quizes qm = QuizManager() self._setObject(qm.id, qm) # add jamming self._setObject('jamming', Jamming('jamming')) # add wordmaps self.createWordmaps() # add assignments k=Kodutoo.KodutoodeKataloog() k.id='kodutood' self._setObject(k.id, k) self.index_object() # add blog b = Blog.Blog() b.id = "Blog" self._setObject(b.id,b) # add calendar cal = Kalender.KalendriSyndmusteKataloog() self._setObject('syndmused', cal) # add tracker self.createTracker() # add SQL row self.Statistics.sql_methods._sql_populate_course_table(course_list='('+str(self.getId())+')') security.declarePrivate('manage_beforeDelete') def manage_beforeDelete(self, item, container): """ manage before delete """ self.unindex_object() self.Statistics.delCourseHistory(int(self.get_id())) security.declareProtected(perm_edit, 'add_course_form_handler') def add_course_form_handler(self, REQUEST, course_id, my_name, desc, organisation, methods, start_date, end_date, cancel='', # submit buttons add='', # tekst='', logo_upload=None, staatus=0, regStatus=0, cCat = 0, credits='', courseID='', delete_logo='', lock_course=0, ): """ modify course info, override CourseManager method. """ if cancel: return REQUEST.RESPONSE.redirect('course_info?course_id=%s' % course_id) elif not add: raise 'IVA Error', 'Inknown button' if course_id != self.get_id() or not course_id: return action=apply( make_action, ['manage_course_info'] + [(x, eval(x)) for x in ('my_name', 'desc', 'organisation', 'methods', 'start_date', 'end_date')]) action += '&course_id=' + course_id my_name=my_name.strip() if not is_valid_title(my_name): return self.message_dialog_error( self, REQUEST, title='Invalid name', message='Give valid name', action=action) if my_name != self.get_name(): if my_name in [x.get_name for x in self.get_courses()]: return self.message_dialog_error( self, REQUEST, title='Invalid name', message="Name '%s' taken" % my_name, action=action) from common import convert_date # convert dates to time.time()-format errors = [] if not start_date: starting_date = 0 else: try: time_tuple = strptime.strptime(start_date, translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST))) starting_date = convert_date(str(time_tuple[2]), # day str(time_tuple[1]), # month str(time_tuple[0])) # year except: errors.append(translate(self,'Starting date:',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),) if not end_date: ending_date = 0 else: try: time_tuple = strptime.strptime(end_date, translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST))) ending_date = convert_date(str(time_tuple[2]), # day str(time_tuple[1]), # month str(time_tuple[0])) # year except: errors.append(translate(self,'Ending date:',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),) organisation = organisation.strip() if organisation and not is_valid_title(organisation): errors.append(translate(self,'Organization',target=self.giveLanguage(REQUEST))) if len(errors) > 0: return self.message_dialog_error( self, REQUEST, title='Invalid input', message=translate(self,'Invalid fields',target=self.giveLanguage(REQUEST)) + ": '" + \ "' , '".join(errors) + "'", action=action) logo = None try: if len(logo_upload.filename)>0: logo=logo_upload.read() else: logo = None except AttributeError: pass self.update( my_name, desc, organisation, methods, starting_date, ending_date, tekst, logo, staatus, regStatus, cCat, credits, courseID, delete_logo, lock_course, ) return REQUEST.RESPONSE.redirect(self.absolute_url()+'/gf/course_info') def is_courseContext(self): """ is course context? """ return def kasTeadmuspajasKontekste(self): "jah/ei" return len(self.objectValues('CourseContext'))>0 def kasMeediapajasProjekte(self): "jah/ei" return len(self.jamming.objectValues('JamSession'))>0 def kysiOotajad(self, REQUEST=None): "Kursusele registreerunud, keda pole veel kinnitatud" return getattr(self, 'ootajad', []) def isLocked(self): """ is course locked """ try: return self.course_locked except AttributeError: return False security.declareProtected(perm_edit,'manage_pendingUsers_handler') def manage_pendingUsers_handler(self, REQUEST, addtocourse='',deletefromlist='',pender=''): "Lisamine vői kustutus" #from Kirjakast import Kirjakast temp = getattr(self.fle_root(), 'fle_users') if not pender: return self.message_dialog_error(self, REQUEST, title="Error", message='Please select some users first', action='manage_pendingUsers') if type(pender)==types.StringType: pender=(pender,) penders=self.kysiOotajad() new_penders = [] if deletefromlist: for x in pender: puser = getattr(temp, x) for y in penders: if y == x: continue new_penders.append(y) if addtocourse: for x in pender: puser = getattr(temp, x) self.add_student(puser.get_uname()) mess_title = translate(self,'Added to course',target=self.giveLanguage(REQUEST)) mess_body = translate(self,'You have been added to course ',target=self.giveLanguage(REQUEST))+' '+self.get_name() puser.kirjad.saadaKiri(REQUEST,puser.get_uname(),puser.get_uname(),mess_title,mess_body,999999) for x in penders: if x not in pender: new_penders.append(x) self.ootajad=new_penders return REQUEST.RESPONSE.redirect('haldusleht') def ootajastKursuseLiikmeks(self, REQUEST, kasutaja): "Lisamine" self.add_student(kasutaja) return 1 def lisaOotaja(self, REQUEST, ootaja): """ Kasutaja lisab end ootama """ if self.getRegStatus()=='2': #automaatregistreerumine self.ootajastKursuseLiikmeks(REQUEST, ootaja) else: m=self.kysiOotajad(REQUEST) if ootaja not in m: m.append(ootaja) elif ootaja in m: m.remove(ootaja) self.ootajad=m return 0 def kasOotaja(self, nimi): "Kas jooksev kasutaja on kursusele ootaja" m=self.kysiOotajad() kas=0 for x in m: if nimi==x: kas=1 return kas security.declareProtected(perm_view, 'get_printable_name') # No additional comments. def get_printable_name(self): """Return name of the course.""" return self.__name security.declareProtected(perm_view, 'get_bg_colour_name') def get_bg_colour_name(self): """...""" return 'gr' # security.declareProtected(perm_view, 'get_name') security.declarePublic('get_name') # No additional comments. def get_name(self): """Get course name.""" return self.__name security.declareProtected(perm_view, 'get_organisation') # No additional comments. def get_organisation(self): """Get organisation name.""" return self.__organisation security.declareProtected(perm_view, 'get_description') # No additional comments. def get_description(self): """Get description.""" return self.__description security.declareProtected(perm_view,'render_description') def render_description(self): """Render description.""" return render( self.get_description(), legal_tags=normal_entry_tags_and_link) security.declareProtected(perm_view, 'get_methods') # No additional comments. def get_methods(self): """Get info on methods.""" return self.__methods security.declareProtected(perm_view,'render_methods') def render_methods(self): """Render methods.""" return render( self.get_methods(), legal_tags=normal_entry_tags_and_link) def get_courseID(self): """ get course id """ try: return self.__courseID except AttributeError: return "" def set_courseID(self,courseID): """ set course id(the other one) """ self.__courseID = courseID def get_credits(self): """ get credits info """ try: return self.__credits except AttributeError: return "" def set_credits(self,credits): """ set credits """ self.__credits = credits # security.declareProtected(perm_view, 'get_teachers') # No additional comments. security.declarePublic('get_teachers') def get_teachers(self): """Get teachers.""" retval = [] for (user, roles) in self.get_local_roles(): if 'Teacher' in roles: retval.append(user) return retval security.declareProtected(perm_view, 'get_start_dd') # No additional comments. def get_start_dd(self): """Return starting day.""" if self.__starting_date: return time.localtime(self.__starting_date)[2] else: return '' security.declareProtected(perm_view, 'get_start_mm') # No additional comments. def get_start_mm(self): """Return starting month.""" if self.__starting_date: return time.localtime(self.__starting_date)[1] else: return '' security.declareProtected(perm_view, 'get_start_yyyy') # No additional comments. def get_start_yyyy(self): """Return starting year.""" if self.__starting_date: return time.localtime(self.__starting_date)[0] else: return '' security.declareProtected(perm_view, 'get_end_dd') # No additional comments. def get_end_dd(self): """Return ending day.""" if self.__ending_date: return time.localtime(self.__ending_date)[2] else: return '' security.declareProtected(perm_view, 'get_end_mm') # No additional comments. def get_end_mm(self): """Return ending month.""" if self.__ending_date: return time.localtime(self.__ending_date)[1] else: return '' security.declareProtected(perm_view, 'get_end_yyyy') # No additional comments. def get_end_yyyy(self): """Return ending year.""" if self.__ending_date: return time.localtime(self.__ending_date)[0] else: return '' # security.declareProtected(perm_view, 'get_printable_starting_date') security.declarePublic('get_printable_starting_date') #FIX:security check # No additional comments. def get_printable_starting_date(self, REQUEST): """Get starting date.""" return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)), time.localtime(self.__starting_date)) # security.declareProtected(perm_view, 'get_printable_ending_date') security.declarePublic('get_printable_ending_date') #FIX:security check # No additional comments. def get_printable_ending_date(self, REQUEST): """Get ending date.""" if self.__ending_date == 0: return '' return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)), time.localtime(self.__ending_date)) security.declarePrivate('get_start_date') def get_start_date(self): return self.__starting_date security.declarePrivate('get_end_date') def get_end_date(self): return self.__ending_date security.declareProtected(perm_view, 'get_users') def get_users(self, REQUEST): """Return dict with 2 members of attendees as UserInfo objects. 'active_d' is the list of active members. 'others_d' is the rest .. [[UIObj'active1'], [UIObj'other1', UIObj'other2', ..]]""" rv = {'active_d':[], 'others_d': []} au = str(REQUEST.AUTHENTICATED_USER) for uname in [(u[0]) for u in self.get_local_roles()]: from Errors import FleError try: o = self.fle_users.get_user_info(uname) if uname == au or uname in self.active_memb_cache: rv['active_d'].append(o) else: rv['others_d'].append(o) except FleError: pass return rv security.declarePublic('get_n_users') def get_n_users(self): return len(self.get_all_users_id()) security.declareProtected(perm_view, 'get_all_users') def get_all_users(self): """Return a list of all users on course.""" rv = [] for uname in self.get_all_users_id(): try: rv.append(self.fle_users.get_user_info(uname)) except Exception: pass return rv security.declareProtected(perm_edit, 'kasutajateKodutoodeArhiiv') def kasutajateKodutoodeArhiiv(self, REQUEST): """ Arhiiv, kuhu paigutatakse kasutaja esitatud materjalid """ import tempfile import zipfile failinimi=tempfile.mktemp() arhiiv=zipfile.ZipFile(failinimi,"w",zipfile.ZIP_DEFLATED) kasutajad=self.get_all_users() for x in kasutajad: try: kc=getattr(x.webtop, 'c'+self.id) except AttributeError: continue for too in self.kodutood.objectValues(): if too.tyyp==1: #materjal if hasattr(kc, 'portfolio'): if hasattr(kc.portfolio, too.id): tookataloog=getattr(kc.portfolio, too.id) tookataloog.looArhiiv(REQUEST, arhiiv, x.id+'/') if too.tyyp==2: #grupp mitu=0 punkte=-1 for kg in x.kysiKursuseSisegrupid(self): if kg.id in too.grupid: #XXX:TODO: check this, might be incorrect esitluskaust = getattr(kg.portfolio, too.id) esitluskaust.looArhiiv(REQUEST, arhiiv, kg.id+'/') arhiiv.close() file = open(failinimi,"rb") export_data=file.read() file.close() import os os.remove(failinimi) REQUEST.RESPONSE.setHeader('Content-type','application/zip') REQUEST.RESPONSE.setHeader('Content-disposition','attachment; filename=tooarhiiv.zip') return export_data def fotogaKasutajad(self): "kasutajad, kel on foto" m=[] for x in self.get_all_users(): if x.has_photo(): m.append(x) return m def fototaKasutajad(self): "kasutajad, kel pole fotot" m=[] for x in self.get_all_users(): if not x.has_photo(): m.append(x) return m security.declareProtected(perm_view, 'get_all_users_id') def get_all_users_id(self): """Return a list of all users on this course. Same thing as get_all_users, but this one returns a list of id's of UserInfo objects, not the UserInfo object reference.""" cl = [] for u in self.get_local_roles(): if 'Student' in u[1] or 'Teacher' in u[1]: cl.append(u[0]) return cl # return [u[0] for u in self.get_local_roles()] security.declareProtected(perm_view, 'get_users_with_role') def get_users_with_role(self, role): """Return a list of participants who have a specified role.""" rv = [] for e in self.get_local_roles(): if role in e[1]: # try: rv.append(self.fle_users.get_user_info(e[0])) # except Exception: # pass return rv def get_sorted_user_list(self, role): nimed = [] rolliga = self.get_users_with_role(role) for aaa in rolliga: temp = [] if aaa.get_last_name()=='' and aaa.get_first_name()=='': temp.append(aaa.get_id()) temp.append(aaa.get_id()) temp.append(aaa) nimed.append(temp) else: temp.append(aaa.get_last_name().lower()) temp.append(aaa.get_first_name().lower()) temp.append(aaa) nimed.append(temp) nimed.sort() uuserid = [] for kasutajad in nimed: uuserid.append(kasutajad[2]) return uuserid security.declareProtected(perm_edit, 'add_student') #The person is added with Student role access. # NOTE: This method is no longer needed, except in the test cases! def add_student(self, name): """Add person to the course.""" # Check that user exists... Raises exception if not. #XXX: is this really needed!? now commented out # self.fle_users.get_user_info(name) uinf = self.fle_users.get_user_info(name) # Don't add course to user's cache twice if self.get_id() not in uinf.user_courselist_cache: uinf.user_courselist_cache.append(self.get_id()) uinf._p_changed = True self.set_roles(name, ('Student',)) self.reindex_object() security.declareProtected(perm_view, 'get_valid_roles') # No additional comments. def get_valid_roles(self): """Get the roles valid for persons added to the course.""" from CourseManager import course_level_roles valid_roles = list(course_level_roles) #valid_roles.append('Teacher') return valid_roles security.declareProtected(perm_manage, 'remove_person') def remove_person(self, person): """Remove person from the course.""" # Check that user exists... Raises exception if not. kasutaja=self.fle_users.get_user_info(person) try: kasutaja.user_courselist_cache.remove(self.get_id()) except: pass kasutaja._p_changed = True #We have to do it the hard way since set_jooksev_kursus turns me away kasutaja.jooksev_kursus=0 # We use the get_local_roles method, because we need to # see if the user has a role attached to this course object # specifically, and we don't want the roles in the acquisition # tree to interfere. if len(get_local_roles(self,person))==0: #raise FleError, ("User "+person+" does not belong to this course.") pass self.__unset_roles((person,)) # Note: person _must_ be a sequence! def __unset_roles(self, persons): """Unset roles of one user.""" self.manage_delLocalRoles(persons) security.declareProtected(perm_view, 'has_role') # No additional comments. def has_role(self, person, role): """Return whether the user is in the specified role.""" return role in get_roles(self,person) security.declareProtected(perm_view, 'get_teacher') def get_teacher(self): """Get the name of the teacher (creator of the course).""" for user,roles in self.get_local_roles(): if 'Teacher' in roles: return user raise 'Course has no teacher!' # FIXME: input_checks security.declareProtected(perm_edit, 'add_users_form_handler') def add_users_form_handler( self, course_id, REQUEST, users_None='', # checkboxes users_Teacher='', users_Tutor='', users_Student='', None_to_Teacher='', # submit buttons Teacher_to_None='', None_to_Tutor='', Tutor_to_None='', None_to_Student='', Student_to_None='', Tutor_to_Teacher='', Teacher_to_Tutor='', Student_to_Tutor='', Tutor_to_Student='', ): """Adding and removing users from a given course.""" from Kirjakast import Kirjakast temp = getattr(self.fle_root(), 'fle_users') for x in ('None_to_Teacher', # left/right 'Teacher_to_None', 'None_to_Tutor', 'Tutor_to_None', 'None_to_Student', 'Student_to_None', 'Tutor_to_Teacher', # up/down 'Teacher_to_Tutor', 'Student_to_Tutor', 'Tutor_to_Student'): if eval(x): m = re.match("^(.*)_to_(.*)", x) old_role = m.group(1) new_role = m.group(2) if 'Student' in new_role and not self.isCourseReady(self): return self.message_dialog_error( title="Error", message="Students can be added to the course when all required course information is submitted and course status is open(not preparing, not finished). Adding teachers is OK.", action="manage_participants") users = "users_%s" % old_role if eval(users): owners = [u[0] for u in self.get_local_roles()] if type(eval(users)) is types.StringType: exec '%s = [%s,]' % (users, users) if new_role == 'None': for user in eval(users): self.remove_person(user) if self.has_group_folder(): self.remove_folder_link( (self.fle_users.get_user_info(user),)) else: for user in eval(users): if user in owners: new_roles = (new_role, 'Owner') else: new_roles = (new_role,) uinf = self.fle_users.get_user_info(user) if self.get_id() not in uinf.user_courselist_cache: uinf.user_courselist_cache.append(self.get_id()) uinf._p_changed = True self.set_roles(user, new_roles) try: koht = getattr(temp, user) koht2 = getattr(koht, 'kirjad') tlang = uinf.get_language() teade=translate(self,'You have been added to course ',target=tlang) koht2.saadaKiri(REQUEST, uinf.get_uname(), uinf.get_uname(), teade,'\n\n\n'+\ teade+": "+\ str(self.get_name()),9999) except: pass else: return self.message_dialog_error( self, REQUEST, title='Error', message='Please select some users first', action='manage_participants') self.reindex_object() REQUEST.RESPONSE.redirect('manage_participants') #'add_course_form_2_2?course_id=' + course_id)) return # Should be never reached. return REQUEST security.declareProtected(perm_edit, 'set_roles') # Note: roles _must_ be a sequence! # Called from CourseManager.add_users_form_handler def set_roles(self, person, roles): """Set roles of one person.""" self.__unset_roles(person) self.manage_setLocalRoles(person, roles) # FIXME: input_checks: tt_set_name not checked # FIXME: input_checks: two course contexts can have identical name. security.declareProtected(perm_add_lo, 'add_course_context') # Handler for add_course_context_form def add_course_context( self, my_name, description, tt_set_name, description_long, REQUEST, use_roleplay='', # New in ZPTIVA show_only_roleplay=0, # Submit buttons. publish='', cancel='', ): """Add CourseContext object.""" if publish: error_fields = [] errors = [] my_name = my_name.strip() if not is_valid_title(my_name): error_fields.append(translate(self,'title of context',target=self.giveLanguage(REQUEST))) if my_name in [x[1] for x in self.get_course_context_names()]: errors.append(translate(self,"Name '%s' taken",target=self.giveLanguage(REQUEST)) % my_name) # Variables 'description' and 'description_long' are not checked # because render_description() and render_long_description() # methods in CourseContext filter out unwanted HTML tags. if len(error_fields) > 0 or len(errors) > 0: msg = ", ".join(errors) if len(error_fields) > 0: msg = msg + "
" + 'Invalid fields' + \ ": '" + "' , '".join(error_fields) + "'" return self.message_dialog_error( self, REQUEST, title='Invalid input', message=msg, action=apply( make_action, ['add_course_context_form'] + [(x, eval(x)) for x in ('my_name', 'description', 'tt_set_name', 'description_long')])) uname=str(REQUEST.AUTHENTICATED_USER) obj = CourseContext( self, my_name, description, description_long, tt_set_name, uname,) obj.public = 1 if int(show_only_roleplay)==1: #obj.__roleplay_only = 1 obj.set_roleplay_only(1) id = obj.get_id() self._setObject(id, obj) #XXX: statistics - lisaSyndmus(konteksteLisatud. should it be somewhere? #self.lisaSyndmus(REQUEST, 'konteksteLisatud') try: obj.changeOwnership(self.acl_users.getUser(uname).__of__(self.acl_users)) obj.manage_setLocalRoles(uname,('Owner',)) except: pass if REQUEST: pagename="" if use_roleplay: pagename="course_setup_roleplay_form" REQUEST.RESPONSE.redirect('%s/%s' % (str(id),pagename)) elif cancel: return REQUEST.RESPONSE.redirect(REQUEST.URL1) else: raise "add_course_context called without 'publish' or 'cancel'" security.declareProtected(perm_view, 'get_course_context_names') # No additional comments. def get_course_context_names(self): """Return a list of CourseContext names.""" retval = {} for e in self.get_children('CourseContext'): id = e.get_id() name = e.get_name() retval[id] = name return retval.items() #security.declareProtected(perm_view, 'get_n_notes') def get_n_notes(self): """Returns a sum of all notes in all contexts.""" count = 0 for cc in self.get_course_contexts(): count += cc.get_n_notes() return count security.declareProtected(perm_view, 'get_n_unread_notes') def get_n_unread_notes(self,uname): """Returns a sum of all unread notes in all contexts.""" count = 0 for cc in self.get_course_contexts(): count += cc.get_n_unread_notes(uname) return count security.declarePublic('update') # Parameters are received from the form (apparently). def update( self, name, description, organisation, methods, starting_date, ending_date, tekst, logo=None, status=0, regStatus = 0, cCat = 0, credits='', courseID='', delete_logo = '', lock_course=0, ): """Edit course information.""" self.__name = name self.__description = description self.__organisation = organisation self.__methods = methods self.__starting_date = starting_date self.__ending_date = ending_date self.setQuote(tekst) if logo: self.setLogo(logo) self.setStatus(status) self.setRegStatus(regStatus) if cCat: self.setCourseCategory(int(cCat)) self.set_credits(credits) self.set_courseID(courseID) self.course_locked = lock_course if delete_logo: self.removeLogo() #XXX: remove self.default_catalog = 'courses_zcatalog' self.reindex_object() security.declareProtected(perm_view, 'get_course_contexts') def get_course_contexts(self): """Return a list of all course contexts in this course.""" for abc in self.get_children('CourseContext'): if getattr(abc, 'public', None) is None: abc.public = 1 return self.get_children('CourseContext') security.declareProtected(perm_view, 'get_course_context_ids_in_order') def get_course_context_ids_in_order(self, id_list): """Return a list of ids of all course contexts in this course.""" return [o.get_id() for o in self.get_course_contexts_in_order(id_list)] security.declareProtected(perm_edit, 'delete_course_context') def context_form_handler(self,REQUEST,cc_id,staatus='', kustuta=''): """ delete or change status """ self.hide_show_course_context(cc_id) return REQUEST.RESPONSE.redirect(self.absolute_url()+'/course_html') security.declareProtected(perm_edit, 'delete_course_context') def delete_course_context(self,id): for c in self.get_children('CourseContext'): if c.get_id() == id: self._delObject(id) return security.declareProtected(perm_edit, 'hide_show_course_context') def hide_show_course_context(self,id): for c in self.get_children('CourseContext'): if c.get_id() == id: if c.public == 1: c.public = 0 else: c.public = 1 c._p_changed = 1 break return # FIXME: See the random comment below, random is not # FIXME: probably the order that we really want. security.declareProtected(perm_view, 'get_course_contexts_in_order') def get_course_contexts_in_order(self, id_list): """Return a list of all course contexts in this course.""" contexts = {} for c in self.get_children('CourseContext'): contexts[c.get_id()] = c retval = [] if id_list and id_list != ['']: if type(id_list) == types.StringType: id_list = (id_list, ) # Return courses contexts in a given order. for identifier in id_list: try: retval.append(contexts[identifier]) del contexts[identifier] except KeyError: # invalid id_list pass # If we still course contexts left (id_list is shorter # than the actual number of course_contexts), append # them to list in a random order. for key in contexts.keys(): retval.append(contexts[key]) return retval security.declarePublic('may_view_course') def may_view_course(self, REQUEST): """Return boolean depending on wether user may or may not view the course.""" from AccessControl.PermissionRole import rolesForPermissionOn return intersect_bool( get_roles(self,str(REQUEST.AUTHENTICATED_USER)), rolesForPermissionOn(perm_view,self)) security.declareProtected(perm_view, 'may_add_course_context') def may_add_course_context(self, person): """Return boolean depending on wether user may or may not add a course context to the course.""" from AccessControl.PermissionRole import rolesForPermissionOn return intersect_bool( get_roles(self,person), rolesForPermissionOn(perm_add_lo,self)) security.declareProtected(perm_view, 'may_edit_course') def may_edit_course(self, person): """Return boolean depending on whether person can edit the course or not.""" from AccessControl.PermissionRole import rolesForPermissionOn return intersect_bool( get_roles(self,person), rolesForPermissionOn(perm_edit,self)) security.declareProtected(perm_view, 'has_group_folder') def has_group_folder(self): """Return whether course has a group folder or not.""" return len(self.objectIds('GroupFolder'))>0 def add_folder(self, my_name): from GroupFolder import GroupFolder from GroupFolderProxy import GroupFolderProxy new_id = 'gf' fol = GroupFolder(None,my_name) fol.id=new_id self._setObject(new_id,fol) fol=fol.__of__(self) # self.make_group_folder_proxies(self.get_all_users()) proxy = GroupFolderProxy(None, # any sense? self.get_name(), self.get_id()) self.jamming._setObject('0', proxy) return fol def make_group_folder_proxies(self, users): for user in users: # user.webtop.add_link(self.get_name()+" (Shared)",self.get_url_to_object(self.gf),1) user.webtop.add_group_folder_proxy(self.get_name(), self.get_id()) def remove_folder_link(self,users): gf = self.get_child('gf') for user in users: fol = user.webtop self.__recurse_remove_folder_link(fol, gf) def __recurse_remove_folder_link(self, folder, gf): for (_id, proxy_fol) in folder.objectItems('GroupFolderProxy'): if proxy_fol.is_proxy_for(gf): folder._delObject(_id) for fol in folder.objectValues('WebtopFolder'): self.__recurse_remove_folder_link(fol, gf) security.declareProtected(perm_edit, 'teacher_import_handler') def teacher_import_handler(self, REQUEST, file='', to_import=''): """ Form handler for teacher """ kursusenr = self.get_course_id_from_req(REQUEST) if not self.kas_opetaja(REQUEST): return "Keelatud" uname = REQUEST.AUTHENTICATED_USER.getUserName() from ImportIMS import Importer from ImportExport import Exporter from StringIO import StringIO out = StringIO() import tempfile, os filename = tempfile.mktemp() f = open(filename,"w+b") f.write(file.read()) f.close() import_data=None imported = Importer(uname,self.fle_root(),'',getattr(self.fle_root().courses, kursusenr),kasuta_jooksvat=1, out=out) imported.loadZip(filename) element = imported.loadFile('imsmanifest.xml') imported.processFile(self,element) return REQUEST.RESPONSE.redirect(self.absolute_url()+'/'+str(kursusenr)+'/gf/course_info') security.declareProtected(perm_edit, 'teacher_export') def teacher_export(self,REQUEST): """ teacher export """ return self.iva_ims_export(REQUEST) security.declareProtected(perm_edit, 'iva_ims_export') def iva_ims_export(self, REQUEST=None, testinimi='',teacherExporting=1): """ Course or a quiz export. XML format. """ from ExportIMS import Kirjutus from ExportIMS import kirjutaYldM import tempfile, os failinimi = tempfile.mktemp() # self.fle_users.export_users(REQUEST,failinimi='ivaexport.zip',kursusega='jah',palju='self.fle_root().fle_users.get_users()') if testinimi: yld = kirjutaYldM() yld.kirjutaYldmanifest(self.get_id(),'QTI test','imsqti_xmlv1p1','','qti.xml') yld.lisaFaili(failinimi) k= Kirjutus(self.fle_root(), self.get_id()) #k.looKursuseFail() #k.exportQTI(failinimi,'') parent = k.looTestiFail() k.kirjutaTestid(parent,testinimi, users='None', teacherExporting=teacherExporting) k.kirjutaTestidZipi(failinimi,'') #k.pakiKursusFaili(failinimi) # k.pakiTulemusFaili(failinimi,testsonly="testsonly") else: yld = kirjutaYldM() yld.kirjutaYldmanifest(self.get_id(), self.get_description(),base=str(self.get_id())+"/") yld.lisaFaili(failinimi) k = Kirjutus(self.fle_root(), self.get_id()) k.looKursuseFail() k.kirjutavCal(self.syndmused,'kursus','GROUP') k.kirjutaWebtop(self.gf,tiitel='RAAMATURIIUL') for sisegr in self.subgroups.objectValues('GroupFolder'): k.kirjutaWebtop(sisegr,'SISEGRUPP:'+sisegr.get_name()) try: k.kirjutavCal(sisegr.syndmused,sisegr.get_name(),'GROUP') except: pass if not teacherExporting: k.kirjutaKasutajaWebtop(self.get_all_users()) k.kirjutaKasutajaBlog(self.get_all_users()) #k.kirjutaKasutajaSyndmus(self.get_all_users()) self.fle_users.export_users(REQUEST,failinimi=failinimi,kursusega=self.get_id(),palju='kursus.get_all_users()') k.kirjutaItem(k.organization, tiitel='KURSUSE KASUTAJAD',parameters='KURSUSE KASUTAJAD') k.kirjutaResource(k.resources, 'imsent_xmlv1p1','users.xml') k.kirjutaBlog(self.Blog, k.organization, k.resources) k.exportQTI(failinimi,str(self.get_id())+"/",teacherExporting=teacherExporting) k.exportPajad(failinimi,str(self.get_id())+"/",startonly=teacherExporting) k.exportKodutood(self.kodutood,failinimi,str(self.get_id())+"/") k.pakiKursusFaili(failinimi) if REQUEST: file = open(failinimi,"rb") export_data=file.read() file.close() os.remove(failinimi) REQUEST.RESPONSE.setHeader('Content-disposition','attachment; filename=ivaexport.zip') REQUEST.RESPONSE.setHeader('content-type','application/zip') return export_data else: import os import shutil name='iva_'+re.sub('[^a-zA-Z0-9]', '_', self.get_name()) name2 = name number = 1 searched=0 print os.path.isfile(os.path.join(Globals.INSTANCE_HOME,'var',name+'.zip')) print os.path.join(Globals.INSTANCE_HOME,'var',name+'.zip') while os.path.isfile(os.path.join(Globals.INSTANCE_HOME,'var',name+'.zip')): number = number + 1 name = name2+'_ver'+str(number) searched=1 name += '.zip' shutil.move(failinimi,os.path.join(Globals.INSTANCE_HOME,'var',name)) return self.get_name() + ' - ' + os.path.join(Globals.INSTANCE_HOME,'var',name) def write_touchgraph_data(self,obj,type,children_types=None,extra_links=None): links = '' if children_types: links = ' '.join(obj.objectIds(children_types)) if extra_links: links = ' '.join((links,extra_links)) if not links: links=' ' return obj.get_id()+'\t'+\ type+"\t"+\ self.REQUEST.BASE0+self.get_url_to_object(obj)+'\t'+\ obj.get_name()+'\t'\ +links+'\t' # But should we protect it somehow? # The Java client would then need authentication def touchgraph_data(self): """This is a public method.""" data=self.write_touchgraph_data(self,"COURSE","CourseContext") for ctx in self.objectValues("CourseContext"): data=data+ctx.touchgraph_data() return data+"[END DATA]" def getQuote(self): "Kursuse uudis/moto. Vastab tyhja kui pole" return self.__quote def setQuote(self, tekst): """ set quote """ self.__quote=tekst security.declareProtected(perm_edit, 'removeLogo') def removeLogo(self): """ remove logo """ imgp = getattr(self.fle_root().images, 'courses_imgs', None) try: imgp._delObject('course_'+str(self.get_id())+'_image') except AttributeError: pass security.declareProtected(perm_edit, 'setLogo') def setLogo(self, img): """ add course logo """ resize = 1 try: from PIL import Image except ImportError: resize = 0 import cStringIO imgp = getattr(self.fle_root().images, 'courses_imgs', None) if imgp is None: return 0 try: s = cStringIO.StringIO(img) if resize: im = Image.open(s) (width, height) = im.size if height == 80 and im.format == 'JPEG': pass else: if im.mode != 'RGB': im = im.convert('RGB') if height > 300: mod = float(300)/float(height) width = width*mod height = height*mod im = im.resize((int(width),int(height))) if width > 300: mod = float(300)/float(width) width = width*mod height = height*mod im = im.resize((int(width),int(height))) s = cStringIO.StringIO() try: im.save(s, "JPEG", quality=100) except KeyError: im.save(s, "GIF", quality=100) s.seek(0) img = s.read() try: imgp._delObject('course_'+str(self.get_id())+'_image') except AttributeError: pass imgp.manage_addImage('course_'+str(self.get_id())+'_image', img, 'image') img = getattr(imgp, 'course_'+str(self.get_id())+'_image') img.ZCacheable_setManagerId('HTTPCache') img.ZCacheable_invalidate() except: return 0 self.setLogoThumbnail() return 1 security.declarePrivate('setLogoThumbnail') def setLogoThumbnail(self): """ creates thumbnail from uploaded course logo """ resize = 1 try: from PIL import Image except ImportError: resize = 0 if not resize: return 1 import cStringIO imgp = getattr(self.fle_root().images, 'courses_imgs', None) if imgp is None: return 1 if 1==1: oimg = getattr(imgp, 'course_'+str(self.get_id())+'_image', None) try: s = cStringIO.StringIO(oimg.data) except TypeError: s = cStringIO.StringIO(str(oimg.data)) if s is None: return 1 im = Image.open(s) (width, height) = im.size # resize image 59x59 if height > 59: mod = float(59)/float(height) width = width*mod height = height*mod im = im.resize((int(width), int(height))) if width > 59: mod = float(59)/float(width) width = width*mod height = height*mod im = im.resize((int(width), int(height))) s = cStringIO.StringIO() try: im.save(s, "JPEG", quality=100) except KeyError: im.save(s, "GIF", quality=100) s.seek(0) img = s.read() try: imgp._delObject('course_'+str(self.get_id())+'_image_thumbnail') except AttributeError: pass imgp.manage_addImage('course_'+str(self.get_id())+'_image_thumbnail', img, 'image') img = getattr(imgp, 'course_'+str(self.get_id())+'_image_thumbnail') img.ZCacheable_setManagerId('HTTPCache') img.ZCacheable_invalidate() def hasLogo(self): """ if course has its own logo """ # XXX:weird hack if not getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image', None): return 0 return 1 def hasLogoThumbnail(self): """ if course logo has thumbnail """ if not self.hasLogo(): return 0 if not getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image_thumbnail', None): self.setLogoThumbnail() return 0 return 1 def getLogoTag(self, REQUEST=None): """ \"\"" return getattr(getattr(self, self.getSkinsFolderName()).images, 'course_default.gif') def getLogoURL(self): """ URL to course logo """ if self.hasLogo(): return getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image', None).absolute_url(1) return getattr(getattr(self, self.getSkinsFolderName()).images, 'course_default.gif').absolute_url(1) def getLogoThumbnailURL(self): """ URL to course logo thumbnail """ if self.hasLogoThumbnail(): return getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image_thumbnail', None).absolute_url(1) return getattr(getattr(self, self.getSkinsFolderName()).images, 'course_default.gif').absolute_url(1) def setStatus(self, sttus): """ set course statuse """ self.staatus=sttus def setRegStatus(self, regStatus): """ set registration status """ self.regStatus = regStatus def getStatus(self): """ get course status """ if getattr(self, 'staatus', None) is None: return 0 else: return self.staatus def getRegStatus(self): """ return how students get to course """ return getattr(self, 'regStatus', 0) security.declareProtected(perm_view, 'search_form_handler') def search_form_handler( self, REQUEST, cancel=None, # submit buttons submit=None, # get_name = None, get_content= None ): """Search form handler.""" if (submit) and ((get_name) or (get_content)): for s in 'get_name', 'get_content': REQUEST.set(s, REQUEST[s]) if REQUEST['get_author_name'] == '___anyone___': uname = str(self.REQUEST.AUTHENTICATED_USER) if len(self.fle_users.get_user_info(uname).user_courses()) > 0: REQUEST.set('get_author_name', self.courses.get_unames_on_my_courses(REQUEST)) else: REQUEST.set('get_author_name', uname) else: REQUEST.set('get_author_name', REQUEST['get_author_name']) return self.wt_search_results(self, REQUEST) elif cancel: REQUEST.RESPONSE.redirect('index_html') else: REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER']) security.declareProtected(perm_view,'getCourseCategory') def getCourseCategory(self): return self.course_category security.declarePrivate('setcourseCategory') def setCourseCategory(self, id): self.course_category = id self._p_changed = 1 def get_n_artefacts(self): """ number of artefacts on a course """ total = 0 for x in self.jamming.objectValues('JamSession'): total += x.get_n_artefacts() return total def total_n_unseen_jams(self,REQUEST): total = 0 for x in self.jamming.objectValues('JamSession'): total += x.get_n_unread_artefacts(REQUEST) return total def isCourseAlive(self): """ when was last input, new objects, artefacts? """ #XXX: isCourseAlive ZZZ, this is serious and need a rewrite! curtime = time.time() aeg = curtime-86400*30 #XXX: go away! if self.lastModify() time: time = tmp return float(time) def lastModify_printable(self,REQUEST): """ returns printable last modification date """ return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),time.localtime(self.lastModify())) def lastActive(self): """ return last visit to course """ last = 0 for x in self.get_all_users(): tmp = self.Statistics.getUserCourseStat(int(self.get_id()), 'lastAccess', x.get_uname()) if tmp > last: last = tmp return last def lastActive_printable(self,REQUEST): """ return last visit to course """ return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),time.localtime(self.lastActive())) security.declareProtected(perm_manage, 'getUniqId') def getUniqId(self): """ return uniq id. we take it with us when exporting and put it back when importing should be random enough to avoid collision """ return self.__uniq_id security.declarePrivate('setUniqId') def setUniqId(self, uniq_id): """ set course uniq id. don't change it. method is here only for beauty """ if not uniq_id: raise 'IVA error:', 'uniq id not passed' self.__uniq_id = uniq_id return 1 security.declareProtected(perm_view, 'hasWordmaps') def hasWordmaps(self): """ if course has wordmaps """ return getattr(self, 'wordmaps', None) security.declareProtected(perm_edit, 'createWordmaps') def createWordmaps(self): """ create wordmaps """ import WordMapTools wf=WordMapTools.WordmapFolder() wf.id="wordmaps" self._setObject(wf.id, wf) security.declarePrivate('createTracker') def createTracker(self, REQUEST=None): """ create tracker. Trackers came in IVA 1.0 """ from Tracker import Tracker tracker = Tracker() try: self._setObject(tracker.getId(), tracker) except: self._delObject(tracker.getId()) self._setObject(tracker.getId(), tracker) if REQUEST: return self.message_dialog(message='Tracker created', action='tracker') else: return 0 Globals.default__class_init__(Course) # EOF