Advertisement
Guest User

exercise.class.php

a guest
Apr 18th, 2013
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 156.92 KB | None | 0 0
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4.  * Exercise class: This class allows to instantiate an object of type Exercise
  5.  * @package chamilo.exercise
  6.  * @author Olivier Brouckaert
  7.  * @author Julio Montoya Cleaning exercises
  8.  * Modified by Hubert Borderiou #294
  9.  */
  10. /**
  11.  * Code
  12.  */
  13. define('ALL_ON_ONE_PAGE',                   1);
  14. define('ONE_PER_PAGE',                      2);
  15.  
  16. define('EXERCISE_FEEDBACK_TYPE_END',        0); //Feedback       - show score and expected answers
  17. define('EXERCISE_FEEDBACK_TYPE_DIRECT',     1); //DirectFeedback - Do not show score nor answers
  18. define('EXERCISE_FEEDBACK_TYPE_EXAM',       2); //NoFeedback     - Show score only
  19.  
  20. define('RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS',        0); //show score and expected answers
  21. define('RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS',     1); //Do not show score nor answers
  22. define('RESULT_DISABLE_SHOW_SCORE_ONLY',       2); //Show score only
  23. define('RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES',       3); //Show final score only with categories
  24.  
  25. define('EXERCISE_MAX_NAME_SIZE',            80);
  26.  
  27. $debug = false; //All exercise scripts should depend in this debug variable
  28.  
  29. require_once dirname(__FILE__).'/../inc/lib/exercise_show_functions.lib.php';
  30.  
  31. class Exercise {
  32.  
  33.     public $id;
  34.     public $name;
  35.     public $title;
  36.     public $exercise;
  37.     public $description;
  38.     public $sound;
  39.     public $type; //ALL_ON_ONE_PAGE or ONE_PER_PAGE
  40.     public $random;
  41.     public $random_answers;
  42.     public $active;
  43.     public $timeLimit;
  44.     public $attempts;
  45.     public $feedback_type;
  46.     public $end_time;
  47.     public $start_time;
  48.     public $questionList;  // array with the list of this exercise's questions
  49.     public $results_disabled;
  50.     public $expired_time;
  51.     public $course;
  52.     public $course_id;
  53.     public $propagate_neg;
  54.     public $review_answers;
  55.     public $randomByCat;
  56.     public $text_when_finished;
  57.     public $display_category_name;
  58.     public $pass_percentage;
  59.     public $edit_exercise_in_lp = false;
  60.     public $is_gradebook_locked = false;
  61.     public $exercise_was_added_in_lp = false;
  62.     public $force_edit_exercise_in_lp = false;
  63.  
  64.  
  65.     /**
  66.      * Constructor of the class
  67.      *
  68.      * @author - Olivier Brouckaert
  69.      */
  70.     function Exercise($course_id = null) {
  71.         $this->id               = 0;
  72.         $this->exercise         = '';
  73.         $this->description      = '';
  74.         $this->sound            = '';
  75.         $this->type             = ALL_ON_ONE_PAGE;
  76.         $this->random           = 0;
  77.         $this->random_answers   = 0;
  78.         $this->active           = 1;
  79.         $this->questionList     = array();
  80.         $this->timeLimit        = 0;
  81.         $this->end_time         = '0000-00-00 00:00:00';
  82.         $this->start_time       = '0000-00-00 00:00:00';
  83.         $this->results_disabled = 1;
  84.         $this->expired_time     = '0000-00-00 00:00:00';
  85.         $this->propagate_neg    = 0;
  86.         $this->review_answers   = false;
  87.         $this->randomByCat      = 0;    //
  88.         $this->text_when_finished = ""; //
  89.         $this->display_category_name = 0;
  90.         $this->pass_percentage  = null;
  91.  
  92.         if (!empty($course_id)) {
  93.             $course_info        = api_get_course_info_by_id($course_id);
  94.         } else {
  95.             $course_info        = api_get_course_info();
  96.         }
  97.         $this->course_id    = $course_info['real_id'];
  98.         $this->course       = $course_info;
  99.     }
  100.  
  101.     /**
  102.      * reads exercise informations from the data base
  103.      *
  104.      * @author - Olivier Brouckaert
  105.      * @param - integer $id - exercise ID
  106.      * @return - boolean - true if exercise exists, otherwise false
  107.      */
  108.     function read($id) {
  109.         global $_configuration;
  110.         $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  111.         $table_lp_item = Database::get_course_table(TABLE_LP_ITEM);
  112.  
  113.         $id  = intval($id);
  114.         if (empty($this->course_id)) {
  115.             return false;
  116.         }
  117.         $sql = "SELECT * FROM $TBL_EXERCICES WHERE c_id = ".$this->course_id." AND id = ".$id;
  118.         $result = Database::query($sql);
  119.  
  120.         // if the exercise has been found
  121.         if ($object = Database::fetch_object($result)) {
  122.             $this->id                       = $id;
  123.             $this->exercise                 = $object->title;
  124.             $this->name                     = $object->title;
  125.             $this->title                    = $object->title;
  126.             $this->description              = $object->description;
  127.             $this->sound                    = $object->sound;
  128.             $this->type                     = $object->type;
  129.             if (empty($this->type)) {
  130.                 $this->type = ONE_PER_PAGE;
  131.             }
  132.             $this->random                   = $object->random;
  133.             $this->random_answers           = $object->random_answers;
  134.             $this->active                   = $object->active;
  135.             $this->results_disabled         = $object->results_disabled;
  136.             $this->attempts                 = $object->max_attempt;
  137.             $this->feedback_type            = $object->feedback_type;
  138.             $this->propagate_neg            = $object->propagate_neg;
  139.             $this->randomByCat              = $object->random_by_category;
  140.             $this->text_when_finished       = $object->text_when_finished;
  141.             $this->display_category_name    = $object->display_category_name;
  142.             $this->pass_percentage          = $object->pass_percentage;
  143.  
  144.             $this->is_gradebook_locked      = api_resource_is_locked_by_gradebook($id, LINK_EXERCISE);
  145.  
  146.             $this->review_answers   = (isset($object->review_answers) && $object->review_answers == 1) ? true : false;
  147.  
  148.             $sql = "SELECT max_score FROM $table_lp_item
  149.                    WHERE   c_id = {$this->course_id} AND
  150.                            item_type = '".TOOL_QUIZ."' AND
  151.                            path = '".$id."'";
  152.             $result = Database::query($sql);
  153.  
  154.             if (Database::num_rows($result) > 0) {
  155.                 $this->exercise_was_added_in_lp = true;
  156.             }
  157.  
  158.             $this->force_edit_exercise_in_lp = isset($_configuration['force_edit_exercise_in_lp']) ? $_configuration['force_edit_exercise_in_lp'] : false;
  159.  
  160.             if ($this->exercise_was_added_in_lp) {
  161.                 $this->edit_exercise_in_lp = $this->force_edit_exercise_in_lp == true;
  162.             } else {
  163.                 $this->edit_exercise_in_lp = true;
  164.             }
  165.  
  166.             if ($object->end_time != '0000-00-00 00:00:00') {
  167.                 $this->end_time     = $object->end_time;
  168.             }
  169.             if ($object->start_time != '0000-00-00 00:00:00') {
  170.                 $this->start_time   = $object->start_time;
  171.             }
  172.  
  173.             //control time
  174.             $this->expired_time     = $object->expired_time;
  175.  
  176.             //Checking if question_order is correctly set
  177.             $this->questionList     = $this->selectQuestionList(true);
  178.  
  179.             //overload questions list with recorded questions list
  180.             //load questions only for exercises of type 'one question per page'
  181.             //this is needed only is there is no questions
  182.             /*
  183.             // @todo not sure were in the code this is used somebody mess with the exercise tool
  184.             // @todo don't know who add that config and why $_configuration['live_exercise_tracking']
  185.             global $_configuration, $questionList;
  186.             if ($this->type == ONE_PER_PAGE && $_SERVER['REQUEST_METHOD'] != 'POST' && defined('QUESTION_LIST_ALREADY_LOGGED') &&
  187.             isset($_configuration['live_exercise_tracking']) && $_configuration['live_exercise_tracking']) {
  188.                 $this->questionList = $questionList;
  189.             }*/
  190.             return true;
  191.         }
  192.         // exercise not found
  193.         return false;
  194.     }
  195.  
  196.     function getCutTitle() {
  197.         return cut($this->exercise, EXERCISE_MAX_NAME_SIZE);
  198.     }
  199.  
  200.     /**
  201.      * returns the exercise ID
  202.      *
  203.      * @author - Olivier Brouckaert
  204.      * @return - integer - exercise ID
  205.      */
  206.     function selectId() {
  207.         return $this->id;
  208.     }
  209.  
  210.     /**
  211.      * returns the exercise title
  212.      *
  213.      * @author - Olivier Brouckaert
  214.      * @return - string - exercise title
  215.      */
  216.     function selectTitle() {
  217.         return $this->exercise;
  218.     }
  219.  
  220.     /**
  221.      * returns the number of attempts setted
  222.      *
  223.      * @return - numeric - exercise attempts
  224.      */
  225.     function selectAttempts() {
  226.         return $this->attempts;
  227.     }
  228.  
  229.     /** returns the number of FeedbackType  *
  230.      *  0=>Feedback , 1=>DirectFeedback, 2=>NoFeedback
  231.      * @return - numeric - exercise attempts
  232.      */
  233.     function selectFeedbackType() {
  234.         return $this->feedback_type;
  235.     }
  236.  
  237.  
  238.     /**
  239.      * returns the time limit
  240.      */
  241.     function selectTimeLimit() {
  242.         return $this->timeLimit;
  243.     }
  244.  
  245.     /**
  246.      * returns the exercise description
  247.      *
  248.      * @author - Olivier Brouckaert
  249.      * @return - string - exercise description
  250.      */
  251.     function selectDescription() {
  252.         return $this->description;
  253.     }
  254.  
  255.     /**
  256.      * returns the exercise sound file
  257.      *
  258.      * @author - Olivier Brouckaert
  259.      * @return - string - exercise description
  260.      */
  261.     function selectSound() {
  262.         return $this->sound;
  263.     }
  264.  
  265.     /**
  266.      * returns the exercise type
  267.      *
  268.      * @author - Olivier Brouckaert
  269.      * @return - integer - exercise type
  270.      */
  271.     function selectType() {
  272.         return $this->type;
  273.     }
  274.  
  275.     /**
  276.      * @author - hubert borderiou 30-11-11
  277.      * @return - integer : do we display the question category name for students
  278.      */
  279.     function selectDisplayCategoryName() {
  280.         return $this->display_category_name;
  281.     }
  282.  
  283.     function selectPassPercentage() {
  284.         return $this->pass_percentage;
  285.     }
  286.  
  287.     /**
  288.      * @author - hubert borderiou 30-11-11
  289.      * @return - : modify object to update the switch display_category_name
  290.      * $in_txt is an integer 0 or 1
  291.      */
  292.     function updateDisplayCategoryName($in_txt) {
  293.         $this->display_category_name = $in_txt;
  294.     }
  295.  
  296.     /**
  297.      * @author - hubert borderiou 28-11-11
  298.      * @return - html text : the text to display ay the end of the test.
  299.      */
  300.     function selectTextWhenFinished() {
  301.         return $this->text_when_finished;
  302.     }
  303.  
  304.     /**
  305.      * @author - hubert borderiou 28-11-11
  306.      * @return - html text : update the text to display ay the end of the test.
  307.      */
  308.     function updateTextWhenFinished($in_txt) {
  309.         $this->text_when_finished = $in_txt;
  310.     }
  311.  
  312.     /**
  313.      * return 1 or 2 if randomByCat
  314.      * @author - hubert borderiou
  315.      * @return - integer - quiz random by category
  316.      */
  317.     function selectRandomByCat() {
  318.         return $this->randomByCat;
  319.     }
  320.  
  321.     /**
  322.      * return 0 if no random by cat
  323.      * return 1 if random by cat, categories shuffled
  324.      * return 2 if random by cat, categories sorted by alphabetic order
  325.      * @author - hubert borderiou
  326.      * @return - integer - quiz random by category
  327.      */
  328.     function isRandomByCat() {
  329.         $res = 0;
  330.         if ($this->randomByCat == 1) {
  331.             $res = 1;
  332.         } else if ($this->randomByCat == 2) {
  333.             $res = 2;
  334.         }
  335.         return $res;
  336.     }
  337.  
  338.     /**
  339.      * return nothing
  340.      * update randomByCat value for object
  341.      * @author - hubert borderiou
  342.      */
  343.     function updateRandomByCat($in_randombycat) {
  344.      if ($in_randombycat == 1) {
  345.             $this->randomByCat = 1;
  346.         } else if ($in_randombycat == 2) {
  347.             $this->randomByCat = 2;
  348.         } else {
  349.             $this->randomByCat = 0;
  350.         }
  351.     }
  352.  
  353.  
  354.     /**
  355.      * tells if questions are selected randomly, and if so returns the draws
  356.      *
  357.      * @author - Carlos Vargas
  358.      * @return - integer - results disabled exercise
  359.      */
  360.     function selectResultsDisabled() {
  361.         return $this->results_disabled;
  362.     }
  363.  
  364.     /**
  365.      * tells if questions are selected randomly, and if so returns the draws
  366.      *
  367.      * @author - Olivier Brouckaert
  368.      * @return - integer - 0 if not random, otherwise the draws
  369.      */
  370.     function isRandom() {
  371.         if($this->random > 0 || $this->random == -1) {
  372.             return true;
  373.         } else {
  374.             return false;
  375.         }
  376.     }
  377.  
  378.     /**
  379.      * returns random answers status.
  380.      *
  381.      * @author - Juan Carlos Ra�a
  382.      */
  383.     function selectRandomAnswers() {
  384.         return $this->random_answers;
  385.     }
  386.  
  387.     /**
  388.      * Same as isRandom() but has a name applied to values different than 0 or 1
  389.      */
  390.     function getShuffle() {
  391.         return $this->random;
  392.     }
  393.  
  394.     /**
  395.      * returns the exercise status (1 = enabled ; 0 = disabled)
  396.      *
  397.      * @author - Olivier Brouckaert
  398.      * @return - boolean - true if enabled, otherwise false
  399.      */
  400.     function selectStatus() {
  401.         return $this->active;
  402.     }
  403.  
  404.     /**
  405.      * returns the array with the question ID list
  406.      *
  407.      * @author - Olivier Brouckaert
  408.      * @return - array - question ID list
  409.      */
  410.     function selectQuestionList($from_db = false) {
  411.         if ($from_db && !empty($this->id)) {
  412.             $TBL_EXERCICE_QUESTION  = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  413.             $TBL_QUESTIONS          = Database::get_course_table(TABLE_QUIZ_QUESTION);
  414.  
  415.             $sql = "SELECT DISTINCT e.question_order
  416.                    FROM $TBL_EXERCICE_QUESTION e INNER JOIN $TBL_QUESTIONS  q
  417.                        ON (e.question_id = q.id AND e.c_id = ".$this->course_id." AND q.c_id = ".$this->course_id.")
  418.                     WHERE e.exercice_id = '".Database::escape_string($this->id)."'";
  419.             $result = Database::query($sql);
  420.  
  421.             $count_question_orders = Database::num_rows($result);
  422.  
  423.             $sql = "SELECT e.question_id, e.question_order
  424.                    FROM $TBL_EXERCICE_QUESTION e INNER JOIN $TBL_QUESTIONS  q
  425.                        ON (e.question_id= q.id AND e.c_id = ".$this->course_id." AND q.c_id = ".$this->course_id.")
  426.                     WHERE e.exercice_id = '".Database::escape_string($this->id)."'
  427.                     ORDER BY question_order";
  428.             $result = Database::query($sql);
  429.  
  430.             // fills the array with the question ID for this exercise
  431.             // the key of the array is the question position
  432.             $temp_question_list = array();
  433.             $counter = 1;
  434.             $question_list = array();
  435.  
  436.             while ($new_object = Database::fetch_object($result)) {
  437.                 $question_list[$new_object->question_order]=  $new_object->question_id;
  438.                 $temp_question_list[$counter] = $new_object->question_id;
  439.                 $counter++;
  440.             }
  441.  
  442.             if (!empty($temp_question_list)) {
  443.                 if (count($temp_question_list) != $count_question_orders) {
  444.                     $question_list = $temp_question_list;
  445.                 }
  446.             }
  447.             return $question_list;
  448.         }
  449.         return $this->questionList;
  450.     }
  451.  
  452.     /**
  453.      * returns the number of questions in this exercise
  454.      *
  455.      * @author - Olivier Brouckaert
  456.      * @return - integer - number of questions
  457.      */
  458.     function selectNbrQuestions() {
  459.         return sizeof($this->questionList);
  460.     }
  461.  
  462.     function selectPropagateNeg() {
  463.         return $this->propagate_neg;
  464.     }
  465.  
  466.     /**
  467.      * Selects questions randomly in the question list
  468.      *
  469.      * @author - Olivier Brouckaert
  470.      * @return - array - if the exercise is not set to take questions randomly, returns the question list
  471.      *                   without randomizing, otherwise, returns the list with questions selected randomly
  472.      * Modified by Hubert Borderiou 15 nov 2011
  473.      */
  474.     function selectRandomList() {
  475.         $nbQuestions    = $this->selectNbrQuestions();
  476.         $temp_list      = $this->questionList;
  477.  
  478.         //Not a random exercise, or if there are not at least 2 questions
  479.         if($this->random == 0 || $nbQuestions < 2) {
  480.             return $this->questionList;
  481.         }
  482.         if ($nbQuestions != 0) {
  483.             shuffle($temp_list);
  484.             $my_random_list = array_combine(range(1,$nbQuestions),$temp_list);
  485.             $my_question_list = array();
  486.             // $this->random == -1 if random with all questions
  487.             if ($this->random > 0) {
  488.                 $i = 0;
  489.                 foreach ($my_random_list as $item) {
  490.                     if ($i < $this->random) {
  491.                         $my_question_list[$i] = $item;
  492.                     } else {
  493.                         break;
  494.                     }
  495.                     $i++;
  496.                 }
  497.             } else {
  498.                 $my_question_list = $my_random_list;
  499.             }
  500.             return $my_question_list;
  501.         }
  502.     }
  503.  
  504.     /**
  505.      * returns 'true' if the question ID is in the question list
  506.      *
  507.      * @author - Olivier Brouckaert
  508.      * @param - integer $questionId - question ID
  509.      * @return - boolean - true if in the list, otherwise false
  510.      */
  511.     function isInList($questionId) {
  512.         if (is_array($this->questionList))
  513.         return in_array($questionId,$this->questionList);
  514.         else
  515.         return false;
  516.     }
  517.  
  518.     /**
  519.      * changes the exercise title
  520.      *
  521.      * @author - Olivier Brouckaert
  522.      * @param - string $title - exercise title
  523.      */
  524.     function updateTitle($title) {
  525.         $this->exercise=$title;
  526.     }
  527.  
  528.     /**
  529.      * changes the exercise max attempts
  530.      *
  531.      * @param - numeric $attempts - exercise max attempts
  532.      */
  533.     function updateAttempts($attempts) {
  534.         $this->attempts=$attempts;
  535.     }
  536.  
  537.  
  538.     /**
  539.      * changes the exercise feedback type
  540.      *
  541.      * @param - numeric $attempts - exercise max attempts
  542.      */
  543.     function updateFeedbackType($feedback_type) {
  544.         $this->feedback_type=$feedback_type;
  545.     }
  546.  
  547.     /**
  548.      * changes the exercise description
  549.      *
  550.      * @author - Olivier Brouckaert
  551.      * @param - string $description - exercise description
  552.      */
  553.     function updateDescription($description) {
  554.         $this->description=$description;
  555.     }
  556.  
  557.     /**
  558.      * changes the exercise expired_time
  559.      *
  560.      * @author - Isaac flores
  561.      * @param - int The expired time of the quiz
  562.      */
  563.     function updateExpiredTime($expired_time) {
  564.         $this->expired_time = $expired_time;
  565.     }
  566.  
  567.     function updatePropagateNegative($value) {
  568.         $this->propagate_neg = $value;
  569.     }
  570.  
  571.     function updateReviewAnswers($value) {
  572.         $this->review_answers = (isset($value) && $value) ? true : false;
  573.     }
  574.  
  575.     function updatePassPercentage($value) {
  576.         $this->pass_percentage = $value;
  577.     }
  578.  
  579.     /**
  580.      * changes the exercise sound file
  581.      *
  582.      * @author - Olivier Brouckaert
  583.      * @param - string $sound - exercise sound file
  584.      * @param - string $delete - ask to delete the file
  585.      */
  586.     function updateSound($sound,$delete) {
  587.         global $audioPath, $documentPath;
  588.         $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
  589.  
  590.         if ($sound['size'] && (strstr($sound['type'],'audio') || strstr($sound['type'],'video'))) {
  591.             $this->sound=$sound['name'];
  592.  
  593.             if (@move_uploaded_file($sound['tmp_name'],$audioPath.'/'.$this->sound)) {
  594.                 $query="SELECT 1 FROM $TBL_DOCUMENT  WHERE c_id = ".$this->course_id." AND path='".str_replace($documentPath,'',$audioPath).'/'.$this->sound."'";
  595.                 $result=Database::query($query);
  596.  
  597.                 if(!Database::num_rows($result)) {
  598.                     /*$query="INSERT INTO $TBL_DOCUMENT(path,filetype) VALUES "
  599.                      ." ('".str_replace($documentPath,'',$audioPath).'/'.$this->sound."','file')";
  600.                     Database::query($query);*/
  601.                     $id = add_document($this->course,str_replace($documentPath,'',$audioPath).'/'.$this->sound,'file',$sound['size'],$sound['name']);
  602.                     api_item_property_update($this->course, TOOL_DOCUMENT, $id, 'DocumentAdded',api_get_user_id());
  603.                     item_property_update_on_folder($this->course,str_replace($documentPath,'',$audioPath),api_get_user_id());
  604.                 }
  605.             }
  606.         } elseif($delete && is_file($audioPath.'/'.$this->sound)) {
  607.             $this->sound='';
  608.         }
  609.     }
  610.  
  611.     /**
  612.      * changes the exercise type
  613.      *
  614.      * @author - Olivier Brouckaert
  615.      * @param - integer $type - exercise type
  616.      */
  617.     function updateType($type) {
  618.         $this->type=$type;
  619.     }
  620.  
  621.     /**
  622.      * sets to 0 if questions are not selected randomly
  623.      * if questions are selected randomly, sets the draws
  624.      *
  625.      * @author - Olivier Brouckaert
  626.      * @param - integer $random - 0 if not random, otherwise the draws
  627.      */
  628.     function setRandom($random) {
  629.         /*if ($random == 'all') {
  630.             $random = $this->selectNbrQuestions();
  631.         }*/
  632.         $this->random = $random;
  633.     }
  634.  
  635.  
  636.     /**
  637.      * sets to 0 if answers are not selected randomly
  638.      * if answers are selected randomly
  639.      * @author - Juan Carlos Ra�a
  640.      * @param - integer $random_answers - random answers
  641.      */
  642.     function updateRandomAnswers($random_answers) {
  643.         $this->random_answers = $random_answers;
  644.     }
  645.  
  646.     /**
  647.      * enables the exercise
  648.      *
  649.      * @author - Olivier Brouckaert
  650.      */
  651.     function enable() {
  652.         $this->active=1;
  653.     }
  654.  
  655.     /**
  656.      * disables the exercise
  657.      *
  658.      * @author - Olivier Brouckaert
  659.      */
  660.     function disable() {
  661.         $this->active=0;
  662.     }
  663.  
  664.     function disable_results() {
  665.         $this->results_disabled = true;
  666.     }
  667.  
  668.     function enable_results() {
  669.         $this->results_disabled = false;
  670.     }
  671.  
  672.     function updateResultsDisabled($results_disabled) {
  673.         $this->results_disabled = intval($results_disabled);
  674.     }
  675.  
  676.  
  677.     /**
  678.      * updates the exercise in the data base
  679.      *
  680.      * @author - Olivier Brouckaert
  681.      */
  682.     function save($type_e = '') {
  683.         global $_course;
  684.         $TBL_EXERCICES      = Database::get_course_table(TABLE_QUIZ_TEST);
  685.  
  686.         $id                     = $this->id;
  687.         $exercise               = $this->exercise;
  688.         $description            = $this->description;
  689.         $sound                  = $this->sound;
  690.         $type                   = $this->type;
  691.         $attempts               = $this->attempts;
  692.         $feedback_type          = $this->feedback_type;
  693.         $random                 = $this->random;
  694.         $random_answers         = $this->random_answers;
  695.         $active                 = $this->active;
  696.         $propagate_neg          = $this->propagate_neg;
  697.         $review_answers         = (isset($this->review_answers) && $this->review_answers) ? 1 : 0;
  698.         $randomByCat            = $this->randomByCat;
  699.         $text_when_finished     = $this->text_when_finished;
  700.         $display_category_name  = intval($this->display_category_name);
  701.         $pass_percentage        = intval($this->pass_percentage);
  702.  
  703.         $session_id             = api_get_session_id();
  704.  
  705.         //If direct we do not show results
  706.         if ($feedback_type == EXERCISE_FEEDBACK_TYPE_DIRECT) {
  707.             $results_disabled = 0;
  708.         } else {
  709.             $results_disabled = intval($this->results_disabled);
  710.         }
  711.  
  712.         $expired_time = intval($this->expired_time);
  713.  
  714.         if (!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') {
  715.             $start_time = Database::escape_string(api_get_utc_datetime($this->start_time));
  716.         } else {
  717.             $start_time = '0000-00-00 00:00:00';
  718.         }
  719.         if (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00') {
  720.             $end_time   = Database::escape_string(api_get_utc_datetime($this->end_time));
  721.         } else {
  722.             $end_time = '0000-00-00 00:00:00';
  723.         }
  724.  
  725.         // Exercise already exists
  726.         if ($id) {
  727.             $sql="UPDATE $TBL_EXERCICES SET
  728.                     title='".Database::escape_string($exercise)."',
  729.                     description='".Database::escape_string($description)."'";
  730.  
  731.             if ($type_e != 'simple') {
  732.                 $sql .= ",sound='".Database::escape_string($sound)."',
  733.                     type           ='".Database::escape_string($type)."',
  734.                     random         ='".Database::escape_string($random)."',
  735.                     random_answers ='".Database::escape_string($random_answers)."',
  736.                     active         ='".Database::escape_string($active)."',
  737.                     feedback_type  ='".Database::escape_string($feedback_type)."',
  738.                     start_time     = '$start_time',
  739.                     end_time       = '$end_time',
  740.                     max_attempt    ='".Database::escape_string($attempts)."',
  741.                     expired_time   ='".Database::escape_string($expired_time)."',
  742.                     propagate_neg  ='".Database::escape_string($propagate_neg)."',
  743.                     review_answers  ='".Database::escape_string($review_answers)."',
  744.                     random_by_category='".Database::escape_string($randomByCat)."',
  745.                     text_when_finished = '".Database::escape_string($text_when_finished)."',
  746.                     display_category_name = '".Database::escape_string($display_category_name)."',
  747.                    pass_percentage = '".Database::escape_string($pass_percentage)."',
  748.                     results_disabled='".Database::escape_string($results_disabled)."'";
  749.             }
  750.             $sql .= " WHERE c_id = ".$this->course_id." AND id='".Database::escape_string($id)."'";
  751.             Database::query($sql);
  752.  
  753.             // update into the item_property table
  754.             api_item_property_update($_course, TOOL_QUIZ, $id,'QuizUpdated',api_get_user_id());
  755.  
  756.             if (api_get_setting('search_enabled')=='true') {
  757.                 $this->search_engine_edit();
  758.             }
  759.         } else {
  760.             // creates a new exercise
  761.             $sql = "INSERT INTO $TBL_EXERCICES (c_id, start_time, end_time, title, description, sound, type, random, random_answers, active,
  762.                                                results_disabled, max_attempt, feedback_type, expired_time, session_id, review_answers, random_by_category,
  763.                                                text_when_finished, display_category_name, pass_percentage)
  764.                     VALUES(
  765.                         ".$this->course_id.",
  766.                         '$start_time','$end_time',
  767.                         '".Database::escape_string($exercise)."',
  768.                         '".Database::escape_string($description)."',
  769.                         '".Database::escape_string($sound)."',
  770.                         '".Database::escape_string($type)."',
  771.                         '".Database::escape_string($random)."',
  772.                         '".Database::escape_string($random_answers)."',
  773.                         '".Database::escape_string($active)."',
  774.                         '".Database::escape_string($results_disabled)."',
  775.                         '".Database::escape_string($attempts)."',
  776.                         '".Database::escape_string($feedback_type)."',
  777.                         '".Database::escape_string($expired_time)."',
  778.                         '".Database::escape_string($session_id)."',
  779.                         '".Database::escape_string($review_answers)."',
  780.                         '".Database::escape_string($randomByCat)."',
  781.                         '".Database::escape_string($text_when_finished)."',
  782.                         '".Database::escape_string($display_category_name)."',
  783.                        '".Database::escape_string($pass_percentage)."'
  784.                         )";
  785.             Database::query($sql);
  786.             $this->id = Database::insert_id();
  787.  
  788.             // insert into the item_property table
  789.             api_item_property_update($this->course, TOOL_QUIZ, $this->id, 'QuizAdded', api_get_user_id());
  790.             api_set_default_visibility($this->id, TOOL_QUIZ);
  791.  
  792.             if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) {
  793.                 $this->search_engine_save();
  794.             }
  795.         }
  796.  
  797.         // updates the question position
  798.         $this->update_question_positions();
  799.     }
  800.  
  801.     /* Updates question position */
  802.     function update_question_positions() {
  803.         $quiz_question_table = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  804.         //Fixes #3483 when updating order
  805.         $question_list = $this->selectQuestionList(true);
  806.         if (!empty($question_list)) {
  807.             foreach ($question_list as $position => $questionId) {
  808.                 $sql="UPDATE $quiz_question_table SET question_order ='".intval($position)."'".
  809.                      "WHERE c_id = ".$this->course_id." AND question_id = ".intval($questionId)." AND exercice_id=".intval($this->id);
  810.                 Database::query($sql);
  811.             }
  812.         }
  813.     }
  814.  
  815.     /**
  816.      * Adds a question into the question list
  817.      *
  818.      * @author - Olivier Brouckaert
  819.      * @param - integer $questionId - question ID
  820.      * @return - boolean - true if the question has been added, otherwise false
  821.      */
  822.     function addToList($questionId) {
  823.         // checks if the question ID is not in the list
  824.         if(!$this->isInList($questionId)) {
  825.             // selects the max position
  826.             if(!$this->selectNbrQuestions()) {
  827.                 $pos=1;
  828.             } else {
  829.                 if (is_array($this->questionList))
  830.                 $pos=max(array_keys($this->questionList))+1;
  831.             }
  832.             $this->questionList[$pos]=$questionId;
  833.             return true;
  834.         }
  835.         return false;
  836.     }
  837.  
  838.     /**
  839.      * removes a question from the question list
  840.      *
  841.      * @author - Olivier Brouckaert
  842.      * @param - integer $questionId - question ID
  843.      * @return - boolean - true if the question has been removed, otherwise false
  844.      */
  845.     function removeFromList($questionId) {
  846.         // searches the position of the question ID in the list
  847.         $pos = array_search($questionId,$this->questionList);
  848.  
  849.         // question not found
  850.         if ($pos === false) {
  851.             return false;
  852.         } else {
  853.             // dont reduce the number of random question if we use random by category option, or if
  854.             // random all questions
  855.             if ($this->isRandom() && $this->isRandomByCat() == 0) {
  856.                 if (count($this->questionList) >= $this->random && $this->random > 0) {
  857.                     $this->random -= 1;
  858.                     $this->save();
  859.                 }
  860.             }
  861.             // deletes the position from the array containing the wanted question ID
  862.             unset($this->questionList[$pos]);
  863.             return true;
  864.         }
  865.     }
  866.  
  867.     /**
  868.      * deletes the exercise from the database
  869.      * Notice : leaves the question in the data base
  870.      *
  871.      * @author - Olivier Brouckaert
  872.      */
  873.     function delete() {
  874.         $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  875.         $sql="UPDATE $TBL_EXERCICES SET active='-1' WHERE c_id = ".$this->course_id." AND id='".Database::escape_string($this->id)."'";
  876.         Database::query($sql);
  877.         api_item_property_update($this->course, TOOL_QUIZ, $this->id,'QuizDeleted',api_get_user_id());
  878.  
  879.         if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian') ) {
  880.             $this->search_engine_delete();
  881.         }
  882.     }
  883.  
  884.     /**
  885.      * Creates the form to create / edit an exercise
  886.      * @param FormValidator $form the formvalidator instance (by reference)
  887.      */
  888.     function createForm ($form, $type='full') {
  889.         global $id;
  890.  
  891.         if (empty($type)){
  892.             $type='full';
  893.         }
  894.  
  895.         // form title
  896.         if (!empty($_GET['exerciseId'])) {
  897.             $form_title = get_lang('ModifyExercise');
  898.         } else {
  899.             $form_title = get_lang('NewEx');
  900.         }
  901.  
  902.         $form->addElement('header', $form_title);
  903.  
  904.         // title
  905.         $form->addElement('text', 'exerciseTitle', get_lang('ExerciseName'), array('class' => 'span6','id'=>'exercise_title'));
  906.         //$form->applyFilter('exerciseTitle','html_filter');
  907.  
  908.         $form->addElement('advanced_settings','
  909.             <a href="javascript://" onclick=" return show_media()">
  910.                 <span id="media_icon">
  911.                     <img style="vertical-align: middle;" src="../img/looknfeel.png" alt="" /> '.addslashes(api_htmlentities(get_lang('ExerciseDescription'))).'
  912.                     </span>
  913.             </a>
  914.         ');
  915.  
  916.         $editor_config = array('ToolbarSet' => 'TestQuestionDescription', 'Width' => '100%', 'Height' => '150');
  917.         if (is_array($type)){
  918.             $editor_config = array_merge($editor_config, $type);
  919.         }
  920.  
  921.         $form->addElement ('html','<div class="HideFCKEditor" id="HiddenFCKexerciseDescription" >');
  922.         $form->add_html_editor('exerciseDescription', get_lang('ExerciseDescription'), false, false, $editor_config);
  923.         $form->addElement ('html','</div>');
  924.  
  925.         $form->addElement('advanced_settings','<a href="javascript://" onclick=" return advanced_parameters()"><span id="img_plus_and_minus"><div style="vertical-align:top;" >
  926.                            <img style="vertical-align:middle;" src="../img/div_show.gif" alt="" /> '.addslashes(api_htmlentities(get_lang('AdvancedParameters'))).'</div></span></a>');
  927.  
  928.         // Random questions
  929.         // style="" and not "display:none" to avoid #4029 Random and number of attempt menu empty
  930.         $form->addElement('html','<div id="options" style="">');
  931.  
  932.         if ($type=='full') {
  933.  
  934.             /*$feedback_option[0]=get_lang('ExerciseAtTheEndOfTheTest');
  935.              $feedback_option[1]=get_lang('DirectFeedback');
  936.             $feedback_option[2]=get_lang('NoFeedback');
  937.             */
  938.             //Can't modify a DirectFeedback question
  939.             if ($this->selectFeedbackType() != EXERCISE_FEEDBACK_TYPE_DIRECT ) {
  940.                 // feedback type
  941.                 $radios_feedback = array();
  942.                 $radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('ExerciseAtTheEndOfTheTest'),'0',array('id' =>'exerciseType_0', 'onclick' => 'check_feedback()'));
  943.  
  944.                 if (api_get_setting('enable_quiz_scenario') == 'true') {
  945.                     //Can't convert a question from one feedback to another if there is more than 1 question already added
  946.                     if ($this->selectNbrQuestions() == 0) {
  947.                         $radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('DirectFeedback'),'1',array('id' =>'exerciseType_1' , 'onclick' => 'check_direct_feedback()'));
  948.                     }
  949.                 }
  950.  
  951.                 $radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('NoFeedback'),'2',array('id' =>'exerciseType_2'));
  952.                 $form->addGroup($radios_feedback, null, get_lang('FeedbackType'), '');
  953.  
  954.                 // test type
  955.                 $radios = array();
  956.  
  957.                 $radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SimpleExercise'),    '1', array('onclick' => 'check_per_page_all()', 'id'=>'option_page_all'));
  958.                 $radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2', array('onclick' => 'check_per_page_one()', 'id'=>'option_page_one'));
  959.  
  960.                 $form->addGroup($radios, null, get_lang('QuestionsPerPage'), '');
  961.  
  962.                 $radios_results_disabled = array();
  963.                 $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ShowScoreAndRightAnswer'), '0', array('id'=>'result_disabled_0'));
  964.                 $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('DoNotShowScoreNorRightAnswer'),  '1',array('id'=>'result_disabled_1','onclick' => 'check_results_disabled()'));
  965.                 $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('OnlyShowScore'),  '2', array('id'=>'result_disabled_2','onclick' => 'check_results_disabled()'));
  966.                 //$radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ExamModeWithFinalScoreShowOnlyFinalScoreWithCategoriesIfAvailable'),  '3', array('id'=>'result_disabled_3','onclick' => 'check_results_disabled()'));
  967.  
  968.                 $form->addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'), '');
  969.  
  970.             } else {
  971.                 // if is Directfeedback but has not questions we can allow to modify the question type
  972.                 if ($this->selectNbrQuestions() == 0) {
  973.  
  974.                     // feedback type
  975.                     $radios_feedback = array();
  976.                     $radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('ExerciseAtTheEndOfTheTest'),'0',array('id' =>'exerciseType_0', 'onclick' => 'check_feedback()'));
  977.  
  978.                     if (api_get_setting('enable_quiz_scenario') == 'true') {
  979.                         $radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('DirectFeedback'), '1', array('id' =>'exerciseType_1' , 'onclick' => 'check_direct_feedback()'));
  980.                     }
  981.                     $radios_feedback[] = $form->createElement('radio', 'exerciseFeedbackType', null, get_lang('NoFeedback'),'2',array('id' =>'exerciseType_2'));
  982.                     $form->addGroup($radios_feedback, null, get_lang('FeedbackType'));
  983.  
  984.  
  985.                     //$form->addElement('select', 'exerciseFeedbackType',get_lang('FeedbackType'),$feedback_option,'onchange="javascript:feedbackselection()"');
  986.                     // test type
  987.                     $radios = array();
  988.                     $radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SimpleExercise'),    '1');
  989.                     $radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2');
  990.                     $form->addGroup($radios, null, get_lang('ExerciseType'));
  991.  
  992.                     $radios_results_disabled = array();
  993.                     $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ShowScoreAndRightAnswer'), '0', array('id'=>'result_disabled_0'));
  994.                     $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('DoNotShowScoreNorRightAnswer'),  '1',array('id'=>'result_disabled_1','onclick' => 'check_results_disabled()'));
  995.                     $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('OnlyShowScore'),  '2',array('id'=>'result_disabled_2','onclick' => 'check_results_disabled()'));
  996.                     $form->addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'),'');
  997.                 } else {
  998.                     //Show options freeze
  999.                     $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('ShowScoreAndRightAnswer'), '0', array('id'=>'result_disabled_0'));
  1000.                     $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('DoNotShowScoreNorRightAnswer'),  '1',array('id'=>'result_disabled_1','onclick' => 'check_results_disabled()'));
  1001.                     $radios_results_disabled[] = $form->createElement('radio', 'results_disabled', null, get_lang('OnlyShowScore'),  '2',array('id'=>'result_disabled_2','onclick' => 'check_results_disabled()'));
  1002.                     $result_disable_group = $form->addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'),'');
  1003.                     $result_disable_group->freeze();
  1004.  
  1005.                     $radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SimpleExercise'),    '1', array('onclick' => 'check_per_page_all()', 'id'=>'option_page_all'));
  1006.                     $radios[] = $form->createElement('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2', array('onclick' => 'check_per_page_one()', 'id'=>'option_page_one'));
  1007.  
  1008.                     $type_group = $form->addGroup($radios, null, get_lang('QuestionsPerPage'), '');
  1009.                     $type_group->freeze();
  1010.  
  1011.                     //we force the options to the DirectFeedback exercisetype
  1012.                     $form->addElement('hidden', 'exerciseFeedbackType', EXERCISE_FEEDBACK_TYPE_DIRECT);
  1013.                     $form->addElement('hidden', 'exerciseType', ONE_PER_PAGE);
  1014.                 }
  1015.             }
  1016.  
  1017.             // number of random question
  1018.  
  1019.             $max = ($this->id > 0) ? $this->selectNbrQuestions() : 10 ;
  1020.             $option = range(0,$max);
  1021.             $option[0] = get_lang('No');
  1022.             $option[-1] = get_lang('AllQuestionsShort');
  1023.             $form->addElement('select', 'randomQuestions',array(get_lang('RandomQuestions'), get_lang('RandomQuestionsHelp')), $option, array('id'=>'randomQuestions','class'=>'chzn-select'));
  1024.  
  1025.             //random answers
  1026.             $radios_random_answers = array();
  1027.             $radios_random_answers[] = $form->createElement('radio', 'randomAnswers', null, get_lang('Yes'),'1');
  1028.             $radios_random_answers[] = $form->createElement('radio', 'randomAnswers', null, get_lang('No'),'0');
  1029.             $form->addGroup($radios_random_answers, null, get_lang('RandomAnswers'), '');
  1030.  
  1031.             //randow by category
  1032.             $form->addElement('html','<div class="clear">&nbsp;</div>');
  1033.             $radiocat = array();
  1034.             $radiocat[] = $form->createElement('radio', 'randomByCat', null, get_lang('YesWithCategoriesShuffled'),'1');
  1035.             $radiocat[] = $form->createElement('radio', 'randomByCat', null, get_lang('YesWithCategoriesSorted'),'2');
  1036.             $radiocat[] = $form->createElement('radio', 'randomByCat', null, get_lang('No'),'0');
  1037.             $form->addGroup($radiocat, null, get_lang('RandomQuestionByCategory'), '');
  1038.             $form->addElement('html','<div class="clear">&nbsp;</div>');
  1039.  
  1040.             // add the radio display the category name for student
  1041.             $radio_display_cat_name = array();
  1042.             $radio_display_cat_name[] = $form->createElement('radio', 'display_category_name', null, get_lang('Yes'),'1');
  1043.             $radio_display_cat_name[] = $form->createElement('radio', 'display_category_name', null, get_lang('No'),'0');
  1044.             $form->addGroup($radio_display_cat_name, null, get_lang('QuestionDisplayCategoryName'), '');
  1045.  
  1046.             //Attempts
  1047.             $attempt_option=range(0,10);
  1048.             $attempt_option[0]=get_lang('Infinite');
  1049.  
  1050.             $form->addElement('select', 'exerciseAttempts',get_lang('ExerciseAttempts'),$attempt_option, array('id'=>'exerciseAttempts','class'=>'chzn-select'));
  1051.  
  1052.             // Exercice time limit
  1053.             $form->addElement('checkbox', 'activate_start_date_check',null, get_lang('EnableStartTime'), array('onclick' => 'activate_start_date()'));
  1054.  
  1055.             $var = Exercise::selectTimeLimit();
  1056.  
  1057.             if (($this->start_time != '0000-00-00 00:00:00'))
  1058.                 $form->addElement('html','<div id="start_date_div" style="display:block;">');
  1059.             else
  1060.                 $form->addElement('html','<div id="start_date_div" style="display:none;">');
  1061.  
  1062.             $form->addElement('datepicker', 'start_time', '', array('form_name'=>'exercise_admin'), 5);
  1063.  
  1064.             $form->addElement('html','</div>');
  1065.  
  1066.             $form->addElement('checkbox', 'activate_end_date_check', null , get_lang('EnableEndTime'), array('onclick' => 'activate_end_date()'));
  1067.  
  1068.             if (($this->end_time != '0000-00-00 00:00:00'))
  1069.                 $form->addElement('html','<div id="end_date_div" style="display:block;">');
  1070.             else
  1071.                 $form->addElement('html','<div id="end_date_div" style="display:none;">');
  1072.  
  1073.             $form->addElement('datepicker', 'end_time', '', array('form_name'=>'exercise_admin'), 5);
  1074.             $form->addElement('html','</div>');
  1075.  
  1076.             //$check_option=$this->selectType();
  1077.             $diplay = 'block';
  1078.             $form->addElement('checkbox', 'propagate_neg', null, get_lang('PropagateNegativeResults'));
  1079.             $form->addElement('html','<div class="clear">&nbsp;</div>');
  1080.             $form->addElement('checkbox', 'review_answers', null, get_lang('ReviewAnswers'));
  1081.  
  1082.             $form->addElement('html','<div id="divtimecontrol"  style="display:'.$diplay.';">');
  1083.  
  1084.             //Timer control
  1085.             //$time_hours_option = range(0,12);
  1086.             //$time_minutes_option = range(0,59);
  1087.             $form->addElement('checkbox', 'enabletimercontrol', null, get_lang('EnableTimerControl'), array('onclick' =>'option_time_expired()','id'=>'enabletimercontrol','onload'=>'check_load_time()'));
  1088.             $expired_date = (int)$this->selectExpiredTime();
  1089.  
  1090.             if (($expired_date!='0')) {
  1091.                 $form->addElement('html','<div id="timercontrol" style="display:block;">');
  1092.             } else {
  1093.                 $form->addElement('html','<div id="timercontrol" style="display:none;">');
  1094.             }
  1095.             $form->addElement('text', 'enabletimercontroltotalminutes',get_lang('ExerciseTotalDurationInMinutes'),array('style' => 'width : 35px','id' => 'enabletimercontroltotalminutes'));
  1096.             $form->addElement('html','</div>');
  1097.  
  1098.             $form->addElement('text', 'pass_percentage', array(get_lang('PassPercentage'), null, '%'),  array('id' => 'pass_percentage'));
  1099.             $form->addRule('pass_percentage', get_lang('Numeric'), 'numeric');
  1100.  
  1101.             // add the text_when_finished textbox
  1102.             $form -> add_html_editor('text_when_finished', get_lang('TextWhenFinished'), false, false, $editor_config);
  1103.  
  1104.             $defaults = array();
  1105.  
  1106.             if (api_get_setting('search_enabled') === 'true') {
  1107.                 require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
  1108.  
  1109.                 $form->addElement ('checkbox', 'index_document','', get_lang('SearchFeatureDoIndexDocument'));
  1110.                 $form->addElement ('select_language', 'language', get_lang('SearchFeatureDocumentLanguage'));
  1111.  
  1112.                 $specific_fields = get_specific_field_list();
  1113.  
  1114.                 foreach ($specific_fields as $specific_field) {
  1115.                     $form->addElement ('text', $specific_field['code'], $specific_field['name']);
  1116.                     $filter = array('c_id'=> "'". api_get_course_int_id() ."'", 'field_id' => $specific_field['id'], 'ref_id' => $this->id, 'tool_id' => '\''. TOOL_QUIZ .'\'');
  1117.                     $values = get_specific_field_values_list($filter, array('value'));
  1118.                     if ( !empty($values) ) {
  1119.                         $arr_str_values = array();
  1120.                         foreach ($values as $value) {
  1121.                             $arr_str_values[] = $value['value'];
  1122.                         }
  1123.                         $defaults[$specific_field['code']] = implode(', ', $arr_str_values);
  1124.                     }
  1125.                 }
  1126.                 //$form->addElement ('html','</div>');
  1127.             }
  1128.  
  1129.             $form->addElement('html','</div>');  //End advanced setting
  1130.             $form->addElement('html','</div>');
  1131.         }
  1132.  
  1133.         // submit
  1134.         $text = isset($_GET['exerciseId']) ? get_lang('ModifyExercise') : get_lang('ProcedToQuestions');
  1135.  
  1136.         $form->addElement('style_submit_button', 'submitExercise', $text, 'class="save"');
  1137.  
  1138.         $form->addRule('exerciseTitle', get_lang('GiveExerciseName'), 'required');
  1139.  
  1140.         if ($type == 'full') {
  1141.             // rules
  1142.             $form->addRule('exerciseAttempts', get_lang('Numeric'), 'numeric');
  1143.             $form->addRule('start_time', get_lang('InvalidDate'), 'date');
  1144.             $form->addRule('end_time', get_lang('InvalidDate'), 'date');
  1145.         }
  1146.  
  1147.         // defaults
  1148.         if ($type=='full') {
  1149.             if ($this->id > 0) {
  1150.                 if ($this->random > $this->selectNbrQuestions()) {
  1151.                     $defaults['randomQuestions'] =  $this->selectNbrQuestions();
  1152.                 } else {
  1153.                     $defaults['randomQuestions'] = $this->random;
  1154.                 }
  1155.  
  1156.                 $defaults['randomAnswers']          = $this->selectRandomAnswers();
  1157.                 $defaults['exerciseType']           = $this->selectType();
  1158.                 $defaults['exerciseTitle']          = $this->selectTitle();
  1159.                 $defaults['exerciseDescription']    = $this->selectDescription();
  1160.                 $defaults['exerciseAttempts']       = $this->selectAttempts();
  1161.                 $defaults['exerciseFeedbackType']   = $this->selectFeedbackType();
  1162.                 $defaults['results_disabled']       = $this->selectResultsDisabled();
  1163.                 $defaults['propagate_neg']          = $this->selectPropagateNeg();
  1164.                 $defaults['review_answers']         = $this->review_answers;
  1165.                 $defaults['randomByCat']            = $this->selectRandomByCat(); //
  1166.                 $defaults['text_when_finished']     = $this->selectTextWhenFinished(); //
  1167.                 $defaults['display_category_name']  = $this->selectDisplayCategoryName(); //
  1168.                 $defaults['pass_percentage']        = $this->selectPassPercentage();
  1169.  
  1170.                 if (($this->start_time != '0000-00-00 00:00:00'))
  1171.                     $defaults['activate_start_date_check'] = 1;
  1172.                 if ($this->end_time != '0000-00-00 00:00:00')
  1173.                     $defaults['activate_end_date_check'] = 1;
  1174.  
  1175.                 $defaults['start_time'] = ($this->start_time!='0000-00-00 00:00:00') ? api_get_local_time($this->start_time) : date('Y-m-d 12:00:00');
  1176.                 $defaults['end_time']   = ($this->end_time!='0000-00-00 00:00:00') ? api_get_local_time($this->end_time) : date('Y-m-d 12:00:00', time()+84600);
  1177.  
  1178.                 //Get expired time
  1179.                 if($this->expired_time != '0') {
  1180.                     $defaults['enabletimercontrol'] = 1;
  1181.                     $defaults['enabletimercontroltotalminutes'] = $this->expired_time;
  1182.                 } else {
  1183.                     $defaults['enabletimercontroltotalminutes'] = 0;
  1184.                 }
  1185.             } else {
  1186.                 $defaults['exerciseType'] = 2;
  1187.                 $defaults['exerciseAttempts'] = 0;
  1188.                 $defaults['randomQuestions'] = 0;
  1189.                 $defaults['randomAnswers'] = 0;
  1190.                 $defaults['exerciseDescription'] = '';
  1191.                 $defaults['exerciseFeedbackType'] = 0;
  1192.                 $defaults['results_disabled'] = 0;
  1193.                 $defaults['randomByCat'] = 0;   //
  1194.                 $defaults['text_when_finished'] = ""; //
  1195.                 $defaults['start_time'] = date('Y-m-d 12:00:00');
  1196.                 $defaults['display_category_name'] = 1; //
  1197.                 $defaults['end_time']   = date('Y-m-d 12:00:00',time()+84600);
  1198.                 $defaults['pass_percentage'] = '';
  1199.             }
  1200.         } else {
  1201.             $defaults['exerciseTitle'] = $this->selectTitle();
  1202.             $defaults['exerciseDescription'] = $this->selectDescription();
  1203.         }
  1204.         if (api_get_setting('search_enabled') === 'true') {
  1205.             $defaults['index_document'] = 'checked="checked"';
  1206.         }
  1207.         $form->setDefaults($defaults);
  1208.     }
  1209.  
  1210.     /**
  1211.      * function which process the creation of exercises
  1212.      * @param FormValidator $form the formvalidator instance
  1213.      */
  1214.     function processCreation($form, $type='') {
  1215.  
  1216.         $this->updateTitle($form->getSubmitValue('exerciseTitle'));
  1217.         $this->updateDescription($form->getSubmitValue('exerciseDescription'));
  1218.         $this->updateAttempts($form->getSubmitValue('exerciseAttempts'));
  1219.         $this->updateFeedbackType($form->getSubmitValue('exerciseFeedbackType'));
  1220.         $this->updateType($form->getSubmitValue('exerciseType'));
  1221.         $this->setRandom($form->getSubmitValue('randomQuestions'));
  1222.         $this->updateRandomAnswers($form->getSubmitValue('randomAnswers'));
  1223.         $this->updateResultsDisabled($form->getSubmitValue('results_disabled'));
  1224.         $this->updateExpiredTime($form->getSubmitValue('enabletimercontroltotalminutes'));
  1225.         $this->updatePropagateNegative($form->getSubmitValue('propagate_neg'));
  1226.         $this->updateRandomByCat($form->getSubmitValue('randomByCat'));
  1227.         $this->updateTextWhenFinished($form->getSubmitValue('text_when_finished'));
  1228.         $this->updateDisplayCategoryName($form->getSubmitValue('display_category_name'));
  1229.         $this->updateReviewAnswers($form->getSubmitValue('review_answers'));
  1230.         $this->updatePassPercentage($form->getSubmitValue('pass_percentage'));
  1231.  
  1232.         if ($form->getSubmitValue('activate_start_date_check') == 1) {
  1233.             $start_time = $form->getSubmitValue('start_time');
  1234.             $start_time['F'] = sprintf('%02d', $start_time['F']);
  1235.             $start_time['i'] = sprintf('%02d', $start_time['i']);
  1236.             $start_time['d'] = sprintf('%02d', $start_time['d']);
  1237.  
  1238.             $this->start_time = $start_time['Y'].'-'.$start_time['F'].'-'.$start_time['d'].' '.$start_time['H'].':'.$start_time['i'].':00';
  1239.         } else {
  1240.             $this->start_time = '0000-00-00 00:00:00';
  1241.         }
  1242.  
  1243.         if ($form->getSubmitValue('activate_end_date_check') == 1) {
  1244.             $end_time = $form->getSubmitValue('end_time');
  1245.             $end_time['F'] = sprintf('%02d', $end_time['F']);
  1246.             $end_time['i'] = sprintf('%02d', $end_time['i']);
  1247.             $end_time['d'] = sprintf('%02d', $end_time['d']);
  1248.  
  1249.             $this->end_time = $end_time['Y'].'-'.$end_time['F'].'-'.$end_time['d'].' '.$end_time['H'].':'.$end_time['i'].':00';
  1250.         } else {
  1251.             $this->end_time   = '0000-00-00 00:00:00';
  1252.         }
  1253.  
  1254.         if ($form->getSubmitValue('enabletimercontrol') == 1) {
  1255.             $expired_total_time = $form->getSubmitValue('enabletimercontroltotalminutes');
  1256.             if ($this->expired_time == 0) {
  1257.                 $this->expired_time = $expired_total_time;
  1258.             }
  1259.         } else {
  1260.             $this->expired_time = 0;
  1261.         }
  1262.  
  1263.         if ($form->getSubmitValue('randomAnswers') == 1) {
  1264.             $this->random_answers=1;
  1265.         } else {
  1266.             $this->random_answers=0;
  1267.         }
  1268.         $this->save($type);
  1269.     }
  1270.  
  1271.     function search_engine_save() {
  1272.         if ($_POST['index_document'] != 1) {
  1273.             return;
  1274.         }
  1275.         $course_id = api_get_course_id();
  1276.  
  1277.         require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php';
  1278.         require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php';
  1279.         require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
  1280.  
  1281.         $specific_fields = get_specific_field_list();
  1282.         $ic_slide = new IndexableChunk();
  1283.  
  1284.         $all_specific_terms = '';
  1285.         foreach ($specific_fields as $specific_field) {
  1286.             if (isset($_REQUEST[$specific_field['code']])) {
  1287.                 $sterms = trim($_REQUEST[$specific_field['code']]);
  1288.                 if (!empty($sterms)) {
  1289.                     $all_specific_terms .= ' '. $sterms;
  1290.                     $sterms = explode(',', $sterms);
  1291.                     foreach ($sterms as $sterm) {
  1292.                         $ic_slide->addTerm(trim($sterm), $specific_field['code']);
  1293.                         add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm);
  1294.                     }
  1295.                 }
  1296.             }
  1297.         }
  1298.  
  1299.         // build the chunk to index
  1300.         $ic_slide->addValue("title", $this->exercise);
  1301.         $ic_slide->addCourseId($course_id);
  1302.         $ic_slide->addToolId(TOOL_QUIZ);
  1303.         $xapian_data = array(
  1304.         SE_COURSE_ID => $course_id,
  1305.         SE_TOOL_ID => TOOL_QUIZ,
  1306.         SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id),
  1307.         SE_USER => (int)api_get_user_id(),
  1308.         );
  1309.         $ic_slide->xapian_data = serialize($xapian_data);
  1310.         $exercise_description = $all_specific_terms .' '. $this->description;
  1311.         $ic_slide->addValue("content", $exercise_description);
  1312.  
  1313.         $di = new ChamiloIndexer();
  1314.         isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english';
  1315.         $di->connectDb(NULL, NULL, $lang);
  1316.         $di->addChunk($ic_slide);
  1317.  
  1318.         //index and return search engine document id
  1319.         $did = $di->index();
  1320.         if ($did) {
  1321.             // save it to db
  1322.             $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1323.             $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  1324.                 VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  1325.             $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did);
  1326.             Database::query($sql);
  1327.         }
  1328.     }
  1329.  
  1330.     function search_engine_edit() {
  1331.         // update search enchine and its values table if enabled
  1332.         if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) {
  1333.             $course_id = api_get_course_id();
  1334.  
  1335.             // actually, it consists on delete terms from db, insert new ones, create a new search engine document, and remove the old one
  1336.             // get search_did
  1337.             $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1338.             $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
  1339.             $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1340.             $res = Database::query($sql);
  1341.  
  1342.             if (Database::num_rows($res) > 0) {
  1343.                 require_once(api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php');
  1344.                 require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php');
  1345.                 require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  1346.  
  1347.                 $se_ref = Database::fetch_array($res);
  1348.                 $specific_fields = get_specific_field_list();
  1349.                 $ic_slide = new IndexableChunk();
  1350.  
  1351.                 $all_specific_terms = '';
  1352.                 foreach ($specific_fields as $specific_field) {
  1353.                     delete_all_specific_field_value($course_id, $specific_field['id'], TOOL_QUIZ, $this->id);
  1354.                     if (isset($_REQUEST[$specific_field['code']])) {
  1355.                         $sterms = trim($_REQUEST[$specific_field['code']]);
  1356.                         $all_specific_terms .= ' '. $sterms;
  1357.                         $sterms = explode(',', $sterms);
  1358.                         foreach ($sterms as $sterm) {
  1359.                             $ic_slide->addTerm(trim($sterm), $specific_field['code']);
  1360.                             add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm);
  1361.                         }
  1362.                     }
  1363.                 }
  1364.  
  1365.                 // build the chunk to index
  1366.                 $ic_slide->addValue("title", $this->exercise);
  1367.                 $ic_slide->addCourseId($course_id);
  1368.                 $ic_slide->addToolId(TOOL_QUIZ);
  1369.                 $xapian_data = array(
  1370.                 SE_COURSE_ID => $course_id,
  1371.                 SE_TOOL_ID => TOOL_QUIZ,
  1372.                 SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id),
  1373.                 SE_USER => (int)api_get_user_id(),
  1374.                 );
  1375.                 $ic_slide->xapian_data = serialize($xapian_data);
  1376.                 $exercise_description = $all_specific_terms .' '. $this->description;
  1377.                 $ic_slide->addValue("content", $exercise_description);
  1378.  
  1379.                 $di = new ChamiloIndexer();
  1380.                 isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english';
  1381.                 $di->connectDb(NULL, NULL, $lang);
  1382.                 $di->remove_document((int)$se_ref['search_did']);
  1383.                 $di->addChunk($ic_slide);
  1384.  
  1385.                 //index and return search engine document id
  1386.                 $did = $di->index();
  1387.                 if ($did) {
  1388.                     // save it to db
  1389.                     $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=\'%s\'';
  1390.                     $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1391.                     Database::query($sql);
  1392.                     $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  1393.                        VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  1394.                     $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did);
  1395.                     Database::query($sql);
  1396.                 }
  1397.             } else {
  1398.                 $this->search_engine_save();
  1399.             }
  1400.         }
  1401.  
  1402.     }
  1403.  
  1404.     function search_engine_delete() {
  1405.         // remove from search engine if enabled
  1406.         if (api_get_setting('search_enabled') == 'true' && extension_loaded('xapian') ) {
  1407.             $course_id = api_get_course_id();
  1408.             $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1409.             $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level IS NULL LIMIT 1';
  1410.             $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1411.             $res = Database::query($sql);
  1412.             if (Database::num_rows($res) > 0) {
  1413.                 $row = Database::fetch_array($res);
  1414.                 require_once(api_get_path(LIBRARY_PATH) .'search/ChamiloIndexer.class.php');
  1415.                 $di = new ChamiloIndexer();
  1416.                 $di->remove_document((int)$row['search_did']);
  1417.                 unset($di);
  1418.                 $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
  1419.                 foreach ( $this->questionList as $question_i) {
  1420.                     $sql = 'SELECT type FROM %s WHERE id=%s';
  1421.                     $sql = sprintf($sql, $tbl_quiz_question, $question_i);
  1422.                     $qres = Database::query($sql);
  1423.                     if (Database::num_rows($qres) > 0) {
  1424.                         $qrow = Database::fetch_array($qres);
  1425.                         $objQuestion = Question::getInstance($qrow['type']);
  1426.                         $objQuestion = Question::read((int)$question_i);
  1427.                         $objQuestion->search_engine_edit($this->id, FALSE, TRUE);
  1428.                         unset($objQuestion);
  1429.                     }
  1430.                 }
  1431.             }
  1432.             $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level IS NULL LIMIT 1';
  1433.             $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1434.             Database::query($sql);
  1435.  
  1436.             // remove terms from db
  1437.             require_once(api_get_path(LIBRARY_PATH) .'specific_fields_manager.lib.php');
  1438.             delete_all_values_for_item($course_id, TOOL_QUIZ, $this->id);
  1439.         }
  1440.     }
  1441.     function selectExpiredTime() {
  1442.         return $this->expired_time;
  1443.     }
  1444.  
  1445.     /**
  1446.      * Cleans the student's results only for the Exercise tool (Not from the LP)
  1447.      * The LP results are NOT deleted
  1448.      * Works with exercises in sessions
  1449.      * @return int quantity of user's exercises deleted
  1450.      */
  1451.     function clean_results() {
  1452.         $table_track_e_exercises = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  1453.         $table_track_e_attempt   = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1454.  
  1455.         $sql_select = "SELECT exe_id FROM $table_track_e_exercises
  1456.                        WHERE    exe_cours_id = '".api_get_course_id()."' AND
  1457.                                 exe_exo_id = ".$this->id." AND
  1458.                                 orig_lp_id = 0 AND
  1459.                                 orig_lp_item_id = 0 AND
  1460.                                 session_id = ".api_get_session_id()."";
  1461.  
  1462.         $result   = Database::query($sql_select);
  1463.         $exe_list = Database::store_result($result);
  1464.  
  1465.         //deleting TRACK_E_ATTEMPT table
  1466.         $i = 0;
  1467.         if (is_array($exe_list) && count($exe_list) > 0) {
  1468.             foreach($exe_list as $item) {
  1469.                 $sql = "DELETE FROM $table_track_e_attempt WHERE exe_id = '".$item['exe_id']."'";
  1470.                 Database::query($sql);
  1471.                 $i++;
  1472.             }
  1473.         }
  1474.  
  1475.         //delete TRACK_E_EXERCICES table
  1476.         $sql = "DELETE FROM $table_track_e_exercises
  1477.                 WHERE exe_cours_id = '".api_get_course_id()."' AND exe_exo_id = ".$this->id." AND orig_lp_id = 0 AND orig_lp_item_id = 0 AND session_id = ".api_get_session_id()."";
  1478.         Database::query($sql);
  1479.         return $i;
  1480.     }
  1481.  
  1482.     /**
  1483.      * Copies an exercise (duplicate all questions and answers)
  1484.      */
  1485.  
  1486.     public function copy_exercise() {
  1487.         $exercise_obj= new Exercise();
  1488.         $exercise_obj = $this;
  1489.  
  1490.         // force the creation of a new exercise
  1491.         $exercise_obj->updateTitle($exercise_obj->selectTitle().' - '.get_lang('Copy'));
  1492.         //Hides the new exercise
  1493.         $exercise_obj->updateStatus(false);
  1494.         $exercise_obj->updateId(0);
  1495.         $exercise_obj->save();
  1496.  
  1497.         $new_exercise_id = $exercise_obj->selectId();
  1498.         $question_list   = $exercise_obj->selectQuestionList();
  1499.  
  1500.         if (!empty($question_list)) {
  1501.             //Question creation
  1502.  
  1503.             foreach ($question_list as $old_question_id) {
  1504.                 $old_question_obj = Question::read($old_question_id);
  1505.                 $new_id = $old_question_obj->duplicate();
  1506.                 if ($new_id) {
  1507.                     $new_question_obj = Question::read($new_id);
  1508.  
  1509.                     if (isset($new_question_obj) && $new_question_obj) {
  1510.                         $new_question_obj->addToList($new_exercise_id);
  1511.                         // This should be moved to the duplicate function
  1512.                         $new_answer_obj = new Answer($old_question_id);
  1513.                         $new_answer_obj->read();
  1514.                         $new_answer_obj->duplicate($new_id);
  1515.                     }
  1516.                 }
  1517.             }
  1518.         }
  1519.     }
  1520.  
  1521.     /**
  1522.      * Changes the exercise id
  1523.      *
  1524.      * @param - in $id - exercise id
  1525.      */
  1526.     private function updateId($id) {
  1527.         $this->id = $id;
  1528.     }
  1529.  
  1530.     /**
  1531.      * Changes the exercise status
  1532.      *
  1533.      * @param - string $status - exercise status
  1534.      */
  1535.     function updateStatus($status) {
  1536.         $this->active = $status;
  1537.     }
  1538.  
  1539.     public function get_stat_track_exercise_info($lp_id = 0, $lp_item_id = 0, $lp_item_view_id = 0, $status = 'incomplete') {
  1540.         $track_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  1541.         if (empty($lp_id)) {
  1542.             $lp_id = 0;
  1543.         }
  1544.         if (empty($lp_item_id)) {
  1545.             $lp_item_id = 0;
  1546.         }
  1547.         if (empty($lp_item_view_id)) {
  1548.             $lp_item_view_id = 0;
  1549.         }
  1550.         $condition = ' WHERE exe_exo_id     = ' . "'" . $this->id . "'" .' AND
  1551.                        exe_user_id          = ' . "'" . api_get_user_id() . "'" . ' AND
  1552.                        exe_cours_id         = ' . "'" . api_get_course_id() . "'" . ' AND
  1553.                        status               = ' . "'" . Database::escape_string($status). "'" . ' AND
  1554.                        orig_lp_id           = ' . "'" . $lp_id . "'" . ' AND
  1555.                        orig_lp_item_id      = ' . "'" . $lp_item_id . "'" . ' AND
  1556.                       orig_lp_item_view_id = ' . "'" . $lp_item_view_id . "'" . ' AND
  1557.                        session_id           = ' . "'" . api_get_session_id() . "' LIMIT 1"; //Adding limit 1 just in case
  1558.  
  1559.         $sql_track = 'SELECT * FROM '.$track_exercises.$condition;
  1560.  
  1561.         $result = Database::query($sql_track);
  1562.         $new_array = array();
  1563.         if (Database::num_rows($result) > 0 ) {
  1564.             $new_array = Database::fetch_array($result, 'ASSOC');
  1565.         }
  1566.         return $new_array;
  1567.     }
  1568.  
  1569.     /**
  1570.      * Saves a test attempt
  1571.      *
  1572.      * @param int  clock_expired_time
  1573.      * @param int  lp id
  1574.      * @param int  lp item id
  1575.      * @param int  lp item_view id
  1576.      * @param array question list
  1577.  
  1578.      */
  1579.     public function save_stat_track_exercise_info($clock_expired_time = 0, $safe_lp_id = 0, $safe_lp_item_id = 0, $safe_lp_item_view_id = 0, $questionList = array(), $weight = 0) {
  1580.         $track_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  1581.         $safe_lp_id             = intval($safe_lp_id);
  1582.         $safe_lp_item_id        = intval($safe_lp_item_id);
  1583.         $safe_lp_item_view_id   = intval($safe_lp_item_view_id);
  1584.  
  1585.         if (empty($safe_lp_id)) {
  1586.             $safe_lp_id = 0;
  1587.         }
  1588.         if (empty($safe_lp_item_id)) {
  1589.             $safe_lp_item_id = 0;
  1590.         }
  1591.         if (empty($clock_expired_time)) {
  1592.             $clock_expired_time = 0;
  1593.         }
  1594.         if ($this->expired_time != 0) {
  1595.             $sql_fields = "expired_time_control, ";
  1596.             $sql_fields_values = "'"."$clock_expired_time"."',";
  1597.         } else {
  1598.             $sql_fields = "";
  1599.             $sql_fields_values = "";
  1600.         }
  1601.         $questionList = array_map('intval', $questionList);
  1602.  
  1603.         $weight = Database::escape_string($weight);
  1604.         $sql = "INSERT INTO $track_exercises ($sql_fields exe_exo_id, exe_user_id, exe_cours_id, status,session_id, data_tracking, start_date, orig_lp_id, orig_lp_item_id, exe_weighting)
  1605.                VALUES($sql_fields_values '".$this->id."','" . api_get_user_id() . "','" . api_get_course_id() . "','incomplete','" . api_get_session_id() . "','" . implode(',', $questionList) . "', '" . api_get_utc_datetime() . "', '$safe_lp_id', '$safe_lp_item_id', '$weight' )";
  1606.  
  1607.         Database::query($sql);
  1608.         $id = Database::insert_id();
  1609.         return $id;
  1610.     }
  1611.  
  1612.     public function show_button($question_id, $questionNum, $questions_in_media = array()) {
  1613.         global $origin, $safe_lp_id, $safe_lp_item_id, $safe_lp_item_view_id;
  1614.  
  1615.         $nbrQuestions = $this->get_count_question_list();
  1616.  
  1617.         $all_button = $html = $label = '';
  1618.         $hotspot_get = isset($_POST['hotspot']) ? Security::remove_XSS($_POST['hotspot']):null;
  1619.  
  1620.         if ($this->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT && $this->type == ONE_PER_PAGE) {
  1621.             $html .='<a href="exercise_submit_modal.php?learnpath_id='.$safe_lp_id.'&learnpath_item_id='.$safe_lp_item_id.'&learnpath_item_view_id='.$safe_lp_item_view_id.'&origin='.$origin.'&hotspot='.$hotspot_get.'&nbrQuestions='.$nbrQuestions.'&num='.$questionNum.'&exerciseType='.$this->type.'&exerciseId='.$this->id.'&placeValuesBeforeTB_=savedValues&TB_iframe=true&height=480&width=640&modal=true" title="" class="thickbox btn">';
  1622.             if ($questionNum == count($this->questionList)) {
  1623.                 $html .= get_lang('EndTest').'</a>';
  1624.             } else {
  1625.                 $html .= get_lang('ContinueTest').'</a>';
  1626.             }
  1627.             $html .='<br />';
  1628.         } else {
  1629.             //User
  1630.             if (api_is_allowed_to_session_edit()) {
  1631.                 if ($this->type == ALL_ON_ONE_PAGE || $nbrQuestions == $questionNum) {
  1632.                     if ($this->review_answers) {
  1633.                         $label = get_lang('ReviewQuestions');
  1634.                         $class = 'btn btn-success';
  1635.                     } else {
  1636.                         $label = get_lang('EndTest');
  1637.                         $class = 'btn btn-warning';
  1638.                     }
  1639.                 } else {
  1640.                     $label = get_lang('NextQuestion');
  1641.                     $class = 'btn btn-primary';
  1642.                 }
  1643.                 if ($this->type == ONE_PER_PAGE) {
  1644.                     if ($questionNum != 1) {
  1645.                         $prev_question = $questionNum - 2;
  1646.                         $all_button .= '<a href="javascript://" class="btn" onclick="previous_question_and_save('.$prev_question.', '.$question_id.' ); ">'.get_lang('PreviousQuestion').'</a>';
  1647.                     }
  1648.  
  1649.                     //Next question
  1650.                     if (!empty($questions_in_media)) {
  1651.                         $questions_in_media = "['".implode("','",$questions_in_media)."']";
  1652.                         $all_button .= '&nbsp;<a href="javascript://" class="'.$class.'" onclick="save_question_list('.$questions_in_media.'); ">'.$label.'</a>';
  1653.                     } else {
  1654.                         $all_button .= '&nbsp;<a href="javascript://" class="'.$class.'" onclick="save_now('.$question_id.'); ">'.$label.'</a>';
  1655.                     }
  1656.                     $all_button .= '<span id="save_for_now_'.$question_id.'" class="exercise_save_mini_message"></span>&nbsp;';
  1657.  
  1658.                     $html .= $all_button;
  1659.                 } else {
  1660.                     if ($this->review_answers) {
  1661.                         $all_label = get_lang('ReviewQuestions');
  1662.                         $class = 'btn btn-success';
  1663.                     } else {
  1664.                         $all_label = get_lang('EndTest');
  1665.                         $class = 'btn btn-warning';
  1666.                     }
  1667.                     $all_button = '&nbsp;<a href="javascript://" class="'.$class.'" onclick="validate_all(); ">'.$all_label.'</a>';
  1668.                     $all_button .= '&nbsp;<span id="save_all_reponse"></span>';
  1669.                     $html .= $all_button;
  1670.                 }
  1671.             }
  1672.         }
  1673.         return $html;
  1674.     }
  1675.  
  1676.     /**
  1677.      * So the time control will work
  1678.      */
  1679.     public function show_time_control_js($time_left) {
  1680.         $time_left = intval($time_left);
  1681.         return "<script>
  1682.  
  1683.            function get_expired_date_string(expired_time) {
  1684.                var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  1685.                var day, month, year, hours, minutes, seconds, date_string;
  1686.                var obj_date = new Date(expired_time);
  1687.                day     = obj_date.getDate();
  1688.                if (day < 10) day = '0' + day;
  1689.                    month   = obj_date.getMonth();
  1690.                    year    = obj_date.getFullYear();
  1691.                    hours   = obj_date.getHours();
  1692.                if (hours < 10) hours = '0' + hours;
  1693.                minutes = obj_date.getMinutes();
  1694.                if (minutes < 10) minutes = '0' + minutes;
  1695.                seconds = obj_date.getSeconds();
  1696.                if (seconds < 10) seconds = '0' + seconds;
  1697.                date_string = months[month] +' ' + day + ', ' + year + ' ' + hours + ':' + minutes + ':' + seconds;
  1698.                return date_string;
  1699.            }
  1700.  
  1701.            function open_clock_warning() {
  1702.                $('#clock_warning').dialog({
  1703.                    modal:true,
  1704.                    height:250,
  1705.                    closeOnEscape: false,
  1706.                    resizable: false,
  1707.                    buttons: {
  1708.                        '".addslashes(get_lang("EndTest"))."': function() {
  1709.                            $('#clock_warning').dialog('close');
  1710.                        },
  1711.                    },
  1712.                    close: function() {
  1713.                        send_form();
  1714.                    }
  1715.                });
  1716.                $('#clock_warning').dialog('open');
  1717.  
  1718.                $('#counter_to_redirect').epiclock({
  1719.                    mode: $.epiclock.modes.countdown,
  1720.                    offset: {seconds: 5},
  1721.                    format: 's'
  1722.                }).bind('timer', function () {
  1723.                    send_form();
  1724.                });
  1725.  
  1726.            }
  1727.  
  1728.            function send_form() {
  1729.                if ($('#exercise_form').length) {
  1730.                    $('#exercise_form').submit();
  1731.                } else {
  1732.                    //In reminder
  1733.                    final_submit();
  1734.                }
  1735.            }
  1736.  
  1737.            function onExpiredTimeExercise() {
  1738.                $('#wrapper-clock').hide();
  1739.                $('#exercise_form').hide();
  1740.                $('#expired-message-id').show();
  1741.  
  1742.                //Fixes bug #5263
  1743.                $('#num_current_id').attr('value', '".$this->selectNbrQuestions()."');
  1744.                open_clock_warning();
  1745.            }
  1746.  
  1747.             $(document).ready(function() {
  1748.  
  1749.                 var current_time = new Date().getTime();
  1750.                var time_left    = parseInt(".$time_left."); // time in seconds when using minutes there are some seconds lost
  1751.                 var expired_time = current_time + (time_left*1000);
  1752.                 var expired_date = get_expired_date_string(expired_time);
  1753.  
  1754.                $('#exercise_clock_warning').epiclock({
  1755.                    mode: $.epiclock.modes.countdown,
  1756.                    offset: {seconds: time_left},
  1757.                    format: 'x:i:s',
  1758.                    renderer: 'minute'
  1759.                }).bind('timer', function () {
  1760.                    onExpiredTimeExercise();
  1761.                });
  1762.                 $('#submit_save').click(function () {});
  1763.         });
  1764.         </script>";
  1765.     }
  1766.  
  1767.     /**
  1768.      * Lp javascript for hotspots
  1769.      */
  1770.     public function show_lp_javascript() {
  1771.  
  1772.         return "<script type=\"text/javascript\" src=\"../plugin/hotspot/JavaScriptFlashGateway.js\"></script>
  1773.                    <script src=\"../plugin/hotspot/hotspot.js\" type=\"text/javascript\"></script>
  1774.                    <script language=\"JavaScript\" type=\"text/javascript\">
  1775.                    <!--
  1776.                    // -----------------------------------------------------------------------------
  1777.                    // Globals
  1778.                    // Major version of Flash required
  1779.                    var requiredMajorVersion = 7;
  1780.                    // Minor version of Flash required
  1781.                    var requiredMinorVersion = 0;
  1782.                    // Minor version of Flash required
  1783.                    var requiredRevision = 0;
  1784.                    // the version of javascript supported
  1785.                    var jsVersion = 1.0;
  1786.                    // -----------------------------------------------------------------------------
  1787.                    // -->
  1788.                    </script>
  1789.                    <script language=\"VBScript\" type=\"text/vbscript\">
  1790.                    <!-- // Visual basic helper required to detect Flash Player ActiveX control version information
  1791.                    Function VBGetSwfVer(i)
  1792.                      on error resume next
  1793.                      Dim swControl, swVersion
  1794.                      swVersion = 0
  1795.  
  1796.                      set swControl = CreateObject(\"ShockwaveFlash.ShockwaveFlash.\" + CStr(i))
  1797.                      if (IsObject(swControl)) then
  1798.                        swVersion = swControl.GetVariable(\"\$version\")
  1799.                      end if
  1800.                      VBGetSwfVer = swVersion
  1801.                    End Function
  1802.                    // -->
  1803.                    </script>
  1804.  
  1805.                    <script language=\"JavaScript1.1\" type=\"text/javascript\">
  1806.                    <!-- // Detect Client Browser type
  1807.                    var isIE  = (navigator.appVersion.indexOf(\"MSIE\") != -1) ? true : false;
  1808.                    var isWin = (navigator.appVersion.toLowerCase().indexOf(\"win\") != -1) ? true : false;
  1809.                    var isOpera = (navigator.userAgent.indexOf(\"Opera\") != -1) ? true : false;
  1810.                    jsVersion = 1.1;
  1811.                    // JavaScript helper required to detect Flash Player PlugIn version information
  1812.                    function JSGetSwfVer(i){
  1813.                        // NS/Opera version >= 3 check for Flash plugin in plugin array
  1814.                        if (navigator.plugins != null && navigator.plugins.length > 0) {
  1815.                            if (navigator.plugins[\"Shockwave Flash 2.0\"] || navigator.plugins[\"Shockwave Flash\"]) {
  1816.                                var swVer2 = navigator.plugins[\"Shockwave Flash 2.0\"] ? \" 2.0\" : \"\";
  1817.                                var flashDescription = navigator.plugins[\"Shockwave Flash\" + swVer2].description;
  1818.                                descArray = flashDescription.split(\" \");
  1819.                                tempArrayMajor = descArray[2].split(\".\");
  1820.                                versionMajor = tempArrayMajor[0];
  1821.                                versionMinor = tempArrayMajor[1];
  1822.                                if ( descArray[3] != \"\" ) {
  1823.                                    tempArrayMinor = descArray[3].split(\"r\");
  1824.                                } else {
  1825.                                    tempArrayMinor = descArray[4].split(\"r\");
  1826.                                }
  1827.                                versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
  1828.                                flashVer = versionMajor + \".\" + versionMinor + \".\" + versionRevision;
  1829.                            } else {
  1830.                                flashVer = -1;
  1831.                            }
  1832.                        }
  1833.                        // MSN/WebTV 2.6 supports Flash 4
  1834.                        else if (navigator.userAgent.toLowerCase().indexOf(\"webtv/2.6\") != -1) flashVer = 4;
  1835.                        // WebTV 2.5 supports Flash 3
  1836.                        else if (navigator.userAgent.toLowerCase().indexOf(\"webtv/2.5\") != -1) flashVer = 3;
  1837.                        // older WebTV supports Flash 2
  1838.                        else if (navigator.userAgent.toLowerCase().indexOf(\"webtv\") != -1) flashVer = 2;
  1839.                        // Can't detect in all other cases
  1840.                        else {
  1841.  
  1842.                            flashVer = -1;
  1843.                        }
  1844.                        return flashVer;
  1845.                    }
  1846.                    // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
  1847.                    function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
  1848.                    {
  1849.                        reqVer = parseFloat(reqMajorVer + \".\" + reqRevision);
  1850.                        // loop backwards through the versions until we find the newest version
  1851.                        for (i=25;i>0;i--) {
  1852.                            if (isIE && isWin && !isOpera) {
  1853.                                versionStr = VBGetSwfVer(i);
  1854.                            } else {
  1855.                                versionStr = JSGetSwfVer(i);
  1856.                            }
  1857.                            if (versionStr == -1 ) {
  1858.                                return false;
  1859.                            } else if (versionStr != 0) {
  1860.                                if(isIE && isWin && !isOpera) {
  1861.                                    tempArray         = versionStr.split(\" \");
  1862.                                    tempString        = tempArray[1];
  1863.                                    versionArray      = tempString .split(\",\");
  1864.                                } else {
  1865.                                    versionArray      = versionStr.split(\".\");
  1866.                                }
  1867.                                versionMajor      = versionArray[0];
  1868.                                versionMinor      = versionArray[1];
  1869.                                versionRevision   = versionArray[2];
  1870.  
  1871.                                versionString     = versionMajor + \".\" + versionRevision;   // 7.0r24 == 7.24
  1872.                                versionNum        = parseFloat(versionString);
  1873.                                // is the major.revision >= requested major.revision AND the minor version >= requested minor
  1874.                                if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) {
  1875.                                    return true;
  1876.                                } else {
  1877.                                    return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false );
  1878.                                }
  1879.                            }
  1880.                        }
  1881.                    }
  1882.                    // -->
  1883.                    </script>";
  1884.     }
  1885.  
  1886.     /**
  1887.      * This function was originally found in the exercise_show.php
  1888.      * @param   int     exe id
  1889.      * @param   int     question id
  1890.      * @param   int     the choice the user selected
  1891.      * @param   array   the hotspot coordinates $hotspot[$question_id] = coordinates
  1892.      * @param   string  function is called from 'exercise_show' or 'exercise_result'
  1893.      * @param   bool    save results in the DB or just show the reponse
  1894.      * @param   bool    gets information from DB or from the current selection
  1895.      * @param   bool    show results or not
  1896.      * @todo    reduce parameters of this function
  1897.      * @return  string  html code
  1898.      */
  1899.     public function manage_answer($exeId, $questionId, $choice, $from = 'exercise_show', $exerciseResultCoordinates = array(), $saved_results = true, $from_database = false, $show_result = true, $propagate_neg = 0, $hotspot_delineation_result = array()) {
  1900.         global $feedback_type, $debug;
  1901.         global $learnpath_id, $learnpath_item_id; //needed in order to use in the exercise_attempt() for the time
  1902.         require_once api_get_path(LIBRARY_PATH).'geometry.lib.php';
  1903.  
  1904.         if ($debug) error_log("<------ manage_answer ------> ");
  1905.         if ($debug) error_log('manage_answer called exe_id: '.$exeId);
  1906.         if ($debug) error_log('manage_answer $from:  '.$from);
  1907.         if ($debug) error_log('manage_answer $saved_results: '.$saved_results);
  1908.         if ($debug) error_log('manage_answer $from_database: '.$from_database);
  1909.         if ($debug) error_log('manage_answer $show_result: '.$show_result);
  1910.         if ($debug) error_log('manage_answer $propagate_neg: '.$propagate_neg);
  1911.         if ($debug) error_log('manage_answer $exerciseResultCoordinates: '.print_r($exerciseResultCoordinates, 1));
  1912.         if ($debug) error_log('manage_answer $hotspot_delineation_result: '.print_r($hotspot_delineation_result, 1));
  1913.         if ($debug) error_log('manage_answer $learnpath_id: '.$learnpath_id);
  1914.         if ($debug) error_log('manage_answer $learnpath_item_id: '.$learnpath_item_id);
  1915.  
  1916.         $extra_data = array();
  1917.         $final_overlap = 0;
  1918.         $final_missing =0;
  1919.         $final_excess =0;
  1920.         $overlap_color =0;
  1921.         $missing_color =0;
  1922.         $excess_color =0;
  1923.         $threadhold1 =0;
  1924.         $threadhold2 =0;
  1925.         $threadhold3 = 0;
  1926.  
  1927.         $arrques = null;
  1928.         $arrans  = null;
  1929.  
  1930.         $questionId   = intval($questionId);
  1931.         $exeId        = intval($exeId);
  1932.         $TBL_TRACK_ATTEMPT      = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1933.         $table_ans              = Database::get_course_table(TABLE_QUIZ_ANSWER);
  1934.  
  1935.         // Creates a temporary Question object
  1936.         $course_id              = api_get_course_int_id();
  1937.         $objQuestionTmp         = Question::read($questionId, $course_id);
  1938.                 if ($objQuestionTmp === false) {
  1939.                     return false;
  1940.                 }
  1941.  
  1942.         $questionName           = $objQuestionTmp->selectTitle();
  1943.         $questionWeighting      = $objQuestionTmp->selectWeighting();
  1944.         $answerType             = $objQuestionTmp->selectType();
  1945.         $quesId                 = $objQuestionTmp->selectId();
  1946.         $extra                  = $objQuestionTmp->extra;
  1947.  
  1948.         $next = 1; //not for now
  1949.  
  1950.         //Extra information of the question
  1951.         if (!empty($extra)) {
  1952.             $extra          = explode(':', $extra);
  1953.             if ($debug) error_log(print_r($extra, 1));
  1954.             //Fixes problems with negatives values using intval
  1955.             $true_score     = intval($extra[0]);
  1956.             $false_score    = intval($extra[1]);
  1957.             $doubt_score    = intval($extra[2]);
  1958.         }
  1959.  
  1960.         $totalWeighting         = 0;
  1961.         $totalScore             = 0;
  1962.  
  1963.         // Destruction of the Question object
  1964.         unset ($objQuestionTmp);
  1965.  
  1966.         // Construction of the Answer object
  1967.         $objAnswerTmp = new Answer($questionId);
  1968.         $nbrAnswers = $objAnswerTmp->selectNbrAnswers();
  1969.  
  1970.         if ($debug) error_log('Count of answers: '.$nbrAnswers);
  1971.         if ($debug) error_log('$answerType: '.$answerType);
  1972.  
  1973.         if ($answerType == FREE_ANSWER || $answerType == ORAL_EXPRESSION) {
  1974.             $nbrAnswers = 1;
  1975.         }
  1976.  
  1977.         $nano = null;
  1978.  
  1979.         if ($answerType == ORAL_EXPRESSION) {
  1980.             require_once api_get_path(LIBRARY_PATH).'nanogong.lib.php';
  1981.             $exe_info = get_exercise_results_by_attempt($exeId);
  1982.             $exe_info = $exe_info[$exeId];
  1983.  
  1984.             $params = array();
  1985.             $params['course_id']    = api_get_course_int_id();
  1986.             $params['session_id']   = api_get_session_id();
  1987.             $params['user_id']      = isset($exe_info['exe_user_id'])? $exe_info['exe_user_id'] : api_get_user_id();
  1988.             $params['exercise_id']  = isset($exe_info['exe_exo_id'])? $exe_info['exe_exo_id'] : $this->id;
  1989.             $params['question_id']  = $questionId;
  1990.             $params['exe_id']       = isset($exe_info['exe_id']) ? $exe_info['exe_id'] : $exeId;
  1991.  
  1992.             $nano = new Nanogong($params);
  1993.  
  1994.             //probably this attempt came in an exercise all question by page
  1995.             if ($feedback_type == 0) {
  1996.                 $nano->replace_with_real_exe($exeId);
  1997.             }
  1998.         }
  1999.  
  2000.         $user_answer = '';
  2001.  
  2002.         // Get answer list for matching
  2003.         $sql_answer = 'SELECT id, answer FROM '.$table_ans.' WHERE c_id = '.$course_id.' AND question_id = "'.$questionId.'" ';
  2004.         $res_answer = Database::query($sql_answer);
  2005.  
  2006.         $answer_matching =array();
  2007.         while ($real_answer = Database::fetch_array($res_answer)) {
  2008.             $answer_matching[$real_answer['id']]= $real_answer['answer'];
  2009.         }
  2010.  
  2011.         $real_answers = array();
  2012.         $quiz_question_options = Question::readQuestionOption($questionId, $course_id);
  2013.  
  2014.         $organs_at_risk_hit = 0;
  2015.  
  2016.         $questionScore = 0;
  2017.  
  2018.         if ($debug) error_log('Start answer loop ');
  2019.  
  2020.         $answer_correct_array = array();
  2021.  
  2022.         for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
  2023.             $answer             = $objAnswerTmp->selectAnswer($answerId);
  2024.             $answerComment      = $objAnswerTmp->selectComment($answerId);
  2025.             $answerCorrect      = $objAnswerTmp->isCorrect($answerId);
  2026.             $answerWeighting    = (float)$objAnswerTmp->selectWeighting($answerId);
  2027.  
  2028.             $numAnswer          = $objAnswerTmp->selectAutoId($answerId);
  2029.  
  2030.             $answer_correct_array[$answerId] = (bool)$answerCorrect;
  2031.  
  2032.             if ($debug) error_log("answer auto id: $numAnswer ");
  2033.             if ($debug) error_log("answer correct : $answerCorrect ");
  2034.  
  2035.             //delineation
  2036.             $delineation_cord   = $objAnswerTmp->selectHotspotCoordinates(1);
  2037.             $answer_delineation_destination=$objAnswerTmp->selectDestination(1);
  2038.  
  2039.             switch ($answerType) {
  2040.                 // for unique answer
  2041.                 case UNIQUE_ANSWER :
  2042.                 case UNIQUE_ANSWER_NO_OPTION :
  2043.                     if ($from_database) {
  2044.                         $queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".$exeId."' and question_id= '".$questionId."'";
  2045.                         $resultans = Database::query($queryans);
  2046.                         $choice = Database::result($resultans,0,"answer");
  2047.  
  2048.                         $studentChoice=($choice == $numAnswer)?1:0;
  2049.                         if ($studentChoice) {
  2050.                             $questionScore+=$answerWeighting;
  2051.                             $totalScore+=$answerWeighting;
  2052.                         }
  2053.                     } else {
  2054.                         $studentChoice=($choice == $numAnswer)?1:0;
  2055.                         if ($studentChoice) {
  2056.                             $questionScore+=$answerWeighting;
  2057.                             $totalScore+=$answerWeighting;
  2058.                         }
  2059.                     }
  2060.                     break;
  2061.                     // for multiple answers
  2062.                 case MULTIPLE_ANSWER_TRUE_FALSE :
  2063.                     if ($from_database) {
  2064.                         $choice = array();
  2065.                         $queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = ".$exeId." and question_id = ".$questionId;
  2066.                         $resultans = Database::query($queryans);
  2067.                         while ($row = Database::fetch_array($resultans)) {
  2068.                             $ind          = $row['answer'];
  2069.                             $result       = explode(':',$ind);
  2070.                             $my_answer_id = $result[0];
  2071.                             $option       = $result[1];
  2072.                             $choice[$my_answer_id] = $option;
  2073.                         }
  2074.                         $studentChoice  =$choice[$numAnswer];
  2075.                     } else {
  2076.                         $studentChoice  =$choice[$numAnswer];
  2077.                     }
  2078.  
  2079.                     if (!empty($studentChoice)) {
  2080.                         if ($studentChoice == $answerCorrect ) {
  2081.                             $questionScore  += $true_score;
  2082.                         } else {
  2083.                             if ($quiz_question_options[$studentChoice]['name'] != "Don't know") {
  2084.                                 $questionScore   +=  $false_score;
  2085.                             } else {
  2086.                                 $questionScore  +=  $doubt_score;
  2087.                             }
  2088.                         }
  2089.                     } else {
  2090.                         //if no result then the user just hit don't know
  2091.                         $studentChoice = 3;
  2092.                         $questionScore  +=  $doubt_score;
  2093.                     }
  2094.                     $totalScore = $questionScore;
  2095.                     break;
  2096.                 case MULTIPLE_ANSWER : //2
  2097.                     if ($from_database) {
  2098.                         $choice = array();
  2099.                         $queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'";
  2100.                         $resultans = Database::query($queryans);
  2101.                         while ($row = Database::fetch_array($resultans)) {
  2102.                             $ind = $row['answer'];
  2103.                             $choice[$ind] = 1;
  2104.                         }
  2105.  
  2106.                         $studentChoice = $choice[$numAnswer];
  2107.                         $real_answers[$answerId] = (bool)$studentChoice;
  2108.  
  2109.                         if ($studentChoice) {
  2110.                             $questionScore  +=$answerWeighting;
  2111.                         }
  2112.                     } else {
  2113.                         $studentChoice = $choice[$numAnswer];
  2114.                         $real_answers[$answerId] = (bool)$studentChoice;
  2115.  
  2116.                         if (isset($studentChoice)) {
  2117.                             $questionScore  += $answerWeighting;
  2118.                         }
  2119.                     }
  2120.                     $totalScore     += $answerWeighting;
  2121.  
  2122.                     if ($debug) error_log("studentChoice: $studentChoice");
  2123.                     break;
  2124.                 case GLOBAL_MULTIPLE_ANSWER :
  2125.                     if ($from_database) {
  2126.                         $choice = array();
  2127.                         $queryans = "SELECT answer FROM $TBL_TRACK_ATTEMPT WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'";
  2128.                         $resultans = Database::query($queryans);
  2129.                         while ($row = Database::fetch_array($resultans)) {
  2130.                             $ind = $row['answer'];
  2131.                             $choice[$ind] = 1;
  2132.                         }
  2133.                         $studentChoice = $choice[$numAnswer];
  2134.                         $real_answers[$answerId] = (bool)$studentChoice;
  2135.                         if ($studentChoice) {
  2136.                             $questionScore  +=$answerWeighting;
  2137.                         }
  2138.                     } else {
  2139.                         $studentChoice = $choice[$numAnswer];
  2140.                         if (isset($studentChoice)) {
  2141.                             $questionScore  += $answerWeighting;
  2142.                         }
  2143.                         $real_answers[$answerId] = (bool)$studentChoice;
  2144.                     }
  2145.                     $totalScore     += $answerWeighting;
  2146.                     if ($debug) error_log("studentChoice: $studentChoice");
  2147.                     break;
  2148.                 case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE:
  2149.                     if ($from_database) {
  2150.                         $queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = ".$exeId." AND question_id= ".$questionId;
  2151.                         $resultans = Database::query($queryans);
  2152.                         while ($row = Database::fetch_array($resultans)) {
  2153.                             $ind = $row['answer'];
  2154.                             $result = explode(':',$ind);
  2155.                             $my_answer_id = $result[0];
  2156.                             $option       = $result[1];
  2157.                             $choice[$my_answer_id] = $option;
  2158.                         }
  2159.                         $numAnswer = $objAnswerTmp->selectAutoId($answerId);
  2160.                         $studentChoice = $choice[$numAnswer];
  2161.  
  2162.                         if ($answerCorrect == $studentChoice) {
  2163.                             //$answerCorrect = 1;
  2164.                             $real_answers[$answerId] = true;
  2165.                         } else {
  2166.                             //$answerCorrect = 0;
  2167.                             $real_answers[$answerId] = false;
  2168.                         }
  2169.                     } else {
  2170.                         $studentChoice = $choice[$numAnswer];
  2171.                         if ($answerCorrect == $studentChoice) {
  2172.                             //$answerCorrect = 1;
  2173.                             $real_answers[$answerId] = true;
  2174.                         } else {
  2175.                             //$answerCorrect = 0;
  2176.                             $real_answers[$answerId] = false;
  2177.                         }
  2178.                     }
  2179.                     break;
  2180.                 case MULTIPLE_ANSWER_COMBINATION:
  2181.                     if ($from_database) {
  2182.                         $queryans = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." where exe_id = '".$exeId."' and question_id= '".$questionId."'";
  2183.                         $resultans = Database::query($queryans);
  2184.                         while ($row = Database::fetch_array($resultans)) {
  2185.                             $ind = $row['answer'];
  2186.                             $choice[$ind] = 1;
  2187.                         }
  2188.                         $numAnswer=$objAnswerTmp->selectAutoId($answerId);
  2189.                         $studentChoice=$choice[$numAnswer];
  2190.  
  2191.                         if ($answerCorrect == 1) {
  2192.                             if ($studentChoice) {
  2193.                                 $real_answers[$answerId] = true;
  2194.                             } else {
  2195.                                 $real_answers[$answerId] = false;
  2196.                             }
  2197.                         } else {
  2198.                             if ($studentChoice) {
  2199.                                 $real_answers[$answerId] = false;
  2200.                             } else {
  2201.                                 $real_answers[$answerId] = true;
  2202.                             }
  2203.                         }
  2204.                     } else {
  2205.                         $studentChoice=$choice[$numAnswer];
  2206.                         if ($answerCorrect == 1) {
  2207.                             if ($studentChoice) {
  2208.                                 $real_answers[$answerId] = true;
  2209.                             } else {
  2210.                                 $real_answers[$answerId] = false;
  2211.                             }
  2212.                         } else {
  2213.                             if ($studentChoice) {
  2214.                                 $real_answers[$answerId] = false;
  2215.                             } else {
  2216.                                 $real_answers[$answerId] = true;
  2217.                             }
  2218.                         }
  2219.                     }
  2220.                     break;
  2221.                     // for fill in the blanks
  2222.                 case FILL_IN_BLANKS :
  2223.                     // the question is encoded like this
  2224.                     // [A] B [C] D [E] F::10,10,10@1
  2225.                     // number 1 before the "@" means that is a switchable fill in blank question
  2226.                     // [A] B [C] D [E] F::10,10,10@ or  [A] B [C] D [E] F::10,10,10
  2227.                     // means that is a normal fill blank question
  2228.                     // first we explode the "::"
  2229.                     $pre_array = explode('::', $answer);
  2230.                     // is switchable fill blank or not
  2231.                     $last = count($pre_array) - 1;
  2232.                     $is_set_switchable = explode('@', $pre_array[$last]);
  2233.                     $switchable_answer_set = false;
  2234.                     if (isset ($is_set_switchable[1]) && $is_set_switchable[1] == 1) {
  2235.                         $switchable_answer_set = true;
  2236.                     }
  2237.                     $answer = '';
  2238.                     for ($k = 0; $k < $last; $k++) {
  2239.                         $answer .= $pre_array[$k];
  2240.                     }
  2241.                     // splits weightings that are joined with a comma
  2242.                     $answerWeighting = explode(',', $is_set_switchable[0]);
  2243.  
  2244.                     // we save the answer because it will be modified
  2245.                     //$temp = $answer;
  2246.                     $temp = $answer;
  2247.  
  2248.                     $answer = '';
  2249.                     $j = 0;
  2250.                     //initialise answer tags
  2251.                     $user_tags = $correct_tags = $real_text = array ();
  2252.                     // the loop will stop at the end of the text
  2253.                     while (1) {
  2254.                         // quits the loop if there are no more blanks (detect '[')
  2255.                         if (($pos = api_strpos($temp, '[')) === false) {
  2256.                             // adds the end of the text
  2257.                             $answer = $temp;
  2258.                             /* // Deprecated code
  2259.                              // TeX parsing - replacement of texcode tags
  2260.                             $texstring = api_parse_tex($texstring);
  2261.                             $answer = str_replace("{texcode}", $texstring, $answer);
  2262.                             */
  2263.                             $real_text[] = $answer;
  2264.                             break; //no more "blanks", quit the loop
  2265.                         }
  2266.                         // adds the piece of text that is before the blank
  2267.                         //and ends with '[' into a general storage array
  2268.                         $real_text[] = api_substr($temp, 0, $pos +1);
  2269.                         $answer .= api_substr($temp, 0, $pos +1);
  2270.                         //take the string remaining (after the last "[" we found)
  2271.                         $temp = api_substr($temp, $pos +1);
  2272.                         // quit the loop if there are no more blanks, and update $pos to the position of next ']'
  2273.                         if (($pos = api_strpos($temp, ']')) === false) {
  2274.                             // adds the end of the text
  2275.                             $answer .= $temp;
  2276.                             break;
  2277.                         }
  2278.                         if ($from_database) {
  2279.                             $queryfill = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".$exeId."' AND question_id= '".Database::escape_string($questionId)."'";
  2280.                             $resfill = Database::query($queryfill);
  2281.                             $str = Database::result($resfill,0,'answer');
  2282.  
  2283.                             api_preg_match_all('#\[([^[]*)\]#', $str, $arr);
  2284.                             $str = str_replace('\r\n', '', $str);
  2285.                             $choice = $arr[1];
  2286.  
  2287.                             $tmp = api_strrpos($choice[$j],' / ');
  2288.                             $choice[$j] = api_substr($choice[$j],0,$tmp);
  2289.                             $choice[$j] = trim($choice[$j]);
  2290.  
  2291.                             //Needed to let characters ' and " to work as part of an answer
  2292.                             $choice[$j] = stripslashes($choice[$j]);
  2293.                         } else {
  2294.                             $choice[$j] = trim($choice[$j]);
  2295.                         }
  2296.  
  2297.                         //No idea why we api_strtolower user reponses
  2298.                         //$user_tags[] = api_strtolower($choice[$j]);
  2299.                         $user_tags[] = $choice[$j];
  2300.                         //put the contents of the [] answer tag into correct_tags[]
  2301.                         //$correct_tags[] = api_strtolower(api_substr($temp, 0, $pos));
  2302.                         $correct_tags[] = api_substr($temp, 0, $pos);
  2303.                         $j++;
  2304.                         $temp = api_substr($temp, $pos +1);
  2305.                     }
  2306.                     $answer = '';
  2307.                     $real_correct_tags = $correct_tags;
  2308.                     $chosen_list = array();
  2309.  
  2310.                     for ($i = 0; $i < count($real_correct_tags); $i++) {
  2311.                         if ($i == 0) {
  2312.                             $answer .= $real_text[0];
  2313.                         }
  2314.                         if (!$switchable_answer_set) {
  2315.                             //needed to parse ' and " characters
  2316.                             $user_tags[$i] = stripslashes($user_tags[$i]);
  2317.                             if ($correct_tags[$i] == $user_tags[$i]) {
  2318.                                 // gives the related weighting to the student
  2319.                                 $questionScore += $answerWeighting[$i];
  2320.                                 // increments total score
  2321.                                 $totalScore += $answerWeighting[$i];
  2322.                                 // adds the word in green at the end of the string
  2323.                                 $answer .= $correct_tags[$i];
  2324.                             }
  2325.                             // else if the word entered by the student IS NOT the same as the one defined by the professor
  2326.                             elseif (!empty ($user_tags[$i])) {
  2327.                                 // adds the word in red at the end of the string, and strikes it
  2328.                                 $answer .= '<font color="red"><s>' . $user_tags[$i] . '</s></font>';
  2329.                             } else {
  2330.                                 // adds a tabulation if no word has been typed by the student
  2331.                                 $answer .= '&nbsp;&nbsp;&nbsp;';
  2332.                             }
  2333.                         } else {
  2334.                             // switchable fill in the blanks
  2335.                             if (in_array($user_tags[$i], $correct_tags)) {
  2336.                                 $chosen_list[] = $user_tags[$i];
  2337.                                 $correct_tags = array_diff($correct_tags, $chosen_list);
  2338.  
  2339.                                 // gives the related weighting to the student
  2340.                                 $questionScore += $answerWeighting[$i];
  2341.                                 // increments total score
  2342.                                 $totalScore += $answerWeighting[$i];
  2343.                                 // adds the word in green at the end of the string
  2344.                                 $answer .= $user_tags[$i];
  2345.                             }
  2346.                             elseif (!empty ($user_tags[$i])) {
  2347.                                 // else if the word entered by the student IS NOT the same as the one defined by the professor
  2348.                                 // adds the word in red at the end of the string, and strikes it
  2349.                                 $answer .= '<font color="red"><s>' . $user_tags[$i] . '</s></font>';
  2350.                             } else {
  2351.                                 // adds a tabulation if no word has been typed by the student
  2352.                                 $answer .= '&nbsp;&nbsp;&nbsp;';
  2353.                             }
  2354.                         }
  2355.                         // adds the correct word, followed by ] to close the blank
  2356.                         $answer .= ' / <font color="green"><b>' . $real_correct_tags[$i] . '</b></font>]';
  2357.                         if (isset ($real_text[$i +1])) {
  2358.                             $answer .= $real_text[$i +1];
  2359.                         }
  2360.                     }
  2361.                     break;
  2362.                     // for free answer
  2363.                 case FREE_ANSWER :
  2364.                     if ($from_database) {
  2365.                         $query  = "SELECT answer, marks FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'";
  2366.                         $resq   = Database::query($query);
  2367.                         $choice = Database::result($resq,0,'answer');
  2368.                         $choice = str_replace('\r\n', '', $choice);
  2369.                         $choice = stripslashes($choice);
  2370.                         $questionScore = Database::result($resq, 0, "marks");
  2371.                         if ($questionScore == -1) {
  2372.                             $totalScore+= 0;
  2373.                         } else {
  2374.                             $totalScore+= $questionScore;
  2375.                         }
  2376.                         if ($questionScore == '') {
  2377.                             $questionScore = 0;
  2378.                         }
  2379.                         $arrques = $questionName;
  2380.                         $arrans  = $choice;
  2381.                     } else {
  2382.                         $studentChoice = $choice;
  2383.                         if ($studentChoice) {
  2384.                             //Fixing negative puntation see #2193
  2385.                             $questionScore = 0;
  2386.                             $totalScore += 0;
  2387.                         }
  2388.                     }
  2389.                     break;
  2390.                 case ORAL_EXPRESSION :
  2391.                     if ($from_database) {
  2392.                         $query  = "SELECT answer, marks FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".$exeId."' AND question_id= '".$questionId."'";
  2393.                         $resq   = Database::query($query);
  2394.                         $choice = Database::result($resq,0,'answer');
  2395.                         $choice = str_replace('\r\n', '', $choice);
  2396.                         $choice = stripslashes($choice);
  2397.                         $questionScore = Database::result($resq,0,"marks");
  2398.                         if ($questionScore==-1) {
  2399.                             $totalScore+=0;
  2400.                         } else {
  2401.                             $totalScore+=$questionScore;
  2402.                         }
  2403.                         $arrques = $questionName;
  2404.                         $arrans  = $choice;
  2405.                     } else {
  2406.                         $studentChoice = $choice;
  2407.                         if ($studentChoice) {
  2408.                             //Fixing negative puntation see #2193
  2409.                             $questionScore = 0;
  2410.                             $totalScore += 0;
  2411.                         }
  2412.                     }
  2413.                     break;
  2414.                     // for matching
  2415.                 case MATCHING :
  2416.                     if ($from_database) {
  2417.                         $sql_answer = 'SELECT id, answer FROM '.$table_ans.' WHERE c_id = '.$course_id.' AND question_id="'.$questionId.'" AND correct=0';
  2418.                         $res_answer = Database::query($sql_answer);
  2419.                         // getting the real answer
  2420.                         $real_list =array();
  2421.                         while ($real_answer = Database::fetch_array($res_answer)) {
  2422.                             $real_list[$real_answer['id']]= $real_answer['answer'];
  2423.                         }
  2424.                         $sql_select_answer = 'SELECT id, answer, correct, id_auto FROM '.$table_ans.'
  2425.                                              WHERE c_id = '.$course_id.' AND question_id="'.$questionId.'" AND correct <> 0 ORDER BY id_auto';
  2426.                         $res_answers = Database::query($sql_select_answer);
  2427.  
  2428.                         $questionScore = 0;
  2429.  
  2430.                         while ($a_answers = Database::fetch_array($res_answers)) {
  2431.                             $i_answer_id    = $a_answers['id']; //3
  2432.                             $s_answer_label = $a_answers['answer'];  // your daddy - your mother
  2433.                             $i_answer_correct_answer = $a_answers['correct']; //1 - 2
  2434.                             $i_answer_id_auto = $a_answers['id_auto']; // 3 - 4
  2435.  
  2436.                             $sql_user_answer = "SELECT answer FROM $TBL_TRACK_ATTEMPT
  2437.                                                WHERE exe_id = '$exeId' AND question_id = '$questionId' AND position='$i_answer_id_auto'";
  2438.  
  2439.                             $res_user_answer = Database::query($sql_user_answer);
  2440.  
  2441.                             if (Database::num_rows($res_user_answer)>0 ) {
  2442.                                 $s_user_answer = Database::result($res_user_answer,0,0); //  rich - good looking
  2443.                             } else {
  2444.                                 $s_user_answer = 0;
  2445.                             }
  2446.                             $i_answerWeighting=$objAnswerTmp->selectWeighting($i_answer_id);
  2447.                             $user_answer = '';
  2448.                             if (!empty($s_user_answer)) {
  2449.                                 if ($s_user_answer == $i_answer_correct_answer) {
  2450.                                     $questionScore  += $i_answerWeighting;
  2451.                                     $totalScore     += $i_answerWeighting;
  2452.                                     $user_answer = '<span>'.$real_list[$i_answer_correct_answer].'</span>';
  2453.                                 } else {
  2454.                                     $user_answer = '<span style="color: #FF0000; text-decoration: line-through;">'.$real_list[$s_user_answer].'</span>';
  2455.                                 }
  2456.                             }
  2457.                             if ($show_result) {
  2458.                                 echo '<tr>';
  2459.                                 echo '<td>'.$s_answer_label.'</td><td>'.$user_answer.' <b><span style="color: #008000;">'.$real_list[$i_answer_correct_answer].'</span></b></td>';
  2460.                                 echo '</tr>';
  2461.                             }
  2462.                         }
  2463.                         break(2); //break the switch and the "for" condition
  2464.                     } else {
  2465.                         $numAnswer=$objAnswerTmp->selectAutoId($answerId);
  2466.                         if ($answerCorrect) {
  2467.                             if ($answerCorrect == $choice[$numAnswer]) {
  2468.                                 $questionScore  += $answerWeighting;
  2469.                                 $totalScore     += $answerWeighting;
  2470.                                 $user_answer = '<span>'.$answer_matching[$choice[$numAnswer]].'</span>';
  2471.                             } else {
  2472.                                 $user_answer = '<span style="color: #FF0000; text-decoration: line-through;">'.$answer_matching[$choice[$numAnswer]].'</span>';
  2473.                             }
  2474.                             $matching[$numAnswer] =  $choice[$numAnswer];
  2475.                         }
  2476.                         break;
  2477.                     }
  2478.                     // for hotspot with no order
  2479.                 case HOT_SPOT :
  2480.                     if ($from_database) {
  2481.                         if ($show_result) {
  2482.                             $TBL_TRACK_HOTSPOT = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
  2483.                             $query = "SELECT hotspot_correct FROM ".$TBL_TRACK_HOTSPOT." WHERE hotspot_exe_id = '".$exeId."' and hotspot_question_id= '".$questionId."' AND hotspot_answer_id='".Database::escape_string($answerId)."'";
  2484.                             $resq = Database::query($query);
  2485.                             $studentChoice = Database::result($resq,0,"hotspot_correct");
  2486.  
  2487.                             if ($studentChoice) {
  2488.                                 $questionScore  += $answerWeighting;
  2489.                                 $totalScore     += $answerWeighting;
  2490.                             }
  2491.                         }
  2492.                     }  else {
  2493.                         $studentChoice = $choice[$answerId];
  2494.                         if ($studentChoice) {
  2495.                             $questionScore  += $answerWeighting;
  2496.                             $totalScore     += $answerWeighting;
  2497.                         }
  2498.                     }
  2499.                     break;
  2500.                     // @todo never added to chamilo
  2501.                     //for hotspot with fixed order
  2502.                 case HOT_SPOT_ORDER :
  2503.                     $studentChoice = $choice['order'][$answerId];
  2504.                     if ($studentChoice == $answerId) {
  2505.                         $questionScore  += $answerWeighting;
  2506.                         $totalScore     += $answerWeighting;
  2507.                         $studentChoice = true;
  2508.                     } else {
  2509.                         $studentChoice = false;
  2510.                     }
  2511.                     break;
  2512.                     // for hotspot with delineation
  2513.                 case HOT_SPOT_DELINEATION :
  2514.                     if ($from_database) {
  2515.                         // getting the user answer
  2516.                         $TBL_TRACK_HOTSPOT = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
  2517.                         $query   = "SELECT hotspot_correct, hotspot_coordinate from ".$TBL_TRACK_HOTSPOT." where hotspot_exe_id = '".$exeId."' and hotspot_question_id= '".$questionId."' AND hotspot_answer_id='1'"; //by default we take 1 because it's a delineation
  2518.                         $resq    = Database::query($query);
  2519.                         $row     = Database::fetch_array($resq,'ASSOC');
  2520.  
  2521.                         $choice      = $row['hotspot_correct'];
  2522.                         $user_answer = $row['hotspot_coordinate'];
  2523.  
  2524.                         // THIS is very important otherwise the poly_compile will throw an error!!
  2525.                         // round-up the coordinates
  2526.                         $coords = explode('/',$user_answer);
  2527.                         $user_array = '';
  2528.                         foreach ($coords as $coord) {
  2529.                             list($x,$y) = explode(';',$coord);
  2530.                             $user_array .= round($x).';'.round($y).'/';
  2531.                         }
  2532.                         $user_array = substr($user_array,0,-1);
  2533.                     } else {
  2534.                         if ($studentChoice) {
  2535.                             $newquestionList[]=$questionId;
  2536.                         }
  2537.  
  2538.                         if ($answerId===1) {
  2539.                             $studentChoice  =$choice[$answerId];
  2540.                             $questionScore  +=$answerWeighting;
  2541.  
  2542.                             if ($hotspot_delineation_result[1]==1) {
  2543.                                 $totalScore +=$answerWeighting; //adding the total
  2544.                             }
  2545.                         }
  2546.                     }
  2547.                     $_SESSION['hotspot_coord'][1]   = $delineation_cord;
  2548.                     $_SESSION['hotspot_dest'][1]    = $answer_delineation_destination;
  2549.                     break;
  2550.             } // end switch Answertype
  2551.  
  2552.             global $origin;
  2553.  
  2554.             if ($show_result) {
  2555.  
  2556.                 if ($debug) error_log('show result '.$show_result);
  2557.  
  2558.                 if ($from == 'exercise_result') {
  2559.                     if ($debug) error_log('Showing questions $from '.$from);
  2560.                     //display answers (if not matching type, or if the answer is correct)
  2561.                     if ($answerType != MATCHING || $answerCorrect) {
  2562.                         if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, GLOBAL_MULTIPLE_ANSWER))) {
  2563.                             if ($origin != 'learnpath') {
  2564.                                 ExerciseShowFunctions::display_unique_or_multiple_answer($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,0,0,0);
  2565.                             }
  2566.                         } elseif($answerType == MULTIPLE_ANSWER_TRUE_FALSE) {
  2567.                             if ($origin!='learnpath') {
  2568.                                 ExerciseShowFunctions::display_multiple_answer_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,0,$questionId,0);
  2569.                             }
  2570.                         } elseif($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE ) {
  2571.                             if ($origin!='learnpath') {
  2572.                                 ExerciseShowFunctions::display_multiple_answer_combination_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,0,0,0);
  2573.                             }
  2574.                         } elseif($answerType == FILL_IN_BLANKS) {
  2575.                             if ($origin!='learnpath') {
  2576.                                 ExerciseShowFunctions::display_fill_in_blanks_answer($answer,0,0);
  2577.                             }
  2578.                         } elseif($answerType == FREE_ANSWER) {
  2579.                             if($origin != 'learnpath') {
  2580.                                 ExerciseShowFunctions::display_free_answer($choice, $exeId, $questionId, $questionScore);
  2581.                             }
  2582.                         } elseif($answerType == ORAL_EXPRESSION) {
  2583.                             // to store the details of open questions in an array to be used in mail
  2584.                             if ($origin != 'learnpath') {
  2585.                                 ExerciseShowFunctions::display_oral_expression_answer($choice, 0, 0, $nano);
  2586.                             }
  2587.                         } elseif($answerType == HOT_SPOT) {
  2588.                             if ($origin != 'learnpath') {
  2589.                                 ExerciseShowFunctions::display_hotspot_answer($answerId, $answer, $studentChoice, $answerComment);
  2590.                             }
  2591.                         } elseif($answerType == HOT_SPOT_ORDER) {
  2592.                             if ($origin != 'learnpath') {
  2593.                                 ExerciseShowFunctions::display_hotspot_order_answer($answerId, $answer, $studentChoice, $answerComment);
  2594.                             }
  2595.                         } elseif ($answerType == HOT_SPOT_DELINEATION) {
  2596.                             $user_answer = $_SESSION['exerciseResultCoordinates'][$questionId];
  2597.  
  2598.                             //round-up the coordinates
  2599.                             $coords = explode('/',$user_answer);
  2600.                             $user_array = '';
  2601.                             foreach ($coords as $coord) {
  2602.                                 list($x,$y) = explode(';',$coord);
  2603.                                 $user_array .= round($x).';'.round($y).'/';
  2604.                             }
  2605.                             $user_array = substr($user_array,0,-1);
  2606.  
  2607.                             if ($next) {
  2608.                                 //$tbl_track_e_hotspot = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
  2609.                                 // Save into db
  2610.                                 /*  $sql = "INSERT INTO $tbl_track_e_hotspot (hotspot_user_id, hotspot_course_code, hotspot_exe_id, hotspot_question_id, hotspot_answer_id, hotspot_correct, hotspot_coordinate )
  2611.                                 VALUES ('".Database::escape_string($_user['user_id'])."', '".Database::escape_string($_course['id'])."', '".Database::escape_string($exeId)."', '".Database::escape_string($questionId)."', '".Database::escape_string($answerId)."', '".Database::escape_string($studentChoice)."', '".Database::escape_string($user_array)."')";
  2612.                                 $result = api_sql_query($sql,__FILE__,__LINE__);*/
  2613.                                 $user_answer = $user_array;
  2614.  
  2615.                                 // we compare only the delineation not the other points
  2616.                                 $answer_question    = $_SESSION['hotspot_coord'][1];
  2617.                                 $answerDestination  = $_SESSION['hotspot_dest'][1];
  2618.  
  2619.                                 //calculating the area
  2620.                                 $poly_user          = convert_coordinates($user_answer,'/');
  2621.                                 $poly_answer        = convert_coordinates($answer_question,'|');
  2622.                                 $max_coord          = poly_get_max($poly_user,$poly_answer);
  2623.                                 $poly_user_compiled = poly_compile($poly_user,$max_coord);
  2624.                                 $poly_answer_compiled = poly_compile($poly_answer,$max_coord);
  2625.                                 $poly_results       = poly_result($poly_answer_compiled,$poly_user_compiled,$max_coord);
  2626.  
  2627.                                 $overlap = $poly_results['both'];
  2628.                                 $poly_answer_area = $poly_results['s1'];
  2629.                                 $poly_user_area = $poly_results['s2'];
  2630.                                 $missing = $poly_results['s1Only'];
  2631.                                 $excess = $poly_results['s2Only'];
  2632.  
  2633.                                 //$overlap = round(polygons_overlap($poly_answer,$poly_user)); //this is an area in pixels
  2634.                                 if ($debug>0)  error_log(__LINE__.' - Polygons results are '.print_r($poly_results,1),0);
  2635.  
  2636.                                 if ($overlap < 1) {
  2637.                                     //shortcut to avoid complicated calculations
  2638.                                     $final_overlap = 0;
  2639.                                     $final_missing = 100;
  2640.                                     $final_excess = 100;
  2641.                                 } else {
  2642.                                     // the final overlap is the percentage of the initial polygon that is overlapped by the user's polygon
  2643.                                     $final_overlap = round(((float)$overlap / (float)$poly_answer_area)*100);
  2644.                                     if ($debug>1) error_log(__LINE__.' - Final overlap is '.$final_overlap,0);
  2645.                                     // the final missing area is the percentage of the initial polygon that is not overlapped by the user's polygon
  2646.                                     $final_missing = 100 - $final_overlap;
  2647.                                     if ($debug>1) {
  2648.                                         error_log(__LINE__.' - Final missing is '.$final_missing,0);
  2649.                                     }
  2650.                                     // the final excess area is the percentage of the initial polygon's size that is covered by the user's polygon outside of the initial polygon
  2651.                                     $final_excess = round((((float)$poly_user_area-(float)$overlap)/(float)$poly_answer_area)*100);
  2652.                                     if ($debug>1) {
  2653.                                         error_log(__LINE__.' - Final excess is '.$final_excess,0);
  2654.                                     }
  2655.                                 }
  2656.  
  2657.                                 //checking the destination parameters parsing the "@@"
  2658.                                 $destination_items= explode('@@', $answerDestination);
  2659.                                 $threadhold_total = $destination_items[0];
  2660.                                 $threadhold_items=explode(';',$threadhold_total);
  2661.                                 $threadhold1 = $threadhold_items[0]; // overlap
  2662.                                 $threadhold2 = $threadhold_items[1]; // excess
  2663.                                 $threadhold3 = $threadhold_items[2];     //missing
  2664.  
  2665.                                 // if is delineation
  2666.                                 if ($answerId===1) {
  2667.                                     //setting colors
  2668.                                     if ($final_overlap>=$threadhold1) {
  2669.                                         $overlap_color=true; //echo 'a';
  2670.                                     }
  2671.                                     //echo $excess.'-'.$threadhold2;
  2672.                                     if ($final_excess<=$threadhold2) {
  2673.                                         $excess_color=true; //echo 'b';
  2674.                                     }
  2675.                                     //echo '--------'.$missing.'-'.$threadhold3;
  2676.                                     if ($final_missing<=$threadhold3) {
  2677.                                         $missing_color=true; //echo 'c';
  2678.                                     }
  2679.  
  2680.                                     // if pass
  2681.                                     if ($final_overlap>=$threadhold1 && $final_missing<=$threadhold3 && $final_excess<=$threadhold2) {
  2682.                                         $next=1; //go to the oars
  2683.                                         $result_comment=get_lang('Acceptable');
  2684.                                         $final_answer = 1;  // do not update with  update_exercise_attempt
  2685.                                     } else {
  2686.                                         $next=0;
  2687.                                         $result_comment=get_lang('Unacceptable');
  2688.                                         $comment=$answerDestination=$objAnswerTmp->selectComment(1);
  2689.                                         $answerDestination=$objAnswerTmp->selectDestination(1);
  2690.                                         //checking the destination parameters parsing the "@@"
  2691.                                         $destination_items= explode('@@', $answerDestination);
  2692.                                     }
  2693.                                 } elseif($answerId>1) {
  2694.                                     if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') {
  2695.                                         if ($debug>0) {
  2696.                                             error_log(__LINE__.' - answerId is of type noerror',0);
  2697.                                         }
  2698.                                         //type no error shouldn't be treated
  2699.                                         $next = 1;
  2700.                                         continue;
  2701.                                     }
  2702.                                     if ($debug>0) {
  2703.                                         error_log(__LINE__.' - answerId is >1 so we\'re probably in OAR',0);
  2704.                                     }
  2705.                                     //check the intersection between the oar and the user
  2706.                                     //echo 'user';  print_r($x_user_list);      print_r($y_user_list);
  2707.                                     //echo 'official';print_r($x_list);print_r($y_list);
  2708.                                     //$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list);
  2709.                                     $inter= $result['success'];
  2710.  
  2711.                                     //$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
  2712.                                     $delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
  2713.  
  2714.                                     $poly_answer = convert_coordinates($delineation_cord,'|');
  2715.                                     $max_coord = poly_get_max($poly_user,$poly_answer);
  2716.                                     $poly_answer_compiled = poly_compile($poly_answer,$max_coord);
  2717.                                     $overlap = poly_touch($poly_user_compiled, $poly_answer_compiled,$max_coord);
  2718.  
  2719.                                     if ($overlap == false) {
  2720.                                         //all good, no overlap
  2721.                                         $next = 1;
  2722.                                         continue;
  2723.                                     } else {
  2724.                                         if ($debug>0) {
  2725.                                             error_log(__LINE__.' - Overlap is '.$overlap.': OAR hit',0);
  2726.                                         }
  2727.                                         $organs_at_risk_hit++;
  2728.                                         //show the feedback
  2729.                                         $next=0;
  2730.                                         $comment=$answerDestination=$objAnswerTmp->selectComment($answerId);
  2731.                                         $answerDestination=$objAnswerTmp->selectDestination($answerId);
  2732.  
  2733.                                         $destination_items= explode('@@', $answerDestination);
  2734.                                         $try_hotspot=$destination_items[1];
  2735.                                         $lp_hotspot=$destination_items[2];
  2736.                                         $select_question_hotspot=$destination_items[3];
  2737.                                         $url_hotspot=$destination_items[4];
  2738.                                     }
  2739.                                 }
  2740.                             } else {    // the first delineation feedback
  2741.                                 if ($debug>0) {
  2742.                                     error_log(__LINE__.' first',0);
  2743.                                 }
  2744.                             }
  2745.  
  2746.  
  2747.                         } elseif($answerType==MATCHING) {
  2748.                             if ($origin != 'learnpath') {
  2749.                                 echo '<tr>';
  2750.                                 echo '<td>'.$answer_matching[$answerId].'</td><td>'.$user_answer.' / <b><span style="color: #008000;">'.text_filter($answer_matching[$answerCorrect]).'</span></b></td>';
  2751.                                 echo '</tr>';
  2752.                             }
  2753.                         }
  2754.                     }
  2755.                 } else {
  2756.                     if ($debug) error_log('Showing questions $from '.$from);
  2757.  
  2758.                     switch ($answerType) {
  2759.                         case UNIQUE_ANSWER :
  2760.                         case UNIQUE_ANSWER_NO_OPTION:
  2761.                         case MULTIPLE_ANSWER :
  2762.                         case GLOBAL_MULTIPLE_ANSWER :
  2763.                         case MULTIPLE_ANSWER_COMBINATION :
  2764.                             if ($answerId==1) {
  2765.                                 ExerciseShowFunctions::display_unique_or_multiple_answer($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,$exeId,$questionId,$answerId);
  2766.                             } else {
  2767.                                 ExerciseShowFunctions::display_unique_or_multiple_answer($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,$exeId,$questionId,"");
  2768.                             }
  2769.                             break;
  2770.                         case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE:
  2771.                             if ($answerId==1) {
  2772.                                 ExerciseShowFunctions::display_multiple_answer_combination_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,$exeId,$questionId,$answerId);
  2773.                             } else {
  2774.                                 ExerciseShowFunctions::display_multiple_answer_combination_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,$exeId,$questionId,"");
  2775.                             }
  2776.                             break;
  2777.                         case MULTIPLE_ANSWER_TRUE_FALSE :
  2778.                             if ($answerId==1) {
  2779.                                 ExerciseShowFunctions::display_multiple_answer_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,$exeId,$questionId,$answerId);
  2780.                             } else {
  2781.                                 ExerciseShowFunctions::display_multiple_answer_true_false($answerType, $studentChoice, $answer, $answerComment, $answerCorrect,$exeId,$questionId, "");
  2782.                             }
  2783.                             break;
  2784.                         case FILL_IN_BLANKS:
  2785.                             ExerciseShowFunctions::display_fill_in_blanks_answer($answer,$exeId,$questionId);
  2786.                             break;
  2787.                         case FREE_ANSWER:
  2788.                             echo ExerciseShowFunctions::display_free_answer($choice, $exeId, $questionId, $questionScore);
  2789.                             break;
  2790.                         case ORAL_EXPRESSION:
  2791.                             echo '<tr>
  2792.                                     <td valign="top">'.ExerciseShowFunctions::display_oral_expression_answer($choice, $exeId, $questionId, $nano).'</td>
  2793.                                     </tr>
  2794.                                     </table>';
  2795.                             break;
  2796.                         case HOT_SPOT:
  2797.                             ExerciseShowFunctions::display_hotspot_answer($answerId, $answer, $studentChoice, $answerComment);
  2798.                             break;
  2799.                         case HOT_SPOT_DELINEATION:
  2800.                             $user_answer = $user_array;
  2801.                             if ($next) {
  2802.                                 //$tbl_track_e_hotspot = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
  2803.                                 // Save into db
  2804.                                 /*  $sql = "INSERT INTO $tbl_track_e_hotspot (hotspot_user_id, hotspot_course_code, hotspot_exe_id, hotspot_question_id, hotspot_answer_id, hotspot_correct, hotspot_coordinate )
  2805.                                 VALUES ('".Database::escape_string($_user['user_id'])."', '".Database::escape_string($_course['id'])."', '".Database::escape_string($exeId)."', '".Database::escape_string($questionId)."', '".Database::escape_string($answerId)."', '".Database::escape_string($studentChoice)."', '".Database::escape_string($user_array)."')";
  2806.                                 $result = api_sql_query($sql,__FILE__,__LINE__);*/
  2807.                                 $user_answer = $user_array;
  2808.  
  2809.                                 // we compare only the delineation not the other points
  2810.                                 $answer_question    = $_SESSION['hotspot_coord'][1];
  2811.                                 $answerDestination  = $_SESSION['hotspot_dest'][1];
  2812.  
  2813.                                 //calculating the area
  2814.                                 $poly_user          = convert_coordinates($user_answer,'/');
  2815.                                 $poly_answer        = convert_coordinates($answer_question,'|');
  2816.  
  2817.                                 $max_coord          = poly_get_max($poly_user,$poly_answer);
  2818.                                 $poly_user_compiled = poly_compile($poly_user,$max_coord);
  2819.                                 $poly_answer_compiled = poly_compile($poly_answer,$max_coord);
  2820.                                 $poly_results       = poly_result($poly_answer_compiled,$poly_user_compiled,$max_coord);
  2821.  
  2822.                                 $overlap = $poly_results['both'];
  2823.                                 $poly_answer_area = $poly_results['s1'];
  2824.                                 $poly_user_area = $poly_results['s2'];
  2825.                                 $missing = $poly_results['s1Only'];
  2826.                                 $excess = $poly_results['s2Only'];
  2827.  
  2828.                                 //$overlap = round(polygons_overlap($poly_answer,$poly_user)); //this is an area in pixels
  2829.                                 if ($debug>0) {
  2830.                                     error_log(__LINE__.' - Polygons results are '.print_r($poly_results,1),0);
  2831.                                 }
  2832.                                 if ($overlap < 1) {
  2833.                                     //shortcut to avoid complicated calculations
  2834.                                     $final_overlap = 0;
  2835.                                     $final_missing = 100;
  2836.                                     $final_excess = 100;
  2837.                                 } else {
  2838.                                     // the final overlap is the percentage of the initial polygon that is overlapped by the user's polygon
  2839.                                     $final_overlap = round(((float)$overlap / (float)$poly_answer_area)*100);
  2840.                                     if ($debug>1) {
  2841.                                         error_log(__LINE__.' - Final overlap is '.$final_overlap,0);
  2842.                                     }
  2843.                                     // the final missing area is the percentage of the initial polygon that is not overlapped by the user's polygon
  2844.                                     $final_missing = 100 - $final_overlap;
  2845.                                     if ($debug>1) {
  2846.                                         error_log(__LINE__.' - Final missing is '.$final_missing,0);
  2847.                                     }
  2848.                                     // the final excess area is the percentage of the initial polygon's size that is covered by the user's polygon outside of the initial polygon
  2849.                                     $final_excess = round((((float)$poly_user_area-(float)$overlap)/(float)$poly_answer_area)*100);
  2850.                                     if ($debug>1) {
  2851.                                         error_log(__LINE__.' - Final excess is '.$final_excess,0);
  2852.                                     }
  2853.                                 }
  2854.  
  2855.                                 //checking the destination parameters parsing the "@@"
  2856.                                 $destination_items= explode('@@', $answerDestination);
  2857.                                 $threadhold_total = $destination_items[0];
  2858.                                 $threadhold_items=explode(';',$threadhold_total);
  2859.                                 $threadhold1 = $threadhold_items[0]; // overlap
  2860.                                 $threadhold2 = $threadhold_items[1]; // excess
  2861.                                 $threadhold3 = $threadhold_items[2];     //missing
  2862.  
  2863.                                 // if is delineation
  2864.                                 if ($answerId===1) {
  2865.                                     //setting colors
  2866.                                     if ($final_overlap>=$threadhold1) {
  2867.                                         $overlap_color=true; //echo 'a';
  2868.                                     }
  2869.                                     //echo $excess.'-'.$threadhold2;
  2870.                                     if ($final_excess<=$threadhold2) {
  2871.                                         $excess_color=true; //echo 'b';
  2872.                                     }
  2873.                                     //echo '--------'.$missing.'-'.$threadhold3;
  2874.                                     if ($final_missing<=$threadhold3) {
  2875.                                         $missing_color=true; //echo 'c';
  2876.                                     }
  2877.  
  2878.                                     // if pass
  2879.                                     if ($final_overlap>=$threadhold1 && $final_missing<=$threadhold3 && $final_excess<=$threadhold2) {
  2880.                                         $next=1; //go to the oars
  2881.                                         $result_comment=get_lang('Acceptable');
  2882.                                         $final_answer = 1;  // do not update with  update_exercise_attempt
  2883.                                     } else {
  2884.                                         $next=0;
  2885.                                         $result_comment=get_lang('Unacceptable');
  2886.                                         $comment=$answerDestination=$objAnswerTmp->selectComment(1);
  2887.                                         $answerDestination=$objAnswerTmp->selectDestination(1);
  2888.                                         //checking the destination parameters parsing the "@@"
  2889.                                         $destination_items= explode('@@', $answerDestination);
  2890.                                     }
  2891.                                 } elseif($answerId>1) {
  2892.                                     if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') {
  2893.                                         if ($debug>0) {
  2894.                                             error_log(__LINE__.' - answerId is of type noerror',0);
  2895.                                         }
  2896.                                         //type no error shouldn't be treated
  2897.                                         $next = 1;
  2898.                                         continue;
  2899.                                     }
  2900.                                     if ($debug>0) {
  2901.                                         error_log(__LINE__.' - answerId is >1 so we\'re probably in OAR',0);
  2902.                                     }
  2903.                                     //check the intersection between the oar and the user
  2904.                                     //echo 'user';  print_r($x_user_list);      print_r($y_user_list);
  2905.                                     //echo 'official';print_r($x_list);print_r($y_list);
  2906.                                     //$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list);
  2907.                                     $inter= $result['success'];
  2908.  
  2909.                                     //$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
  2910.                                     $delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId);
  2911.  
  2912.                                     $poly_answer = convert_coordinates($delineation_cord,'|');
  2913.                                     $max_coord = poly_get_max($poly_user,$poly_answer);
  2914.                                     $poly_answer_compiled = poly_compile($poly_answer,$max_coord);
  2915.                                     $overlap = poly_touch($poly_user_compiled, $poly_answer_compiled,$max_coord);
  2916.  
  2917.                                     if ($overlap == false) {
  2918.                                         //all good, no overlap
  2919.                                         $next = 1;
  2920.                                         continue;
  2921.                                     } else {
  2922.                                         if ($debug>0) {
  2923.                                             error_log(__LINE__.' - Overlap is '.$overlap.': OAR hit',0);
  2924.                                         }
  2925.                                         $organs_at_risk_hit++;
  2926.                                         //show the feedback
  2927.                                         $next=0;
  2928.                                         $comment=$answerDestination=$objAnswerTmp->selectComment($answerId);
  2929.                                         $answerDestination=$objAnswerTmp->selectDestination($answerId);
  2930.  
  2931.                                         $destination_items= explode('@@', $answerDestination);
  2932.                                         $try_hotspot=$destination_items[1];
  2933.                                         $lp_hotspot=$destination_items[2];
  2934.                                         $select_question_hotspot=$destination_items[3];
  2935.                                         $url_hotspot=$destination_items[4];
  2936.                                     }
  2937.                                 }
  2938.                             } else {    // the first delineation feedback
  2939.                                 if ($debug>0) {
  2940.                                     error_log(__LINE__.' first',0);
  2941.                                 }
  2942.                             }
  2943.                             break;
  2944.                         case HOT_SPOT_ORDER:
  2945.                             ExerciseShowFunctions::display_hotspot_order_answer($answerId, $answer, $studentChoice, $answerComment);
  2946.                             break;
  2947.                         case MATCHING:
  2948.                             if ($origin != 'learnpath') {
  2949.                                 echo '<tr>';
  2950.                                 echo '<td>'.text_filter($answer_matching[$answerId]).'</td><td>'.text_filter($user_answer).' / <b><span style="color: #008000;">'.text_filter($answer_matching[$answerCorrect]).'</span></b></td>';
  2951.                                 echo '</tr>';
  2952.                             }
  2953.                             break;
  2954.                     }
  2955.                 }
  2956.             }
  2957.             if ($debug) error_log(' ------ ');
  2958.         } // end for that loops over all answers of the current question
  2959.  
  2960.         if ($debug) error_log('-- end answer loop --');
  2961.  
  2962.         /*
  2963.         if (!$saved_results && $answerType == HOT_SPOT) {
  2964.             $queryfree      = "SELECT marks FROM ".$TBL_TRACK_ATTEMPT." WHERE exe_id = '".Database::escape_string($exeId)."' and question_id= '".Database::escape_string($questionId)."'";
  2965.             $resfree        = Database::query($queryfree);
  2966.             $questionScore  = Database::result($resfree,0,"marks");
  2967.         }*/
  2968.  
  2969.         $final_answer = true;
  2970.         foreach ($real_answers as $my_answer) {
  2971.             if (!$my_answer) {
  2972.                 $final_answer = false;
  2973.             }
  2974.         }
  2975.  
  2976.         //we add the total score after dealing with the answers
  2977.         if ($answerType == MULTIPLE_ANSWER_COMBINATION || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE ) {
  2978.             if ($final_answer) {
  2979.                 //getting only the first score where we save the weight of all the question
  2980.                 $answerWeighting     = $objAnswerTmp->selectWeighting(1);
  2981.                 $questionScore      += $answerWeighting;
  2982.                 $totalScore         += $answerWeighting;
  2983.             }
  2984.         }
  2985.  
  2986.         //Fixes multiple answer question in order to be exact
  2987.         if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) {
  2988.             //var_dump($answer_correct_array, $real_answers);
  2989.             $diff = @array_diff($answer_correct_array, $real_answers);
  2990.             //var_dump($diff);
  2991.             /*
  2992.              * All good answers or nothing works like exact
  2993.             $counter = 1;
  2994.             $correct_answer = true;
  2995.             foreach ($real_answers as $my_answer) {
  2996.                 if ($debug) error_log(" my_answer: $my_answer answer_correct_array[counter]: ".$answer_correct_array[$counter]);
  2997.                 if ($my_answer != $answer_correct_array[$counter]) {
  2998.                     $correct_answer = false;
  2999.                     break;
  3000.                 }
  3001.                 $counter++;
  3002.             }*/
  3003.             if ($debug) error_log(" answer_correct_array: ".print_r($answer_correct_array, 1)."");
  3004.             if ($debug) error_log(" real_answers: ".print_r($real_answers, 1)."");
  3005.             //if ($debug) error_log(" correct_answer: ".$correct_answer);
  3006.  
  3007.             /*if ($correct_answer == false) {
  3008.                 $questionScore = 0;
  3009.             }*/
  3010.  
  3011.             //This makes the result non exact
  3012.             if (!empty($diff)) {
  3013.                 //$questionScore = 0;
  3014.             }
  3015.         }
  3016.  
  3017.         $extra_data = array('final_overlap' => $final_overlap,
  3018.                             'final_missing'=>$final_missing,
  3019.                             'final_excess'=> $final_excess,
  3020.                             'overlap_color' => $overlap_color,
  3021.                             'missing_color'=>$missing_color,
  3022.                             'excess_color'=> $excess_color,
  3023.                             'threadhold1'   => $threadhold1,
  3024.                             'threadhold2'=>$threadhold2,
  3025.                             'threadhold3'=> $threadhold3,
  3026.         );
  3027.  
  3028.         if ($from == 'exercise_result') {
  3029.             // if answer is hotspot. To the difference of exercise_show.php, we use the results from the session (from_db=0)
  3030.             // TODO Change this, because it is wrong to show the user some results that haven't been stored in the database yet
  3031.             if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER || $answerType == HOT_SPOT_DELINEATION ) {
  3032.  
  3033.                 if ($debug) error_log('$from AND this is a hotspot kind of question ');
  3034.  
  3035.                 $my_exe_id = 0;
  3036.                 $from_database = 0;
  3037.                 if ($answerType == HOT_SPOT_DELINEATION) {
  3038.                     if (0) {
  3039.                         if ($overlap_color) {
  3040.                             $overlap_color='green';
  3041.                         } else {
  3042.                             $overlap_color='red';
  3043.                         }
  3044.                         if ($missing_color) {
  3045.                             $missing_color='green';
  3046.                         } else {
  3047.                             $missing_color='red';
  3048.                         }
  3049.                         if ($excess_color) {
  3050.                             $excess_color='green';
  3051.                         } else {
  3052.                             $excess_color='red';
  3053.                         }
  3054.                         if (!is_numeric($final_overlap)) {
  3055.                             $final_overlap = 0;
  3056.                         }
  3057.                         if (!is_numeric($final_missing)) {
  3058.                             $final_missing = 0;
  3059.                         }
  3060.                         if (!is_numeric($final_excess)) {
  3061.                             $final_excess = 0;
  3062.                         }
  3063.  
  3064.                         if ($final_overlap>100) {
  3065.                             $final_overlap = 100;
  3066.                         }
  3067.  
  3068.                         $table_resume='<table class="data_table">
  3069.                         <tr class="row_odd" >
  3070.                             <td></td>
  3071.                             <td ><b>'.get_lang('Requirements').'</b></td>
  3072.                             <td><b>'.get_lang('YourAnswer').'</b></td>
  3073.                         </tr>
  3074.                         <tr class="row_even">
  3075.                             <td><b>'.get_lang('Overlap').'</b></td>
  3076.                             <td>'.get_lang('Min').' '.$threadhold1.'</td>
  3077.                             <td><div style="color:'.$overlap_color.'">'.(($final_overlap < 0)?0:intval($final_overlap)).'</div></td>
  3078.                         </tr>
  3079.                         <tr>
  3080.                             <td><b>'.get_lang('Excess').'</b></td>
  3081.                             <td>'.get_lang('Max').' '.$threadhold2.'</td>
  3082.                             <td><div style="color:'.$excess_color.'">'.(($final_excess < 0)?0:intval($final_excess)).'</div></td>
  3083.                         </tr>
  3084.                         <tr class="row_even">
  3085.                             <td><b>'.get_lang('Missing').'</b></td>
  3086.                             <td>'.get_lang('Max').' '.$threadhold3.'</td>
  3087.                             <td><div style="color:'.$missing_color.'">'.(($final_missing < 0)?0:intval($final_missing)).'</div></td>
  3088.                         </tr>
  3089.                         </table>';
  3090.                         if ($next==0) {
  3091.                             $try = $try_hotspot;
  3092.                             $lp = $lp_hotspot;
  3093.                             $destinationid= $select_question_hotspot;
  3094.                             $url=$url_hotspot;
  3095.                         } else {
  3096.                             //show if no error
  3097.                             //echo 'no error';
  3098.                             $comment=$answerComment=$objAnswerTmp->selectComment($nbrAnswers);
  3099.                             $answerDestination=$objAnswerTmp->selectDestination($nbrAnswers);
  3100.                         }
  3101.  
  3102.                         echo '<h1><div style="color:#333;">'.get_lang('Feedback').'</div></h1>
  3103.                         <p style="text-align:center">';
  3104.  
  3105.                         $message='<p>'.get_lang('YourDelineation').'</p>';
  3106.                         $message.=$table_resume;
  3107.                         $message.='<br />'.get_lang('ResultIs').' '.$result_comment.'<br />';
  3108.                         if ($organs_at_risk_hit>0) {
  3109.                             $message.='<p><b>'.get_lang('OARHit').'</b></p>';
  3110.                         }
  3111.                         $message.='<p>'.$comment.'</p>';
  3112.                         echo $message;
  3113.                     } else {
  3114.                         echo $hotspot_delineation_result[0]; //prints message
  3115.                         $from_database = 1;  // the hotspot_solution.swf needs this variable
  3116.                     }
  3117.  
  3118.                     //save the score attempts
  3119.  
  3120.                     if (1) {
  3121.                         $final_answer = $hotspot_delineation_result[1]; //getting the answer 1 or 0 comes from exercise_submit_modal.php
  3122.                         if ($final_answer == 0) {
  3123.                             $questionScore = 0;
  3124.                         }
  3125.                         exercise_attempt($questionScore, 1, $quesId, $exeId, 0); // we always insert the answer_id 1 = delineation
  3126.                         //in delineation mode, get the answer from $hotspot_delineation_result[1]
  3127.                         exercise_attempt_hotspot($exeId,$quesId,1, $hotspot_delineation_result[1], $exerciseResultCoordinates[$quesId]);
  3128.                     } else {
  3129.                         if ($final_answer==0) {
  3130.                             $questionScore = 0;
  3131.                             $answer=0;
  3132.                             exercise_attempt($questionScore, $answer, $quesId, $exeId, 0);
  3133.                             if (is_array($exerciseResultCoordinates[$quesId])) {
  3134.                                 foreach($exerciseResultCoordinates[$quesId] as $idx => $val) {
  3135.                                     exercise_attempt_hotspot($exeId,$quesId,$idx,0,$val);
  3136.                                 }
  3137.                             }
  3138.                         } else {
  3139.                             exercise_attempt($questionScore, $answer, $quesId, $exeId, 0);
  3140.                             if (is_array($exerciseResultCoordinates[$quesId])) {
  3141.                                 foreach($exerciseResultCoordinates[$quesId] as $idx => $val) {
  3142.                                     exercise_attempt_hotspot($exeId,$quesId,$idx,$choice[$idx],$val);
  3143.                                 }
  3144.                             }
  3145.                         }
  3146.                     }
  3147.                     $my_exe_id = $exeId;
  3148.                 }
  3149.             }
  3150.  
  3151.             if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER) {
  3152.                 // We made an extra table for the answers
  3153.  
  3154.                 if ($show_result) {
  3155.                     if ($origin != 'learnpath') {
  3156.                         echo '</table></td></tr>';
  3157.                         echo '<tr>
  3158.                            <td colspan="2">';
  3159.                         echo '<i>'.get_lang('HotSpot').'</i><br /><br />';
  3160.  
  3161.                         echo '<object type="application/x-shockwave-flash" data="'.api_get_path(WEB_CODE_PATH).'plugin/hotspot/hotspot_solution.swf?modifyAnswers='.Security::remove_XSS($questionId).'&exe_id='.$exeId.'&from_db=1" width="552" height="352">
  3162.                                 <param name="movie" value="../plugin/hotspot/hotspot_solution.swf?modifyAnswers='.Security::remove_XSS($questionId).'&exe_id='.$exeId.'&from_db=1" />
  3163.                             </object>';
  3164.                         echo '</td>
  3165.                        </tr>';
  3166.                     }
  3167.                 }
  3168.             }
  3169.  
  3170.             if ($origin != 'learnpath') {
  3171.                 if ($show_result) {
  3172.                     echo '</table>';
  3173.                 }
  3174.             }
  3175.         }
  3176.         unset ($objAnswerTmp);
  3177.  
  3178.         $totalWeighting += $questionWeighting;
  3179.         // Store results directly in the database
  3180.         // For all in one page exercises, the results will be
  3181.         // stored by exercise_results.php (using the session)
  3182.  
  3183.         if ($saved_results) {
  3184.             if ($debug) error_log("Save question results $saved_results");
  3185.             if ($debug) error_log(print_r($choice ,1 ));
  3186.  
  3187.             if (empty($choice)) {
  3188.                 $choice = 0;
  3189.             }
  3190.             if ($answerType ==  MULTIPLE_ANSWER_TRUE_FALSE || $answerType ==  MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE ) {
  3191.                 if ($choice != 0) {
  3192.                     $reply = array_keys($choice);
  3193.                     for ($i = 0; $i < sizeof($reply); $i++) {
  3194.                         $ans = $reply[$i];
  3195.                         exercise_attempt($questionScore, $ans.':'.$choice[$ans], $quesId, $exeId, $i, $this->id);
  3196.                         if ($debug) error_log('result =>'.$questionScore.' '.$ans.':'.$choice[$ans]);
  3197.                     }
  3198.                 } else {
  3199.                     exercise_attempt($questionScore, 0, $quesId, $exeId, 0, $this->id);
  3200.                 }
  3201.             } elseif ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) {
  3202.                 if ($choice != 0) {
  3203.                     $reply = array_keys($choice);
  3204.  
  3205.                     if ($debug) error_log("reply ".print_r($reply, 1)."");
  3206.                     for ($i = 0; $i < sizeof($reply); $i++) {
  3207.                         $ans = $reply[$i];
  3208.                         exercise_attempt($questionScore, $ans, $quesId, $exeId, $i, $this->id);
  3209.                     }
  3210.                 } else {
  3211.                     exercise_attempt($questionScore, 0, $quesId, $exeId, 0, $this->id);
  3212.                 }
  3213.             } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) {
  3214.                 if ($choice != 0) {
  3215.                     $reply = array_keys($choice);
  3216.                     for ($i = 0; $i < sizeof($reply); $i++) {
  3217.                         $ans = $reply[$i];
  3218.                         exercise_attempt($questionScore, $ans, $quesId, $exeId, $i, $this->id);
  3219.                     }
  3220.                 } else {
  3221.                     exercise_attempt($questionScore, 0, $quesId, $exeId, 0, $this->id);
  3222.                 }
  3223.             } elseif ($answerType == MATCHING) {
  3224.                 if (isset($matching)) {
  3225.                     foreach ($matching as $j => $val) {
  3226.                         exercise_attempt($questionScore, $val, $quesId, $exeId, $j, $this->id);
  3227.                     }
  3228.                 }
  3229.             } elseif ($answerType == FREE_ANSWER) {
  3230.                 $answer = $choice;
  3231.                 exercise_attempt($questionScore, $answer, $quesId, $exeId, 0, $this->id);
  3232.             } elseif ($answerType == ORAL_EXPRESSION) {
  3233.                 $answer = $choice;
  3234.                 exercise_attempt($questionScore, $answer, $quesId, $exeId, 0, $this->id, $nano);
  3235.  
  3236.             } elseif ($answerType == UNIQUE_ANSWER || $answerType == UNIQUE_ANSWER_NO_OPTION) {
  3237.                 $answer = $choice;
  3238.                 exercise_attempt($questionScore, $answer, $quesId, $exeId, 0, $this->id);
  3239.                 //            } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) {
  3240.             } elseif ($answerType == HOT_SPOT) {
  3241.                 exercise_attempt($questionScore, $answer, $quesId, $exeId, 0, $this->id);
  3242.                 if (isset($exerciseResultCoordinates[$questionId]) && !empty($exerciseResultCoordinates[$questionId])) {
  3243.                     foreach ($exerciseResultCoordinates[$questionId] as $idx => $val) {
  3244.                         exercise_attempt_hotspot($exeId,$quesId,$idx,$choice[$idx],$val,$this->id);
  3245.                     }
  3246.                 }
  3247.             } else {
  3248.                 exercise_attempt($questionScore, $answer, $quesId, $exeId, 0,$this->id);
  3249.             }
  3250.         }
  3251.  
  3252.         if ($propagate_neg == 0 && $questionScore < 0) {
  3253.             $questionScore = 0;
  3254.         }
  3255.  
  3256.         if ($saved_results) {
  3257.             $stat_table = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  3258.             $sql_update = 'UPDATE ' . $stat_table . ' SET exe_result = exe_result + ' . floatval($questionScore) . ' WHERE exe_id = ' . $exeId;
  3259.             if ($debug) error_log($sql_update);
  3260.             Database::query($sql_update);
  3261.         }
  3262.  
  3263.         $return_array = array(  'score'         => $questionScore,
  3264.                                 'weight'        => $questionWeighting,
  3265.                                 'extra'         => $extra_data,
  3266.                                 'open_question' => $arrques,
  3267.                                 'open_answer'   => $arrans,
  3268.                                 'answer_type'   => $answerType
  3269.         );
  3270.  
  3271.  
  3272.         return $return_array;
  3273.     } //End function
  3274.  
  3275.     /**
  3276.      * Sends a notification when a user ends an examn
  3277.      *
  3278.      */
  3279.     function send_notification_for_open_questions($question_list_answers, $origin, $exe_id) {
  3280.         if (api_get_course_setting('email_alert_manager_on_new_quiz') != 1 ) {
  3281.             return null;
  3282.         }
  3283.         // Email configuration settings
  3284.         $coursecode     = api_get_course_id();
  3285.         $course_info    = api_get_course_info(api_get_course_id());
  3286.  
  3287.         $url_email = api_get_path(WEB_CODE_PATH).'exercice/exercise_show.php?'.api_get_cidreq().'&id_session='.api_get_session_id().'&id='.$exe_id.'&action=qualify';
  3288.         $user_info = UserManager::get_user_info_by_id(api_get_user_id());
  3289.  
  3290.         $msg = '<p>'.get_lang('OpenQuestionsAttempted').' :</p>
  3291.                    <p>'.get_lang('AttemptDetails').' : </p>
  3292.                    <table class="data_table">
  3293.                        <tr>
  3294.                            <td><h3>'.get_lang('CourseName').'</h3></td>
  3295.                            <td><h3>#course#</h3></td>
  3296.                        </tr>
  3297.                        <tr>
  3298.                            <td>'.get_lang('TestAttempted').'</span></td>
  3299.                            <td>#exercise#</td>
  3300.                        </tr>
  3301.                        <tr>
  3302.                            <td>'.get_lang('StudentName').'</td>
  3303.                            <td>#firstName# #lastName#</td>
  3304.                        </tr>
  3305.                        <tr>
  3306.                            <td>'.get_lang('StudentEmail').'</td>
  3307.                            <td>#mail#</td>
  3308.                        </tr>
  3309.                    </table>';
  3310.             $open_question_list = null;
  3311.             foreach ($question_list_answers as $item) {
  3312.                 $question    = $item['question'];
  3313.                 $answer      = $item['answer'];
  3314.                 $answer_type = $item['answer_type'];
  3315.  
  3316.                 if (!empty($question) && !empty($answer) && $answer_type == FREE_ANSWER ) {
  3317.                     $open_question_list.='<tr>
  3318.                            <td width="220" valign="top" bgcolor="#E5EDF8">&nbsp;&nbsp;'.get_lang('Question').'</td>
  3319.                            <td width="473" valign="top" bgcolor="#F3F3F3">'.$question.'</td>
  3320.                        </tr>
  3321.                        <tr>
  3322.                            <td width="220" valign="top" bgcolor="#E5EDF8">&nbsp;&nbsp;'.get_lang('Answer').'</td>
  3323.                            <td valign="top" bgcolor="#F3F3F3">'.$answer.'</td>
  3324.                        </tr>';
  3325.                 }
  3326.             }
  3327.  
  3328.             if (!empty($open_question_list)) {
  3329.                 $msg .=  '<p><br />'.get_lang('OpenQuestionsAttemptedAre').' :</p>
  3330.                    <table width="730" height="136" border="0" cellpadding="3" cellspacing="3">';
  3331.                 $msg .= $open_question_list;
  3332.                 $msg.='</table><br />';
  3333.  
  3334.  
  3335.                 $msg1   = str_replace("#exercise#",    $this->exercise, $msg);
  3336.                 $msg    = str_replace("#firstName#",   $user_info['firstname'],$msg1);
  3337.                 $msg1   = str_replace("#lastName#",    $user_info['lastname'],$msg);
  3338.                 $msg    = str_replace("#mail#",        $user_info['email'],$msg1);
  3339.                 $msg    = str_replace("#course#",      $course_info['name'],$msg1);
  3340.  
  3341.                 if ($origin != 'learnpath') {
  3342.                     $msg.= get_lang('ClickToCommentAndGiveFeedback').', <br />
  3343.                            <a href="#url#">#url#</a>';
  3344.                 }
  3345.                 $msg1 = str_replace("#url#", $url_email, $msg);
  3346.                 $mail_content = $msg1;
  3347.                 $subject = get_lang('OpenQuestionsAttempted');
  3348.  
  3349.                 $teachers = array();
  3350.                 if (api_get_session_id()) {
  3351.                     $teachers = CourseManager::get_coach_list_from_course_code($coursecode, api_get_session_id());
  3352.                 } else {
  3353.                     $teachers = CourseManager::get_teacher_list_from_course_code($coursecode);
  3354.                 }
  3355.  
  3356.                 if (!empty($teachers)) {
  3357.                     foreach ($teachers as $user_id => $teacher_data) {
  3358.                         MessageManager::send_message_simple($user_id, $subject, $mail_content);
  3359.                     }
  3360.                 }
  3361.             }
  3362.     }
  3363.  
  3364.     function send_notification_for_oral_questions($question_list_answers, $origin, $exe_id) {
  3365.         if (api_get_course_setting('email_alert_manager_on_new_quiz') != 1 ) {
  3366.             return null;
  3367.         }
  3368.         // Email configuration settings
  3369.         $coursecode     = api_get_course_id();
  3370.         $course_info    = api_get_course_info(api_get_course_id());
  3371.  
  3372.         $url_email = api_get_path(WEB_CODE_PATH).'exercice/exercise_show.php?'.api_get_cidreq().'&id_session='.api_get_session_id().'&id='.$exe_id.'&action=qualify';
  3373.         $user_info = UserManager::get_user_info_by_id(api_get_user_id());
  3374.  
  3375.  
  3376.             $oral_question_list = null;
  3377.             foreach ($question_list_answers as $item) {
  3378.                 $question    = $item['question'];
  3379.                 $answer      = $item['answer'];
  3380.                 $answer_type = $item['answer_type'];
  3381.  
  3382.                 if (!empty($question) && !empty($answer) && $answer_type == ORAL_EXPRESSION) {
  3383.                     $oral_question_list.='<br /><table width="730" height="136" border="0" cellpadding="3" cellspacing="3"><tr>
  3384.                            <td width="220" valign="top" bgcolor="#E5EDF8">&nbsp;&nbsp;'.get_lang('Question').'</td>
  3385.                            <td width="473" valign="top" bgcolor="#F3F3F3">'.$question.'</td>
  3386.                        </tr>
  3387.                        <tr>
  3388.                            <td width="220" valign="top" bgcolor="#E5EDF8">&nbsp;&nbsp;'.get_lang('Answer').'</td>
  3389.                            <td valign="top" bgcolor="#F3F3F3">'.$answer.'</td>
  3390.                        </tr></table>';
  3391.                 }
  3392.             }
  3393.  
  3394.             if (!empty($oral_question_list)) {
  3395.                  $msg = '<p>'.get_lang('OralQuestionsAttempted').' :</p>
  3396.                    <p>'.get_lang('AttemptDetails').' : </p>
  3397.                    <table class="data_table">
  3398.                        <tr>
  3399.                            <td><h3>'.get_lang('CourseName').'</h3></td>
  3400.                            <td><h3>#course#</h3></td>
  3401.                        </tr>
  3402.                        <tr>
  3403.                            <td>'.get_lang('TestAttempted').'</span></td>
  3404.                            <td>#exercise#</td>
  3405.                        </tr>
  3406.                        <tr>
  3407.                            <td>'.get_lang('StudentName').'</td>
  3408.                            <td>#firstName# #lastName#</td>
  3409.                        </tr>
  3410.                        <tr>
  3411.                            <td>'.get_lang('StudentEmail').'</td>
  3412.                            <td>#mail#</td>
  3413.                        </tr>
  3414.                    </table>';
  3415.                 $msg .=  '<br />'.sprintf(get_lang('OralQuestionsAttemptedAreX'),$oral_question_list).'<br />';
  3416.                 $msg1   = str_replace("#exercise#",    $this->exercise, $msg);
  3417.                 $msg    = str_replace("#firstName#",   $user_info['firstname'],$msg1);
  3418.                 $msg1   = str_replace("#lastName#",    $user_info['lastname'],$msg);
  3419.                 $msg    = str_replace("#mail#",        $user_info['email'],$msg1);
  3420.                 $msg    = str_replace("#course#",      $course_info['name'],$msg1);
  3421.  
  3422.                 if ($origin != 'learnpath') {
  3423.                     $msg.= get_lang('ClickToCommentAndGiveFeedback').', <br />
  3424.                            <a href="#url#">#url#</a>';
  3425.                 }
  3426.                 $msg1 = str_replace("#url#", $url_email, $msg);
  3427.                 $mail_content = $msg1;
  3428.                 $subject = get_lang('OralQuestionsAttempted');
  3429.  
  3430.                 $teachers = array();
  3431.                 if (api_get_session_id()) {
  3432.                     $teachers = CourseManager::get_coach_list_from_course_code($coursecode, api_get_session_id());
  3433.                 } else {
  3434.                     $teachers = CourseManager::get_teacher_list_from_course_code($coursecode);
  3435.                 }
  3436.  
  3437.                 if (!empty($teachers)) {
  3438.                     foreach ($teachers as $user_id => $teacher_data) {
  3439.                         MessageManager::send_message_simple($user_id, $subject, $mail_content);
  3440.                     }
  3441.                 }
  3442.             }
  3443.     }
  3444.  
  3445.     function show_exercise_result_header($user_data, $start_date = null, $duration = null) {
  3446.         $array = array();
  3447.  
  3448.         if (!empty($user_data)) {
  3449.             $array[] = array('title' => get_lang("User"), 'content' => $user_data);
  3450.         }
  3451.  
  3452.         if (!empty($this->description)) {
  3453.             $array[] = array('title' => get_lang("Description"), 'content' => $this->description);
  3454.         }
  3455.  
  3456.         if (!empty($start_date)) {
  3457.             $array[] = array('title' => get_lang("StartDate"), 'content' => $start_date);
  3458.         }
  3459.  
  3460.         if (!empty($duration)) {
  3461.             $array[] = array('title' => get_lang("Duration"), 'content' => $duration);
  3462.         }
  3463.  
  3464.         $html  = Display::page_header(Display::return_icon('quiz_big.png', get_lang('Result')).' '.$this->exercise.' : '.get_lang('Result'));
  3465.         $html .= Display::description($array);
  3466.         return $html;
  3467.     }
  3468.  
  3469.     /**
  3470.      * Create a quiz from quiz data
  3471.      * @param string  Title
  3472.      * @param int     Time before it expires (in minutes)
  3473.      * @param int     Type of exercise
  3474.      * @param int     Whether it's randomly picked questions (1) or not (0)
  3475.      * @param int     Whether the exercise is visible to the user (1) or not (0)
  3476.      * @param int     Whether the results are show to the user (0) or not (1)
  3477.      * @param int     Maximum number of attempts (0 if no limit)
  3478.      * @param int     Feedback type
  3479.      * @todo this was function was added due the import exercise via CSV
  3480.      * @return    int New exercise ID
  3481.      */
  3482.     function create_quiz ($title, $expired_time = 0, $type = 2, $random = 0, $active = 1, $results_disabled = 0, $max_attempt = 0, $feedback = 3) {
  3483.         $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
  3484.         $expired_time = filter_var($expired_time,FILTER_SANITIZE_NUMBER_INT);
  3485.         $type = filter_var($type,FILTER_SANITIZE_NUMBER_INT);
  3486.         $random = filter_var($random,FILTER_SANITIZE_NUMBER_INT);
  3487.         $active = filter_var($active,FILTER_SANITIZE_NUMBER_INT);
  3488.         $results_disabled = filter_var($results_disabled,FILTER_SANITIZE_NUMBER_INT);
  3489.         $max_attempt = filter_var($max_attempt,FILTER_SANITIZE_NUMBER_INT);
  3490.         $feedback = filter_var($feedback,FILTER_SANITIZE_NUMBER_INT);
  3491.         $sid = api_get_session_id();
  3492.         $course_id = api_get_course_int_id();
  3493.         // Save a new quiz
  3494.         $sql = "INSERT INTO $tbl_quiz (c_id, title,type,random,active,results_disabled, max_attempt,start_time,end_time,feedback_type,expired_time, session_id) ".
  3495.                 " VALUES('$course_id', '".Database::escape_string($title)."',$type,$random,$active, $results_disabled,$max_attempt,'','',$feedback,$expired_time,$sid)";
  3496.         $rs = Database::query($sql);
  3497.         $quiz_id = Database::get_last_insert_id();
  3498.         return $quiz_id;
  3499.     }
  3500.  
  3501.     function process_geometry() {
  3502.  
  3503.     }
  3504.  
  3505.     /**
  3506.      * Returns the exercise result
  3507.      * @param   int     attempt id
  3508.      * @return  float   exercise result
  3509.      */
  3510.  
  3511.     public function get_exercise_result($exe_id) {
  3512.         $result = array();
  3513.         $track_exercise_info = get_exercise_track_exercise_info($exe_id);
  3514.         if (!empty($track_exercise_info)) {
  3515.             $objExercise = new Exercise();
  3516.             $objExercise->read($track_exercise_info['exe_exo_id']);
  3517.             if (!empty($track_exercise_info['data_tracking'])) {
  3518.                 $question_list = explode(',', $track_exercise_info['data_tracking']);
  3519.             }
  3520.             foreach ($question_list as $questionId) {
  3521.                 $question_result = $objExercise->manage_answer($exe_id, $questionId, '','exercise_show', array(), false, true, false, $objExercise->selectPropagateNeg());
  3522.                 $questionScore   = $question_result['score'];
  3523.                 $totalScore      += $question_result['score'];
  3524.             }
  3525.  
  3526.             if ($objExercise->selectPropagateNeg() == 0 && $totalScore < 0) {
  3527.                 $totalScore = 0;
  3528.             }
  3529.             $result = array(
  3530.                 'score' => $totalScore,
  3531.                 'weight' => $track_exercise_info['exe_weighting']
  3532.             );
  3533.         }
  3534.         return $result;
  3535.     }
  3536.  
  3537.     /**
  3538.      *  Checks if the exercise is visible due a lot of conditions - visibility, time limits, student attempts
  3539.      * @return bool true if is active
  3540.      */
  3541.      public function is_visible($lp_id = 0, $lp_item_id = 0 , $lp_item_view_id = 0, $filter_by_admin = true) {
  3542.         //1. By default the exercise is visible
  3543.         $is_visible = true;
  3544.         $message = null;
  3545.  
  3546.         //1.1 Admins and teachers can access to the exercise
  3547.         if ($filter_by_admin) {
  3548.             if (api_is_platform_admin() || api_is_course_admin()) {
  3549.                 return array('value' => true, 'message' => '');
  3550.             }
  3551.         }
  3552.  
  3553.         //Checking visibility in the item_property table
  3554.         $visibility = api_get_item_visibility(api_get_course_info(), TOOL_QUIZ, $this->id, api_get_session_id());
  3555.  
  3556.         if ($visibility == 0) {
  3557.             $this->active = 0;
  3558.         }
  3559.  
  3560.         //2. If the exercise is not active
  3561.         if (empty($lp_id)) {
  3562.             //2.1 LP is OFF
  3563.             if ($this->active == 0) {
  3564.                 return array('value' => false, 'message' => Display::return_message(get_lang('ExerciseNotFound'), 'warning', false));
  3565.             }
  3566.         } else {
  3567.             //2.1 LP is loaded
  3568.             if ($this->active == 0 AND !learnpath::is_lp_visible_for_student($lp_id, api_get_user_id())) {
  3569.                 return array('value' => false, 'message' => Display::return_message(get_lang('ExerciseNotFound'), 'warning', false));
  3570.             }
  3571.         }
  3572.  
  3573.         //3. We check if the time limits are on
  3574.         $limit_time_exists = ((!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') || (!empty($this->end_time) && $this->end_time != '0000-00-00 00:00:00')) ? true : false;
  3575.  
  3576.         if ($limit_time_exists) {
  3577.             $time_now = time();
  3578.  
  3579.             if (!empty($this->start_time) && $this->start_time != '0000-00-00 00:00:00') {
  3580.                 $is_visible = (($time_now - api_strtotime($this->start_time, 'UTC')) > 0) ? true : false;
  3581.             }
  3582.  
  3583.             if ($is_visible == false) {
  3584.                 $message = sprintf(get_lang('ExerciseAvailableFromX'), api_convert_and_format_date($this->start_time));
  3585.             }
  3586.  
  3587.             if ($is_visible == true) {
  3588.                 if ($this->end_time != '0000-00-00 00:00:00') {
  3589.                     $is_visible = ((api_strtotime($this->end_time, 'UTC') > $time_now) > 0) ? true : false;
  3590.                     if ($is_visible == false) {
  3591.                         $message = sprintf(get_lang('ExerciseAvailableUntilX'), api_convert_and_format_date($this->end_time));
  3592.                     }
  3593.                 }
  3594.             }
  3595.             if ($is_visible == false && $this->start_time != '0000-00-00 00:00:00' && $this->end_time != '0000-00-00 00:00:00') {
  3596.                 $message =  sprintf(get_lang('ExerciseWillBeActivatedFromXToY'), api_convert_and_format_date($this->start_time), api_convert_and_format_date($this->end_time));
  3597.             }
  3598.         }
  3599.  
  3600.         // 4. We check if the student have attempts
  3601.         if ($is_visible) {
  3602.             if ($this->selectAttempts() > 0) {
  3603.                 $attempt_count = get_attempt_count_not_finished(api_get_user_id(), $this->id, $lp_id, $lp_item_id, $lp_item_view_id);
  3604.  
  3605.                 if ($attempt_count >= $this->selectAttempts()) {
  3606.                     $message = sprintf(get_lang('ReachedMaxAttempts'), $this->name, $this->selectAttempts());
  3607.                     $is_visible = false;
  3608.                 }
  3609.             }
  3610.         }
  3611.         if (!empty($message)){
  3612.             $message = Display :: return_message($message, 'warning', false);
  3613.         }
  3614.         return array('value' => $is_visible, 'message' => $message);
  3615.     }
  3616.  
  3617.     function added_in_lp() {
  3618.         $TBL_LP_ITEM    = Database::get_course_table(TABLE_LP_ITEM);
  3619.         $sql = "SELECT max_score FROM $TBL_LP_ITEM WHERE c_id = ".$this->course_id." AND item_type = '".TOOL_QUIZ."' AND path = '".$this->id."'";
  3620.         $result = Database::query($sql);
  3621.         if (Database::num_rows($result) > 0) {
  3622.             return true;
  3623.         }
  3624.         return false;
  3625.     }
  3626.  
  3627.     function get_media_list() {
  3628.         $media_questions = array();
  3629.         $question_list = $this->get_validated_question_list();
  3630.         if (!empty($question_list)) {
  3631.             foreach ($question_list as $questionId) {
  3632.                 $objQuestionTmp = Question::read($questionId);
  3633.                 if (isset($objQuestionTmp->parent_id) && $objQuestionTmp->parent_id != 0) {
  3634.                     $media_questions[$objQuestionTmp->parent_id][] = $objQuestionTmp->id;
  3635.                 } else {
  3636.                     //Always the last item
  3637.                     $media_questions[999][] = $objQuestionTmp->id;
  3638.                 }
  3639.             }
  3640.         }
  3641.         return $media_questions;
  3642.     }
  3643.  
  3644.     function media_is_activated($media_list) {
  3645.         $active = false;
  3646.         if (isset($media_list) && !empty($media_list)) {
  3647.             $media_count = count($media_list);
  3648.             if ($media_count > 1) {
  3649.                 return true;
  3650.             } elseif ($media_count == 1) {
  3651.                 if (isset($media_list[999])) {
  3652.                     return false;
  3653.                 } else {
  3654.                     return true;
  3655.                 }
  3656.             }
  3657.         }
  3658.         return $active;
  3659.     }
  3660.  
  3661.     function get_validated_question_list() {
  3662.         $tabres = array();
  3663.         $isRandomByCategory = $this->isRandomByCat();
  3664.         if ($isRandomByCategory == 0) {
  3665.             if ($this->isRandom()) {
  3666.                 $tabres = $this->selectRandomList();
  3667.             } else {
  3668.                 $tabres = $this->selectQuestionList();
  3669.             }
  3670.         } else {
  3671.             if ($this->isRandom()) {
  3672.                 // USE question categories
  3673.                 // get questions by category for this exercice
  3674.                 // we have to choice $objExercise->random question in each array values of $tabCategoryQuestions
  3675.                 // key of $tabCategoryQuestions are the categopy id (0 for not in a category)
  3676.                 // value is the array of question id of this category
  3677.                 $questionList = array();
  3678.                 $tabCategoryQuestions = Testcategory::getQuestionsByCat($this->id);
  3679.                 $isRandomByCategory = $this->selectRandomByCat();
  3680.                 // on tri les categories en fonction du terme entre [] en tête de la description de la catégorie
  3681.                 /*
  3682.                  * ex de catégories :
  3683.                  * [biologie] Maîtriser les mécanismes de base de la génétique
  3684.                  * [biologie] Relier les moyens de défenses et les agents infectieux
  3685.                  * [biologie] Savoir où est produite l'énergie dans les cellules et sous quelle forme
  3686.                  * [chimie] Classer les molécules suivant leur pouvoir oxydant ou réducteur
  3687.                  * [chimie] Connaître la définition de la théorie acide/base selon Brönsted
  3688.                  * [chimie] Connaître les charges des particules
  3689.                  * On veut dans l'ordre des groupes définis par le terme entre crochet au début du titre de la catégorie
  3690.                 */
  3691.                 // If test option is Grouped By Categories
  3692.                 if ($isRandomByCategory == 2) {
  3693.                     $tabCategoryQuestions = Testcategory::sortTabByBracketLabel($tabCategoryQuestions);
  3694.                 }
  3695.                 while (list($cat_id, $tabquestion) = each($tabCategoryQuestions)) {
  3696.                     $number_of_random_question = $this->random;
  3697.                     if ($this->random == -1) {
  3698.                         $number_of_random_question = count($this->questionList);
  3699.                     }
  3700.                     $questionList = array_merge($questionList, Testcategory::getNElementsFromArray($tabquestion, $number_of_random_question));
  3701.                 }
  3702.                 // shuffle the question list if test is not grouped by categories
  3703.                 if ($isRandomByCategory == 1) {
  3704.                     shuffle($questionList); // or not
  3705.                 }
  3706.                 $tabres = $questionList;
  3707.             } else {
  3708.                 // Problem, random by category has been selected and we have no $this->isRandom nnumber of question selected
  3709.                 // Should not happened
  3710.             }
  3711.         }
  3712.         return $tabres;
  3713.     }
  3714.  
  3715.     function get_question_list($expand_media_questions = false) {
  3716.         $question_list = $this->get_validated_question_list();
  3717.         $question_list = $this->transform_question_list_with_medias($question_list, $expand_media_questions);
  3718.         return $question_list;
  3719.     }
  3720.  
  3721.     function transform_question_list_with_medias($question_list, $expand_media_questions = false) {
  3722.         $new_question_list = array();
  3723.         if (!empty($question_list)) {
  3724.             $media_questions = $this->get_media_list();
  3725.             $media_active = $this->media_is_activated($media_questions);
  3726.  
  3727.             if ($media_active) {
  3728.                 $counter = 1;
  3729.                 foreach ($question_list as $question_id) {
  3730.                     $add_question = true;
  3731.                     foreach ($media_questions as $media_id => $question_list_in_media) {
  3732.                         if ($media_id != 999 && in_array($question_id, $question_list_in_media)) {
  3733.                             $add_question = false;
  3734.                             if (!in_array($media_id, $new_question_list)) {
  3735.                                 $new_question_list[$counter] = $media_id;
  3736.                                 $counter++;
  3737.                             }
  3738.                             break;
  3739.                         }
  3740.                     }
  3741.                     if ($add_question) {
  3742.                         $new_question_list[$counter] = $question_id;
  3743.                         $counter++;
  3744.                     }
  3745.                 }
  3746.                 if ($expand_media_questions) {
  3747.                     $media_key_list = array_keys($media_questions);
  3748.                     foreach ($new_question_list as &$question_id) {
  3749.                         if (in_array($question_id, $media_key_list)) {
  3750.                             $question_id = $media_questions[$question_id];
  3751.                         }
  3752.                     }
  3753.                     $new_question_list = array_flatten($new_question_list);
  3754.                 }
  3755.             } else {
  3756.                 $new_question_list = $question_list;
  3757.             }
  3758.         }
  3759.         return $new_question_list;
  3760.     }
  3761.  
  3762.     public function get_stat_track_exercise_info_by_exe_id($exe_id) {
  3763.         $track_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  3764.         $exe_id = intval($exe_id);
  3765.         $sql_track = "SELECT * FROM $track_exercises WHERE exe_id = $exe_id ";
  3766.         $result = Database::query($sql_track);
  3767.         $new_array = array();
  3768.         if (Database::num_rows($result) > 0 ) {
  3769.             $new_array = Database::fetch_array($result, 'ASSOC');
  3770.  
  3771.             $new_array['duration'] = null;
  3772.  
  3773.             $start_date = api_get_utc_datetime($new_array['start_date'], true);
  3774.             $end_date = api_get_utc_datetime($new_array['exe_date'], true);
  3775.  
  3776.             if (!empty($start_date) && !empty($end_date)) {
  3777.                 $start_date = api_strtotime($start_date, 'UTC');
  3778.                 $end_date = api_strtotime($end_date, 'UTC');
  3779.                 if ($start_date && $end_date) {
  3780.                     $mytime = $end_date- $start_date;
  3781.                     $new_learnpath_item = new learnpathItem(null);
  3782.                     $time_attemp = $new_learnpath_item->get_scorm_time('js', $mytime);
  3783.                     $h = get_lang('h');
  3784.                     $time_attemp = str_replace('NaN', '00' . $h . '00\'00"', $time_attemp);
  3785.                     $new_array['duration'] = $time_attemp;
  3786.                 }
  3787.             }
  3788.         }
  3789.         return $new_array;
  3790.     }
  3791.  
  3792.     public function edit_question_to_remind($exe_id, $question_id, $action = 'add') {
  3793.         $exercise_info = self::get_stat_track_exercise_info_by_exe_id($exe_id);
  3794.         $question_id = intval($question_id);
  3795.         $exe_id = intval($exe_id);
  3796.         $track_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  3797.         if ($exercise_info) {
  3798.  
  3799.             if (empty($exercise_info['questions_to_check'])) {
  3800.                 if ($action == 'add') {
  3801.                     $sql = "UPDATE $track_exercises SET questions_to_check = '$question_id' WHERE exe_id = $exe_id ";
  3802.                     $result = Database::query($sql);
  3803.                 }
  3804.             } else {
  3805.                 $remind_list = explode(',',$exercise_info['questions_to_check']);
  3806.  
  3807.                 $remind_list_string = '';
  3808.                 if ($action == 'add') {
  3809.                     if (!in_array($question_id, $remind_list)) {
  3810.                         $remind_list[] = $question_id;
  3811.                         if (!empty($remind_list)) {
  3812.                             sort($remind_list);
  3813.                             array_filter($remind_list);
  3814.                         }
  3815.                         $remind_list_string = implode(',', $remind_list);
  3816.                     }
  3817.                 } elseif ($action == 'delete')  {
  3818.                     if (!empty($remind_list)) {
  3819.                         if (in_array($question_id, $remind_list)) {
  3820.                             $remind_list = array_flip($remind_list);
  3821.                             unset($remind_list[$question_id]);
  3822.                             $remind_list = array_flip($remind_list);
  3823.  
  3824.                             if (!empty($remind_list)) {
  3825.                                 sort($remind_list);
  3826.                                 array_filter($remind_list);
  3827.                                 $remind_list_string = implode(',', $remind_list);
  3828.                             }
  3829.                         }
  3830.                     }
  3831.                 }
  3832.                 $remind_list_string = Database::escape_string($remind_list_string);
  3833.                 $sql = "UPDATE $track_exercises SET questions_to_check = '$remind_list_string' WHERE exe_id = $exe_id ";
  3834.                 $result = Database::query($sql);
  3835.             }
  3836.         }
  3837.     }
  3838.  
  3839.     public function fill_in_blank_answer_to_array($answer) {
  3840.         api_preg_match_all('/\[[^]]+\]/', $answer, $teacher_answer_list);
  3841.         $teacher_answer_list = $teacher_answer_list[0];
  3842.         return $teacher_answer_list;
  3843.     }
  3844.  
  3845.     public function fill_in_blank_answer_to_string($answer) {
  3846.         $teacher_answer_list = $this->fill_in_blank_answer_to_array($answer);
  3847.         $result = '';
  3848.         if (!empty($teacher_answer_list)) {
  3849.             $i = 0;
  3850.             foreach ($teacher_answer_list as $teacher_item) {
  3851.                 $value = null;
  3852.                 //Cleaning student answer list
  3853.                 $value = strip_tags($teacher_item);
  3854.                 $value = api_substr($value,1, api_strlen($value)-2);
  3855.                 $value = explode('/', $value);
  3856.                 if (!empty($value[0])) {
  3857.                     $value = trim($value[0]);
  3858.                     $value = str_replace('&nbsp;', '',  $value);
  3859.                     $result .= $value;
  3860.                 }
  3861.             }
  3862.         }
  3863.         return $result;
  3864.     }
  3865.  
  3866.     function return_time_left_div() {
  3867.         $html  = '<div id="clock_warning" style="display:none">'.Display::return_message(get_lang('ReachedTimeLimit'), 'warning').' '.sprintf(get_lang('YouWillBeRedirectedInXSeconds'), '<span id="counter_to_redirect" class="red_alert"></span>').'</div>';
  3868.         $html .= '<div id="exercise_clock_warning" class="well count_down"></div>';
  3869.         return $html;
  3870.     }
  3871.  
  3872.     function get_count_question_list() {
  3873.         //Real question count
  3874.         $question_count = 0;
  3875.         $question_list = $this->get_question_list();
  3876.         if (!empty($question_list)) {
  3877.             $question_count = count($question_list);
  3878.         }
  3879.         return $question_count;
  3880.     }
  3881.  
  3882.     function get_exercise_list_ordered() {
  3883.         $table_exercise_order = Database::get_course_table(TABLE_QUIZ_ORDER);
  3884.         $course_id = api_get_course_int_id();
  3885.         $session_id = api_get_session_id();
  3886.         $sql = "SELECT exercise_id, exercise_order FROM $table_exercise_order WHERE c_id = $course_id AND session_id = $session_id ORDER BY exercise_order";
  3887.         $result = Database::query($sql);
  3888.         $list = array();
  3889.         if (Database::num_rows($result)) {
  3890.             while($row = Database::fetch_array($result, 'ASSOC')) {
  3891.                 $list[$row['exercise_order']] = $row['exercise_id'];
  3892.             }
  3893.         }
  3894.         return $list;
  3895.     }
  3896. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement