Feedback , 1=>DirectFeedback, 2=>NoFeedback define('EXERCISE_FEEDBACK_TYPE_END',0); define('EXERCISE_FEEDBACK_TYPE_DIRECT',1); define('EXERCISE_FEEDBACK_TYPE_EXAM',2); $debug = 1; //All exercise scripts should depend in this debug variable require_once dirname(__FILE__).'/../inc/lib/exercise_show_functions.lib.php'; if(!class_exists('Exercise')): class Exercise { public $id; public $exercise; public $description; public $sound; public $type; //ALL_ON_ONE_PAGE or ONE_PER_PAGE public $random; public $random_answers; public $active; public $timeLimit; public $attempts; public $feedbacktype; public $end_time; public $start_time; public $questionList; // array with the list of this exercise's questions public $results_disabled; public $expired_time; public $course; public $propagate_neg; /** * Constructor of the class * * @author - Olivier Brouckaert */ function Exercise($course_id = null) { $this->id = 0; $this->exercise = ''; $this->description = ''; $this->sound = ''; $this->type = ALL_ON_ONE_PAGE; $this->random = 0; $this->random_answers = 0; $this->active = 1; $this->questionList = array(); $this->timeLimit = 0; $this->end_time = '0000-00-00 00:00:00'; $this->start_time = '0000-00-00 00:00:00'; $this->results_disabled = 1; $this->expired_time = '0000-00-00 00:00:00'; $this->propagate_neg = 0; if (!empty($course_id)) { $this->course_id = intval($course_id); $course_info = api_get_course_info_by_id($this->course_id); } else { $course_info = api_get_course_info(); } $this->course = $course_info; } /** * reads exercise informations from the data base * * @author - Olivier Brouckaert * @param - integer $id - exercise ID * @return - boolean - true if exercise exists, otherwise false */ function read($id) { $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION,$this->course['db_name']); $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST,$this->course['db_name']); $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION,$this->course['db_name']); #$TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER); $sql="SELECT * FROM $TBL_EXERCICES WHERE id='".Database::escape_string($id)."'"; $result=Database::query($sql); // if the exercise has been found if ($object=Database::fetch_object($result)) { $this->id = $id; $this->exercise = $object->title; $this->description = $object->description; $this->sound = $object->sound; $this->type = $object->type; $this->random = $object->random; $this->random_answers = $object->random_answers; $this->active = $object->active; $this->results_disabled = $object->results_disabled; $this->attempts = $object->max_attempt; $this->feedbacktype = $object->feedback_type; $this->propagate_neg = $object->propagate_neg; if ($object->end_time != '0000-00-00 00:00:00') { $this->end_time = api_get_local_time($object->end_time); } if ($object->start_time != '0000-00-00 00:00:00') { $this->start_time = api_get_local_time($object->start_time); } $this->expired_time = $object->expired_time; //control time $sql="SELECT question_id, question_order FROM $TBL_EXERCICE_QUESTION, $TBL_QUESTIONS WHERE question_id=id AND exercice_id='".Database::escape_string($id)."' ORDER BY question_order"; $result=Database::query($sql); // fills the array with the question ID for this exercise // the key of the array is the question position while ($new_object = Database::fetch_object($result)) { $this->questionList[$new_object->question_order]= $new_object->question_id; } //overload questions list with recorded questions list //load questions only for exercises of type 'one question per page' //this is needed only is there is no questions // // @todo not sure were in the code this is used somebody mess with the exercise tool global $_configuration, $questionList; if ($this->type == ONE_PER_PAGE && $_configuration['live_exercise_tracking'] && $_SERVER['REQUEST_METHOD'] != 'POST' && defined('QUESTION_LIST_ALREADY_LOGGED')) { //if(empty($_SESSION['questionList'])) $this->questionList = $questionList; } return true; } // exercise not found return false; } /** * returns the exercise ID * * @author - Olivier Brouckaert * @return - integer - exercise ID */ function selectId() { return $this->id; } /** * returns the exercise title * * @author - Olivier Brouckaert * @return - string - exercise title */ function selectTitle() { return $this->exercise; } /** * returns the number of attempts setted * * @return - numeric - exercise attempts */ function selectAttempts() { return $this->attempts; } /** returns the number of FeedbackType * * 0=>Feedback , 1=>DirectFeedback, 2=>NoFeedback * @return - numeric - exercise attempts */ function selectFeedbackType() { return $this->feedbacktype; } /** * returns the time limit */ function selectTimeLimit() { return $this->timeLimit; } /** * returns the exercise description * * @author - Olivier Brouckaert * @return - string - exercise description */ function selectDescription() { return $this->description; } /** * returns the exercise sound file * * @author - Olivier Brouckaert * @return - string - exercise description */ function selectSound() { return $this->sound; } /** * returns the exercise type * * @author - Olivier Brouckaert * @return - integer - exercise type */ function selectType() { return $this->type; } /** * tells if questions are selected randomly, and if so returns the draws * * @author - Carlos Vargas * @return - integer - results disabled exercise */ function selectResultsDisabled() { return $this->results_disabled; } /** * tells if questions are selected randomly, and if so returns the draws * * @author - Olivier Brouckaert * @return - integer - 0 if not random, otherwise the draws */ function isRandom() { if($this->random > 0){ return true; } else { return false; } } /** * returns random answers status. * * @author - Juan Carlos Ra�a */ function selectRandomAnswers() { $this->random_answers; return $this->random_answers; } /** * Same as isRandom() but has a name applied to values different than 0 or 1 */ function getShuffle() { return $this->random; } /** * returns the exercise status (1 = enabled ; 0 = disabled) * * @author - Olivier Brouckaert * @return - boolean - true if enabled, otherwise false */ function selectStatus() { return $this->active; } /** * returns the array with the question ID list * * @author - Olivier Brouckaert * @return - array - question ID list */ function selectQuestionList() { return $this->questionList; } /** * returns the number of questions in this exercise * * @author - Olivier Brouckaert * @return - integer - number of questions */ function selectNbrQuestions() { return sizeof($this->questionList); } function selectPropagateNeg() { return $this->propagate_neg; } /** * Selects questions randomly in the question list * * @author - Olivier Brouckaert * @return - array - if the exercise is not set to take questions randomly, returns the question list * without randomizing, otherwise, returns the list with questions selected randomly */ function selectRandomList() { $nbQuestions = $this->selectNbrQuestions(); $temp_list = $this->questionList; //Not a random exercise, or if there are not at least 2 questions if($this->random == 0 || $nbQuestions < 2) { return $this->questionList; } if ($nbQuestions != 0) { shuffle($temp_list); $my_random_list = array_combine(range(1,$nbQuestions),$temp_list); $my_question_list = array(); $i = 0; foreach ($my_random_list as $item) { if ($i < $this->random) { $my_question_list[$i] = $item; } else { break; } $i++; } return $my_question_list; } } /** * returns 'true' if the question ID is in the question list * * @author - Olivier Brouckaert * @param - integer $questionId - question ID * @return - boolean - true if in the list, otherwise false */ function isInList($questionId) { if (is_array($this->questionList)) return in_array($questionId,$this->questionList); else return false; } /** * changes the exercise title * * @author - Olivier Brouckaert * @param - string $title - exercise title */ function updateTitle($title) { $this->exercise=$title; } /** * changes the exercise max attempts * * @param - numeric $attempts - exercise max attempts */ function updateAttempts($attempts) { $this->attempts=$attempts; } /** * changes the exercise feedback type * * @param - numeric $attempts - exercise max attempts */ function updateFeedbackType($feedback_type) { $this->feedbacktype=$feedback_type; } /** * changes the exercise description * * @author - Olivier Brouckaert * @param - string $description - exercise description */ function updateDescription($description) { $this->description=$description; } /** * changes the exercise expired_time * * @author - Isaac flores * @param - int The expired time of the quiz */ function updateExpiredTime($expired_time) { $this->expired_time = $expired_time; } function updatePropagateNegative($value) { $this->propagate_neg = $value; } /** * changes the exercise sound file * * @author - Olivier Brouckaert * @param - string $sound - exercise sound file * @param - string $delete - ask to delete the file */ function updateSound($sound,$delete) { global $audioPath, $documentPath; $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT, $this->course['db_name']); $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY,$this->course['db_name']); if ($sound['size'] && (strstr($sound['type'],'audio') || strstr($sound['type'],'video'))) { $this->sound=$sound['name']; if (@move_uploaded_file($sound['tmp_name'],$audioPath.'/'.$this->sound)) { $query="SELECT 1 FROM $TBL_DOCUMENT WHERE path='".str_replace($documentPath,'',$audioPath).'/'.$this->sound."'"; $result=Database::query($query); if(!Database::num_rows($result)) { /*$query="INSERT INTO $TBL_DOCUMENT(path,filetype) VALUES " ." ('".str_replace($documentPath,'',$audioPath).'/'.$this->sound."','file')"; Database::query($query);*/ $id = add_document($this->course,str_replace($documentPath,'',$audioPath).'/'.$this->sound,'file',$sound['size'],$sound['name']); //$id = Database::insert_id(); //$time = time(); //$time = date("Y-m-d H:i:s", $time); // insert into the item_property table, using default visibility of "visible" /*$query = "INSERT INTO $TBL_ITEM_PROPERTY " ."(tool, ref, insert_user_id,to_group_id, insert_date, lastedit_date, lastedit_type) " ." VALUES " ."('".TOOL_DOCUMENT."', $id, $_user['user_id'], 0, '$time', '$time', 'DocumentAdded' )"; Database::query($query);*/ api_item_property_update($this->course, TOOL_DOCUMENT, $id, 'DocumentAdded',api_get_user_id()); item_property_update_on_folder($this->course,str_replace($documentPath,'',$audioPath),api_get_user_id()); } } } elseif($delete && is_file($audioPath.'/'.$this->sound)) { $this->sound=''; } } /** * changes the exercise type * * @author - Olivier Brouckaert * @param - integer $type - exercise type */ function updateType($type) { $this->type=$type; } /** * sets to 0 if questions are not selected randomly * if questions are selected randomly, sets the draws * * @author - Olivier Brouckaert * @param - integer $random - 0 if not random, otherwise the draws */ function setRandom($random) { $this->random=$random; } /** * sets to 0 if answers are not selected randomly * if answers are selected randomly * @author - Juan Carlos Ra�a * @param - integer $random_answers - random answers */ function updateRandomAnswers($random_answers) { $this->$random_answers = $random_answers; } /** * enables the exercise * * @author - Olivier Brouckaert */ function enable() { $this->active=1; } /** * disables the exercise * * @author - Olivier Brouckaert */ function disable() { $this->active=0; } function disable_results() { $this->results_disabled = true; } function enable_results() { $this->results_disabled = false; } function updateResultsDisabled($results_disabled) { $this->results_disabled = intval($results_disabled); } /** * updates the exercise in the data base * * @author - Olivier Brouckaert */ function save($type_e='') { global $_course; $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST); $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION); $TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); $id = $this->id; $exercise = $this->exercise; $description = $this->description; $sound = $this->sound; $type = $this->type; $attempts = $this->attempts; $feedbacktype = $this->feedbacktype; $random = $this->random; $random_answers = $this->random_answers; $active = $this->active; $propagate_neg = $this->propagate_neg; $session_id = api_get_session_id(); //If direct we do not show results if ($feedbacktype == EXERCISE_FEEDBACK_TYPE_DIRECT) { //$results_disabled = 1; } else { $results_disabled = intval($this->results_disabled); } $expired_time = intval($this->expired_time); $start_time = Database::escape_string(api_get_utc_datetime($this->start_time)); $end_time = Database::escape_string(api_get_utc_datetime($this->end_time)); // Exercise already exists if($id) { $sql="UPDATE $TBL_EXERCICES SET title='".Database::escape_string($exercise)."', description='".Database::escape_string($description)."'"; if ($type_e != 'simple') { $sql .= ", sound='".Database::escape_string($sound)."', type='".Database::escape_string($type)."', random='".Database::escape_string($random)."', random_answers='".Database::escape_string($random_answers)."', active='".Database::escape_string($active)."', feedback_type='".Database::escape_string($feedbacktype)."', start_time='$start_time',end_time='$end_time', max_attempt='".Database::escape_string($attempts)."', expired_time='".Database::escape_string($expired_time)."', propagate_neg='".Database::escape_string($propagate_neg)."', results_disabled='".Database::escape_string($results_disabled)."'"; } $sql .= " WHERE id='".Database::escape_string($id)."'"; Database::query($sql); // update into the item_property table api_item_property_update($_course, TOOL_QUIZ, $id,'QuizUpdated',api_get_user_id()); if (api_get_setting('search_enabled')=='true') { $this -> search_engine_edit(); } } else {// creates a new exercise //add condition by anonymous user /*if (!api_is_anonymous()) { //is course manager $cond1=Database::escape_string($exercise); $cond2=Database::escape_string($description); } else { //is anonymous user $cond1=Database::escape_string(Security::remove_XSS($exercise)); $cond2=Database::escape_string(Security::remove_XSS(api_html_entity_decode($description),COURSEMANAGERLOWSECURITY)); }*/ $sql="INSERT INTO $TBL_EXERCICES (start_time, end_time, title, description, sound, type, random, random_answers,active, results_disabled, max_attempt, feedback_type, expired_time, session_id) VALUES( '$start_time','$end_time', '".Database::escape_string($exercise)."', '".Database::escape_string($description)."', '".Database::escape_string($sound)."', '".Database::escape_string($type)."', '".Database::escape_string($random)."', '".Database::escape_string($random_answers)."', '".Database::escape_string($active)."', '".Database::escape_string($results_disabled)."', '".Database::escape_string($attempts)."', '".Database::escape_string($feedbacktype)."', '".Database::escape_string($expired_time)."', '".Database::escape_string($session_id)."' )"; Database::query($sql); $this->id=Database::insert_id(); // insert into the item_property table api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizAdded',api_get_user_id()); if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) { $this -> search_engine_save(); } } // updates the question position $this->update_question_positions(); } function update_question_positions() { // updates the question position $TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); foreach($this->questionList as $position=>$questionId) { //$sql="UPDATE $TBL_QUESTIONS SET position='".Database::escape_string($position)."' WHERE id='".Database::escape_string($questionId)."'"; $sql="UPDATE $TBL_QUIZ_QUESTION SET question_order='".Database::escape_string($position)."' " . "WHERE question_id='".Database::escape_string($questionId)."' and exercice_id=".Database::escape_string($this->id).""; Database::query($sql); } } /** * moves a question up in the list * * @author - Olivier Brouckaert * @author - Julio Montoya (rewrote the code) * @param - integer $id - question ID to move up */ function moveUp($id) { // there is a bug with some version of PHP with the key and prev functions // the script commented was tested in dev.dokeos.com with no success // Instead of using prev and next this was change with arrays. /* foreach($this->questionList as $position=>$questionId) { // if question ID found if($questionId == $id) { // position of question in the array echo $pos1=$position; //1 echo "
"; prev($this->questionList); prev($this->questionList); // position of previous question in the array $pos2=key($this->questionList); //if the cursor of the array hit the end // then we must reset the array to get the previous key if($pos2===null) { end($this->questionList); prev($this->questionList); $pos2=key($this->questionList); } // error, can't move question if(!$pos2) { //echo 'cant move!'; $pos2=key($this->questionList); reset($this->questionList); } $id2=$this->questionList[$pos2]; // exits foreach() break; } $i++; } */ $question_list =array(); foreach($this->questionList as $position=>$questionId) { $question_list[]= $questionId; } $len=count($question_list); $orderlist=array_keys($this->questionList); for($i=0;$i<$len;$i++) { $questionId = $question_list[$i]; if($questionId == $id) { // position of question in the array $pos1=$orderlist[$i]; $pos2=$orderlist[$i-1]; if($pos2===null) { $pos2 = $orderlist[$len-1]; } // error, can't move question if(!$pos2) { $pos2=$orderlist[0]; $i=0; } break; } } // permutes questions in the array $temp=$this->questionList[$pos2]; $this->questionList[$pos2]=$this->questionList[$pos1]; $this->questionList[$pos1]=$temp; } /** * moves a question down in the list * * @author - Olivier Brouckaert * @param - integer $id - question ID to move down */ function moveDown($id) { // there is a bug with some version of PHP with the key and prev functions // the script commented was tested in dev.dokeos.com with no success // Instead of using prev and next this was change with arrays. /* foreach($this->questionList as $position=>$questionId) { // if question ID found if($questionId == $id) { // position of question in the array $pos1=$position; //next($this->questionList); // position of next question in the array $pos2=key($this->questionList); // error, can't move question if(!$pos2) { //echo 'cant move!'; return; } $id2=$this->questionList[$pos2]; // exits foreach() break; } } */ $question_list =array(); foreach($this->questionList as $position=>$questionId) { $question_list[]= $questionId; } $len=count($question_list); $orderlist=array_keys($this->questionList); for($i=0;$i<$len;$i++) { $questionId = $question_list[$i]; if($questionId == $id) { $pos1=$orderlist[$i+1]; $pos2 =$orderlist[$i]; if(!$pos2) { //echo 'cant move!'; } break; } } // permutes questions in the array $temp=$this->questionList[$pos2]; $this->questionList[$pos2]=$this->questionList[$pos1]; $this->questionList[$pos1]=$temp; } /** * adds a question into the question list * * @author - Olivier Brouckaert * @param - integer $questionId - question ID * @return - boolean - true if the question has been added, otherwise false */ function addToList($questionId) { // checks if the question ID is not in the list if(!$this->isInList($questionId)) { // selects the max position if(!$this->selectNbrQuestions()) { $pos=1; } else { if (is_array($this->questionList)) $pos=max(array_keys($this->questionList))+1; } $this->questionList[$pos]=$questionId; return true; } return false; } /** * removes a question from the question list * * @author - Olivier Brouckaert * @param - integer $questionId - question ID * @return - boolean - true if the question has been removed, otherwise false */ function removeFromList($questionId) { // searches the position of the question ID in the list $pos=array_search($questionId,$this->questionList); // question not found if($pos === false) { return false; } else { // deletes the position from the array containing the wanted question ID unset($this->questionList[$pos]); return true; } } /** * deletes the exercise from the database * Notice : leaves the question in the data base * * @author - Olivier Brouckaert */ function delete() { global $_course; $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST); $sql="UPDATE $TBL_EXERCICES SET active='-1' WHERE id='".Database::escape_string($this->id)."'"; Database::query($sql); api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizDeleted',api_get_user_id()); if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian') ) { $this -> search_engine_delete(); } } /** * Creates the form to create / edit an exercise * @param FormValidator $form the formvalidator instance (by reference) */ function createForm ($form, $type='full') { global $id; if(empty($type)){ $type='full'; } // form title if (!empty($_GET['exerciseId'])) { $form_title = get_lang('ModifyExercise'); } else { $form_title = get_lang('NewEx'); } $form->addElement('header', '', $form_title); // title $form -> addElement('text', 'exerciseTitle', get_lang('ExerciseName'),'class="input_titles" id="exercise_title"'); //$form->applyFilter('exerciseTitle','html_filter'); $form -> addElement('html','
 '.get_lang('ExerciseDescription').'
'); $editor_config = array('ToolbarSet' => 'TestQuestionDescription', 'Width' => '100%', 'Height' => '150'); if(is_array($type)){ $editor_config = array_merge($editor_config, $type); } $form -> addElement ('html','
'); $form -> add_html_editor('exerciseDescription', get_lang('langExerciseDescription'), false, false, $editor_config); $form -> addElement ('html','
'); $form -> addElement('html','
 
 '.get_lang('AdvancedParameters').'
'); // Random questions $form -> addElement('html','