/** * This function was originally found in the exercise_show.php * @param int $exeId * @param int $questionId * @param int $choice the user selected * @param string $from function is called from 'exercise_show' or 'exercise_result' * @param array $exerciseResultCoordinates the hotspot coordinates $hotspot[$question_id] = coordinates * @param bool $saved_results save results in the DB or just show the reponse * @param bool $from_database gets information from DB or from the current selection * @param bool $show_result show results or not * @param int $propagate_neg * @param array $hotspot_delineation_result * * @todo reduce parameters of this function * @return string html code */ 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()) { global $debug; //needed in order to use in the exercise_attempt() for the time global $learnpath_id, $learnpath_item_id; require_once api_get_path(LIBRARY_PATH) . 'geometry.lib.php'; $feedback_type = $this->selectFeedbackType(); $results_disabled = $this->selectResultsDisabled(); if ($debug) { error_log("<------ manage_answer ------> "); error_log('exe_id: ' . $exeId); error_log('$from: ' . $from); error_log('$saved_results: ' . intval($saved_results)); error_log('$from_database: ' . intval($from_database)); error_log('$show_result: ' . $show_result); error_log('$propagate_neg: ' . $propagate_neg); error_log('$exerciseResultCoordinates: ' . print_r($exerciseResultCoordinates, 1)); error_log('$hotspot_delineation_result: ' . print_r($hotspot_delineation_result, 1)); error_log('$learnpath_id: ' . $learnpath_id); error_log('$learnpath_item_id: ' . $learnpath_item_id); error_log('$choice: ' . print_r($choice, 1)); } $extra_data = array(); $final_overlap = 0; $final_missing = 0; $final_excess = 0; $overlap_color = 0; $missing_color = 0; $excess_color = 0; $threadhold1 = 0; $threadhold2 = 0; $threadhold3 = 0; $arrques = null; $arrans = null; $questionId = intval($questionId); $exeId = intval($exeId); $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); $table_ans = Database::get_course_table(TABLE_QUIZ_ANSWER); // Creates a temporary Question object $course_id = $this->course_id; $objQuestionTmp = Question::read($questionId, $course_id); if ($objQuestionTmp === false) { return false; } $questionName = $objQuestionTmp->selectTitle(); $questionWeighting = $objQuestionTmp->selectWeighting(); $answerType = $objQuestionTmp->selectType(); $quesId = $objQuestionTmp->selectId(); $extra = $objQuestionTmp->extra; $next = 1; //not for now // Extra information of the question if (!empty($extra)) { $extra = explode(':', $extra); if ($debug) { error_log(print_r($extra, 1)); } // Fixes problems with negatives values using intval $true_score = floatval(trim($extra[0])); $false_score = floatval(trim($extra[1])); $doubt_score = floatval(trim($extra[2])); } $totalWeighting = 0; $totalScore = 0; // Destruction of the Question object unset($objQuestionTmp); // Construction of the Answer object $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); if ($debug) { error_log('Count of answers: ' . $nbrAnswers); error_log('$answerType: ' . $answerType); } if ($answerType == FREE_ANSWER || $answerType == ORAL_EXPRESSION || $answerType == CALCULATED_ANSWER) { $nbrAnswers = 1; } $nano = null; if ($answerType == ORAL_EXPRESSION) { $exe_info = Event::get_exercise_results_by_attempt($exeId); $exe_info = isset($exe_info[$exeId]) ? $exe_info[$exeId] : null; $params = array(); $params['course_id'] = $course_id; $params['session_id'] = api_get_session_id(); $params['user_id'] = isset($exe_info['exe_user_id']) ? $exe_info['exe_user_id'] : api_get_user_id(); $params['exercise_id'] = isset($exe_info['exe_exo_id']) ? $exe_info['exe_exo_id'] : $this->id; $params['question_id'] = $questionId; $params['exe_id'] = isset($exe_info['exe_id']) ? $exe_info['exe_id'] : $exeId; $nano = new Nanogong($params); //probably this attempt came in an exercise all question by page if ($feedback_type == 0) { $nano->replace_with_real_exe($exeId); } } $user_answer = ''; // Get answer list for matching $sql = "SELECT id_auto, id, answer\n FROM {$table_ans}\n WHERE c_id = {$course_id} AND question_id = {$questionId}"; $res_answer = Database::query($sql); $answerMatching = array(); while ($real_answer = Database::fetch_array($res_answer)) { $answerMatching[$real_answer['id_auto']] = $real_answer['answer']; } $real_answers = array(); $quiz_question_options = Question::readQuestionOption($questionId, $course_id); $organs_at_risk_hit = 0; $questionScore = 0; if ($debug) { error_log('Start answer loop '); } $answer_correct_array = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answer = $objAnswerTmp->selectAnswer($answerId); $answerComment = $objAnswerTmp->selectComment($answerId); $answerCorrect = $objAnswerTmp->isCorrect($answerId); $answerWeighting = (double) $objAnswerTmp->selectWeighting($answerId); $answerAutoId = $objAnswerTmp->selectAutoId($answerId); $answer_correct_array[$answerId] = (bool) $answerCorrect; if ($debug) { error_log("answer auto id: {$answerAutoId} "); error_log("answer correct: {$answerCorrect} "); } // Delineation $delineation_cord = $objAnswerTmp->selectHotspotCoordinates(1); $answer_delineation_destination = $objAnswerTmp->selectDestination(1); switch ($answerType) { // for unique answer case UNIQUE_ANSWER: case UNIQUE_ANSWER_IMAGE: case UNIQUE_ANSWER_NO_OPTION: if ($from_database) { $sql = "SELECT answer FROM {$TBL_TRACK_ATTEMPT}\n WHERE\n exe_id = '" . $exeId . "' AND\n question_id= '" . $questionId . "'"; $result = Database::query($sql); $choice = Database::result($result, 0, "answer"); $studentChoice = $choice == $answerAutoId ? 1 : 0; if ($studentChoice) { $questionScore += $answerWeighting; $totalScore += $answerWeighting; } } else { $studentChoice = $choice == $answerAutoId ? 1 : 0; if ($studentChoice) { $questionScore += $answerWeighting; $totalScore += $answerWeighting; } } break; // for multiple answers // for multiple answers case MULTIPLE_ANSWER_TRUE_FALSE: if ($from_database) { $choice = array(); $sql = "SELECT answer FROM {$TBL_TRACK_ATTEMPT}\n WHERE\n exe_id = {$exeId} AND\n question_id = " . $questionId; $result = Database::query($sql); while ($row = Database::fetch_array($result)) { $ind = $row['answer']; $values = explode(':', $ind); $my_answer_id = isset($values[0]) ? $values[0] : ''; $option = isset($values[1]) ? $values[1] : ''; $choice[$my_answer_id] = $option; } } $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; if (!empty($studentChoice)) { if ($studentChoice == $answerCorrect) { $questionScore += $true_score; } else { if ($quiz_question_options[$studentChoice]['name'] == "Don't know" || $quiz_question_options[$studentChoice]['name'] == "DoubtScore") { $questionScore += $doubt_score; } else { $questionScore += $false_score; } } } else { // If no result then the user just hit don't know $studentChoice = 3; $questionScore += $doubt_score; } $totalScore = $questionScore; break; case MULTIPLE_ANSWER: //2 if ($from_database) { $choice = array(); $sql = "SELECT answer FROM " . $TBL_TRACK_ATTEMPT . "\n WHERE exe_id = '" . $exeId . "' AND question_id= '" . $questionId . "'"; $resultans = Database::query($sql); while ($row = Database::fetch_array($resultans)) { $ind = $row['answer']; $choice[$ind] = 1; } $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; $real_answers[$answerId] = (bool) $studentChoice; if ($studentChoice) { $questionScore += $answerWeighting; } } else { $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; $real_answers[$answerId] = (bool) $studentChoice; if (isset($studentChoice)) { $questionScore += $answerWeighting; } } $totalScore += $answerWeighting; if ($debug) { error_log("studentChoice: {$studentChoice}"); } break; case GLOBAL_MULTIPLE_ANSWER: if ($from_database) { $choice = array(); $sql = "SELECT answer FROM {$TBL_TRACK_ATTEMPT}\n WHERE exe_id = '" . $exeId . "' AND question_id= '" . $questionId . "'"; $resultans = Database::query($sql); while ($row = Database::fetch_array($resultans)) { $ind = $row['answer']; $choice[$ind] = 1; } $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; $real_answers[$answerId] = (bool) $studentChoice; if ($studentChoice) { $questionScore += $answerWeighting; } } else { $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; if (isset($studentChoice)) { $questionScore += $answerWeighting; } $real_answers[$answerId] = (bool) $studentChoice; } $totalScore += $answerWeighting; if ($debug) { error_log("studentChoice: {$studentChoice}"); } break; case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE: if ($from_database) { $sql = "SELECT answer FROM " . $TBL_TRACK_ATTEMPT . "\n WHERE exe_id = {$exeId} AND question_id= " . $questionId; $resultans = Database::query($sql); while ($row = Database::fetch_array($resultans)) { $ind = $row['answer']; $result = explode(':', $ind); if (isset($result[0])) { $my_answer_id = isset($result[0]) ? $result[0] : ''; $option = isset($result[1]) ? $result[1] : ''; $choice[$my_answer_id] = $option; } } $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : ''; if ($answerCorrect == $studentChoice) { //$answerCorrect = 1; $real_answers[$answerId] = true; } else { //$answerCorrect = 0; $real_answers[$answerId] = false; } } else { $studentChoice = $choice[$answerAutoId]; if ($answerCorrect == $studentChoice) { //$answerCorrect = 1; $real_answers[$answerId] = true; } else { //$answerCorrect = 0; $real_answers[$answerId] = false; } } break; case MULTIPLE_ANSWER_COMBINATION: if ($from_database) { $sql = "SELECT answer FROM {$TBL_TRACK_ATTEMPT}\n WHERE exe_id = {$exeId} AND question_id= {$questionId}"; $resultans = Database::query($sql); while ($row = Database::fetch_array($resultans)) { $ind = $row['answer']; $choice[$ind] = 1; } $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; if ($answerCorrect == 1) { if ($studentChoice) { $real_answers[$answerId] = true; } else { $real_answers[$answerId] = false; } } else { if ($studentChoice) { $real_answers[$answerId] = false; } else { $real_answers[$answerId] = true; } } } else { $studentChoice = isset($choice[$answerAutoId]) ? $choice[$answerAutoId] : null; if ($answerCorrect == 1) { if ($studentChoice) { $real_answers[$answerId] = true; } else { $real_answers[$answerId] = false; } } else { if ($studentChoice) { $real_answers[$answerId] = false; } else { $real_answers[$answerId] = true; } } } break; case FILL_IN_BLANKS: $str = ''; if ($from_database) { $sql = "SELECT answer\n FROM {$TBL_TRACK_ATTEMPT}\n WHERE\n exe_id = {$exeId} AND\n question_id= " . intval($questionId); $result = Database::query($sql); $str = Database::result($result, 0, 'answer'); } if ($saved_results == false && strpos($str, 'font color') !== false) { // the question is encoded like this // [A] B [C] D [E] F::10,10,10@1 // number 1 before the "@" means that is a switchable fill in blank question // [A] B [C] D [E] F::10,10,10@ or [A] B [C] D [E] F::10,10,10 // means that is a normal fill blank question // first we explode the "::" $pre_array = explode('::', $answer); // is switchable fill blank or not $last = count($pre_array) - 1; $is_set_switchable = explode('@', $pre_array[$last]); $switchable_answer_set = false; if (isset($is_set_switchable[1]) && $is_set_switchable[1] == 1) { $switchable_answer_set = true; } $answer = ''; for ($k = 0; $k < $last; $k++) { $answer .= $pre_array[$k]; } // splits weightings that are joined with a comma $answerWeighting = explode(',', $is_set_switchable[0]); // we save the answer because it will be modified $temp = $answer; $answer = ''; $j = 0; //initialise answer tags $user_tags = $correct_tags = $real_text = array(); // the loop will stop at the end of the text while (1) { // quits the loop if there are no more blanks (detect '[') if (($pos = api_strpos($temp, '[')) === false) { // adds the end of the text $answer = $temp; $real_text[] = $answer; break; //no more "blanks", quit the loop } // adds the piece of text that is before the blank //and ends with '[' into a general storage array $real_text[] = api_substr($temp, 0, $pos + 1); $answer .= api_substr($temp, 0, $pos + 1); //take the string remaining (after the last "[" we found) $temp = api_substr($temp, $pos + 1); // quit the loop if there are no more blanks, and update $pos to the position of next ']' if (($pos = api_strpos($temp, ']')) === false) { // adds the end of the text $answer .= $temp; break; } if ($from_database) { $queryfill = "SELECT answer FROM " . $TBL_TRACK_ATTEMPT . "\n WHERE\n exe_id = '" . $exeId . "' AND\n question_id= " . intval($questionId) . ""; $resfill = Database::query($queryfill); $str = Database::result($resfill, 0, 'answer'); api_preg_match_all('#\\[([^[]*)\\]#', $str, $arr); $str = str_replace('\\r\\n', '', $str); $choice = $arr[1]; if (isset($choice[$j])) { $tmp = api_strrpos($choice[$j], ' / '); $choice[$j] = api_substr($choice[$j], 0, $tmp); $choice[$j] = trim($choice[$j]); // Needed to let characters ' and " to work as part of an answer $choice[$j] = stripslashes($choice[$j]); } else { $choice[$j] = null; } } else { // This value is the user input, not escaped while correct answer is escaped by fckeditor $choice[$j] = api_htmlentities(trim($choice[$j])); } $user_tags[] = $choice[$j]; //put the contents of the [] answer tag into correct_tags[] $correct_tags[] = api_substr($temp, 0, $pos); $j++; $temp = api_substr($temp, $pos + 1); } $answer = ''; $real_correct_tags = $correct_tags; $chosen_list = array(); for ($i = 0; $i < count($real_correct_tags); $i++) { if ($i == 0) { $answer .= $real_text[0]; } if (!$switchable_answer_set) { // Needed to parse ' and " characters $user_tags[$i] = stripslashes($user_tags[$i]); if ($correct_tags[$i] == $user_tags[$i]) { // gives the related weighting to the student $questionScore += $answerWeighting[$i]; // increments total score $totalScore += $answerWeighting[$i]; // adds the word in green at the end of the string $answer .= $correct_tags[$i]; } elseif (!empty($user_tags[$i])) { // else if the word entered by the student IS NOT the same as the one defined by the professor // adds the word in red at the end of the string, and strikes it $answer .= '<font color="red"><s>' . $user_tags[$i] . '</s></font>'; } else { // adds a tabulation if no word has been typed by the student $answer .= ''; // remove that causes issue } } else { // switchable fill in the blanks if (in_array($user_tags[$i], $correct_tags)) { $chosen_list[] = $user_tags[$i]; $correct_tags = array_diff($correct_tags, $chosen_list); // gives the related weighting to the student $questionScore += $answerWeighting[$i]; // increments total score $totalScore += $answerWeighting[$i]; // adds the word in green at the end of the string $answer .= $user_tags[$i]; } elseif (!empty($user_tags[$i])) { // else if the word entered by the student IS NOT the same as the one defined by the professor // adds the word in red at the end of the string, and strikes it $answer .= '<font color="red"><s>' . $user_tags[$i] . '</s></font>'; } else { // adds a tabulation if no word has been typed by the student $answer .= ''; // remove that causes issue } } // adds the correct word, followed by ] to close the blank $answer .= ' / <font color="green"><b>' . $real_correct_tags[$i] . '</b></font>]'; if (isset($real_text[$i + 1])) { $answer .= $real_text[$i + 1]; } } } else { // insert the student result in the track_e_attempt table, field answer // $answer is the answer like in the c_quiz_answer table for the question // student data are choice[] $listCorrectAnswers = FillBlanks::getAnswerInfo($answer); $switchableAnswerSet = $listCorrectAnswers["switchable"]; $answerWeighting = $listCorrectAnswers["tabweighting"]; // user choices is an array $choice // get existing user data in n the BDD if ($from_database) { $sql = "SELECT answer\n FROM {$TBL_TRACK_ATTEMPT}\n WHERE\n exe_id = {$exeId} AND\n question_id= " . intval($questionId); $result = Database::query($sql); $str = Database::result($result, 0, 'answer'); $listStudentResults = FillBlanks::getAnswerInfo($str, true); $choice = $listStudentResults['studentanswer']; } // loop other all blanks words if (!$switchableAnswerSet) { // not switchable answer, must be in the same place than teacher order for ($i = 0; $i < count($listCorrectAnswers['tabwords']); $i++) { $studentAnswer = isset($choice[$i]) ? trim($choice[$i]) : ''; // This value is the user input, not escaped while correct answer is escaped by fckeditor // Works with cyrillic alphabet and when using ">" chars see #7718 #7610 #7618 if (!$from_database) { $studentAnswer = htmlentities(api_utf8_encode($studentAnswer)); } $correctAnswer = $listCorrectAnswers['tabwords'][$i]; $isAnswerCorrect = 0; if (FillBlanks::isGoodStudentAnswer($studentAnswer, $correctAnswer)) { // gives the related weighting to the student $questionScore += $answerWeighting[$i]; // increments total score $totalScore += $answerWeighting[$i]; $isAnswerCorrect = 1; } $listCorrectAnswers['studentanswer'][$i] = $studentAnswer; $listCorrectAnswers['studentscore'][$i] = $isAnswerCorrect; } } else { // switchable answer $listStudentAnswerTemp = $choice; $listTeacherAnswerTemp = $listCorrectAnswers['tabwords']; // for every teacher answer, check if there is a student answer for ($i = 0; $i < count($listStudentAnswerTemp); $i++) { $studentAnswer = trim($listStudentAnswerTemp[$i]); $found = false; for ($j = 0; $j < count($listTeacherAnswerTemp); $j++) { $correctAnswer = $listTeacherAnswerTemp[$j]; if (!$found) { if (FillBlanks::isGoodStudentAnswer($studentAnswer, $correctAnswer)) { $questionScore += $answerWeighting[$i]; $totalScore += $answerWeighting[$i]; $listTeacherAnswerTemp[$j] = ""; $found = true; } } } $listCorrectAnswers['studentanswer'][$i] = $studentAnswer; if (!$found) { $listCorrectAnswers['studentscore'][$i] = 0; } else { $listCorrectAnswers['studentscore'][$i] = 1; } } } $answer = FillBlanks::getAnswerInStudentAttempt($listCorrectAnswers); } break; // for calculated answer // for calculated answer case CALCULATED_ANSWER: $calculatedAnswer = Session::read('calculatedAnswerId'); $answer = $objAnswerTmp->selectAnswer($calculatedAnswer[$questionId]); $preArray = explode('@@', $answer); $last = count($preArray) - 1; $answer = ''; for ($k = 0; $k < $last; $k++) { $answer .= $preArray[$k]; } $answerWeighting = array($answerWeighting); // we save the answer because it will be modified $temp = $answer; $answer = ''; $j = 0; //initialise answer tags $userTags = $correctTags = $realText = array(); // the loop will stop at the end of the text while (1) { // quits the loop if there are no more blanks (detect '[') if (($pos = api_strpos($temp, '[')) === false) { // adds the end of the text $answer = $temp; $realText[] = $answer; break; //no more "blanks", quit the loop } // adds the piece of text that is before the blank //and ends with '[' into a general storage array $realText[] = api_substr($temp, 0, $pos + 1); $answer .= api_substr($temp, 0, $pos + 1); //take the string remaining (after the last "[" we found) $temp = api_substr($temp, $pos + 1); // quit the loop if there are no more blanks, and update $pos to the position of next ']' if (($pos = api_strpos($temp, ']')) === false) { // adds the end of the text $answer .= $temp; break; } if ($from_database) { $queryfill = "SELECT answer FROM " . $TBL_TRACK_ATTEMPT . "\n WHERE\n exe_id = '" . $exeId . "' AND\n question_id= " . intval($questionId) . ""; $resfill = Database::query($queryfill); $str = Database::result($resfill, 0, 'answer'); api_preg_match_all('#\\[([^[]*)\\]#', $str, $arr); $str = str_replace('\\r\\n', '', $str); $choice = $arr[1]; if (isset($choice[$j])) { $tmp = api_strrpos($choice[$j], ' / '); $choice[$j] = api_substr($choice[$j], 0, $tmp); $choice[$j] = trim($choice[$j]); // Needed to let characters ' and " to work as part of an answer $choice[$j] = stripslashes($choice[$j]); } else { $choice[$j] = null; } } else { // This value is the user input, not escaped while correct answer is escaped by fckeditor $choice[$j] = api_htmlentities(trim($choice[$j])); } $userTags[] = $choice[$j]; //put the contents of the [] answer tag into correct_tags[] $correctTags[] = api_substr($temp, 0, $pos); $j++; $temp = api_substr($temp, $pos + 1); } $answer = ''; $realCorrectTags = $correctTags; for ($i = 0; $i < count($realCorrectTags); $i++) { if ($i == 0) { $answer .= $realText[0]; } // Needed to parse ' and " characters $userTags[$i] = stripslashes($userTags[$i]); if ($correctTags[$i] == $userTags[$i]) { // gives the related weighting to the student $questionScore += $answerWeighting[$i]; // increments total score $totalScore += $answerWeighting[$i]; // adds the word in green at the end of the string $answer .= $correctTags[$i]; } elseif (!empty($userTags[$i])) { // else if the word entered by the student IS NOT the same as the one defined by the professor // adds the word in red at the end of the string, and strikes it $answer .= '<font color="red"><s>' . $userTags[$i] . '</s></font>'; } else { // adds a tabulation if no word has been typed by the student $answer .= ''; // remove that causes issue } // adds the correct word, followed by ] to close the blank $answer .= ' / <font color="green"><b>' . $realCorrectTags[$i] . '</b></font>]'; if (isset($realText[$i + 1])) { $answer .= $realText[$i + 1]; } } break; // for free answer // for free answer case FREE_ANSWER: if ($from_database) { $query = "SELECT answer, marks FROM " . $TBL_TRACK_ATTEMPT . "\n WHERE exe_id = '" . $exeId . "' AND question_id= '" . $questionId . "'"; $resq = Database::query($query); $data = Database::fetch_array($resq); $choice = $data['answer']; $choice = str_replace('\\r\\n', '', $choice); $choice = stripslashes($choice); $questionScore = $data['marks']; if ($questionScore == -1) { $totalScore += 0; } else { $totalScore += $questionScore; } if ($questionScore == '') { $questionScore = 0; } $arrques = $questionName; $arrans = $choice; } else { $studentChoice = $choice; if ($studentChoice) { //Fixing negative puntation see #2193 $questionScore = 0; $totalScore += 0; } } break; case ORAL_EXPRESSION: if ($from_database) { $query = "SELECT answer, marks FROM " . $TBL_TRACK_ATTEMPT . "\n WHERE exe_id = '" . $exeId . "' AND question_id= '" . $questionId . "'"; $resq = Database::query($query); $choice = Database::result($resq, 0, 'answer'); $choice = str_replace('\\r\\n', '', $choice); $choice = stripslashes($choice); $questionScore = Database::result($resq, 0, "marks"); if ($questionScore == -1) { $totalScore += 0; } else { $totalScore += $questionScore; } $arrques = $questionName; $arrans = $choice; } else { $studentChoice = $choice; if ($studentChoice) { //Fixing negative puntation see #2193 $questionScore = 0; $totalScore += 0; } } break; case DRAGGABLE: //no break //no break case MATCHING_DRAGGABLE: //no break //no break case MATCHING: if ($from_database) { $sql = 'SELECT id, answer, id_auto FROM ' . $table_ans . ' WHERE c_id = ' . $course_id . ' AND question_id = "' . $questionId . '" AND correct = 0'; $res_answer = Database::query($sql); // Getting the real answer $real_list = array(); while ($real_answer = Database::fetch_array($res_answer)) { $real_list[$real_answer['id_auto']] = $real_answer['answer']; } $sql = 'SELECT id, answer, correct, id_auto, ponderation FROM ' . $table_ans . ' WHERE c_id = ' . $course_id . ' AND question_id="' . $questionId . '" AND correct <> 0 ORDER BY id_auto'; $res_answers = Database::query($sql); $questionScore = 0; while ($a_answers = Database::fetch_array($res_answers)) { $i_answer_id = $a_answers['id']; //3 $s_answer_label = $a_answers['answer']; // your daddy - your mother $i_answer_correct_answer = $a_answers['correct']; //1 - 2 $i_answer_id_auto = $a_answers['id_auto']; // 3 - 4 $sql = "SELECT answer FROM {$TBL_TRACK_ATTEMPT}\n WHERE\n exe_id = '{$exeId}' AND\n question_id = '{$questionId}' AND\n position = '{$i_answer_id_auto}'"; $res_user_answer = Database::query($sql); if (Database::num_rows($res_user_answer) > 0) { // rich - good looking $s_user_answer = Database::result($res_user_answer, 0, 0); } else { $s_user_answer = 0; } $i_answerWeighting = $a_answers['ponderation']; $user_answer = ''; if (!empty($s_user_answer)) { if ($answerType == DRAGGABLE) { if ($s_user_answer == $i_answer_correct_answer) { $questionScore += $i_answerWeighting; $totalScore += $i_answerWeighting; $user_answer = Display::label(get_lang('Correct'), 'success'); } else { $user_answer = Display::label(get_lang('Incorrect'), 'danger'); } } else { if ($s_user_answer == $i_answer_correct_answer) { $questionScore += $i_answerWeighting; $totalScore += $i_answerWeighting; if (isset($real_list[$i_answer_id])) { $user_answer = Display::span($real_list[$i_answer_id]); } } else { $user_answer = Display::span($real_list[$s_user_answer], ['style' => 'color: #FF0000; text-decoration: line-through;']); } } } elseif ($answerType == DRAGGABLE) { $user_answer = Display::label(get_lang('Incorrect'), 'danger'); } if ($show_result) { echo '<tr>'; echo '<td>' . $s_answer_label . '</td>'; echo '<td>' . $user_answer; if (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { if (isset($real_list[$i_answer_correct_answer])) { echo Display::span($real_list[$i_answer_correct_answer], ['style' => 'color: #008000; font-weight: bold;']); } } echo '</td>'; echo '</tr>'; } } break 2; // break the switch and the "for" condition } else { if ($answerCorrect) { if (isset($choice[$answerAutoId]) && $answerCorrect == $choice[$answerAutoId]) { $questionScore += $answerWeighting; $totalScore += $answerWeighting; $user_answer = Display::span($answerMatching[$choice[$answerAutoId]]); } else { if (isset($answerMatching[$choice[$answerAutoId]])) { $user_answer = Display::span($answerMatching[$choice[$answerAutoId]], ['style' => 'color: #FF0000; text-decoration: line-through;']); } } $matching[$answerAutoId] = $choice[$answerAutoId]; } break; } case HOT_SPOT: if ($from_database) { $TBL_TRACK_HOTSPOT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT); $sql = "SELECT hotspot_correct\n FROM {$TBL_TRACK_HOTSPOT}\n WHERE\n hotspot_exe_id = '" . $exeId . "' AND\n hotspot_question_id= '" . $questionId . "' AND\n hotspot_answer_id = " . intval($answerAutoId) . ""; $result = Database::query($sql); $studentChoice = Database::result($result, 0, "hotspot_correct"); if ($studentChoice) { $questionScore += $answerWeighting; $totalScore += $answerWeighting; } } else { if (!isset($choice[$answerAutoId])) { $choice[$answerAutoId] = 0; } else { $studentChoice = $choice[$answerAutoId]; $choiceIsValid = false; if (!empty($studentChoice)) { $hotspotType = $objAnswerTmp->selectHotspotType($answerId); $hotspotCoordinates = $objAnswerTmp->selectHotspotCoordinates($answerId); $choicePoint = Geometry::decodePoint($studentChoice); switch ($hotspotType) { case 'square': $hotspotProperties = Geometry::decodeSquare($hotspotCoordinates); $choiceIsValid = Geometry::pointIsInSquare($hotspotProperties, $choicePoint); break; case 'circle': $hotspotProperties = Geometry::decodeEllipse($hotspotCoordinates); $choiceIsValid = Geometry::pointIsInEllipse($hotspotProperties, $choicePoint); break; case 'poly': $hotspotProperties = Geometry::decodePolygon($hotspotCoordinates); $choiceIsValid = Geometry::pointIsInPolygon($hotspotProperties, $choicePoint); break; } } $choice[$answerAutoId] = 0; if ($choiceIsValid) { $questionScore += $answerWeighting; $totalScore += $answerWeighting; $choice[$answerAutoId] = 1; } } } break; // @todo never added to chamilo //for hotspot with fixed order // @todo never added to chamilo //for hotspot with fixed order case HOT_SPOT_ORDER: $studentChoice = $choice['order'][$answerId]; if ($studentChoice == $answerId) { $questionScore += $answerWeighting; $totalScore += $answerWeighting; $studentChoice = true; } else { $studentChoice = false; } break; // for hotspot with delineation // for hotspot with delineation case HOT_SPOT_DELINEATION: if ($from_database) { // getting the user answer $TBL_TRACK_HOTSPOT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT); $query = "SELECT hotspot_correct, hotspot_coordinate\n FROM {$TBL_TRACK_HOTSPOT}\n WHERE\n hotspot_exe_id = '" . $exeId . "' AND\n hotspot_question_id= '" . $questionId . "' AND\n hotspot_answer_id='1'"; //by default we take 1 because it's a delineation $resq = Database::query($query); $row = Database::fetch_array($resq, 'ASSOC'); $choice = $row['hotspot_correct']; $user_answer = $row['hotspot_coordinate']; // THIS is very important otherwise the poly_compile will throw an error!! // round-up the coordinates $coords = explode('/', $user_answer); $user_array = ''; foreach ($coords as $coord) { list($x, $y) = explode(';', $coord); $user_array .= round($x) . ';' . round($y) . '/'; } $user_array = substr($user_array, 0, -1); } else { if (!empty($studentChoice)) { $newquestionList[] = $questionId; } if ($answerId === 1) { $studentChoice = $choice[$answerId]; $questionScore += $answerWeighting; if ($hotspot_delineation_result[1] == 1) { $totalScore += $answerWeighting; //adding the total } } } $_SESSION['hotspot_coord'][1] = $delineation_cord; $_SESSION['hotspot_dest'][1] = $answer_delineation_destination; break; } // end switch Answertype if ($show_result) { if ($debug) { error_log('show result ' . $show_result); } if ($from == 'exercise_result') { if ($debug) { error_log('Showing questions $from ' . $from); } //display answers (if not matching type, or if the answer is correct) if (!in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE]) || $answerCorrect) { if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, GLOBAL_MULTIPLE_ANSWER))) { //if ($origin != 'learnpath') { ExerciseShowFunctions::display_unique_or_multiple_answer($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, 0, 0, 0, $results_disabled); //} } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { //if ($origin!='learnpath') { ExerciseShowFunctions::display_multiple_answer_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, 0, $questionId, 0, $results_disabled); //} } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { // if ($origin!='learnpath') { ExerciseShowFunctions::display_multiple_answer_combination_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, 0, 0, 0, $results_disabled); //} } elseif ($answerType == FILL_IN_BLANKS) { //if ($origin!='learnpath') { ExerciseShowFunctions::display_fill_in_blanks_answer($feedback_type, $answer, 0, 0, $results_disabled); // } } elseif ($answerType == CALCULATED_ANSWER) { //if ($origin!='learnpath') { ExerciseShowFunctions::display_calculated_answer($feedback_type, $answer, 0, 0); // } } elseif ($answerType == FREE_ANSWER) { //if($origin != 'learnpath') { ExerciseShowFunctions::display_free_answer($feedback_type, $choice, $exeId, $questionId, $questionScore); //} } elseif ($answerType == ORAL_EXPRESSION) { // to store the details of open questions in an array to be used in mail //if ($origin != 'learnpath') { ExerciseShowFunctions::display_oral_expression_answer($feedback_type, $choice, 0, 0, $nano); //} } elseif ($answerType == HOT_SPOT) { //if ($origin != 'learnpath') { ExerciseShowFunctions::display_hotspot_answer($feedback_type, $answerId, $answer, $studentChoice, $answerComment, $results_disabled); // } } elseif ($answerType == HOT_SPOT_ORDER) { //if ($origin != 'learnpath') { ExerciseShowFunctions::display_hotspot_order_answer($feedback_type, $answerId, $answer, $studentChoice, $answerComment); //} } elseif ($answerType == HOT_SPOT_DELINEATION) { $user_answer = $_SESSION['exerciseResultCoordinates'][$questionId]; //round-up the coordinates $coords = explode('/', $user_answer); $user_array = ''; foreach ($coords as $coord) { list($x, $y) = explode(';', $coord); $user_array .= round($x) . ';' . round($y) . '/'; } $user_array = substr($user_array, 0, -1); if ($next) { $user_answer = $user_array; // we compare only the delineation not the other points $answer_question = $_SESSION['hotspot_coord'][1]; $answerDestination = $_SESSION['hotspot_dest'][1]; //calculating the area $poly_user = convert_coordinates($user_answer, '/'); $poly_answer = convert_coordinates($answer_question, '|'); $max_coord = poly_get_max($poly_user, $poly_answer); $poly_user_compiled = poly_compile($poly_user, $max_coord); $poly_answer_compiled = poly_compile($poly_answer, $max_coord); $poly_results = poly_result($poly_answer_compiled, $poly_user_compiled, $max_coord); $overlap = $poly_results['both']; $poly_answer_area = $poly_results['s1']; $poly_user_area = $poly_results['s2']; $missing = $poly_results['s1Only']; $excess = $poly_results['s2Only']; //$overlap = round(polygons_overlap($poly_answer,$poly_user)); // //this is an area in pixels if ($debug > 0) { error_log(__LINE__ . ' - Polygons results are ' . print_r($poly_results, 1), 0); } if ($overlap < 1) { //shortcut to avoid complicated calculations $final_overlap = 0; $final_missing = 100; $final_excess = 100; } else { // the final overlap is the percentage of the initial polygon // that is overlapped by the user's polygon $final_overlap = round((double) $overlap / (double) $poly_answer_area * 100); if ($debug > 1) { error_log(__LINE__ . ' - Final overlap is ' . $final_overlap, 0); } // the final missing area is the percentage of the initial polygon // that is not overlapped by the user's polygon $final_missing = 100 - $final_overlap; if ($debug > 1) { error_log(__LINE__ . ' - Final missing is ' . $final_missing, 0); } // 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 $final_excess = round(((double) $poly_user_area - (double) $overlap) / (double) $poly_answer_area * 100); if ($debug > 1) { error_log(__LINE__ . ' - Final excess is ' . $final_excess, 0); } } //checking the destination parameters parsing the "@@" $destination_items = explode('@@', $answerDestination); $threadhold_total = $destination_items[0]; $threadhold_items = explode(';', $threadhold_total); $threadhold1 = $threadhold_items[0]; // overlap $threadhold2 = $threadhold_items[1]; // excess $threadhold3 = $threadhold_items[2]; //missing // if is delineation if ($answerId === 1) { //setting colors if ($final_overlap >= $threadhold1) { $overlap_color = true; //echo 'a'; } //echo $excess.'-'.$threadhold2; if ($final_excess <= $threadhold2) { $excess_color = true; //echo 'b'; } //echo '--------'.$missing.'-'.$threadhold3; if ($final_missing <= $threadhold3) { $missing_color = true; //echo 'c'; } // if pass if ($final_overlap >= $threadhold1 && $final_missing <= $threadhold3 && $final_excess <= $threadhold2) { $next = 1; //go to the oars $result_comment = get_lang('Acceptable'); $final_answer = 1; // do not update with update_exercise_attempt } else { $next = 0; $result_comment = get_lang('Unacceptable'); $comment = $answerDestination = $objAnswerTmp->selectComment(1); $answerDestination = $objAnswerTmp->selectDestination(1); //checking the destination parameters parsing the "@@" $destination_items = explode('@@', $answerDestination); } } elseif ($answerId > 1) { if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') { if ($debug > 0) { error_log(__LINE__ . ' - answerId is of type noerror', 0); } //type no error shouldn't be treated $next = 1; continue; } if ($debug > 0) { error_log(__LINE__ . ' - answerId is >1 so we\'re probably in OAR', 0); } //check the intersection between the oar and the user //echo 'user'; print_r($x_user_list); print_r($y_user_list); //echo 'official';print_r($x_list);print_r($y_list); //$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list); $inter = $result['success']; //$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId); $delineation_cord = $objAnswerTmp->selectHotspotCoordinates($answerId); $poly_answer = convert_coordinates($delineation_cord, '|'); $max_coord = poly_get_max($poly_user, $poly_answer); $poly_answer_compiled = poly_compile($poly_answer, $max_coord); $overlap = poly_touch($poly_user_compiled, $poly_answer_compiled, $max_coord); if ($overlap == false) { //all good, no overlap $next = 1; continue; } else { if ($debug > 0) { error_log(__LINE__ . ' - Overlap is ' . $overlap . ': OAR hit', 0); } $organs_at_risk_hit++; //show the feedback $next = 0; $comment = $answerDestination = $objAnswerTmp->selectComment($answerId); $answerDestination = $objAnswerTmp->selectDestination($answerId); $destination_items = explode('@@', $answerDestination); $try_hotspot = $destination_items[1]; $lp_hotspot = $destination_items[2]; $select_question_hotspot = $destination_items[3]; $url_hotspot = $destination_items[4]; } } } else { // the first delineation feedback if ($debug > 0) { error_log(__LINE__ . ' first', 0); } } } elseif (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { echo '<tr>'; echo Display::tag('td', $answerMatching[$answerId]); echo Display::tag('td', "{$user_answer} / " . Display::tag('strong', $answerMatching[$answerCorrect], ['style' => 'color: #008000; font-weight: bold;'])); echo '</tr>'; } } } else { if ($debug) { error_log('Showing questions $from ' . $from); } switch ($answerType) { case UNIQUE_ANSWER: case UNIQUE_ANSWER_IMAGE: case UNIQUE_ANSWER_NO_OPTION: case MULTIPLE_ANSWER: case GLOBAL_MULTIPLE_ANSWER: case MULTIPLE_ANSWER_COMBINATION: if ($answerId == 1) { ExerciseShowFunctions::display_unique_or_multiple_answer($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $exeId, $questionId, $answerId, $results_disabled); } else { ExerciseShowFunctions::display_unique_or_multiple_answer($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $exeId, $questionId, "", $results_disabled); } break; case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE: if ($answerId == 1) { ExerciseShowFunctions::display_multiple_answer_combination_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $exeId, $questionId, $answerId, $results_disabled); } else { ExerciseShowFunctions::display_multiple_answer_combination_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $exeId, $questionId, "", $results_disabled); } break; case MULTIPLE_ANSWER_TRUE_FALSE: if ($answerId == 1) { ExerciseShowFunctions::display_multiple_answer_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $exeId, $questionId, $answerId, $results_disabled); } else { ExerciseShowFunctions::display_multiple_answer_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $exeId, $questionId, "", $results_disabled); } break; case FILL_IN_BLANKS: ExerciseShowFunctions::display_fill_in_blanks_answer($feedback_type, $answer, $exeId, $questionId, $results_disabled, $str); break; case CALCULATED_ANSWER: ExerciseShowFunctions::display_calculated_answer($feedback_type, $answer, $exeId, $questionId); break; case FREE_ANSWER: echo ExerciseShowFunctions::display_free_answer($feedback_type, $choice, $exeId, $questionId, $questionScore); break; case ORAL_EXPRESSION: echo '<tr> <td valign="top">' . ExerciseShowFunctions::display_oral_expression_answer($feedback_type, $choice, $exeId, $questionId, $nano) . '</td> </tr> </table>'; break; case HOT_SPOT: ExerciseShowFunctions::display_hotspot_answer($feedback_type, $answerId, $answer, $studentChoice, $answerComment, $results_disabled); break; case HOT_SPOT_DELINEATION: $user_answer = $user_array; if ($next) { //$tbl_track_e_hotspot = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT); // Save into db /* $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 * ) 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)."')"; $result = Database::query($sql,__FILE__,__LINE__); */ $user_answer = $user_array; // we compare only the delineation not the other points $answer_question = $_SESSION['hotspot_coord'][1]; $answerDestination = $_SESSION['hotspot_dest'][1]; //calculating the area $poly_user = convert_coordinates($user_answer, '/'); $poly_answer = convert_coordinates($answer_question, '|'); $max_coord = poly_get_max($poly_user, $poly_answer); $poly_user_compiled = poly_compile($poly_user, $max_coord); $poly_answer_compiled = poly_compile($poly_answer, $max_coord); $poly_results = poly_result($poly_answer_compiled, $poly_user_compiled, $max_coord); $overlap = $poly_results['both']; $poly_answer_area = $poly_results['s1']; $poly_user_area = $poly_results['s2']; $missing = $poly_results['s1Only']; $excess = $poly_results['s2Only']; //$overlap = round(polygons_overlap($poly_answer,$poly_user)); //this is an area in pixels if ($debug > 0) { error_log(__LINE__ . ' - Polygons results are ' . print_r($poly_results, 1), 0); } if ($overlap < 1) { //shortcut to avoid complicated calculations $final_overlap = 0; $final_missing = 100; $final_excess = 100; } else { // the final overlap is the percentage of the initial polygon that is overlapped by the user's polygon $final_overlap = round((double) $overlap / (double) $poly_answer_area * 100); if ($debug > 1) { error_log(__LINE__ . ' - Final overlap is ' . $final_overlap, 0); } // the final missing area is the percentage of the initial polygon that is not overlapped by the user's polygon $final_missing = 100 - $final_overlap; if ($debug > 1) { error_log(__LINE__ . ' - Final missing is ' . $final_missing, 0); } // 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 $final_excess = round(((double) $poly_user_area - (double) $overlap) / (double) $poly_answer_area * 100); if ($debug > 1) { error_log(__LINE__ . ' - Final excess is ' . $final_excess, 0); } } //checking the destination parameters parsing the "@@" $destination_items = explode('@@', $answerDestination); $threadhold_total = $destination_items[0]; $threadhold_items = explode(';', $threadhold_total); $threadhold1 = $threadhold_items[0]; // overlap $threadhold2 = $threadhold_items[1]; // excess $threadhold3 = $threadhold_items[2]; //missing // if is delineation if ($answerId === 1) { //setting colors if ($final_overlap >= $threadhold1) { $overlap_color = true; //echo 'a'; } //echo $excess.'-'.$threadhold2; if ($final_excess <= $threadhold2) { $excess_color = true; //echo 'b'; } //echo '--------'.$missing.'-'.$threadhold3; if ($final_missing <= $threadhold3) { $missing_color = true; //echo 'c'; } // if pass if ($final_overlap >= $threadhold1 && $final_missing <= $threadhold3 && $final_excess <= $threadhold2) { $next = 1; //go to the oars $result_comment = get_lang('Acceptable'); $final_answer = 1; // do not update with update_exercise_attempt } else { $next = 0; $result_comment = get_lang('Unacceptable'); $comment = $answerDestination = $objAnswerTmp->selectComment(1); $answerDestination = $objAnswerTmp->selectDestination(1); //checking the destination parameters parsing the "@@" $destination_items = explode('@@', $answerDestination); } } elseif ($answerId > 1) { if ($objAnswerTmp->selectHotspotType($answerId) == 'noerror') { if ($debug > 0) { error_log(__LINE__ . ' - answerId is of type noerror', 0); } //type no error shouldn't be treated $next = 1; continue; } if ($debug > 0) { error_log(__LINE__ . ' - answerId is >1 so we\'re probably in OAR', 0); } //check the intersection between the oar and the user //echo 'user'; print_r($x_user_list); print_r($y_user_list); //echo 'official';print_r($x_list);print_r($y_list); //$result = get_intersection_data($x_list,$y_list,$x_user_list,$y_user_list); $inter = $result['success']; //$delineation_cord=$objAnswerTmp->selectHotspotCoordinates($answerId); $delineation_cord = $objAnswerTmp->selectHotspotCoordinates($answerId); $poly_answer = convert_coordinates($delineation_cord, '|'); $max_coord = poly_get_max($poly_user, $poly_answer); $poly_answer_compiled = poly_compile($poly_answer, $max_coord); $overlap = poly_touch($poly_user_compiled, $poly_answer_compiled, $max_coord); if ($overlap == false) { //all good, no overlap $next = 1; continue; } else { if ($debug > 0) { error_log(__LINE__ . ' - Overlap is ' . $overlap . ': OAR hit', 0); } $organs_at_risk_hit++; //show the feedback $next = 0; $comment = $answerDestination = $objAnswerTmp->selectComment($answerId); $answerDestination = $objAnswerTmp->selectDestination($answerId); $destination_items = explode('@@', $answerDestination); $try_hotspot = $destination_items[1]; $lp_hotspot = $destination_items[2]; $select_question_hotspot = $destination_items[3]; $url_hotspot = $destination_items[4]; } } } else { // the first delineation feedback if ($debug > 0) { error_log(__LINE__ . ' first', 0); } } break; case HOT_SPOT_ORDER: ExerciseShowFunctions::display_hotspot_order_answer($feedback_type, $answerId, $answer, $studentChoice, $answerComment); break; case DRAGGABLE: //no break //no break case MATCHING_DRAGGABLE: //no break //no break case MATCHING: echo '<tr>'; echo Display::tag('td', $answerMatching[$answerId]); echo Display::tag('td', "{$user_answer} / " . Display::tag('strong', $answerMatching[$answerCorrect], ['style' => 'color: #008000; font-weight: bold;'])); echo '</tr>'; break; } } } if ($debug) { error_log(' ------ '); } } // end for that loops over all answers of the current question if ($debug) { error_log('-- end answer loop --'); } $final_answer = true; foreach ($real_answers as $my_answer) { if (!$my_answer) { $final_answer = false; } } //we add the total score after dealing with the answers if ($answerType == MULTIPLE_ANSWER_COMBINATION || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { if ($final_answer) { //getting only the first score where we save the weight of all the question $answerWeighting = $objAnswerTmp->selectWeighting(1); $questionScore += $answerWeighting; $totalScore += $answerWeighting; } } //Fixes multiple answer question in order to be exact //if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { /* if ($answerType == GLOBAL_MULTIPLE_ANSWER) { $diff = @array_diff($answer_correct_array, $real_answers); // All good answers or nothing works like exact $counter = 1; $correct_answer = true; foreach ($real_answers as $my_answer) { if ($debug) error_log(" my_answer: $my_answer answer_correct_array[counter]: ".$answer_correct_array[$counter]); if ($my_answer != $answer_correct_array[$counter]) { $correct_answer = false; break; } $counter++; } if ($debug) error_log(" answer_correct_array: ".print_r($answer_correct_array, 1).""); if ($debug) error_log(" real_answers: ".print_r($real_answers, 1).""); if ($debug) error_log(" correct_answer: ".$correct_answer); if ($correct_answer == false) { $questionScore = 0; } // This makes the result non exact if (!empty($diff)) { $questionScore = 0; } }*/ $extra_data = array('final_overlap' => $final_overlap, 'final_missing' => $final_missing, 'final_excess' => $final_excess, 'overlap_color' => $overlap_color, 'missing_color' => $missing_color, 'excess_color' => $excess_color, 'threadhold1' => $threadhold1, 'threadhold2' => $threadhold2, 'threadhold3' => $threadhold3); if ($from == 'exercise_result') { // if answer is hotspot. To the difference of exercise_show.php, // we use the results from the session (from_db=0) // TODO Change this, because it is wrong to show the user // some results that haven't been stored in the database yet if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER || $answerType == HOT_SPOT_DELINEATION) { if ($debug) { error_log('$from AND this is a hotspot kind of question '); } $my_exe_id = 0; $from_database = 0; if ($answerType == HOT_SPOT_DELINEATION) { if (0) { if ($overlap_color) { $overlap_color = 'green'; } else { $overlap_color = 'red'; } if ($missing_color) { $missing_color = 'green'; } else { $missing_color = 'red'; } if ($excess_color) { $excess_color = 'green'; } else { $excess_color = 'red'; } if (!is_numeric($final_overlap)) { $final_overlap = 0; } if (!is_numeric($final_missing)) { $final_missing = 0; } if (!is_numeric($final_excess)) { $final_excess = 0; } if ($final_overlap > 100) { $final_overlap = 100; } $table_resume = '<table class="data_table"> <tr class="row_odd" > <td></td> <td ><b>' . get_lang('Requirements') . '</b></td> <td><b>' . get_lang('YourAnswer') . '</b></td> </tr> <tr class="row_even"> <td><b>' . get_lang('Overlap') . '</b></td> <td>' . get_lang('Min') . ' ' . $threadhold1 . '</td> <td><div style="color:' . $overlap_color . '">' . ($final_overlap < 0 ? 0 : intval($final_overlap)) . '</div></td> </tr> <tr> <td><b>' . get_lang('Excess') . '</b></td> <td>' . get_lang('Max') . ' ' . $threadhold2 . '</td> <td><div style="color:' . $excess_color . '">' . ($final_excess < 0 ? 0 : intval($final_excess)) . '</div></td> </tr> <tr class="row_even"> <td><b>' . get_lang('Missing') . '</b></td> <td>' . get_lang('Max') . ' ' . $threadhold3 . '</td> <td><div style="color:' . $missing_color . '">' . ($final_missing < 0 ? 0 : intval($final_missing)) . '</div></td> </tr> </table>'; if ($next == 0) { $try = $try_hotspot; $lp = $lp_hotspot; $destinationid = $select_question_hotspot; $url = $url_hotspot; } else { //show if no error //echo 'no error'; $comment = $answerComment = $objAnswerTmp->selectComment($nbrAnswers); $answerDestination = $objAnswerTmp->selectDestination($nbrAnswers); } echo '<h1><div style="color:#333;">' . get_lang('Feedback') . '</div></h1> <p style="text-align:center">'; $message = '<p>' . get_lang('YourDelineation') . '</p>'; $message .= $table_resume; $message .= '<br />' . get_lang('ResultIs') . ' ' . $result_comment . '<br />'; if ($organs_at_risk_hit > 0) { $message .= '<p><b>' . get_lang('OARHit') . '</b></p>'; } $message .= '<p>' . $comment . '</p>'; echo $message; } else { echo $hotspot_delineation_result[0]; //prints message $from_database = 1; // the hotspot_solution.swf needs this variable } //save the score attempts if (1) { //getting the answer 1 or 0 comes from exercise_submit_modal.php $final_answer = $hotspot_delineation_result[1]; if ($final_answer == 0) { $questionScore = 0; } // we always insert the answer_id 1 = delineation Event::saveQuestionAttempt($questionScore, 1, $quesId, $exeId, 0); //in delineation mode, get the answer from $hotspot_delineation_result[1] Event::saveExerciseAttemptHotspot($exeId, $quesId, 1, $hotspot_delineation_result[1], $exerciseResultCoordinates[$quesId]); } else { if ($final_answer == 0) { $questionScore = 0; $answer = 0; Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0); if (is_array($exerciseResultCoordinates[$quesId])) { foreach ($exerciseResultCoordinates[$quesId] as $idx => $val) { Event::saveExerciseAttemptHotspot($exeId, $quesId, $idx, 0, $val); } } } else { Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0); if (is_array($exerciseResultCoordinates[$quesId])) { foreach ($exerciseResultCoordinates[$quesId] as $idx => $val) { Event::saveExerciseAttemptHotspot($exeId, $quesId, $idx, $choice[$idx], $val); } } } } $my_exe_id = $exeId; } } if ($answerType == HOT_SPOT || $answerType == HOT_SPOT_ORDER) { // We made an extra table for the answers if ($show_result) { // if ($origin != 'learnpath') { echo '</table></td></tr>'; echo "\n <tr>\n <td colspan=\"2\">\n <p><em>" . get_lang('HotSpot') . "</em></p>\n <div id=\"hotspot-solution-{$questionId}\"></div>\n\n <script>\n \$(document).on('ready', function () {\n new HotspotQuestion({\n questionId: {$questionId},\n exerciseId: {$exeId},\n selector: '#hotspot-solution-{$questionId}',\n for: 'solution'\n });\n });\n </script>\n </td>\n </tr>\n "; // } } } //if ($origin != 'learnpath') { if ($show_result) { echo '</table>'; } // } } unset($objAnswerTmp); $totalWeighting += $questionWeighting; // Store results directly in the database // For all in one page exercises, the results will be // stored by exercise_results.php (using the session) if ($saved_results) { if ($debug) { error_log("Save question results {$saved_results}"); } if ($debug) { error_log(print_r($choice, 1)); } if (empty($choice)) { $choice = 0; } if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { if ($choice != 0) { $reply = array_keys($choice); for ($i = 0; $i < sizeof($reply); $i++) { $ans = $reply[$i]; Event::saveQuestionAttempt($questionScore, $ans . ':' . $choice[$ans], $quesId, $exeId, $i, $this->id); if ($debug) { error_log('result =>' . $questionScore . ' ' . $ans . ':' . $choice[$ans]); } } } else { Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id); } } elseif ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { if ($choice != 0) { $reply = array_keys($choice); if ($debug) { error_log("reply " . print_r($reply, 1) . ""); } for ($i = 0; $i < sizeof($reply); $i++) { $ans = $reply[$i]; Event::saveQuestionAttempt($questionScore, $ans, $quesId, $exeId, $i, $this->id); } } else { Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id); } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { if ($choice != 0) { $reply = array_keys($choice); for ($i = 0; $i < sizeof($reply); $i++) { $ans = $reply[$i]; Event::saveQuestionAttempt($questionScore, $ans, $quesId, $exeId, $i, $this->id); } } else { Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id); } } elseif (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) { if (isset($matching)) { foreach ($matching as $j => $val) { Event::saveQuestionAttempt($questionScore, $val, $quesId, $exeId, $j, $this->id); } } } elseif ($answerType == FREE_ANSWER) { $answer = $choice; Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id); } elseif ($answerType == ORAL_EXPRESSION) { $answer = $choice; Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id, $nano); } elseif (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION])) { $answer = $choice; Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id); // } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { } elseif ($answerType == HOT_SPOT) { $answer = []; if (isset($exerciseResultCoordinates[$questionId]) && !empty($exerciseResultCoordinates[$questionId])) { Database::delete(Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT), ['hotspot_exe_id = ? AND hotspot_question_id = ? AND c_id = ?' => [$exeId, $questionId, api_get_course_int_id()]]); foreach ($exerciseResultCoordinates[$questionId] as $idx => $val) { $answer[] = $val; Event::saveExerciseAttemptHotspot($exeId, $quesId, $idx, $choice[$idx], $val, false, $this->id); } } Event::saveQuestionAttempt($questionScore, implode('|', $answer), $quesId, $exeId, 0, $this->id); } else { Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id); } } if ($propagate_neg == 0 && $questionScore < 0) { $questionScore = 0; } if ($saved_results) { $stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); $sql = 'UPDATE ' . $stat_table . ' SET exe_result = exe_result + ' . floatval($questionScore) . ' WHERE exe_id = ' . $exeId; if ($debug) { error_log($sql); } Database::query($sql); } $return_array = array('score' => $questionScore, 'weight' => $questionWeighting, 'extra' => $extra_data, 'open_question' => $arrques, 'open_answer' => $arrans, 'answer_type' => $answerType); return $return_array; }
/** * Restore quiz-questions * @params int question id */ function restore_quiz_question($id) { $resources = $this->course->resources; $question = isset($resources[RESOURCE_QUIZQUESTION][$id]) ? $resources[RESOURCE_QUIZQUESTION][$id] : null; $new_id = 0; if (is_object($question)) { if ($question->is_restored()) { return $question->destination_id; } $table_que = Database::get_course_table(TABLE_QUIZ_QUESTION); $table_ans = Database::get_course_table(TABLE_QUIZ_ANSWER); $table_options = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION); // check resources inside html from ckeditor tool and copy correct urls into recipient course $question->description = DocumentManager::replace_urls_inside_content_html_from_copy_course($question->description, $this->course->code, $this->course->destination_path); $parent_id = 0; if (isset($question->parent_info) && !empty($question->parent_info)) { $question_obj = Question::readByTitle($question->parent_info['question'], $this->destination_course_id); if ($question_obj) { // Reuse media. $parent_id = $question_obj->selectId(); } else { // Create media. $question_obj = new MediaQuestion(); $question_obj->updateCourse($this->destination_course_id); $parent_id = $question_obj->saveMedia(array('questionName' => $question->parent_info['question'], 'questionDescription' => $question->parent_info['description'])); } } $sql = "INSERT INTO " . $table_que . " SET\n c_id = " . $this->destination_course_id . " ,\n question = '" . self::DBUTF8escapestring($question->question) . "',\n description = '" . self::DBUTF8escapestring($question->description) . "',\n ponderation = '" . self::DBUTF8escapestring($question->ponderation) . "',\n position = '" . self::DBUTF8escapestring($question->position) . "',\n type='" . self::DBUTF8escapestring($question->quiz_type) . "',\n picture='" . self::DBUTF8escapestring($question->picture) . "',\n level='" . self::DBUTF8escapestring($question->level) . "',\n parent_id = '" . $parent_id . "',\n extra='" . self::DBUTF8escapestring($question->extra) . "'"; Database::query($sql); $new_id = Database::insert_id(); if ($new_id) { if (!empty($question->picture)) { $question_temp = Question::read($new_id, $this->destination_course_info['real_id']); $documentPath = api_get_path(SYS_COURSE_PATH) . $this->destination_course_info['path'] . '/document'; // picture path $picturePath = $documentPath . '/images'; $old_picture = api_get_path(SYS_COURSE_PATH) . $this->course->info['path'] . '/document/images/' . $question->picture; if (file_exists($old_picture)) { $picture_name = 'quiz-' . $new_id . '.jpg'; $result = $question_temp->uploadPicture($old_picture, $picture_name, $picturePath); if ($result) { $sql = "UPDATE {$table_que} SET picture = '{$picture_name}' WHERE c_id = " . $this->destination_course_id . " AND id = {$new_id} "; Database::query($sql); } } } } if ($question->type == MATCHING) { $temp = array(); $matching_list = array(); $matching_to_update = array(); foreach ($question->answers as $index => $answer) { $temp[$answer['position']] = $answer; if (!empty($answer['correct'])) { $matching_to_update[$answer['iid']] = $answer['correct']; } } foreach ($temp as $index => $answer) { $sql = "INSERT INTO " . $table_ans . " SET\n c_id = " . $this->destination_course_id . " ,\n question_id = '" . $new_id . "',\n answer = '" . self::DBUTF8escapestring($answer['answer']) . "',\n correct = '" . $answer['correct'] . "',\n comment = '" . self::DBUTF8escapestring($answer['comment']) . "',\n ponderation = '" . $answer['ponderation'] . "',\n position = '" . $answer['position'] . "',\n hotspot_coordinates = '" . $answer['hotspot_coordinates'] . "',\n hotspot_type = '" . $answer['hotspot_type'] . "'"; Database::query($sql); $new_answer_id = Database::insert_id(); $matching_list[$answer['iid']] = $new_answer_id; } foreach ($matching_to_update as $old_answer_id => $old_correct_id) { $new_correct = $matching_list[$old_correct_id]; $new_fixed_id = $matching_list[$old_answer_id]; $sql = "UPDATE {$table_ans} SET correct = '{$new_correct}' WHERE iid = {$new_fixed_id} "; Database::query($sql); } } else { $correct_answers = array(); foreach ($question->answers as $index => $answer) { // check resources inside html from fckeditor tool and copy correct urls into recipient course $answer['answer'] = DocumentManager::replace_urls_inside_content_html_from_copy_course($answer['answer'], $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']); $answer['comment'] = DocumentManager::replace_urls_inside_content_html_from_copy_course($answer['comment'], $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']); $sql = "INSERT INTO " . $table_ans . " SET\n c_id = " . $this->destination_course_id . " ,\n id = '" . ($index + 1) . "',\n question_id = '" . $new_id . "',\n answer = '" . self::DBUTF8escapestring($answer['answer']) . "',\n correct = '" . $answer['correct'] . "',\n comment = '" . self::DBUTF8escapestring($answer['comment']) . "',\n ponderation = '" . $answer['ponderation'] . "',\n position = '" . $answer['position'] . "',\n hotspot_coordinates = '" . $answer['hotspot_coordinates'] . "',\n hotspot_type = '" . $answer['hotspot_type'] . "'"; Database::query($sql); $new_answer_id = Database::insert_id(); $correct_answers[$new_answer_id] = $answer['correct']; } } //Current course id $course_id = api_get_course_int_id(); //Moving quiz_question_options if ($question->type == MULTIPLE_ANSWER_TRUE_FALSE) { $question_option_list = Question::readQuestionOption($id, $course_id); //Question copied from the current platform if ($question_option_list) { $old_option_ids = array(); foreach ($question_option_list as $item) { $old_id = $item['iid']; unset($item['iid']); $item['question_id'] = $new_id; $item['c_id'] = $this->destination_course_id; $question_option_id = Database::insert($table_options, $item); $old_option_ids[$old_id] = $question_option_id; } if ($old_option_ids) { $new_answers = Database::select('iid, correct', $table_ans, array('WHERE' => array('question_id = ? AND c_id = ? ' => array($new_id, $this->destination_course_id)))); foreach ($new_answers as $answer_item) { $params = array(); $params['correct'] = $old_option_ids[$answer_item['correct']]; $question_option_id = Database::update($table_ans, $params, array('iid = ? AND c_id = ? AND question_id = ? ' => array($answer_item['iid'], $this->destination_course_id, $new_id)), false); } } } else { $new_options = array(); if (isset($question->question_options)) { foreach ($question->question_options as $obj) { $item = array(); $item['question_id'] = $new_id; $item['c_id'] = $this->destination_course_id; $item['name'] = $obj->obj->name; $item['position'] = $obj->obj->position; $question_option_id = Database::insert($table_options, $item); $new_options[$obj->obj->iid] = $question_option_id; } foreach ($correct_answers as $answer_id => $correct_answer) { $params = array(); $params['correct'] = $new_options[$correct_answer]; Database::update($table_ans, $params, array('iid = ? AND c_id = ? AND question_id = ? ' => array($answer_id, $this->destination_course_id, $new_id)), false); } } } } if ($question->categories) { $cats = array(); foreach ($question->categories as $cat) { $new_category = new Testcategory($cat['category_id']); $new_category = $new_category->get_category_by_title($cat['title'], $this->destination_course_id); if (empty($new_category)) { //Create a new category in this portal if ($cat['category_id'] == 0) { $category_c_id = 0; } else { $category_c_id = $this->destination_course_id; } $new_cat = new Testcategory(null, $cat['title'], $cat['description'], null, 'simple', $category_c_id); $new_cat_id = $new_cat->addCategoryInBDD(); $cats[] = $new_cat_id; } else { $cats[] = $new_category['iid']; } } $question = Question::read($new_id, $this->destination_course_id); if (!empty($cats)) { $question->saveCategories($cats); } } $this->course->resources[RESOURCE_QUIZQUESTION][$id]->destination_id = $new_id; } return $new_id; }
/** * Shows a question * * @param int $questionId question id * @param bool $only_questions if true only show the questions, no exercise title * @param bool $origin i.e = learnpath * @param string $current_item current item from the list of questions * @param bool $show_title * @param bool $freeze * @param array $user_choice * @param bool $show_comment * @param bool $exercise_feedback * @param bool $show_answers * */ public static function showQuestion($questionId, $only_questions = false, $origin = false, $current_item = '', $show_title = true, $freeze = false, $user_choice = array(), $show_comment = false, $exercise_feedback = null, $show_answers = false) { $course_id = api_get_course_int_id(); // Change false to true in the following line to enable answer hinting $debug_mark_answer = $show_answers; // Reads question information if (!($objQuestionTmp = Question::read($questionId))) { // Question not found return false; } if ($exercise_feedback != EXERCISE_FEEDBACK_TYPE_END) { $show_comment = false; } $answerType = $objQuestionTmp->selectType(); $pictureName = $objQuestionTmp->selectPicture(); $s = ''; if ($answerType != HOT_SPOT && $answerType != HOT_SPOT_DELINEATION) { // Question is not a hotspot if (!$only_questions) { $questionDescription = $objQuestionTmp->selectDescription(); if ($show_title) { TestCategory::displayCategoryAndTitle($objQuestionTmp->id); echo Display::div($current_item . '. ' . $objQuestionTmp->selectTitle(), array('class' => 'question_title')); } if (!empty($questionDescription)) { echo Display::div($questionDescription, array('class' => 'question_description')); } } if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION)) && $freeze) { return ''; } echo '<div class="question_options row">'; // construction of the Answer object (also gets all answers details) $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); $quiz_question_options = Question::readQuestionOption($questionId, $course_id); // For "matching" type here, we need something a little bit special // because the match between the suggestions and the answers cannot be // done easily (suggestions and answers are in the same table), so we // have to go through answers first (elems with "correct" value to 0). $select_items = array(); //This will contain the number of answers on the left side. We call them // suggestions here, for the sake of comprehensions, while the ones // on the right side are called answers $num_suggestions = 0; if (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) { if ($answerType == DRAGGABLE) { $s .= '<div class="col-md-12 ui-widget ui-helper-clearfix"> <div class="clearfix"> <ul class="exercise-draggable-answer ui-helper-reset ui-helper-clearfix">'; } else { $s .= <<<HTML <div id="drag{$questionId}_question" class="drag_question"> <table class="data_table"> HTML; } // Iterate through answers $x = 1; //mark letters for each answer $letter = 'A'; $answer_matching = array(); $cpt1 = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $numAnswer = $objAnswerTmp->selectAutoId($answerId); if ($answerCorrect == 0) { // options (A, B, C, ...) that will be put into the list-box // have the "correct" field set to 0 because they are answer $cpt1[$x] = $letter; $answer_matching[$x] = $objAnswerTmp->selectAnswerByAutoId($numAnswer); $x++; $letter++; } } $i = 1; $select_items[0]['id'] = 0; $select_items[0]['letter'] = '--'; $select_items[0]['answer'] = ''; foreach ($answer_matching as $id => $value) { $select_items[$i]['id'] = $value['id']; $select_items[$i]['letter'] = $cpt1[$id]; $select_items[$i]['answer'] = $value['answer']; $i++; } $user_choice_array_position = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array_position[$item['position']] = $item['answer']; } } $num_suggestions = $nbrAnswers - $x + 1; } elseif ($answerType == FREE_ANSWER) { $fck_content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer'] : null; $form = new FormValidator('free_choice_' . $questionId); $config = array('ToolbarSet' => 'TestFreeAnswer'); $form->addHtmlEditor("choice[" . $questionId . "]", null, false, false, $config); $form->setDefaults(array("choice[" . $questionId . "]" => $fck_content)); $s .= $form->returnForm(); } elseif ($answerType == ORAL_EXPRESSION) { // Add nanog if (api_get_setting('enable_nanogong') == 'true') { //@todo pass this as a parameter global $exercise_stat_info, $exerciseId, $exe_id; if (!empty($exercise_stat_info)) { $params = array('exercise_id' => $exercise_stat_info['exe_exo_id'], 'exe_id' => $exercise_stat_info['exe_id'], 'question_id' => $questionId); } else { $params = array('exercise_id' => $exerciseId, 'exe_id' => 'temp_exe', 'question_id' => $questionId); } $nano = new Nanogong($params); echo $nano->show_button(); } $form = new FormValidator('free_choice_' . $questionId); $config = array('ToolbarSet' => 'TestFreeAnswer'); $form->addHtmlEditor("choice[" . $questionId . "]", null, false, false, $config); //$form->setDefaults(array("choice[" . $questionId . "]" => $fck_content)); $s .= $form->return_form(); } // Now navigate through the possible answers, using the max number of // answers for the question as a limiter $lines_count = 1; // a counter for matching-type answers if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $header = Display::tag('th', get_lang('Options')); foreach ($objQuestionTmp->options as $item) { if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { if (in_array($item, $objQuestionTmp->options)) { $header .= Display::tag('th', get_lang($item)); } else { $header .= Display::tag('th', $item); } } else { $header .= Display::tag('th', $item); } } if ($show_comment) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="table table-hover table-striped">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } if ($show_comment) { if (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION, GLOBAL_MULTIPLE_ANSWER))) { $header = Display::tag('th', get_lang('Options')); if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="table table-hover table-striped">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } } $matching_correct_answer = 0; $user_choice_array = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array[] = $item['answer']; } } for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answer = $objAnswerTmp->selectAnswer($answerId); $answerCorrect = $objAnswerTmp->isCorrect($answerId); $numAnswer = $objAnswerTmp->selectAutoId($answerId); $comment = $objAnswerTmp->selectComment($answerId); $attributes = array(); // Unique answer if (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, UNIQUE_ANSWER_IMAGE])) { $input_id = 'choice-' . $questionId . '-' . $answerId; if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } if ($show_comment) { $s .= '<tr><td>'; } if ($answerType == UNIQUE_ANSWER_IMAGE) { if ($show_comment) { if (empty($comment)) { $s .= '<div id="answer' . $questionId . $numAnswer . '" ' . 'class="exercise-unique-answer-image" style="text-align: center">'; } else { $s .= '<div id="answer' . $questionId . $numAnswer . '" ' . 'class="exercise-unique-answer-image col-xs-6 col-sm-12" style="text-align: center">'; } } else { $s .= '<div id="answer' . $questionId . $numAnswer . '" ' . 'class="exercise-unique-answer-image col-xs-6 col-md-3" style="text-align: center">'; } } $answer = Security::remove_XSS($answer, STUDENT); $s .= Display::input('hidden', 'choice2[' . $questionId . ']', '0'); $answer_input = null; if ($answerType == UNIQUE_ANSWER_IMAGE) { $attributes['style'] = 'display: none;'; $answer = '<div class="thumbnail">' . $answer . '</div>'; } $answer_input .= '<label class="radio">'; $answer_input .= Display::input('radio', 'choice[' . $questionId . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($answerType == UNIQUE_ANSWER_IMAGE) { $answer_input .= "</div>"; } if ($show_comment) { $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == GLOBAL_MULTIPLE_ANSWER) { $input_id = 'choice-' . $questionId . '-' . $answerId; $answer = Security::remove_XSS($answer, STUDENT); if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input = '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr><td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); $my_choice[$item[0]] = $item[1]; } } $s .= '<tr>'; $s .= Display::tag('td', $answer); if (!empty($quiz_question_options)) { foreach ($quiz_question_options as $id => $item) { if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($id == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $id, $attributes), array('style' => '')); } } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { // multiple answers $input_id = 'choice-' . $questionId . '-' . $answerId; if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $answer = Security::remove_XSS($answer, STUDENT); $answer_input = '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input .= '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', 1, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr>'; $s .= '<td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); if (isset($item[1]) && isset($item[0])) { $my_choice[$item[0]] = $item[1]; } } } $answer = Security::remove_XSS($answer, STUDENT); $s .= '<tr>'; $s .= Display::tag('td', $answer); foreach ($objQuestionTmp->options as $key => $item) { if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($key == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $key, $attributes)); } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } elseif ($answerType == FILL_IN_BLANKS) { // display the question, with field empty, for student to fill it, // or filled to display the answer in the Question preview of the exercice/admin.php page $displayForStudent = true; $listAnswerInformations = FillBlanks::getAnswerInfo($answer); $separatorStartRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorstart']); $separatorEndRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorend']); list($answer) = explode('::', $answer); //Correct answers $correctAnswerList = $listAnswerInformations['tabwords']; //Student's answer $studentAnswerList = array(); if (isset($user_choice[0]['answer'])) { $arrayStudentAnswer = FillBlanks::getAnswerInfo($user_choice[0]['answer'], true); $studentAnswerList = $arrayStudentAnswer['studentanswer']; } // If the question must be shown with the answer (in page exercice/admin.php) for teacher preview // set the student-answer to the correct answer if ($debug_mark_answer) { $studentAnswerList = $correctAnswerList; $displayForStudent = false; } if (!empty($correctAnswerList) && !empty($studentAnswerList)) { $answer = ""; for ($i = 0; $i < count($listAnswerInformations["commonwords"]) - 1; $i++) { // display the common word $answer .= $listAnswerInformations["commonwords"][$i]; // display the blank word $correctItem = $listAnswerInformations["tabwords"][$i]; $correctItemRegexp = $correctItem; // replace / with \/ to allow the preg_replace bellow and all the regexp char $correctItemRegexp = FillBlanks::getRegexpProtected($correctItemRegexp); if (isset($studentAnswerList[$i])) { // If student already started this test and answered this question, // fill the blank with his previous answers // may be "" if student viewed the question, but did not fill the blanks $correctItem = $studentAnswerList[$i]; } $attributes["style"] = "width:" . $listAnswerInformations["tabinputsize"][$i] . "px"; $answer .= FillBlanks::getFillTheBlankHtml($separatorStartRegexp, $separatorEndRegexp, $correctItemRegexp, $questionId, $correctItem, $attributes, $answer, $listAnswerInformations, $displayForStudent, $i); } // display the last common word $answer .= $listAnswerInformations["commonwords"][$i]; } else { // display empty [input] with the right width for student to fill it $separatorStartRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorstart']); $separatorEndRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorend']); $answer = ""; for ($i = 0; $i < count($listAnswerInformations["commonwords"]) - 1; $i++) { // display the common words $answer .= $listAnswerInformations["commonwords"][$i]; // display the blank word $attributes["style"] = "width:" . $listAnswerInformations["tabinputsize"][$i] . "px"; $correctItem = $listAnswerInformations["tabwords"][$i]; $correctItemRegexp = $correctItem; // replace / with \/ to allow the preg_replace bellow and all the regexp char $correctItemRegexp = FillBlanks::getRegexpProtected($correctItemRegexp); $answer .= FillBlanks::getFillTheBlankHtml($separatorStartRegexp, $separatorEndRegexp, $correctItemRegexp, $questionId, '', $attributes, $answer, $listAnswerInformations, $displayForStudent, $i); } // display the last common word $answer .= $listAnswerInformations["commonwords"][$i]; } $s .= $answer; } elseif ($answerType == CALCULATED_ANSWER) { /* * In the CALCULATED_ANSWER test * you mustn't have [ and ] in the textarea * you mustn't have @@ in the textarea * the text to find mustn't be empty or contains only spaces * the text to find mustn't contains HTML tags * the text to find mustn't contains char " */ if ($origin !== null) { global $exe_id; $trackAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); $sqlTrackAttempt = 'SELECT answer FROM ' . $trackAttempts . ' WHERE exe_id=' . $exe_id . ' AND question_id=' . $questionId; $rsLastAttempt = Database::query($sqlTrackAttempt); $rowLastAttempt = Database::fetch_array($rsLastAttempt); $answer = $rowLastAttempt['answer']; if (empty($answer)) { $_SESSION['calculatedAnswerId'][$questionId] = mt_rand(1, $nbrAnswers); $answer = $objAnswerTmp->selectAnswer($_SESSION['calculatedAnswerId'][$questionId]); } } list($answer) = explode('@@', $answer); // $correctAnswerList array of array with correct anwsers 0=> [0=>[\p] 1=>[plop]] api_preg_match_all('/\\[[^]]+\\]/', $answer, $correctAnswerList); // get student answer to display it if student go back to previous calculated answer question in a test if (isset($user_choice[0]['answer'])) { api_preg_match_all('/\\[[^]]+\\]/', $answer, $studentAnswerList); $studentAnswerListTobecleaned = $studentAnswerList[0]; $studentAnswerList = array(); for ($i = 0; $i < count($studentAnswerListTobecleaned); $i++) { $answerCorrected = $studentAnswerListTobecleaned[$i]; $answerCorrected = api_preg_replace('| / <font color="green"><b>.*$|', '', $answerCorrected); $answerCorrected = api_preg_replace('/^\\[/', '', $answerCorrected); $answerCorrected = api_preg_replace('|^<font color="red"><s>|', '', $answerCorrected); $answerCorrected = api_preg_replace('|</s></font>$|', '', $answerCorrected); $answerCorrected = '[' . $answerCorrected . ']'; $studentAnswerList[] = $answerCorrected; } } // If display preview of answer in test view for exemple, set the student answer to the correct answers if ($debug_mark_answer) { // contain the rights answers surronded with brackets $studentAnswerList = $correctAnswerList[0]; } /* Split the response by bracket tabComments is an array with text surrounding the text to find we add a space before and after the answerQuestion to be sure to have a block of text before and after [xxx] patterns so we have n text to find ([xxx]) and n+1 block of texts before, between and after the text to find */ $tabComments = api_preg_split('/\\[[^]]+\\]/', ' ' . $answer . ' '); if (!empty($correctAnswerList) && !empty($studentAnswerList)) { $answer = ""; $i = 0; foreach ($studentAnswerList as $studentItem) { // remove surronding brackets $studentResponse = api_substr($studentItem, 1, api_strlen($studentItem) - 2); $size = strlen($studentItem); $attributes['class'] = self::detectInputAppropriateClass($size); $answer .= $tabComments[$i] . Display::input('text', "choice[{$questionId}][]", $studentResponse, $attributes); $i++; } $answer .= $tabComments[$i]; } else { // display exercise with empty input fields // every [xxx] are replaced with an empty input field foreach ($correctAnswerList[0] as $item) { $size = strlen($item); $attributes['class'] = self::detectInputAppropriateClass($size); $answer = str_replace($item, Display::input('text', "choice[{$questionId}][]", '', $attributes), $answer); } } if ($origin !== null) { $s = $answer; break; } else { $s .= $answer; } } elseif ($answerType == MATCHING) { // matching type, showing suggestions and answers // TODO: replace $answerId by $numAnswer if ($answerCorrect != 0) { // only show elements to be answered (not the contents of // the select boxes, who are corrrect = 0) $s .= '<tr><td width="45%" valign="top">'; $parsed_answer = $answer; //left part questions $s .= '<p class="indent">' . $lines_count . '. ' . $parsed_answer . '</p></td>'; //middle part (matches selects) $s .= '<td width="10%" valign="top" align="center" > <div class="select-matching"> <select name="choice[' . $questionId . '][' . $numAnswer . ']">'; // fills the list-box foreach ($select_items as $key => $val) { // set $debug_mark_answer to true at function start to // show the correct answer with a suffix '-x' $selected = ''; if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selected = 'selected="selected"'; } } //$user_choice_array_position if (isset($user_choice_array_position[$numAnswer]) && $val['id'] == $user_choice_array_position[$numAnswer]) { $selected = 'selected="selected"'; } $s .= '<option value="' . $val['id'] . '" ' . $selected . '>' . $val['letter'] . '</option>'; } // end foreach() $s .= '</select></div></td><td width="5%" class="separate"> </td>'; $s .= '<td width="40%" valign="top" >'; if (isset($select_items[$lines_count])) { $s .= '<div class="text-right"><p class="indent">' . $select_items[$lines_count]['letter'] . '. ' . $select_items[$lines_count]['answer'] . '</p></div>'; } else { $s .= ' '; } $s .= '</td>'; $s .= '</tr>'; $lines_count++; //if the left side of the "matching" has been completely // shown but the right side still has values to show... if ($lines_count - 1 == $num_suggestions) { // if it remains answers to shown at the right side while (isset($select_items[$lines_count])) { $s .= '<tr> <td colspan="2"></td> <td valign="top">'; $s .= '<b>' . $select_items[$lines_count]['letter'] . '.</b> ' . $select_items[$lines_count]['answer']; $s .= "</td>\n </tr>"; $lines_count++; } // end while() } // end if() $matching_correct_answer++; } } elseif ($answerType == DRAGGABLE) { if ($answerCorrect != 0) { $parsed_answer = $answer; /*$lines_count = ''; $data = $objAnswerTmp->getAnswerByAutoId($numAnswer); $data = $objAnswerTmp->getAnswerByAutoId($data['correct']); $lines_count = $data['answer'];*/ $windowId = $questionId . '_' . $lines_count; $s .= '<li class="touch-items" id="' . $windowId . '">'; $s .= Display::div($parsed_answer, ['id' => "window_{$windowId}", 'class' => "window{$questionId}_question_draggable exercise-draggable-answer-option"]); $selectedValue = 0; $draggableSelectOptions = []; foreach ($select_items as $key => $val) { if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selectedValue = $val['id']; } } if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { $selectedValue = $val['id']; } $draggableSelectOptions[$val['id']] = $val['letter']; } $s .= Display::select("choice[{$questionId}][{$numAnswer}]", $draggableSelectOptions, $selectedValue, ['id' => "window_{$windowId}_select", 'class' => 'select_option', 'style' => 'display: none;'], false); if (!empty($answerCorrect) && !empty($selectedValue)) { $s .= <<<JAVASCRIPT <script> \$(function() { DraggableAnswer.deleteItem( \$('#{$questionId}_{$selectedValue}'), \$('#drop_{$windowId}') ); }); </script> JAVASCRIPT; } if (isset($select_items[$lines_count])) { $s .= Display::div(Display::tag('b', $select_items[$lines_count]['letter']) . $select_items[$lines_count]['answer'], ['id' => "window_{$windowId}_answer", 'style' => 'display: none;']); } else { $s .= ' '; } $lines_count++; if ($lines_count - 1 == $num_suggestions) { while (isset($select_items[$lines_count])) { $s .= Display::tag('b', $select_items[$lines_count]['letter']); $s .= $select_items[$lines_count]['answer']; $lines_count++; } } $matching_correct_answer++; $s .= '</li>'; } } elseif ($answerType == MATCHING_DRAGGABLE) { if ($answerId == 1) { echo $objAnswerTmp->getJs(); } if ($answerCorrect != 0) { $parsed_answer = $answer; $windowId = "{$questionId}_{$lines_count}"; $s .= <<<HTML <tr> <td widht="45%"> <div id="window_{$windowId}" class="window window_left_question window{$questionId}_question"> <strong>{$lines_count}.</strong> {$parsed_answer} </div> </td> <td width="10%"> HTML; $selectedValue = 0; $questionOptions = []; foreach ($select_items as $key => $val) { if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selectedValue = $val['id']; } } if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { $selectedValue = $val['id']; } $questionOptions[$val['id']] = $val['letter']; } $s .= Display::select("choice[{$questionId}][{$numAnswer}]", $questionOptions, $selectedValue, ['id' => "window_{$windowId}_select", 'class' => 'hidden'], false); if (!empty($answerCorrect) && !empty($selectedValue)) { // Show connect if is not freeze (question preview) if (!$freeze) { $s .= <<<JAVASCRIPT <script> jsPlumb.ready(function() { jsPlumb.connect({ source: 'window_{$windowId}', target: 'window_{$questionId}_{$selectedValue}_answer', endpoint: ['Blank', {radius: 15}], anchors: ['RightMiddle', 'LeftMiddle'], paintStyle: {strokeStyle: '#8A8888', lineWidth: 8}, connector: [ MatchingDraggable.connectorType, {curvines: MatchingDraggable.curviness} ] }); }); </script> JAVASCRIPT; } } $s .= <<<HTML </td> <td width="45%"> HTML; if (isset($select_items[$lines_count])) { $s .= <<<HTML <div id="window_{$windowId}_answer" class="window window_right_question"> <strong>{$select_items[$lines_count]['letter']}.</strong> {$select_items[$lines_count]['answer']} </div> HTML; } else { $s .= ' '; } $s .= '</td></tr>'; $lines_count++; if ($lines_count - 1 == $num_suggestions) { while (isset($select_items[$lines_count])) { $s .= <<<HTML <tr> <td colspan="2"></td> <td> <strong>{$select_items[$lines_count]['letter']}</strong> {$select_items[$lines_count]['answer']} </td> </tr> HTML; $lines_count++; } } $matching_correct_answer++; } } } // end for() if ($show_comment) { $s .= '</table>'; } elseif (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER_TRUE_FALSE, MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE])) { $s .= '</table>'; } if ($answerType == DRAGGABLE) { $s .= "</ul></div>"; $counterAnswer = 1; $s .= '<div class="col-md-12"><div class="row">'; for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $windowId = $questionId . '_' . $counterAnswer; if ($answerCorrect) { $s .= Display::div($counterAnswer, ['id' => "drop_{$windowId}", 'class' => 'droppable col-md-2']); $counterAnswer++; } } $s .= '</div></div>'; } if (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { $s .= '</div>'; } $s .= '</div>'; // destruction of the Answer object unset($objAnswerTmp); // destruction of the Question object unset($objQuestionTmp); if ($origin != 'export') { echo $s; } else { return $s; } } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { // Question is a HOT_SPOT //checking document/images visibility if (api_is_platform_admin() || api_is_course_admin()) { $course = api_get_course_info(); $doc_id = DocumentManager::get_document_id($course, '/images/' . $pictureName); if (is_numeric($doc_id)) { $images_folder_visibility = api_get_item_visibility($course, 'document', $doc_id, api_get_session_id()); if (!$images_folder_visibility) { //This message is shown only to the course/platform admin if the image is set to visibility = false Display::display_warning_message(get_lang('ChangeTheVisibilityOfTheCurrentImage')); } } } $questionName = $objQuestionTmp->selectTitle(); $questionDescription = $objQuestionTmp->selectDescription(); if ($freeze) { echo Display::img($objQuestionTmp->selectPicturePath()); return; } // Get the answers, make a list $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); // get answers of hotpost $answers_hotspot = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answers = $objAnswerTmp->selectAnswerByAutoId($objAnswerTmp->selectAutoId($answerId)); $answers_hotspot[$answers['id']] = $objAnswerTmp->selectAnswer($answerId); } // display answers of hotpost order by id $answer_list = '<div style="padding: 10px; margin-left: 0px; border: 1px solid #A4A4A4; height: 408px; width: 200px;"><b>' . get_lang('HotspotZones') . '</b><dl>'; if (!empty($answers_hotspot)) { ksort($answers_hotspot); foreach ($answers_hotspot as $key => $value) { $answer_list .= '<dt>' . $key . '.- ' . $value . '</dt><br />'; } } $answer_list .= '</dl></div>'; if ($answerType == HOT_SPOT_DELINEATION) { $answer_list = ''; $swf_file = 'hotspot_delineation_user'; $swf_height = 405; } else { $swf_file = 'hotspot_user'; $swf_height = 436; } if (!$only_questions) { if ($show_title) { TestCategory::displayCategoryAndTitle($objQuestionTmp->id); echo '<div class="question_title">' . $current_item . '. ' . $questionName . '</div>'; } //@todo I need to the get the feedback type echo '<input type="hidden" name="hidden_hotspot_id" value="' . $questionId . '" />'; echo '<table class="exercise_questions" > <tr> <td valign="top" colspan="2">'; echo $questionDescription; echo '</td></tr>'; } $canClick = isset($_GET['editQuestion']) ? '0' : (isset($_GET['modifyAnswers']) ? '0' : '1'); $s .= '<script type="text/javascript" src="../plugin/hotspot/JavaScriptFlashGateway.js"></script> <script src="../plugin/hotspot/hotspot.js" type="text/javascript" ></script> <script type="text/javascript"> <!-- // Globals // Major version of Flash required var requiredMajorVersion = 7; // Minor version of Flash required var requiredMinorVersion = 0; // Minor version of Flash required var requiredRevision = 0; // the version of javascript supported var jsVersion = 1.0; // --> </script> <script language="VBScript" type="text/vbscript"> <!-- // Visual basic helper required to detect Flash Player ActiveX control version information Function VBGetSwfVer(i) on error resume next Dim swControl, swVersion swVersion = 0 set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i)) if (IsObject(swControl)) then swVersion = swControl.GetVariable("$version") end if VBGetSwfVer = swVersion End Function // --> </script> <script language="JavaScript1.1" type="text/javascript"> <!-- // Detect Client Browser type var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; jsVersion = 1.1; // JavaScript helper required to detect Flash Player PlugIn version information function JSGetSwfVer(i) { // NS/Opera version >= 3 check for Flash plugin in plugin array if (navigator.plugins != null && navigator.plugins.length > 0) { if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; descArray = flashDescription.split(" "); tempArrayMajor = descArray[2].split("."); versionMajor = tempArrayMajor[0]; versionMinor = tempArrayMajor[1]; if ( descArray[3] != "" ) { tempArrayMinor = descArray[3].split("r"); } else { tempArrayMinor = descArray[4].split("r"); } versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0; flashVer = versionMajor + "." + versionMinor + "." + versionRevision; } else { flashVer = -1; } } // MSN/WebTV 2.6 supports Flash 4 else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; // WebTV 2.5 supports Flash 3 else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; // older WebTV supports Flash 2 else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; // Can\'t detect in all other cases else { flashVer = -1; } return flashVer; } // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) { reqVer = parseFloat(reqMajorVer + "." + reqRevision); // loop backwards through the versions until we find the newest version for (i=25;i>0;i--) { if (isIE && isWin && !isOpera) { versionStr = VBGetSwfVer(i); } else { versionStr = JSGetSwfVer(i); } if (versionStr == -1 ) { return false; } else if (versionStr != 0) { if(isIE && isWin && !isOpera) { tempArray = versionStr.split(" "); tempString = tempArray[1]; versionArray = tempString .split(","); } else { versionArray = versionStr.split("."); } versionMajor = versionArray[0]; versionMinor = versionArray[1]; versionRevision = versionArray[2]; versionString = versionMajor + "." + versionRevision; // 7.0r24 == 7.24 versionNum = parseFloat(versionString); // is the major.revision >= requested major.revision AND the minor version >= requested minor if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) { return true; } else { return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false ); } } } } // --> </script>'; $s .= '<tr><td valign="top" colspan="2" width="520"><table><tr><td width="520"> <script> <!-- // Version check based upon the values entered above in "Globals" var hasReqestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision); // Check to see if the version meets the requirements for playback if (hasReqestedVersion) { // if we\'ve detected an acceptable version var oeTags = \'<object type="application/x-shockwave-flash" data="../plugin/hotspot/' . $swf_file . '.swf?modifyAnswers=' . $questionId . '&canClick:' . $canClick . '" width="600" height="' . $swf_height . '">\' + \'<param name="wmode" value="transparent">\' + \'<param name="movie" value="../plugin/hotspot/' . $swf_file . '.swf?modifyAnswers=' . $questionId . '&canClick:' . $canClick . '" />\' + \'<\\/object>\'; document.write(oeTags); // embed the Flash Content SWF when all tests are passed } else { // flash is too old or we can\'t detect the plugin var alternateContent = "Error<br \\/>" + "Hotspots requires Macromedia Flash 7.<br \\/>" + "<a href=\\"http://www.macromedia.com/go/getflash/\\">Get Flash<\\/a>"; document.write(alternateContent); // insert non-flash content } // --> </script> </td> <td valign="top" align="left">' . $answer_list . '</td></tr> </table> </td></tr>'; echo $s; echo '</table>'; } return $nbrAnswers; }
/** * Shows a question * * @param int $questionId question id * @param bool $only_questions if true only show the questions, no exercise title * @param bool $origin i.e = learnpath * @param string $current_item current item from the list of questions * @param bool $show_title * @param bool $freeze * @param array $user_choice * @param bool $show_comment * @param bool $exercise_feedback * @param bool $show_answers * */ public static function showQuestion($questionId, $only_questions = false, $origin = false, $current_item = '', $show_title = true, $freeze = false, $user_choice = array(), $show_comment = false, $exercise_feedback = null, $show_answers = false) { $course_id = api_get_course_int_id(); // Change false to true in the following line to enable answer hinting $debug_mark_answer = $show_answers; // Reads question information if (!($objQuestionTmp = Question::read($questionId))) { // Question not found return false; } if ($exercise_feedback != EXERCISE_FEEDBACK_TYPE_END) { $show_comment = false; } $answerType = $objQuestionTmp->selectType(); $pictureName = $objQuestionTmp->selectPicture(); $s = ''; if ($answerType != HOT_SPOT && $answerType != HOT_SPOT_DELINEATION) { // Question is not a hotspot if (!$only_questions) { $questionDescription = $objQuestionTmp->selectDescription(); if ($show_title) { TestCategory::displayCategoryAndTitle($objQuestionTmp->id); echo Display::div($current_item . '. ' . $objQuestionTmp->selectTitle(), array('class' => 'question_title')); } if (!empty($questionDescription)) { echo Display::div($questionDescription, array('class' => 'question_description')); } } if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION)) && $freeze) { return ''; } echo '<div class="question_options row">'; // construction of the Answer object (also gets all answers details) $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); $quiz_question_options = Question::readQuestionOption($questionId, $course_id); // For "matching" type here, we need something a little bit special // because the match between the suggestions and the answers cannot be // done easily (suggestions and answers are in the same table), so we // have to go through answers first (elems with "correct" value to 0). $select_items = array(); //This will contain the number of answers on the left side. We call them // suggestions here, for the sake of comprehensions, while the ones // on the right side are called answers $num_suggestions = 0; if (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) { if ($answerType == DRAGGABLE) { $s .= '<div class="col-md-12 ui-widget ui-helper-clearfix"> <div class="clearfix"> <ul class="exercise-draggable-answer ui-helper-reset ui-helper-clearfix">'; } else { $s .= '<div id="drag' . $questionId . '_question" class="drag_question"> <table class="data_table">'; } // Iterate through answers $x = 1; //mark letters for each answer $letter = 'A'; $answer_matching = array(); $cpt1 = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $numAnswer = $objAnswerTmp->selectAutoId($answerId); if ($answerCorrect == 0) { // options (A, B, C, ...) that will be put into the list-box // have the "correct" field set to 0 because they are answer $cpt1[$x] = $letter; $answer_matching[$x] = $objAnswerTmp->selectAnswerByAutoId($numAnswer); $x++; $letter++; } } $i = 1; $select_items[0]['id'] = 0; $select_items[0]['letter'] = '--'; $select_items[0]['answer'] = ''; foreach ($answer_matching as $id => $value) { $select_items[$i]['id'] = $value['id_auto']; $select_items[$i]['letter'] = $cpt1[$id]; $select_items[$i]['answer'] = $value['answer']; $i++; } $user_choice_array_position = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array_position[$item['position']] = $item['answer']; } } $num_suggestions = $nbrAnswers - $x + 1; } elseif ($answerType == FREE_ANSWER) { $fck_content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer'] : null; $form = new FormValidator('free_choice_' . $questionId); $config = array('ToolbarSet' => 'TestFreeAnswer'); $form->addHtmlEditor("choice[" . $questionId . "]", null, false, false, $config); $form->setDefaults(array("choice[" . $questionId . "]" => $fck_content)); $s .= $form->returnForm(); } elseif ($answerType == ORAL_EXPRESSION) { // Add nanog if (api_get_setting('document.enable_nanogong') == 'true') { //@todo pass this as a parameter global $exercise_stat_info, $exerciseId, $exe_id; if (!empty($exercise_stat_info)) { $params = array('exercise_id' => $exercise_stat_info['exe_exo_id'], 'exe_id' => $exercise_stat_info['exe_id'], 'question_id' => $questionId); } else { $params = array('exercise_id' => $exerciseId, 'exe_id' => 'temp_exe', 'question_id' => $questionId); } $nano = new Nanogong($params); echo $nano->show_button(); } $form = new FormValidator('free_choice_' . $questionId); $config = array('ToolbarSet' => 'TestFreeAnswer'); $form->addHtmlEditor("choice[" . $questionId . "]", null, false, false, $config); //$form->setDefaults(array("choice[" . $questionId . "]" => $fck_content)); $s .= $form->returnForm(); } // Now navigate through the possible answers, using the max number of // answers for the question as a limiter $lines_count = 1; // a counter for matching-type answers if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $header = Display::tag('th', get_lang('Options')); foreach ($objQuestionTmp->options as $item) { if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { if (in_array($item, $objQuestionTmp->options)) { $header .= Display::tag('th', get_lang($item)); } else { $header .= Display::tag('th', $item); } } else { $header .= Display::tag('th', $item); } } if ($show_comment) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="table table-hover table-striped">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } if ($show_comment) { if (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION, GLOBAL_MULTIPLE_ANSWER))) { $header = Display::tag('th', get_lang('Options')); if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="table table-hover table-striped">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } } $matching_correct_answer = 0; $user_choice_array = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array[] = $item['answer']; } } for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answer = $objAnswerTmp->selectAnswer($answerId); $answerCorrect = $objAnswerTmp->isCorrect($answerId); $numAnswer = $objAnswerTmp->selectAutoId($answerId); $comment = $objAnswerTmp->selectComment($answerId); $attributes = array(); // Unique answer if (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, UNIQUE_ANSWER_IMAGE])) { $input_id = 'choice-' . $questionId . '-' . $answerId; if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } if ($show_comment) { $s .= '<tr><td>'; } if ($answerType == UNIQUE_ANSWER_IMAGE) { if ($show_comment) { if (empty($comment)) { $s .= '<div id="answer' . $questionId . $numAnswer . '" ' . 'class="exercise-unique-answer-image" style="text-align: center">'; } else { $s .= '<div id="answer' . $questionId . $numAnswer . '" ' . 'class="exercise-unique-answer-image col-xs-6 col-sm-12" style="text-align: center">'; } } else { $s .= '<div id="answer' . $questionId . $numAnswer . '" ' . 'class="exercise-unique-answer-image col-xs-6 col-md-3" style="text-align: center">'; } } $answer = Security::remove_XSS($answer, STUDENT); $s .= Display::input('hidden', 'choice2[' . $questionId . ']', '0'); $answer_input = null; if ($answerType == UNIQUE_ANSWER_IMAGE) { $attributes['style'] = 'display: none;'; $answer = '<div class="thumbnail">' . $answer . '</div>'; } $answer_input .= '<label class="radio">'; $answer_input .= Display::input('radio', 'choice[' . $questionId . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($answerType == UNIQUE_ANSWER_IMAGE) { $answer_input .= "</div>"; } if ($show_comment) { $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == GLOBAL_MULTIPLE_ANSWER) { $input_id = 'choice-' . $questionId . '-' . $answerId; $answer = Security::remove_XSS($answer, STUDENT); if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input = '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr><td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); $my_choice[$item[0]] = $item[1]; } } $s .= '<tr>'; $s .= Display::tag('td', $answer); if (!empty($quiz_question_options)) { foreach ($quiz_question_options as $id => $item) { if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($id == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $id, $attributes), array('style' => '')); } } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { // multiple answers $input_id = 'choice-' . $questionId . '-' . $answerId; if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $answer = Security::remove_XSS($answer, STUDENT); $answer_input = '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input .= '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', 1, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr>'; $s .= '<td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); if (isset($item[1]) && isset($item[0])) { $my_choice[$item[0]] = $item[1]; } } } $answer = Security::remove_XSS($answer, STUDENT); $s .= '<tr>'; $s .= Display::tag('td', $answer); foreach ($objQuestionTmp->options as $key => $item) { if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($key == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $key, $attributes)); } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } elseif ($answerType == FILL_IN_BLANKS) { // display the question, with field empty, for student to fill it, // or filled to display the answer in the Question preview of the exercice/admin.php page $displayForStudent = true; $listAnswerInformations = FillBlanks::getAnswerInfo($answer); $separatorStartRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorstart']); $separatorEndRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorend']); list($answer) = explode('::', $answer); //Correct answers $correctAnswerList = $listAnswerInformations['tabwords']; //Student's answer $studentAnswerList = array(); if (isset($user_choice[0]['answer'])) { $arrayStudentAnswer = FillBlanks::getAnswerInfo($user_choice[0]['answer'], true); $studentAnswerList = $arrayStudentAnswer['studentanswer']; } // If the question must be shown with the answer (in page exercice/admin.php) for teacher preview // set the student-answer to the correct answer if ($debug_mark_answer) { $studentAnswerList = $correctAnswerList; $displayForStudent = false; } if (!empty($correctAnswerList) && !empty($studentAnswerList)) { $answer = ""; for ($i = 0; $i < count($listAnswerInformations["commonwords"]) - 1; $i++) { // display the common word $answer .= $listAnswerInformations["commonwords"][$i]; // display the blank word $correctItem = $listAnswerInformations["tabwords"][$i]; $correctItemRegexp = $correctItem; // replace / with \/ to allow the preg_replace bellow and all the regexp char $correctItemRegexp = FillBlanks::getRegexpProtected($correctItemRegexp); if (isset($studentAnswerList[$i])) { // If student already started this test and answered this question, // fill the blank with his previous answers // may be "" if student viewed the question, but did not fill the blanks $correctItem = $studentAnswerList[$i]; } $attributes["style"] = "width:" . $listAnswerInformations["tabinputsize"][$i] . "px"; $answer .= FillBlanks::getFillTheBlankHtml($separatorStartRegexp, $separatorEndRegexp, $correctItemRegexp, $questionId, $correctItem, $attributes, $answer, $listAnswerInformations, $displayForStudent, $i); } // display the last common word $answer .= $listAnswerInformations["commonwords"][$i]; } else { // display empty [input] with the right width for student to fill it $separatorStartRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorstart']); $separatorEndRegexp = FillBlanks::escapeForRegexp($listAnswerInformations['blankseparatorend']); $answer = ""; for ($i = 0; $i < count($listAnswerInformations["commonwords"]) - 1; $i++) { // display the common words $answer .= $listAnswerInformations["commonwords"][$i]; // display the blank word $attributes["style"] = "width:" . $listAnswerInformations["tabinputsize"][$i] . "px"; $correctItem = $listAnswerInformations["tabwords"][$i]; $correctItemRegexp = $correctItem; // replace / with \/ to allow the preg_replace bellow and all the regexp char $correctItemRegexp = FillBlanks::getRegexpProtected($correctItemRegexp); $answer .= FillBlanks::getFillTheBlankHtml($separatorStartRegexp, $separatorEndRegexp, $correctItemRegexp, $questionId, '', $attributes, $answer, $listAnswerInformations, $displayForStudent, $i); } // display the last common word $answer .= $listAnswerInformations["commonwords"][$i]; } $s .= $answer; } elseif ($answerType == CALCULATED_ANSWER) { /* * In the CALCULATED_ANSWER test * you mustn't have [ and ] in the textarea * you mustn't have @@ in the textarea * the text to find mustn't be empty or contains only spaces * the text to find mustn't contains HTML tags * the text to find mustn't contains char " */ if ($origin !== null) { global $exe_id; $trackAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); $sql = 'SELECT answer FROM ' . $trackAttempts . ' WHERE exe_id=' . $exe_id . ' AND question_id=' . $questionId; $rsLastAttempt = Database::query($sql); $rowLastAttempt = Database::fetch_array($rsLastAttempt); $answer = $rowLastAttempt['answer']; if (empty($answer)) { $calculatedAnswerId = []; $calculatedAnswerId[$questionId] = mt_rand(1, $nbrAnswers); $answer = $objAnswerTmp->selectAnswer($calculatedAnswerId[$questionId]); Session::write('calculatedAnswerId', $calculatedAnswerId); } } list($answer) = explode('@@', $answer); // $correctAnswerList array of array with correct anwsers 0=> [0=>[\p] 1=>[plop]] api_preg_match_all('/\\[[^]]+\\]/', $answer, $correctAnswerList); // get student answer to display it if student go back to previous calculated answer question in a test if (isset($user_choice[0]['answer'])) { api_preg_match_all('/\\[[^]]+\\]/', $answer, $studentAnswerList); $studentAnswerListTobecleaned = $studentAnswerList[0]; $studentAnswerList = array(); for ($i = 0; $i < count($studentAnswerListTobecleaned); $i++) { $answerCorrected = $studentAnswerListTobecleaned[$i]; $answerCorrected = api_preg_replace('| / <font color="green"><b>.*$|', '', $answerCorrected); $answerCorrected = api_preg_replace('/^\\[/', '', $answerCorrected); $answerCorrected = api_preg_replace('|^<font color="red"><s>|', '', $answerCorrected); $answerCorrected = api_preg_replace('|</s></font>$|', '', $answerCorrected); $answerCorrected = '[' . $answerCorrected . ']'; $studentAnswerList[] = $answerCorrected; } } // If display preview of answer in test view for exemple, set the student answer to the correct answers if ($debug_mark_answer) { // contain the rights answers surronded with brackets $studentAnswerList = $correctAnswerList[0]; } /* Split the response by bracket tabComments is an array with text surrounding the text to find we add a space before and after the answerQuestion to be sure to have a block of text before and after [xxx] patterns so we have n text to find ([xxx]) and n+1 block of texts before, between and after the text to find */ $tabComments = api_preg_split('/\\[[^]]+\\]/', ' ' . $answer . ' '); if (!empty($correctAnswerList) && !empty($studentAnswerList)) { $answer = ""; $i = 0; foreach ($studentAnswerList as $studentItem) { // remove surronding brackets $studentResponse = api_substr($studentItem, 1, api_strlen($studentItem) - 2); $size = strlen($studentItem); $attributes['class'] = self::detectInputAppropriateClass($size); $answer .= $tabComments[$i] . Display::input('text', "choice[{$questionId}][]", $studentResponse, $attributes); $i++; } $answer .= $tabComments[$i]; } else { // display exercise with empty input fields // every [xxx] are replaced with an empty input field foreach ($correctAnswerList[0] as $item) { $size = strlen($item); $attributes['class'] = self::detectInputAppropriateClass($size); $answer = str_replace($item, Display::input('text', "choice[{$questionId}][]", '', $attributes), $answer); } } if ($origin !== null) { $s = $answer; break; } else { $s .= $answer; } } elseif ($answerType == MATCHING) { // matching type, showing suggestions and answers // TODO: replace $answerId by $numAnswer if ($answerCorrect != 0) { // only show elements to be answered (not the contents of // the select boxes, who are corrrect = 0) $s .= '<tr><td width="45%" valign="top">'; $parsed_answer = $answer; //left part questions $s .= '<p class="indent">' . $lines_count . '. ' . $parsed_answer . '</p></td>'; //middle part (matches selects) $s .= '<td width="10%" valign="top" align="center" > <div class="select-matching"> <select name="choice[' . $questionId . '][' . $numAnswer . ']">'; // fills the list-box foreach ($select_items as $key => $val) { // set $debug_mark_answer to true at function start to // show the correct answer with a suffix '-x' $selected = ''; if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selected = 'selected="selected"'; } } //$user_choice_array_position if (isset($user_choice_array_position[$numAnswer]) && $val['id'] == $user_choice_array_position[$numAnswer]) { $selected = 'selected="selected"'; } $s .= '<option value="' . $val['id'] . '" ' . $selected . '>' . $val['letter'] . '</option>'; } // end foreach() $s .= '</select></div></td><td width="5%" class="separate"> </td>'; $s .= '<td width="40%" valign="top" >'; if (isset($select_items[$lines_count])) { $s .= '<div class="text-right"><p class="indent">' . $select_items[$lines_count]['letter'] . '. ' . $select_items[$lines_count]['answer'] . '</p></div>'; } else { $s .= ' '; } $s .= '</td>'; $s .= '</tr>'; $lines_count++; //if the left side of the "matching" has been completely // shown but the right side still has values to show... if ($lines_count - 1 == $num_suggestions) { // if it remains answers to shown at the right side while (isset($select_items[$lines_count])) { $s .= '<tr> <td colspan="2"></td> <td valign="top">'; $s .= '<b>' . $select_items[$lines_count]['letter'] . '.</b> ' . $select_items[$lines_count]['answer']; $s .= "</td>\n </tr>"; $lines_count++; } // end while() } // end if() $matching_correct_answer++; } } elseif ($answerType == DRAGGABLE) { if ($answerCorrect != 0) { $parsed_answer = $answer; /*$lines_count = ''; $data = $objAnswerTmp->getAnswerByAutoId($numAnswer); $data = $objAnswerTmp->getAnswerByAutoId($data['correct']); $lines_count = $data['answer'];*/ $windowId = $questionId . '_' . $lines_count; $s .= '<li class="touch-items" id="' . $windowId . '">'; $s .= Display::div($parsed_answer, ['id' => "window_{$windowId}", 'class' => "window{$questionId}_question_draggable exercise-draggable-answer-option"]); $selectedValue = 0; $draggableSelectOptions = []; foreach ($select_items as $key => $val) { if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selectedValue = $val['id']; } } if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { $selectedValue = $val['id']; } $draggableSelectOptions[$val['id']] = $val['letter']; } $s .= Display::select("choice[{$questionId}][{$numAnswer}]", $draggableSelectOptions, $selectedValue, ['id' => "window_{$windowId}_select", 'class' => 'select_option', 'style' => 'display: none;'], false); if (!empty($answerCorrect) && !empty($selectedValue)) { $s .= <<<JAVASCRIPT <script> \$(function() { DraggableAnswer.deleteItem( \$('#{$questionId}_{$selectedValue}'), \$('#drop_{$windowId}') ); }); </script> JAVASCRIPT; } if (isset($select_items[$lines_count])) { $s .= Display::div(Display::tag('b', $select_items[$lines_count]['letter']) . $select_items[$lines_count]['answer'], ['id' => "window_{$windowId}_answer", 'style' => 'display: none;']); } else { $s .= ' '; } $lines_count++; if ($lines_count - 1 == $num_suggestions) { while (isset($select_items[$lines_count])) { $s .= Display::tag('b', $select_items[$lines_count]['letter']); $s .= $select_items[$lines_count]['answer']; $lines_count++; } } $matching_correct_answer++; $s .= '</li>'; } } elseif ($answerType == MATCHING_DRAGGABLE) { if ($answerId == 1) { echo $objAnswerTmp->getJs(); } if ($answerCorrect != 0) { $parsed_answer = $answer; $windowId = "{$questionId}_{$lines_count}"; $s .= <<<HTML <tr> <td widht="45%"> <div id="window_{$windowId}" class="window window_left_question window{$questionId}_question"> <strong>{$lines_count}.</strong> {$parsed_answer} </div> </td> <td width="10%"> HTML; $selectedValue = 0; $selectedPosition = 0; $questionOptions = []; $iTempt = 0; foreach ($select_items as $key => $val) { if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selectedValue = $val['id']; $selectedPosition = $iTempt; } } if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { $selectedValue = $val['id']; $selectedPosition = $iTempt; } $questionOptions[$val['id']] = $val['letter']; $iTempt++; } $s .= Display::select("choice[{$questionId}][{$numAnswer}]", $questionOptions, $selectedValue, ['id' => "window_{$windowId}_select", 'class' => 'hidden'], false); if (!empty($answerCorrect) && !empty($selectedValue)) { // Show connect if is not freeze (question preview) if (!$freeze) { $s .= <<<JAVASCRIPT <script> \$(document).on('ready', function () { jsPlumb.ready(function() { jsPlumb.connect({ source: 'window_{$windowId}', target: 'window_{$questionId}_{$selectedPosition}_answer', endpoint: ['Blank', {radius: 15}], anchors: ['RightMiddle', 'LeftMiddle'], paintStyle: {strokeStyle: '#8A8888', lineWidth: 8}, connector: [ MatchingDraggable.connectorType, {curvines: MatchingDraggable.curviness} ] }); }); }); </script> JAVASCRIPT; } } $s .= <<<HTML </td> <td width="45%"> HTML; if (isset($select_items[$lines_count])) { $s .= <<<HTML <div id="window_{$windowId}_answer" class="window window_right_question"> <strong>{$select_items[$lines_count]['letter']}.</strong> {$select_items[$lines_count]['answer']} </div> HTML; } else { $s .= ' '; } $s .= '</td></tr>'; $lines_count++; if ($lines_count - 1 == $num_suggestions) { while (isset($select_items[$lines_count])) { $s .= <<<HTML <tr> <td colspan="2"></td> <td> <strong>{$select_items[$lines_count]['letter']}</strong> {$select_items[$lines_count]['answer']} </td> </tr> HTML; $lines_count++; } } $matching_correct_answer++; } } } // end for() if ($show_comment) { $s .= '</table>'; } elseif (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE, UNIQUE_ANSWER_NO_OPTION, MULTIPLE_ANSWER_TRUE_FALSE, MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE])) { $s .= '</table>'; } if ($answerType == DRAGGABLE) { $s .= "</ul>"; $s .= "</div>"; //clearfix $counterAnswer = 1; $s .= '<div class="col-md-12"><div class="row">'; for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $windowId = $questionId . '_' . $counterAnswer; if ($answerCorrect) { $s .= Display::div($counterAnswer, ['id' => "drop_{$windowId}", 'class' => 'droppable col-md-2']); $counterAnswer++; } } $s .= '</div>'; // row $s .= '</div>'; // col-md-12 $s .= '</div>'; // col-md-12 ui-widget ui-helper-clearfix } if (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) { $s .= '</div>'; //drag_question } $s .= '</div>'; //question_options row // destruction of the Answer object unset($objAnswerTmp); // destruction of the Question object unset($objQuestionTmp); if ($origin != 'export') { echo $s; } else { return $s; } } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { global $exerciseId, $exe_id; // Question is a HOT_SPOT //checking document/images visibility if (api_is_platform_admin() || api_is_course_admin()) { $course = api_get_course_info(); $doc_id = DocumentManager::get_document_id($course, '/images/' . $pictureName); if (is_numeric($doc_id)) { $images_folder_visibility = api_get_item_visibility($course, 'document', $doc_id, api_get_session_id()); if (!$images_folder_visibility) { //This message is shown only to the course/platform admin if the image is set to visibility = false Display::display_warning_message(get_lang('ChangeTheVisibilityOfTheCurrentImage')); } } } $questionName = $objQuestionTmp->selectTitle(); $questionDescription = $objQuestionTmp->selectDescription(); if ($freeze) { echo "\n <script>\n \$(document).on('ready', function () {\n new " . ($answerType == HOT_SPOT ? "HotspotQuestion" : "DelineationQuestion") . "({\n questionId: {$questionId},\n exerciseId: {$exerciseId},\n selector: '#hotspot-preview-{$questionId}',\n for: 'preview'\n });\n });\n </script>\n <div id=\"hotspot-preview-{$questionId}\"></div>\n "; return; } // Get the answers, make a list $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); // get answers of hotpost $answers_hotspot = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answers = $objAnswerTmp->selectAnswerByAutoId($objAnswerTmp->selectAutoId($answerId)); $answers_hotspot[$answers['id']] = $objAnswerTmp->selectAnswer($answerId); } $answerList = ''; if ($answerType != HOT_SPOT_DELINEATION) { $answerList = ' <div class="well well-sm"> <h5 class="page-header">' . get_lang('HotspotZones') . '</h5> <ol> '; if (!empty($answers_hotspot)) { ksort($answers_hotspot); $countAnswers = 1; foreach ($answers_hotspot as $value) { $answerList .= "<li><p>{$countAnswers} - {$value}</p></li>"; $countAnswers++; } } $answerList .= ' </ol> </div> '; } if (!$only_questions) { if ($show_title) { TestCategory::displayCategoryAndTitle($objQuestionTmp->id); echo '<div class="question_title">' . $current_item . '. ' . $questionName . '</div>'; } //@todo I need to the get the feedback type echo <<<HOTSPOT <input type="hidden" name="hidden_hotspot_id" value="{$questionId}" /> <div class="exercise_questions"> {$questionDescription} <div class="row"> HOTSPOT; } $canClick = isset($_GET['editQuestion']) ? '0' : (isset($_GET['modifyAnswers']) ? '0' : '1'); $s .= "\n <div class=\"col-sm-8 col-md-9\">\n <div class=\"hotspot-image\"></div>\n <script>\n \$(document).on('ready', function () {\n new " . ($answerType == HOT_SPOT_DELINEATION ? 'DelineationQuestion' : 'HotspotQuestion') . "({\n questionId: {$questionId},\n exerciseId: {$exe_id},\n selector: '#question_div_' + {$questionId} + ' .hotspot-image',\n for: 'user'\n });\n });\n </script>\n </div>\n <div class=\"col-sm-4 col-md-3\">\n {$answerList}\n </div>\n "; echo <<<HOTSPOT {$s} </div> </div> HOTSPOT; } return $nbrAnswers; }
/** * Duplicates answers by copying them into another question * * @author Olivier Brouckaert * @param int question id * @param array destination course info (result of the function api_get_course_info() ) */ public function duplicate($newQuestionId, $course_info = null) { if (empty($course_info)) { $course_info = $this->course; } $TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER); $fixed_list = array(); if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE || self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE) { // Selecting origin options $origin_options = Question::readQuestionOption($this->selectQuestionId(), $this->course['real_id']); if (!empty($origin_options)) { foreach ($origin_options as $item) { $new_option_list[] = $item['id']; } } $destination_options = Question::readQuestionOption($newQuestionId, $course_info['real_id']); $i = 0; if (!empty($destination_options)) { foreach ($destination_options as $item) { $fixed_list[$new_option_list[$i]] = $item['id']; $i++; } } } // if at least one answer if ($this->nbrAnswers) { // inserts new answers into data base $c_id = $course_info['real_id']; for ($i = 1; $i <= $this->nbrAnswers; $i++) { if ($this->course['id'] != $course_info['id']) { $this->answer[$i] = DocumentManager::replace_urls_inside_content_html_from_copy_course($this->answer[$i], $this->course['id'], $course_info['id']); $this->comment[$i] = DocumentManager::replace_urls_inside_content_html_from_copy_course($this->comment[$i], $this->course['id'], $course_info['id']); } $answer = $this->answer[$i]; $correct = $this->correct[$i]; if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE || self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE) { $correct = $fixed_list[intval($correct)]; } $comment = $this->comment[$i]; $weighting = $this->weighting[$i]; $position = $this->position[$i]; $hotspot_coordinates = $this->hotspot_coordinates[$i]; $hotspot_type = $this->hotspot_type[$i]; $destination = $this->destination[$i]; $params = ['c_id' => $c_id, 'question_id' => $newQuestionId, 'answer' => $answer, 'correct' => $correct, 'comment' => $comment, 'ponderation' => $weighting, 'position' => $position, 'hotspot_coordinates' => $hotspot_coordinates, 'hotspot_type' => $hotspot_type, 'destination' => $destination]; $id = Database::insert($TBL_REPONSES, $params); if ($id) { $sql = "UPDATE {$TBL_REPONSES} SET id = id_auto WHERE id_auto = {$id}"; Database::query($sql); } } } }
/** * Shows a question * * @param int $questionId question id * @param bool $only_questions if true only show the questions, no exercise title * @param bool $origin i.e = learnpath * @param string $current_item current item from the list of questions * @param bool $show_title * @param bool $freeze * @param array $user_choice * @param bool $show_comment * @param bool $exercise_feedback * @param bool $show_answers * */ function showQuestion($questionId, $only_questions = false, $origin = false, $current_item = '', $show_title = true, $freeze = false, $user_choice = array(), $show_comment = false, $exercise_feedback = null, $show_answers = false) { // Text direction for the current language $is_ltr_text_direction = api_get_text_direction() != 'rtl'; // Change false to true in the following line to enable answer hinting $debug_mark_answer = $show_answers; //api_is_allowed_to_edit() && false; // Reads question information if (!($objQuestionTmp = Question::read($questionId))) { // Question not found return false; } if ($exercise_feedback != EXERCISE_FEEDBACK_TYPE_END) { $show_comment = false; } $answerType = $objQuestionTmp->selectType(); $pictureName = $objQuestionTmp->selectPicture(); $s = ''; if ($answerType != HOT_SPOT && $answerType != HOT_SPOT_DELINEATION) { // Question is not a hotspot if (!$only_questions) { $questionDescription = $objQuestionTmp->selectDescription(); if ($show_title) { Testcategory::displayCategoryAndTitle($objQuestionTmp->id); echo Display::div($current_item . '. ' . $objQuestionTmp->selectTitle(), array('class' => 'question_title')); } if (!empty($questionDescription)) { echo Display::div($questionDescription, array('class' => 'question_description')); } } if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION)) && $freeze) { return ''; } echo '<div class="question_options">'; // construction of the Answer object (also gets all answers details) $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); $course_id = api_get_course_int_id(); $quiz_question_options = Question::readQuestionOption($questionId, $course_id); // For "matching" type here, we need something a little bit special // because the match between the suggestions and the answers cannot be // done easily (suggestions and answers are in the same table), so we // have to go through answers first (elems with "correct" value to 0). $select_items = array(); //This will contain the number of answers on the left side. We call them // suggestions here, for the sake of comprehensions, while the ones // on the right side are called answers $num_suggestions = 0; if ($answerType == MATCHING) { $s .= '<table class="data_table">'; // Iterate through answers $x = 1; //mark letters for each answer $letter = 'A'; $answer_matching = array(); $cpt1 = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $numAnswer = $objAnswerTmp->selectAutoId($answerId); $answer = $objAnswerTmp->selectAnswer($answerId); if ($answerCorrect == 0) { // options (A, B, C, ...) that will be put into the list-box // have the "correct" field set to 0 because they are answer $cpt1[$x] = $letter; $answer_matching[$x] = $objAnswerTmp->selectAnswerByAutoId($numAnswer); $x++; $letter++; } } $i = 1; $select_items[0]['id'] = 0; $select_items[0]['letter'] = '--'; $select_items[0]['answer'] = ''; foreach ($answer_matching as $id => $value) { $select_items[$i]['id'] = $value['id']; $select_items[$i]['letter'] = $cpt1[$id]; $select_items[$i]['answer'] = $value['answer']; $i++; } $user_choice_array_position = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array_position[$item['position']] = $item['answer']; } } $num_suggestions = $nbrAnswers - $x + 1; } elseif ($answerType == FREE_ANSWER) { $fck_content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer'] : null; $oFCKeditor = new FCKeditor("choice[" . $questionId . "]"); $oFCKeditor->ToolbarSet = 'TestFreeAnswer'; $oFCKeditor->Width = '100%'; $oFCKeditor->Height = '200'; $oFCKeditor->Value = $fck_content; $s .= $oFCKeditor->CreateHtml(); } elseif ($answerType == ORAL_EXPRESSION) { //Add nanog if (api_get_setting('enable_nanogong') == 'true') { require_once api_get_path(LIBRARY_PATH) . 'nanogong.lib.php'; //@todo pass this as a parameter global $exercise_stat_info, $exerciseId, $exe_id; if (!empty($exercise_stat_info)) { $params = array('exercise_id' => $exercise_stat_info['exe_exo_id'], 'exe_id' => $exercise_stat_info['exe_id'], 'question_id' => $questionId); } else { $params = array('exercise_id' => $exerciseId, 'exe_id' => 'temp_exe', 'question_id' => $questionId); } $nano = new Nanogong($params); echo $nano->show_button(); } $oFCKeditor = new FCKeditor("choice[" . $questionId . "]"); $oFCKeditor->ToolbarSet = 'TestFreeAnswer'; $oFCKeditor->Width = '100%'; $oFCKeditor->Height = '150'; $oFCKeditor->ToolbarStartExpanded = false; $oFCKeditor->Value = ''; $s .= $oFCKeditor->CreateHtml(); } // Now navigate through the possible answers, using the max number of // answers for the question as a limiter $lines_count = 1; // a counter for matching-type answers if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $header = Display::tag('th', get_lang('Options')); foreach ($objQuestionTmp->options as $item) { if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { if (in_array($item, $objQuestionTmp->options)) { $header .= Display::tag('th', get_lang($item)); } else { $header .= Display::tag('th', $item); } } else { $header .= Display::tag('th', $item); } } if ($show_comment) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="data_table">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } if ($show_comment) { if (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, GLOBAL_MULTIPLE_ANSWER))) { $header = Display::tag('th', get_lang('Options')); if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="data_table">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } } $matching_correct_answer = 0; $user_choice_array = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array[] = $item['answer']; } } for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answer = $objAnswerTmp->selectAnswer($answerId); $answerCorrect = $objAnswerTmp->isCorrect($answerId); $numAnswer = $objAnswerTmp->selectAutoId($answerId); $comment = $objAnswerTmp->selectComment($answerId); $attributes = array(); // Unique answer if ($answerType == UNIQUE_ANSWER || $answerType == UNIQUE_ANSWER_NO_OPTION) { $input_id = 'choice-' . $questionId . '-' . $answerId; if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $answer = Security::remove_XSS($answer, STUDENT); $s .= Display::input('hidden', 'choice2[' . $questionId . ']', '0'); $answer_input = '<label class="radio">'; $answer_input .= Display::input('radio', 'choice[' . $questionId . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr><td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == GLOBAL_MULTIPLE_ANSWER) { $input_id = 'choice-' . $questionId . '-' . $answerId; $answer = Security::remove_XSS($answer, STUDENT); if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input = '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr><td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); $my_choice[$item[0]] = $item[1]; } } $s .= '<tr>'; $s .= Display::tag('td', $answer); if (!empty($quiz_question_options)) { foreach ($quiz_question_options as $id => $item) { if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($id == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $id, $attributes), array('style' => '')); } } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { // multiple answers $input_id = 'choice-' . $questionId . '-' . $answerId; if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $answer = Security::remove_XSS($answer, STUDENT); $answer_input = '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input .= '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', 1, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr>'; $s .= '<td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); $my_choice[$item[0]] = $item[1]; } } $answer = Security::remove_XSS($answer, STUDENT); $s .= '<tr>'; $s .= Display::tag('td', $answer); foreach ($objQuestionTmp->options as $key => $item) { if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($key == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $key, $attributes)); } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } elseif ($answerType == FILL_IN_BLANKS) { /* * In the FILL_IN_BLANKS test * you mustn't have [ and ] in the textarea * you mustn't have :: in the textarea * the text to find mustn't be empty or contains only spaces * the text to find mustn't contains HTML tags * the text to find mustn't contains char " */ list($answer) = explode('::', $answer); // $correct_answer_list array of array with correct anwsers 0=> [0=>[\p] 1=>[plop]] api_preg_match_all('/\\[[^]]+\\]/', $answer, $correct_answer_list); // get student answer to display it if student go back to previous fillBlank answer question in a test if (isset($user_choice[0]['answer'])) { api_preg_match_all('/\\[[^]]+\\]/', $user_choice[0]['answer'], $student_answer_list); $student_answer_list_tobecleaned = $student_answer_list[0]; $student_answer_list = array(); // here we got the student answer in a test // let's clean up the results /* Array ( [0] => Array ( [0] => [<font color="red"><s>yer</s></font> / <font color="green"><b>ici</b></font>] [1] => [<font color="red"><s>plop</s></font> / <font color="green"><b>/p</b></font>] ) ) */ for ($i = 0; $i < count($student_answer_list_tobecleaned); $i++) { $answer_corrected = $student_answer_list_tobecleaned[$i]; /* * we got if student answer is wrong * [<font color="red"><s>rrr</s></font> / <font color="green"><b>/p</b></font>] * or if student answer is good * [plop / <font color="green"><b>plop</b></font>] * or if student didn't answer [] */ $answer_corrected = api_preg_replace('| / <font color="green"><b>.*$|', '', $answer_corrected); /* * we got [<font color="red"><s>rrr</s></font> or [plop or [ */ $answer_corrected = api_preg_replace('/^\\[/', '', $answer_corrected); /* * we got <font color="red"><s>rrr</s></font> or plop * non breakable spaces from /main/exercice/exercise.class.php have been removed l 2391 and l 2370 */ $answer_corrected = api_preg_replace('|^<font color="red"><s>|', '', $answer_corrected); $answer_corrected = api_preg_replace('|</s></font>$|', '', $answer_corrected); $answer_corrected = '[' . $answer_corrected . ']'; /* * we got [rrr] or [plop] or [] */ $student_answer_list[] = $answer_corrected; } } // If display preview of answer in test view for exemple, set the student answer to the correct answers if ($debug_mark_answer) { // contain the rights answers surronded with brackets $student_answer_list = $correct_answer_list[0]; } /* Split the response by bracket tab_comments is an array with text surrounding the text to find we add a space before and after the answer_question to be sure to have a block of text before and after [xxx] patterns so we have n text to find ([xxx]) and n+1 block of texts before, between and after the text to find */ $tab_comments = api_preg_split('/\\[[^]]+\\]/', ' ' . $answer . ' '); if (!empty($correct_answer_list) && !empty($student_answer_list)) { $answer = ""; $i = 0; foreach ($student_answer_list as $student_item) { // remove surronding brackets $student_response = api_substr($student_item, 1, api_strlen($student_item) - 2); $size = strlen($student_item); $attributes['class'] = detectInputAppropriateClass($size); $answer .= $tab_comments[$i] . Display::input('text', "choice[{$questionId}][]", $student_response, $attributes); $i++; } $answer .= $tab_comments[$i]; } else { // display exercise with empty input fields // every [xxx] are replaced with an empty input field foreach ($correct_answer_list[0] as $item) { $size = strlen($item); $attributes['class'] = detectInputAppropriateClass($size); $answer = str_replace($item, Display::input('text', "choice[{$questionId}][]", '', $attributes), $answer); } /*$answer = api_preg_replace( '/\[[^]]+\]/', Display::input( 'text', "choice[$questionId][]", '', $attributes ), $answer);*/ } $s .= $answer; } elseif ($answerType == CALCULATED_ANSWER) { /* * In the CALCULATED_ANSWER test * you mustn't have [ and ] in the textarea * you mustn't have @@ in the textarea * the text to find mustn't be empty or contains only spaces * the text to find mustn't contains HTML tags * the text to find mustn't contains char " */ if ($origin !== null) { global $exe_id; $trackAttempts = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); $sqlTrackAttempt = 'SELECT answer FROM ' . $trackAttempts . ' WHERE exe_id=' . $exe_id . ' AND question_id=' . $questionId; $rsLastAttempt = Database::query($sqlTrackAttempt); $rowLastAttempt = Database::fetch_array($rsLastAttempt); $answer = $rowLastAttempt['answer']; if (empty($answer)) { $_SESSION['calculatedAnswerId'][$questionId] = mt_rand(1, $nbrAnswers); $answer = $objAnswerTmp->selectAnswer($_SESSION['calculatedAnswerId'][$questionId]); } } list($answer) = explode('@@', $answer); // $correctAnswerList array of array with correct anwsers 0=> [0=>[\p] 1=>[plop]] api_preg_match_all('/\\[[^]]+\\]/', $answer, $correctAnswerList); // get student answer to display it if student go back to previous calculated answer question in a test if (isset($user_choice[0]['answer'])) { api_preg_match_all('/\\[[^]]+\\]/', $answer, $studentAnswerList); $studentAnswerListTobecleaned = $studentAnswerList[0]; $studentAnswerList = array(); for ($i = 0; $i < count($studentAnswerListTobecleaned); $i++) { $answerCorrected = $studentAnswerListTobecleaned[$i]; $answerCorrected = api_preg_replace('| / <font color="green"><b>.*$|', '', $answerCorrected); $answerCorrected = api_preg_replace('/^\\[/', '', $answerCorrected); $answerCorrected = api_preg_replace('|^<font color="red"><s>|', '', $answerCorrected); $answerCorrected = api_preg_replace('|</s></font>$|', '', $answerCorrected); $answerCorrected = '[' . $answerCorrected . ']'; $studentAnswerList[] = $answerCorrected; } } // If display preview of answer in test view for exemple, set the student answer to the correct answers if ($debug_mark_answer) { // contain the rights answers surronded with brackets $studentAnswerList = $correctAnswerList[0]; } /* Split the response by bracket tabComments is an array with text surrounding the text to find we add a space before and after the answerQuestion to be sure to have a block of text before and after [xxx] patterns so we have n text to find ([xxx]) and n+1 block of texts before, between and after the text to find */ $tabComments = api_preg_split('/\\[[^]]+\\]/', ' ' . $answer . ' '); if (!empty($correctAnswerList) && !empty($studentAnswerList)) { $answer = ""; $i = 0; foreach ($studentAnswerList as $studentItem) { // remove surronding brackets $studentResponse = api_substr($studentItem, 1, api_strlen($studentItem) - 2); $size = strlen($studentItem); $attributes['class'] = detectInputAppropriateClass($size); $answer .= $tabComments[$i] . Display::input('text', "choice[{$questionId}][]", $studentResponse, $attributes); $i++; } $answer .= $tabComments[$i]; } else { // display exercise with empty input fields // every [xxx] are replaced with an empty input field foreach ($correctAnswerList[0] as $item) { $size = strlen($item); $attributes['class'] = detectInputAppropriateClass($size); $answer = str_replace($item, Display::input('text', "choice[{$questionId}][]", '', $attributes), $answer); } } if ($origin !== null) { $s = $answer; break; } else { $s .= $answer; } } elseif ($answerType == MATCHING) { // matching type, showing suggestions and answers // TODO: replace $answerId by $numAnswer if ($answerCorrect != 0) { // only show elements to be answered (not the contents of // the select boxes, who are corrrect = 0) $s .= '<tr><td width="45%" valign="top">'; $parsed_answer = $answer; //left part questions $s .= ' <span style="float:left; width:8%;"><b>' . $lines_count . '</b>. </span> <span style="float:left; width:92%;">' . $parsed_answer . '</span></td>'; //middle part (matches selects) $s .= '<td width="10%" valign="top" align="center"> <select name="choice[' . $questionId . '][' . $numAnswer . ']">'; // fills the list-box foreach ($select_items as $key => $val) { // set $debug_mark_answer to true at function start to // show the correct answer with a suffix '-x' $selected = ''; if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selected = 'selected="selected"'; } } //$user_choice_array_position if (isset($user_choice_array_position[$numAnswer]) && $val['id'] == $user_choice_array_position[$numAnswer]) { $selected = 'selected="selected"'; } /*if (isset($user_choice_array[$matching_correct_answer]) && $val['id'] == $user_choice_array[$matching_correct_answer]['answer']) { $selected = 'selected="selected"'; }*/ $s .= '<option value="' . $val['id'] . '" ' . $selected . '>' . $val['letter'] . '</option>'; } // end foreach() $s .= '</select></td>'; $s .= '<td width="45%" valign="top" >'; if (isset($select_items[$lines_count])) { $s .= '<span style="float:left; width:5%;"><b>' . $select_items[$lines_count]['letter'] . '.</b></span>' . '<span style="float:left; width:95%;">' . $select_items[$lines_count]['answer'] . '</span>'; } else { $s .= ' '; } $s .= '</td>'; $s .= '</tr>'; $lines_count++; //if the left side of the "matching" has been completely // shown but the right side still has values to show... if ($lines_count - 1 == $num_suggestions) { // if it remains answers to shown at the right side while (isset($select_items[$lines_count])) { $s .= '<tr> <td colspan="2"></td> <td valign="top">'; $s .= '<b>' . $select_items[$lines_count]['letter'] . '.</b> ' . $select_items[$lines_count]['answer']; $s .= "</td>\n \t\t\t\t\t\t</tr>"; $lines_count++; } // end while() } // end if() $matching_correct_answer++; } } } // end for() if ($show_comment) { $s .= '</table>'; } else { if ($answerType == MATCHING || $answerType == UNIQUE_ANSWER_NO_OPTION || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $s .= '</table>'; } } $s .= '</div>'; // destruction of the Answer object unset($objAnswerTmp); // destruction of the Question object unset($objQuestionTmp); if ($origin != 'export') { echo $s; } else { return $s; } } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { // Question is a HOT_SPOT //checking document/images visibility if (api_is_platform_admin() || api_is_course_admin()) { require_once api_get_path(LIBRARY_PATH) . 'document.lib.php'; $course = api_get_course_info(); $doc_id = DocumentManager::get_document_id($course, '/images/' . $pictureName); if (is_numeric($doc_id)) { $images_folder_visibility = api_get_item_visibility($course, 'document', $doc_id, api_get_session_id()); if (!$images_folder_visibility) { //This message is shown only to the course/platform admin if the image is set to visibility = false Display::display_warning_message(get_lang('ChangeTheVisibilityOfTheCurrentImage')); } } } $questionName = $objQuestionTmp->selectTitle(); $questionDescription = $objQuestionTmp->selectDescription(); if ($freeze) { echo Display::img($objQuestionTmp->selectPicturePath()); return; } // Get the answers, make a list $objAnswerTmp = new Answer($questionId); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); // get answers of hotpost $answers_hotspot = array(); for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) { $answers = $objAnswerTmp->selectAnswerByAutoId($objAnswerTmp->selectAutoId($answerId)); $answers_hotspot[$answers['id']] = $objAnswerTmp->selectAnswer($answerId); } // display answers of hotpost order by id $answer_list = '<div style="padding: 10px; margin-left: 0px; border: 1px solid #A4A4A4; height: 408px; width: 200px;"><b>' . get_lang('HotspotZones') . '</b><dl>'; if (!empty($answers_hotspot)) { ksort($answers_hotspot); foreach ($answers_hotspot as $key => $value) { $answer_list .= '<dt>' . $key . '.- ' . $value . '</dt><br />'; } } $answer_list .= '</dl></div>'; if ($answerType == HOT_SPOT_DELINEATION) { $answer_list = ''; $swf_file = 'hotspot_delineation_user'; $swf_height = 405; } else { $swf_file = 'hotspot_user'; $swf_height = 436; } if (!$only_questions) { if ($show_title) { Testcategory::displayCategoryAndTitle($objQuestionTmp->id); echo '<div class="question_title">' . $current_item . '. ' . $questionName . '</div>'; } //@todo I need to the get the feedback type echo '<input type="hidden" name="hidden_hotspot_id" value="' . $questionId . '" />'; echo '<table class="exercise_questions" > <tr> <td valign="top" colspan="2">'; echo $questionDescription; echo '</td></tr>'; } $canClick = isset($_GET['editQuestion']) ? '0' : (isset($_GET['modifyAnswers']) ? '0' : '1'); $s .= '<script type="text/javascript" src="../plugin/hotspot/JavaScriptFlashGateway.js"></script> <script src="../plugin/hotspot/hotspot.js" type="text/javascript" ></script> <script type="text/javascript"> <!-- // Globals // Major version of Flash required var requiredMajorVersion = 7; // Minor version of Flash required var requiredMinorVersion = 0; // Minor version of Flash required var requiredRevision = 0; // the version of javascript supported var jsVersion = 1.0; // --> </script> <script language="VBScript" type="text/vbscript"> <!-- // Visual basic helper required to detect Flash Player ActiveX control version information Function VBGetSwfVer(i) on error resume next Dim swControl, swVersion swVersion = 0 set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i)) if (IsObject(swControl)) then swVersion = swControl.GetVariable("$version") end if VBGetSwfVer = swVersion End Function // --> </script> <script language="JavaScript1.1" type="text/javascript"> <!-- // Detect Client Browser type var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; jsVersion = 1.1; // JavaScript helper required to detect Flash Player PlugIn version information function JSGetSwfVer(i) { // NS/Opera version >= 3 check for Flash plugin in plugin array if (navigator.plugins != null && navigator.plugins.length > 0) { if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; descArray = flashDescription.split(" "); tempArrayMajor = descArray[2].split("."); versionMajor = tempArrayMajor[0]; versionMinor = tempArrayMajor[1]; if ( descArray[3] != "" ) { tempArrayMinor = descArray[3].split("r"); } else { tempArrayMinor = descArray[4].split("r"); } versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0; flashVer = versionMajor + "." + versionMinor + "." + versionRevision; } else { flashVer = -1; } } // MSN/WebTV 2.6 supports Flash 4 else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; // WebTV 2.5 supports Flash 3 else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; // older WebTV supports Flash 2 else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; // Can\'t detect in all other cases else { flashVer = -1; } return flashVer; } // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) { reqVer = parseFloat(reqMajorVer + "." + reqRevision); // loop backwards through the versions until we find the newest version for (i=25;i>0;i--) { if (isIE && isWin && !isOpera) { versionStr = VBGetSwfVer(i); } else { versionStr = JSGetSwfVer(i); } if (versionStr == -1 ) { return false; } else if (versionStr != 0) { if(isIE && isWin && !isOpera) { tempArray = versionStr.split(" "); tempString = tempArray[1]; versionArray = tempString .split(","); } else { versionArray = versionStr.split("."); } versionMajor = versionArray[0]; versionMinor = versionArray[1]; versionRevision = versionArray[2]; versionString = versionMajor + "." + versionRevision; // 7.0r24 == 7.24 versionNum = parseFloat(versionString); // is the major.revision >= requested major.revision AND the minor version >= requested minor if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) { return true; } else { return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false ); } } } } // --> </script>'; $s .= '<tr><td valign="top" colspan="2" width="520"><table><tr><td width="520"> <script> <!-- // Version check based upon the values entered above in "Globals" var hasReqestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision); // Check to see if the version meets the requirements for playback if (hasReqestedVersion) { // if we\'ve detected an acceptable version var oeTags = \'<object type="application/x-shockwave-flash" data="../plugin/hotspot/' . $swf_file . '.swf?modifyAnswers=' . $questionId . '&canClick:' . $canClick . '" width="600" height="' . $swf_height . '">\' + \'<param name="wmode" value="transparent">\' + \'<param name="movie" value="../plugin/hotspot/' . $swf_file . '.swf?modifyAnswers=' . $questionId . '&canClick:' . $canClick . '" />\' + \'<\\/object>\'; document.write(oeTags); // embed the Flash Content SWF when all tests are passed } else { // flash is too old or we can\'t detect the plugin var alternateContent = "Error<br \\/>" + "Hotspots requires Macromedia Flash 7.<br \\/>" + "<a href=\\"http://www.macromedia.com/go/getflash/\\">Get Flash<\\/a>"; document.write(alternateContent); // insert non-flash content } // --> </script> </td> <td valign="top" align="left">' . $answer_list . '</td></tr> </table> </td></tr>'; echo $s; echo '</table>'; } return $nbrAnswers; }
/** * Shows a question * @param Question $objQuestionTmp * @param bool $only_questions if true only show the questions, no exercise title * @param bool $origin origin i.e = learnpath * @param string $current_item current item from the list of questions * @param bool $show_title * @param bool $freeze * @param array $user_choice * @param bool $show_comment * @param null $exercise_feedback * @param bool $show_answers * @param null $modelType * @param bool $categoryMinusOne * @return bool|null|string */ public function showQuestion(Question $objQuestionTmp, $only_questions = false, $origin = false, $current_item = '', $show_title = true, $freeze = false, $user_choice = array(), $show_comment = false, $exercise_feedback = null, $show_answers = false, $modelType = null, $categoryMinusOne = true) { // Text direction for the current language //$is_ltr_text_direction = api_get_text_direction() != 'rtl'; // Change false to true in the following line to enable answer hinting $debug_mark_answer = $show_answers; //api_is_allowed_to_edit() && false; // Reads question information if (!$objQuestionTmp) { // Question not found return false; } $html = null; $questionId = $objQuestionTmp->id; if ($exercise_feedback != EXERCISE_FEEDBACK_TYPE_END) { $show_comment = false; } $answerType = $objQuestionTmp->selectType(); $pictureName = $objQuestionTmp->selectPicture(); $s = null; $form = new FormValidator('question'); $renderer = $form->defaultRenderer(); $form_template = '{content}'; $renderer->setFormTemplate($form_template); if ($answerType != HOT_SPOT && $answerType != HOT_SPOT_DELINEATION) { // Question is not a hotspot if (!$only_questions) { $questionDescription = $objQuestionTmp->selectDescription(); if ($show_title) { $categoryName = TestCategory::getCategoryNamesForQuestion($objQuestionTmp->id, null, true, $categoryMinusOne); $html .= $categoryName; $html .= Display::div($current_item . '. ' . $objQuestionTmp->selectTitle(), array('class' => 'question_title')); if (!empty($questionDescription)) { $html .= Display::div($questionDescription, array('class' => 'question_description')); } } else { $html .= '<div class="media">'; $html .= '<div class="pull-left">'; $html .= '<div class="media-object">'; $html .= Display::div($current_item, array('class' => 'question_no_title')); $html .= '</div>'; $html .= '</div>'; $html .= '<div class="media-body">'; if (!empty($questionDescription)) { $html .= Display::div($questionDescription, array('class' => 'question_description')); } $html .= '</div>'; $html .= '</div>'; } } if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION)) && $freeze) { return null; } $html .= '<div class="question_options">'; // construction of the Answer object (also gets all answers details) $objAnswerTmp = new Answer($questionId, null, $this); $nbrAnswers = $objAnswerTmp->selectNbrAnswers(); $course_id = api_get_course_int_id(); $quiz_question_options = Question::readQuestionOption($questionId, $course_id); // For "matching" type here, we need something a little bit special // because the match between the suggestions and the answers cannot be // done easily (suggestions and answers are in the same table), so we // have to go through answers first (elems with "correct" value to 0). $select_items = array(); //This will contain the number of answers on the left side. We call them // suggestions here, for the sake of comprehensions, while the ones // on the right side are called answers $num_suggestions = 0; if ($answerType == MATCHING || $answerType == DRAGGABLE) { if ($answerType == DRAGGABLE) { $s .= '<div class="ui-widget ui-helper-clearfix"> <ul class="drag_question ui-helper-reset ui-helper-clearfix">'; } else { $s .= '<div id="drag' . $questionId . '_question" class="drag_question">'; $s .= '<table class="data_table">'; } $j = 1; //iterate through answers $letter = 'A'; //mark letters for each answer $answer_matching = array(); $capital_letter = array(); //for ($answerId=1; $answerId <= $nbrAnswers; $answerId++) { foreach ($objAnswerTmp->answer as $answerId => $answer_item) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $answer = $objAnswerTmp->selectAnswer($answerId); if ($answerCorrect == 0) { // options (A, B, C, ...) that will be put into the list-box // have the "correct" field set to 0 because they are answer $capital_letter[$j] = $letter; //$answer_matching[$j]=$objAnswerTmp->selectAnswerByAutoId($numAnswer); $answer_matching[$j] = array('id' => $answerId, 'answer' => $answer); $j++; $letter++; } } $i = 1; $select_items[0]['id'] = 0; $select_items[0]['letter'] = '--'; $select_items[0]['answer'] = ''; foreach ($answer_matching as $id => $value) { $select_items[$i]['id'] = $value['id']; $select_items[$i]['letter'] = $capital_letter[$id]; $select_items[$i]['answer'] = $value['answer']; $i++; } $num_suggestions = $nbrAnswers - $j + 1; } elseif ($answerType == FREE_ANSWER) { $content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer'] : null; $toolBar = 'TestFreeAnswer'; if ($modelType == EXERCISE_MODEL_TYPE_COMMITTEE) { $toolBar = 'TestFreeAnswerStrict'; } $form->addElement('html_editor', "choice[" . $questionId . "]", null, array('id' => "choice[" . $questionId . "]"), array('ToolbarSet' => $toolBar)); $form->setDefaults(array("choice[" . $questionId . "]" => $content)); $s .= $form->return_form(); } elseif ($answerType == ORAL_EXPRESSION) { // Add nanogong if (api_get_setting('document.enable_nanogong') == 'true') { //@todo pass this as a parameter global $exercise_stat_info, $exerciseId; if (!empty($exercise_stat_info)) { $params = array('exercise_id' => $exercise_stat_info['exe_exo_id'], 'exe_id' => $exercise_stat_info['exe_id'], 'question_id' => $questionId); } else { $params = array('exercise_id' => $exerciseId, 'exe_id' => 'temp_exe', 'question_id' => $questionId); } $nano = new Nanogong($params); $s .= $nano->show_button(); } $form->addElement('html_editor', "choice[" . $questionId . "]", null, array('id' => "choice[" . $questionId . "]"), array('ToolbarSet' => 'TestFreeAnswer')); //$form->setDefaults(array("choice[".$questionId."]" => $content)); $s .= $form->return_form(); } // Now navigate through the possible answers, using the max number of // answers for the question as a limiter $lines_count = 1; // a counter for matching-type answers if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $header = Display::tag('th', get_lang('Options')); foreach ($objQuestionTmp->options as $item) { $header .= Display::tag('th', $item); } if ($show_comment) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="data_table">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } if ($show_comment) { if (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_COMBINATION, UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION, GLOBAL_MULTIPLE_ANSWER))) { $header = Display::tag('th', get_lang('Options')); if ($exercise_feedback == EXERCISE_FEEDBACK_TYPE_END) { $header .= Display::tag('th', get_lang('Feedback')); } $s .= '<table class="data_table">'; $s .= Display::tag('tr', $header, array('style' => 'text-align:left;')); } } $matching_correct_answer = 0; $user_choice_array = array(); if (!empty($user_choice)) { foreach ($user_choice as $item) { $user_choice_array[] = $item['answer']; } } foreach ($objAnswerTmp->answer as $answerId => $answer_item) { $answer = $objAnswerTmp->selectAnswer($answerId); $answerCorrect = $objAnswerTmp->isCorrect($answerId); $comment = $objAnswerTmp->selectComment($answerId); //$numAnswer = $objAnswerTmp->selectAutoId($answerId); $numAnswer = $answerId; $attributes = array(); // Unique answer if (in_array($answerType, array(UNIQUE_ANSWER, UNIQUE_ANSWER_IMAGE, UNIQUE_ANSWER_NO_OPTION))) { $input_id = 'choice-' . $questionId . '-' . $answerId; if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $answer = Security::remove_XSS($answer); $s .= Display::input('hidden', 'choice2[' . $questionId . ']', '0'); $answer_input = null; if ($answerType == UNIQUE_ANSWER_IMAGE) { $attributes['style'] = 'display:none'; $answer_input .= '<div id="answer' . $questionId . $numAnswer . '" style="float:left" class="highlight_image_default highlight_image">'; } $answer_input .= '<label class="radio">'; $answer_input .= Display::input('radio', 'choice[' . $questionId . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($answerType == UNIQUE_ANSWER_IMAGE) { $answer_input .= "</div>"; } if ($show_comment) { $s .= '<tr><td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif (in_array($answerType, array(MULTIPLE_ANSWER, MULTIPLE_ANSWER_TRUE_FALSE, GLOBAL_MULTIPLE_ANSWER))) { $input_id = 'choice-' . $questionId . '-' . $answerId; $answer = Security::remove_XSS($answer); if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input = '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', $numAnswer, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr><td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) { $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); $my_choice[$item[0]] = $item[1]; } } $s .= '<tr>'; $s .= Display::tag('td', $answer); if (!empty($quiz_question_options)) { foreach ($quiz_question_options as $id => $item) { $id = $item['iid']; if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($id == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $id, $attributes), array('style' => '')); } } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) { // multiple answers $input_id = 'choice-' . $questionId . '-' . $answerId; if (in_array($numAnswer, $user_choice_array)) { $attributes = array('id' => $input_id, 'checked' => 1, 'selected' => 1); } else { $attributes = array('id' => $input_id); } if ($debug_mark_answer) { if ($answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $answer = Security::remove_XSS($answer); $answer_input = '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $answer_input .= '<label class="checkbox">'; $answer_input .= Display::input('checkbox', 'choice[' . $questionId . '][' . $numAnswer . ']', 1, $attributes); $answer_input .= $answer; $answer_input .= '</label>'; if ($show_comment) { $s .= '<tr>'; $s .= '<td>'; $s .= $answer_input; $s .= '</td>'; $s .= '<td>'; $s .= $comment; $s .= '</td>'; $s .= '</tr>'; } else { $s .= $answer_input; } } elseif ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $s .= '<input type="hidden" name="choice2[' . $questionId . ']" value="0" />'; $my_choice = array(); if (!empty($user_choice_array)) { foreach ($user_choice_array as $item) { $item = explode(':', $item); $my_choice[$item[0]] = $item[1]; } } $answer = Security::remove_XSS($answer); $s .= '<tr>'; $s .= Display::tag('td', $answer); foreach ($objQuestionTmp->options as $key => $item) { if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) { $attributes = array('checked' => 1, 'selected' => 1); } else { $attributes = array(); } if ($debug_mark_answer) { if ($key == $answerCorrect) { $attributes['checked'] = 1; $attributes['selected'] = 1; } } $s .= Display::tag('td', Display::input('radio', 'choice[' . $questionId . '][' . $numAnswer . ']', $key, $attributes)); } if ($show_comment) { $s .= '<td>'; $s .= $comment; $s .= '</td>'; } $s .= '</tr>'; } elseif ($answerType == FILL_IN_BLANKS) { list($answer) = explode('::', $answer); //Correct answer api_preg_match_all('/\\[[^]]+\\]/', $answer, $correct_answer_list); //Student's answezr if (isset($user_choice[0]['answer'])) { api_preg_match_all('/\\[[^]]+\\]/', $user_choice[0]['answer'], $student_answer_list); $student_answer_list = $student_answer_list[0]; } //If debug if ($debug_mark_answer) { $student_answer_list = $correct_answer_list[0]; } if (!empty($correct_answer_list) && !empty($student_answer_list)) { $correct_answer_list = $correct_answer_list[0]; $i = 0; foreach ($correct_answer_list as $correct_item) { $value = null; if (isset($student_answer_list[$i]) && !empty($student_answer_list[$i])) { //Cleaning student answer list $value = strip_tags($student_answer_list[$i]); $value = api_substr($value, 1, api_strlen($value) - 2); $value = explode('/', $value); if (!empty($value[0])) { $value = str_replace(' ', '', trim($value[0])); } $correct_item = preg_quote($correct_item); $correct_item = api_preg_replace('|/|', '\\/', $correct_item); // to prevent error if there is a / in the text to find $answer = api_preg_replace('/' . $correct_item . '/', Display::input('text', "choice[{$questionId}][]", $value), $answer, 1); } $i++; } } else { $answer = api_preg_replace('/\\[[^]]+\\]/', Display::input('text', "choice[{$questionId}][]", '', $attributes), $answer); } $s .= $answer; } elseif ($answerType == MATCHING) { // matching type, showing suggestions and answers // TODO: replace $answerId by $numAnswer if ($lines_count == 1) { $s .= $objAnswerTmp->getJs(); } if ($answerCorrect != 0) { // only show elements to be answered (not the contents of // the select boxes, who are correct = 0) $s .= '<tr><td width="45%">'; $parsed_answer = $answer; $windowId = $questionId . '_' . $lines_count; //left part questions $s .= ' <div id="window_' . $windowId . '" class="window window_left_question window' . $questionId . '_question"> <b>' . $lines_count . '</b>. ' . $parsed_answer . ' </div> </td>'; // middle part (matches selects) $s .= '<td width="10%" align="center"> '; $s .= '<div style="display:block">'; $s .= '<select id="window_' . $windowId . '_select" name="choice[' . $questionId . '][' . $numAnswer . ']">'; $selectedValue = 0; // fills the list-box $item = 0; foreach ($select_items as $val) { // set $debug_mark_answer to true at public static function start to // show the correct answer with a suffix '-x' $selected = ''; if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selected = 'selected="selected"'; $selectedValue = $val['id']; } } if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { $selected = 'selected="selected"'; $selectedValue = $val['id']; } //$s .= '<option value="'.$val['id'].'" '.$selected.'>'.$val['letter'].'</option>'; $s .= '<option value="' . $item . '" ' . $selected . '>' . $val['letter'] . '</option>'; $item++; } if (!empty($answerCorrect) && !empty($selectedValue)) { $s .= '<script> jsPlumb.ready(function() { jsPlumb.connect({ source: "window_' . $windowId . '", target: "window_' . $questionId . '_' . $selectedValue . '_answer", endpoint:["Blank", { radius:15 }], anchor:["RightMiddle","LeftMiddle"], paintStyle:{ strokeStyle:"#8a8888" , lineWidth:8 }, connector: [connectorType, { curviness: curvinessValue } ], }) }); </script>'; } $s .= '</select></div></td>'; $s .= '<td width="45%" valign="top" >'; if (isset($select_items[$lines_count])) { $s .= '<div id="window_' . $windowId . '_answer" class="window window_right_question"> <b>' . $select_items[$lines_count]['letter'] . '.</b> ' . $select_items[$lines_count]['answer'] . ' </div>'; } else { $s .= ' '; } $s .= '</td>'; $s .= '</tr>'; $lines_count++; //if the left side of the "matching" has been completely // shown but the right side still has values to show... if ($lines_count - 1 == $num_suggestions) { // if it remains answers to shown at the right side while (isset($select_items[$lines_count])) { $s .= '<tr> <td colspan="2"></td> <td valign="top">'; $s .= '<b>' . $select_items[$lines_count]['letter'] . '.</b>'; $s .= $select_items[$lines_count]['answer']; $s .= "</td>\n </tr>"; $lines_count++; } // end while() } // end if() $matching_correct_answer++; } } elseif ($answerType == DRAGGABLE) { // matching type, showing suggestions and answers // TODO: replace $answerId by $numAnswer if ($answerCorrect != 0) { // only show elements to be answered (not the contents of // the select boxes, who are correct = 0) $s .= '<td>'; $parsed_answer = $answer; $windowId = $questionId . '_' . $numAnswer; //67_293 - 67_294 //left part questions $s .= '<li class="ui-state-default" id="' . $windowId . '">'; $s .= ' <div id="window_' . $windowId . '" class="window' . $questionId . '_question_draggable question_draggable"> ' . $parsed_answer . ' </div>'; $s .= '<div style="display:none">'; $s .= '<select id="window_' . $windowId . '_select" name="choice[' . $questionId . '][' . $numAnswer . ']" class="select_option">'; $selectedValue = 0; // fills the list-box $item = 0; foreach ($select_items as $val) { // set $debug_mark_answer to true at function start to // show the correct answer with a suffix '-x' $selected = ''; if ($debug_mark_answer) { if ($val['id'] == $answerCorrect) { $selected = 'selected="selected"'; $selectedValue = $val['id']; } } if (isset($user_choice[$matching_correct_answer]) && $val['id'] == $user_choice[$matching_correct_answer]['answer']) { $selected = 'selected="selected"'; $selectedValue = $val['id']; } //$s .= '<option value="'.$val['id'].'" '.$selected.'>'.$val['letter'].'</option>'; $s .= '<option value="' . $item . '" ' . $selected . '>' . $val['letter'] . '</option>'; $item++; } $s .= '</select>'; if (!empty($answerCorrect) && !empty($selectedValue)) { $s .= '<script> $(function() { deleteItem($("#' . $questionId . '_' . $selectedValue . '"), $("#drop_' . $windowId . '")); }); </script>'; } if (isset($select_items[$lines_count])) { $s .= '<div id="window_' . $windowId . '_answer" class=""> <b>' . $select_items[$lines_count]['letter'] . '.</b> ' . $select_items[$lines_count]['answer'] . ' </div>'; } else { $s .= ' '; } $lines_count++; //if the left side of the "matching" has been completely // shown but the right side still has values to show... if ($lines_count - 1 == $num_suggestions) { // if it remains answers to shown at the right side while (isset($select_items[$lines_count])) { $s .= '<b>' . $select_items[$lines_count]['letter'] . '.</b>'; $s .= $select_items[$lines_count]['answer']; $lines_count++; } } $s .= '</div>'; $matching_correct_answer++; $s .= '</li>'; } } } // end for() if ($show_comment) { $s .= '</table>'; } else { if ($answerType == MATCHING || $answerType == UNIQUE_ANSWER_NO_OPTION || $answerType == MULTIPLE_ANSWER_TRUE_FALSE || $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) { $s .= '</table>'; } } if ($answerType == DRAGGABLE) { $s .= '</ul><div class="clear"></div>'; $counterAnswer = 1; foreach ($objAnswerTmp->answer as $answerId => $answer_item) { $answerCorrect = $objAnswerTmp->isCorrect($answerId); $windowId = $questionId . '_' . $counterAnswer; if ($answerCorrect == 0) { $s .= '<div id="drop_' . $windowId . '" class="droppable ui-state-default">' . $counterAnswer . '</div>'; $counterAnswer++; } } } if ($answerType == MATCHING) { $s .= '</div>'; } $s .= '</div>'; // destruction of the Answer object unset($objAnswerTmp); // destruction of the Question object unset($objQuestionTmp); $html .= $s; return $html; } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) { // Question is a HOT_SPOT //checking document/images visibility if (api_is_platform_admin() || api_is_course_admin()) { $course = api_get_course_info(); $doc_id = DocumentManager::get_document_id($course, '/images/' . $pictureName); if (is_numeric($doc_id)) { $images_folder_visibility = api_get_item_visibility($course, 'document', $doc_id, api_get_session_id()); if (!$images_folder_visibility) { //This message is shown only to the course/platform admin if the image is set to visibility = false Display::display_warning_message(get_lang('ChangeTheVisibilityOfTheCurrentImage')); } } } $questionName = $objQuestionTmp->selectTitle(); $questionDescription = $objQuestionTmp->selectDescription(); if ($freeze) { $s .= Display::img($objQuestionTmp->selectPicturePath()); $html .= $s; return $html; } // Get the answers, make a list $objAnswerTmp = new Answer($questionId); // get answers of hotpost $answers_hotspot = array(); foreach ($objAnswerTmp->answer as $answerId => $answer_item) { //$answers = $objAnswerTmp->selectAnswerByAutoId($objAnswerTmp->selectAutoId($answerId)); $answers_hotspot[$answerId] = $objAnswerTmp->selectAnswer($answerId); } // display answers of hotpost order by id $answer_list = '<div style="padding: 10px; margin-left: 0px; border: 1px solid #A4A4A4; height: 408px; width: 200px;"><b>' . get_lang('HotspotZones') . '</b><dl>'; if (!empty($answers_hotspot)) { ksort($answers_hotspot); foreach ($answers_hotspot as $key => $value) { $answer_list .= '<dt>' . $key . '.- ' . $value . '</dt><br />'; } } $answer_list .= '</dl></div>'; if ($answerType == HOT_SPOT_DELINEATION) { $answer_list = ''; $swf_file = 'hotspot_delineation_user'; $swf_height = 405; } else { $swf_file = 'hotspot_user'; $swf_height = 436; } if (!$only_questions) { if ($show_title) { $html .= TestCategory::getCategoryNamesForQuestion($objQuestionTmp->id); $html .= '<div class="question_title">' . $current_item . '. ' . $questionName . '</div>'; $html .= $questionDescription; } else { $html .= '<div class="media">'; $html .= '<div class="pull-left">'; $html .= '<div class="media-object">'; $html .= Display::div($current_item . '. ', array('class' => 'question_no_title')); $html .= '</div>'; $html .= '</div>'; $html .= '<div class="media-body">'; if (!empty($questionDescription)) { $html .= Display::div($questionDescription, array('class' => 'question_description')); } $html .= '</div>'; $html .= '</div>'; } //@todo I need to the get the feedback type $html .= '<input type="hidden" name="hidden_hotspot_id" value="' . $questionId . '" />'; $html .= '<table class="exercise_questions"> <tr> <td valign="top" colspan="2">'; $html .= '</td></tr>'; } $canClick = isset($_GET['editQuestion']) ? '0' : (isset($_GET['modifyAnswers']) ? '0' : '1'); $s .= ' <script type="text/javascript" src="../plugin/hotspot/JavaScriptFlashGateway.js"></script> <script src="../plugin/hotspot/hotspot.js" type="text/javascript" ></script> <script type="text/javascript"> <!-- // Globals // Major version of Flash required var requiredMajorVersion = 7; // Minor version of Flash required var requiredMinorVersion = 0; // Minor version of Flash required var requiredRevision = 0; // the version of javascript supported var jsVersion = 1.0; // --> </script> <script language="VBScript" type="text/vbscript"> <!-- // Visual basic helper required to detect Flash Player ActiveX control version information Function VBGetSwfVer(i) on error resume next Dim swControl, swVersion swVersion = 0 set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i)) if (IsObject(swControl)) then swVersion = swControl.GetVariable("$version") end if VBGetSwfVer = swVersion End Function // --> </script> <script language="JavaScript1.1" type="text/javascript"> <!-- // Detect Client Browser type var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; jsVersion = 1.1; // JavaScript helper required to detect Flash Player PlugIn version information function JSGetSwfVer(i) { // NS/Opera version >= 3 check for Flash plugin in plugin array if (navigator.plugins != null && navigator.plugins.length > 0) { if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; descArray = flashDescription.split(" "); tempArrayMajor = descArray[2].split("."); versionMajor = tempArrayMajor[0]; versionMinor = tempArrayMajor[1]; if ( descArray[3] != "" ) { tempArrayMinor = descArray[3].split("r"); } else { tempArrayMinor = descArray[4].split("r"); } versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0; flashVer = versionMajor + "." + versionMinor + "." + versionRevision; } else { flashVer = -1; } } // MSN/WebTV 2.6 supports Flash 4 else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; // WebTV 2.5 supports Flash 3 else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; // older WebTV supports Flash 2 else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; // Can\'t detect in all other cases else { flashVer = -1; } return flashVer; } // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) { reqVer = parseFloat(reqMajorVer + "." + reqRevision); // loop backwards through the versions until we find the newest version for (i=25;i>0;i--) { if (isIE && isWin && !isOpera) { versionStr = VBGetSwfVer(i); } else { versionStr = JSGetSwfVer(i); } if (versionStr == -1 ) { return false; } else if (versionStr != 0) { if(isIE && isWin && !isOpera) { tempArray = versionStr.split(" "); tempString = tempArray[1]; versionArray = tempString .split(","); } else { versionArray = versionStr.split("."); } versionMajor = versionArray[0]; versionMinor = versionArray[1]; versionRevision = versionArray[2]; versionString = versionMajor + "." + versionRevision; // 7.0r24 == 7.24 versionNum = parseFloat(versionString); // is the major.revision >= requested major.revision AND the minor version >= requested minor if ( (versionMajor > reqMajorVer) && (versionNum >= reqVer) ) { return true; } else { return ((versionNum >= reqVer && versionMinor >= reqMinorVer) ? true : false ); } } } } // --> </script>'; $s .= '<tr><td valign="top" colspan="2" width="520"><table><tr><td width="520"> <script> // Version check based upon the values entered above in "Globals" var hasReqestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision); // Check to see if the version meets the requirements for playback if (hasReqestedVersion) { // if we\'ve detected an acceptable version var oeTags = \'<object type="application/x-shockwave-flash" data="../plugin/hotspot/' . $swf_file . '.swf?modifyAnswers=' . $questionId . '&canClick:' . $canClick . '" width="600" height="' . $swf_height . '">\' + \'<param name="wmode" value="transparent">\' + \'<param name="movie" value="../plugin/hotspot/' . $swf_file . '.swf?modifyAnswers=' . $questionId . '&canClick:' . $canClick . '" />\' + \'<\\/object>\'; document.write(oeTags); // embed the Flash Content SWF when all tests are passed } else { // flash is too old or we can\'t detect the plugin var alternateContent = "Error<br \\/>" + "Hotspots requires Macromedia Flash 7.<br \\/>" + "<a href=\\"http://www.macromedia.com/go/getflash/\\">Get Flash<\\/a>"; document.write(alternateContent); // insert non-flash content } </script> </td> <td valign="top" align="left">' . $answer_list . '</td></tr> </table> </td></tr>'; $html .= $s; $html .= '</table>'; return $html; } return $nbrAnswers; }
/** * Display the answers to a multiple choice question * * @param integer Answer type * @param integer Student choice * @param string Textual answer * @param string Comment on answer * @param string Correct answer comment * @param integer Exercise ID * @param integer Question ID * @param boolean Whether to show the answer comment or not * @return void */ public static function display_multiple_answer_true_false($feedback_type, $answerType, $studentChoice, $answer, $answerComment, $answerCorrect, $id, $questionId, $ans, $in_results_disabled) { $hide_expected_answer = false; if ($feedback_type == 0 && $in_results_disabled == 2) { $hide_expected_answer = true; } ?> <tr> <td width="5%"> <?php $question = new MultipleAnswerTrueFalse(); $course_id = api_get_course_int_id(); $new_options = Question::readQuestionOption($questionId, $course_id); //Your choice if (isset($new_options[$studentChoice])) { echo get_lang($new_options[$studentChoice]['name']); } else { echo '-'; } ?> </td> <td width="5%"> <?php //Expected choice if (!$hide_expected_answer) { if (isset($new_options[$answerCorrect])) { echo get_lang($new_options[$answerCorrect]['name']); } else { echo '-'; } } else { echo '-'; } ?> </td> <td width="40%"> <?php echo $answer; ?> </td> <?php if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) { ?> <td width="20%"> <?php $color = "black"; if (isset($new_options[$studentChoice])) { if ($studentChoice == $answerCorrect) { $color = "green"; } echo '<span style="font-weight: bold; color: ' . $color . ';">' . nl2br($answerComment) . '</span>'; } ?> </td> <?php if ($ans == 1) { $comm = Event::get_comments($id, $questionId); } ?> <?php } else { ?> <td> </td> <?php } ?> </tr> <?php }
/** * Restore quiz-questions * @params int question id */ public function restore_quiz_question($id) { $resources = $this->course->resources; $question = isset($resources[RESOURCE_QUIZQUESTION][$id]) ? $resources[RESOURCE_QUIZQUESTION][$id] : null; $new_id = 0; if (is_object($question)) { if ($question->is_restored()) { return $question->destination_id; } $table_que = Database::get_course_table(TABLE_QUIZ_QUESTION); $table_ans = Database::get_course_table(TABLE_QUIZ_ANSWER); $table_options = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION); // check resources inside html from ckeditor tool and copy correct urls into recipient course $question->description = DocumentManager::replace_urls_inside_content_html_from_copy_course($question->description, $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']); $params = ['c_id' => $this->destination_course_id, 'question' => self::DBUTF8($question->question), 'description' => self::DBUTF8($question->description), 'ponderation' => self::DBUTF8($question->ponderation), 'position' => self::DBUTF8($question->position), 'type' => self::DBUTF8($question->quiz_type), 'picture' => self::DBUTF8($question->picture), 'level' => self::DBUTF8($question->level), 'extra' => self::DBUTF8($question->extra)]; $new_id = Database::insert($table_que, $params); if ($new_id) { $sql = "UPDATE {$table_que} SET id = iid WHERE iid = {$new_id}"; Database::query($sql); if (!empty($question->picture)) { $question_temp = Question::read($new_id, $this->destination_course_info['real_id']); $documentPath = api_get_path(SYS_COURSE_PATH) . $this->destination_course_info['path'] . '/document'; // picture path $picturePath = $documentPath . '/images'; $old_picture = api_get_path(SYS_COURSE_PATH) . $this->course->info['path'] . '/document/images/' . $question->picture; if (file_exists($old_picture)) { $picture_name = 'quiz-' . $new_id . '.jpg'; $result = $question_temp->uploadPicture($old_picture, $picture_name, $picturePath); if ($result) { $sql = "UPDATE {$table_que} SET\n picture = '{$picture_name}'\n WHERE\n c_id = " . $this->destination_course_id . " AND\n id = {$new_id} "; Database::query($sql); } } } } if (in_array($question->quiz_type, [MATCHING, MATCHING_DRAGGABLE])) { $temp = array(); foreach ($question->answers as $index => $answer) { $temp[$answer['position']] = $answer; } foreach ($temp as $index => $answer) { //id = '".$index."', $params = ['c_id' => $this->destination_course_id, 'question_id' => $new_id, 'answer' => self::DBUTF8($answer['answer']), 'correct' => $answer['correct'], 'comment' => self::DBUTF8($answer['comment']), 'ponderation' => $answer['ponderation'], 'position' => $answer['position'], 'hotspot_coordinates' => $answer['hotspot_coordinates'], 'hotspot_type' => $answer['hotspot_type']]; $answerId = Database::insert($table_ans, $params); if ($answerId) { $sql = "UPDATE {$table_ans} SET id = iid, id_auto = iid WHERE iid = {$answerId}"; Database::query($sql); } } } else { $correct_answers = array(); foreach ($question->answers as $index => $answer) { // check resources inside html from ckeditor tool and copy correct urls into recipient course $answer['answer'] = DocumentManager::replace_urls_inside_content_html_from_copy_course($answer['answer'], $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']); $answer['comment'] = DocumentManager::replace_urls_inside_content_html_from_copy_course($answer['comment'], $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']); // id = '". ($index + 1)."', $params = ['c_id' => $this->destination_course_id, 'question_id' => $new_id, 'answer' => self::DBUTF8($answer['answer']), 'correct' => $answer['correct'], 'comment' => self::DBUTF8($answer['comment']), 'ponderation' => $answer['ponderation'], 'position' => $answer['position'], 'hotspot_coordinates' => $answer['hotspot_coordinates'], 'hotspot_type' => $answer['hotspot_type']]; $answerId = Database::insert($table_ans, $params); if ($answerId) { $sql = "UPDATE {$table_ans} SET id = iid, id_auto = iid WHERE iid = {$answerId}"; Database::query($sql); } $correct_answers[$answerId] = $answer['correct']; } } //Current course id $course_id = api_get_course_int_id(); //Moving quiz_question_options if ($question->quiz_type == MULTIPLE_ANSWER_TRUE_FALSE) { $question_option_list = Question::readQuestionOption($id, $course_id); //Question copied from the current platform if ($question_option_list) { $old_option_ids = array(); foreach ($question_option_list as $item) { $old_id = $item['id']; unset($item['id']); if (isset($item['iid'])) { unset($item['iid']); } $item['question_id'] = $new_id; $item['c_id'] = $this->destination_course_id; $question_option_id = Database::insert($table_options, $item); if ($question_option_id) { $old_option_ids[$old_id] = $question_option_id; $sql = "UPDATE {$table_options} SET id = iid WHERE iid = {$question_option_id}"; Database::query($sql); } } if ($old_option_ids) { $new_answers = Database::select('iid, correct', $table_ans, array('WHERE' => array('question_id = ? AND c_id = ? ' => array($new_id, $this->destination_course_id)))); foreach ($new_answers as $answer_item) { $params = array(); $params['correct'] = $old_option_ids[$answer_item['correct']]; Database::update($table_ans, $params, array('iid = ? AND c_id = ? AND question_id = ? ' => array($answer_item['iid'], $this->destination_course_id, $new_id)), false); } } } else { $new_options = array(); if (isset($question->question_options)) { foreach ($question->question_options as $obj) { $item = array(); $item['question_id'] = $new_id; $item['c_id'] = $this->destination_course_id; $item['name'] = $obj->obj->name; $item['position'] = $obj->obj->position; $question_option_id = Database::insert($table_options, $item); if ($question_option_id) { $new_options[$obj->obj->id] = $question_option_id; $sql = "UPDATE {$table_options} SET id = iid WHERE iid = {$question_option_id}"; Database::query($sql); } } foreach ($correct_answers as $answer_id => $correct_answer) { $params = array(); $params['correct'] = $new_options[$correct_answer]; Database::update($table_ans, $params, array('id = ? AND c_id = ? AND question_id = ? ' => array($answer_id, $this->destination_course_id, $new_id)), false); } } } } $this->course->resources[RESOURCE_QUIZQUESTION][$id]->destination_id = $new_id; } return $new_id; }
/** * abstract function which creates the form to create / edit the answers of the question * @param FormValidator $form */ public function processAnswersCreation($form) { $questionWeighting = $nbrGoodAnswers = 0; $objAnswer = new Answer($this->id); $nb_answers = $form->getSubmitValue('nb_answers'); $options_count = $form->getSubmitValue('options_count'); $course_id = api_get_course_int_id(); $correct = array(); $options = Question::readQuestionOption($this->id, $course_id); if (!empty($options)) { foreach ($options as $option_data) { $id = $option_data['id']; unset($option_data['id']); Question::updateQuestionOption($id, $option_data, $course_id); } } else { for ($i = 1; $i <= 3; $i++) { $last_id = Question::saveQuestionOption($this->id, $this->options[$i], $course_id, $i); $correct[$i] = $last_id; } } /* Getting quiz_question_options (true, false, doubt) because it's possible that there are more options in the future */ $new_options = Question::readQuestionOption($this->id, $course_id); $sorted_by_position = array(); foreach ($new_options as $item) { $sorted_by_position[$item['position']] = $item; } /* Saving quiz_question.extra values that has the correct scores of the true, false, doubt options registered in this format XX:YY:ZZZ where XX is a float score value.*/ $extra_values = array(); for ($i = 1; $i <= 3; $i++) { $score = trim($form->getSubmitValue('option[' . $i . ']')); $extra_values[] = $score; } $this->setExtra(implode(':', $extra_values)); for ($i = 1; $i <= $nb_answers; $i++) { $answer = trim($form->getSubmitValue('answer[' . $i . ']')); $comment = trim($form->getSubmitValue('comment[' . $i . ']')); $goodAnswer = trim($form->getSubmitValue('correct[' . $i . ']')); if (empty($options)) { //If this is the first time that the question is created when // change the default values from the form 1 and 2 by the correct "option id" registered $goodAnswer = $sorted_by_position[$goodAnswer]['id']; } $questionWeighting += $extra_values[0]; //By default 0 has the correct answers $objAnswer->createAnswer($answer, $goodAnswer, $comment, '', $i); } // saves the answers into the data base $objAnswer->save(); // sets the total weighting of the question $this->updateWeighting($questionWeighting); $this->save(); }
/** * Duplicates answers by copying them into another question * * @author Olivier Brouckaert * @param int question id * @param array destination course info (result of the function api_get_course_info() ) */ public function duplicate($newQuestionId, $course_info = null) { if (empty($course_info)) { $course_info = $this->course; } else { $course_info = $course_info; } $TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER); if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE || self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE) { //Selecting origin options $origin_options = Question::readQuestionOption($this->selectQuestionId(), $this->course['real_id']); if (!empty($origin_options)) { foreach ($origin_options as $item) { $new_option_list[] = $item['id']; } } $destination_options = Question::readQuestionOption($newQuestionId, $course_info['real_id']); $i = 0; $fixed_list = array(); if (!empty($destination_options)) { foreach ($destination_options as $item) { $fixed_list[$new_option_list[$i]] = $item['id']; $i++; } } } // if at least one answer if ($this->nbrAnswers) { // inserts new answers into data base $c_id = $course_info['real_id']; $correct_answers = array(); $new_ids = array(); foreach ($this->answer as $answer_id => $answer_item) { $i = $answer_id; if ($this->course['id'] != $course_info['id']) { $this->answer[$i] = DocumentManager::replace_urls_inside_content_html_from_copy_course($this->answer[$i], $this->course['id'], $course_info['id']); $this->comment[$i] = DocumentManager::replace_urls_inside_content_html_from_copy_course($this->comment[$i], $this->course['id'], $course_info['id']); } $answer = Database::escape_string($this->answer[$i]); $correct = Database::escape_string($this->correct[$i]); if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE || self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE) { $correct = $fixed_list[intval($correct)]; } $comment = Database::escape_string($this->comment[$i]); $weighting = Database::escape_string($this->weighting[$i]); $position = Database::escape_string($this->position[$i]); $hotspot_coordinates = Database::escape_string($this->hotspot_coordinates[$i]); $hotspot_type = Database::escape_string($this->hotspot_type[$i]); $destination = Database::escape_string($this->destination[$i]); $sql = "INSERT INTO {$TBL_REPONSES}(question_id, answer, correct, comment, ponderation, position, hotspot_coordinates, hotspot_type ,destination) VALUES"; $sql .= "('{$newQuestionId}','{$answer}','{$correct}','{$comment}', '{$weighting}','{$position}','{$hotspot_coordinates}','{$hotspot_type}','{$destination}')"; Database::query($sql); $new_id = Database::insert_id(); $new_ids[$answer_id] = $new_id; if ($correct) { $correct_answers[$new_id] = $correct; } } if (self::getQuestionType() == MATCHING) { if (!empty($correct_answers)) { foreach ($correct_answers as $new_id => $correct_id) { $correct = $new_ids[$correct_id]; $sql = "UPDATE {$TBL_REPONSES} SET correct = {$correct} WHERE iid = {$new_id}"; Database::query($sql); } } } } }