exercise_show.php 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use ChamiloSession as Session;
  4. /**
  5. * Shows the exercise results
  6. *
  7. * @author Julio Montoya - Added switchable fill in blank option added
  8. * @version $Id: exercise_show.php 22256 2009-07-20 17:40:20Z ivantcholakov $
  9. * @package chamilo.exercise
  10. * @todo remove the debug code and use the general debug library
  11. * @todo small letters for table variables
  12. *
  13. */
  14. require_once __DIR__.'/../inc/global.inc.php';
  15. $debug = false;
  16. $origin = api_get_origin();
  17. $currentUserId = api_get_user_id();
  18. $printHeaders = $origin == 'learnpath';
  19. $id = intval($_REQUEST['id']); //exe id
  20. if (empty($id)) {
  21. api_not_allowed(true);
  22. }
  23. // Getting results from the exe_id. This variable also contain all the information about the exercise
  24. $track_exercise_info = ExerciseLib::get_exercise_track_exercise_info($id);
  25. //No track info
  26. if (empty($track_exercise_info)) {
  27. api_not_allowed($printHeaders);
  28. }
  29. $exercise_id = $track_exercise_info['id'];
  30. $exercise_date = $track_exercise_info['start_date'];
  31. $student_id = $track_exercise_info['exe_user_id'];
  32. $learnpath_id = $track_exercise_info['orig_lp_id'];
  33. $learnpath_item_id = $track_exercise_info['orig_lp_item_id'];
  34. $lp_item_view_id = $track_exercise_info['orig_lp_item_view_id'];
  35. $isBossOfStudent = false;
  36. if (api_is_student_boss()) {
  37. // Check if boss has access to user info.
  38. if (UserManager::userIsBossOfStudent($currentUserId, $student_id)) {
  39. $isBossOfStudent = true;
  40. } else {
  41. api_not_allowed($printHeaders);
  42. }
  43. } else {
  44. api_protect_course_script($printHeaders, false, true);
  45. }
  46. // Database table definitions
  47. $TBL_EXERCISE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  48. $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
  49. $TBL_TRACK_EXERCISES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  50. $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  51. // General parameters passed via POST/GET
  52. if ($debug) {
  53. error_log('Entered exercise_result.php: '.print_r($_POST, 1));
  54. }
  55. if (empty($formSent)) {
  56. $formSent = isset($_REQUEST['formSent']) ? $_REQUEST['formSent'] : null;
  57. }
  58. if (empty($exerciseResult)) {
  59. $exerciseResult = Session::read('exerciseResult');
  60. }
  61. if (empty($questionId)) {
  62. $questionId = isset($_REQUEST['questionId']) ? $_REQUEST['questionId'] : null;
  63. }
  64. if (empty($choice)) {
  65. $choice = isset($_REQUEST['choice']) ? $_REQUEST['choice'] : null;
  66. }
  67. if (empty($questionNum)) {
  68. $questionNum = isset($_REQUEST['num']) ? $_REQUEST['num'] : null;
  69. }
  70. if (empty($nbrQuestions)) {
  71. $nbrQuestions = isset($_REQUEST['nbrQuestions']) ? $_REQUEST['nbrQuestions'] : null;
  72. }
  73. if (empty($questionList)) {
  74. $questionList = Session::read('questionList');
  75. }
  76. if (empty($objExercise)) {
  77. $objExercise = Session::read('objExercise');
  78. }
  79. if (empty($exeId)) {
  80. $exeId = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
  81. }
  82. if (empty($action)) {
  83. $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
  84. }
  85. $courseInfo = api_get_course_info();
  86. $sessionId = api_get_session_id();
  87. $is_allowedToEdit = api_is_allowed_to_edit(null, true) || api_is_course_tutor() || api_is_session_admin() || api_is_drh() || api_is_student_boss();
  88. if (!empty($sessionId) && !$is_allowedToEdit) {
  89. if (api_is_course_session_coach(
  90. $currentUserId,
  91. api_get_course_int_id(),
  92. $sessionId
  93. )) {
  94. if (!api_coach_can_edit_view_results(api_get_course_int_id(), $sessionId)
  95. ) {
  96. api_not_allowed($printHeaders);
  97. }
  98. }
  99. }
  100. $allowCoachFeedbackExercises = api_get_setting('allow_coach_feedback_exercises') === 'true';
  101. $maxEditors = intval(api_get_setting('exercise_max_ckeditors_in_page'));
  102. $isCoachAllowedToEdit = api_is_allowed_to_edit(false, true);
  103. $isFeedbackAllowed = false;
  104. if (api_is_excluded_user_type(true, $student_id)) {
  105. api_not_allowed($printHeaders);
  106. }
  107. $locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE);
  108. if (empty($objExercise)) {
  109. $objExercise = new Exercise();
  110. $objExercise->read($exercise_id);
  111. }
  112. $feedback_type = $objExercise->feedback_type;
  113. //Only users can see their own results
  114. if (!$is_allowedToEdit) {
  115. if ($student_id != $currentUserId) {
  116. api_not_allowed($printHeaders);
  117. }
  118. }
  119. if (isset($_SESSION['gradebook'])) {
  120. $gradebook = Security::remove_XSS($_SESSION['gradebook']);
  121. }
  122. if (!empty($gradebook) && $gradebook == 'view') {
  123. $interbreadcrumb[] = array('url' => '../gradebook/'.$_SESSION['gradebook_dest'], 'name' => get_lang('ToolGradebook'));
  124. }
  125. $interbreadcrumb[] = array("url" => "exercise.php?".api_get_cidreq(), "name" => get_lang('Exercises'));
  126. $interbreadcrumb[] = array(
  127. "url" => "overview.php?exerciseId=".$exercise_id.'&'.api_get_cidreq(),
  128. "name" => $objExercise->selectTitle(true)
  129. );
  130. $interbreadcrumb[] = array("url" => "#", "name" => get_lang('Result'));
  131. $this_section = SECTION_COURSES;
  132. $htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';
  133. $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>';
  134. $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
  135. if ($origin != 'learnpath') {
  136. Display::display_header('');
  137. } else {
  138. $htmlHeadXtra[] = "
  139. <style>
  140. body { background: none;}
  141. </style>
  142. ";
  143. Display::display_reduced_header();
  144. }
  145. ?>
  146. <script>
  147. var maxEditors = <?php echo intval($maxEditors); ?>;
  148. function showfck(sid, marksid) {
  149. document.getElementById(sid).style.display = 'block';
  150. document.getElementById(marksid).style.display = 'block';
  151. var comment = 'feedback_' + sid;
  152. document.getElementById(comment).style.display = 'none';
  153. }
  154. function openEmailWrapper() {
  155. $('#email_content_wrapper').toggle();
  156. }
  157. function getFCK(vals, marksid) {
  158. var f = document.getElementById('myform');
  159. var m_id = marksid.split(',');
  160. for (var i = 0; i < m_id.length; i++) {
  161. var oHidn = document.createElement("input");
  162. oHidn.type = "hidden";
  163. var selname = oHidn.name = "marks_" + m_id[i];
  164. var selid = document.forms['marksform_' + m_id[i]].marks.selectedIndex;
  165. oHidn.value = document.forms['marksform_' + m_id[i]].marks.options[selid].text;
  166. f.appendChild(oHidn);
  167. }
  168. var ids = vals.split(',');
  169. for (var k = 0; k < ids.length; k++) {
  170. var oHidden = document.createElement("input");
  171. oHidden.type = "hidden";
  172. oHidden.name = "comments_" + ids[k];
  173. if (CKEDITOR.instances[oHidden.name]) {
  174. oHidden.value = CKEDITOR.instances[oHidden.name].getData();
  175. } else {
  176. oHidden.value = $("textarea[name='" + oHidden.name + "']").val();
  177. }
  178. f.appendChild(oHidden);
  179. }
  180. }
  181. </script>
  182. <?php
  183. $show_results = true;
  184. $show_only_total_score = false;
  185. $showTotalScoreAndUserChoicesInLastAttempt = true;
  186. // Avoiding the "Score 0/0" message when the exe_id is not set
  187. if (!empty($track_exercise_info)) {
  188. // if the results_disabled of the Quiz is 1 when block the script
  189. $result_disabled = $track_exercise_info['results_disabled'];
  190. if (true) {
  191. if ($result_disabled == RESULT_DISABLE_NO_SCORE_AND_EXPECTED_ANSWERS) {
  192. $show_results = false;
  193. } elseif ($result_disabled == RESULT_DISABLE_SHOW_SCORE_ONLY) {
  194. $show_results = false;
  195. $show_only_total_score = true;
  196. if ($origin != 'learnpath') {
  197. if ($currentUserId == $student_id) {
  198. echo Display::return_message(get_lang('ThankYouForPassingTheTest'), 'warning', false);
  199. }
  200. }
  201. } elseif ($result_disabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
  202. $attempts = Event::getExerciseResultsByUser(
  203. $currentUserId,
  204. $objExercise->id,
  205. api_get_course_int_id(),
  206. api_get_session_id(),
  207. $track_exercise_info['orig_lp_id'],
  208. $track_exercise_info['orig_lp_item_id'],
  209. 'desc'
  210. );
  211. $numberAttempts = count($attempts);
  212. if ($numberAttempts >= $track_exercise_info['max_attempt']) {
  213. $show_results = true;
  214. $show_only_total_score = true;
  215. // Attempt reach max so show score/feedback now
  216. $showTotalScoreAndUserChoicesInLastAttempt = true;
  217. } else {
  218. $show_results = true;
  219. $show_only_total_score = true;
  220. // Last attempt not reach don't show score/feedback
  221. $showTotalScoreAndUserChoicesInLastAttempt = false;
  222. }
  223. }
  224. }
  225. } else {
  226. echo Display::return_message(get_lang('CantViewResults'), 'warning');
  227. $show_results = false;
  228. }
  229. if ($origin == 'learnpath' && !isset($_GET['fb_type'])) {
  230. $show_results = false;
  231. }
  232. if ($is_allowedToEdit && in_array($action, ['qualify', 'edit'])) {
  233. $show_results = true;
  234. }
  235. if ($show_results || $show_only_total_score || $showTotalScoreAndUserChoicesInLastAttempt) {
  236. $user_info = api_get_user_info($student_id);
  237. //Shows exercise header
  238. echo $objExercise->show_exercise_result_header(
  239. $user_info,
  240. api_convert_and_format_date($exercise_date),
  241. null,
  242. $track_exercise_info['user_ip']
  243. );
  244. }
  245. $i = $totalScore = $totalWeighting = 0;
  246. if ($debug > 0) {
  247. error_log("ExerciseResult: ".print_r($exerciseResult, 1));
  248. error_log("QuestionList: ".print_r($questionList, 1));
  249. }
  250. $arrques = array();
  251. $arrans = array();
  252. $user_restriction = $is_allowedToEdit ? '' : "AND user_id=".intval($student_id)." ";
  253. $sql = "SELECT attempts.question_id, answer
  254. FROM $TBL_TRACK_ATTEMPT as attempts
  255. INNER JOIN ".$TBL_TRACK_EXERCISES." AS stats_exercises
  256. ON stats_exercises.exe_id=attempts.exe_id
  257. INNER JOIN $TBL_EXERCISE_QUESTION AS quizz_rel_questions
  258. ON
  259. quizz_rel_questions.exercice_id=stats_exercises.exe_exo_id AND
  260. quizz_rel_questions.question_id = attempts.question_id AND
  261. quizz_rel_questions.c_id=".api_get_course_int_id()."
  262. INNER JOIN " . $TBL_QUESTIONS." AS questions
  263. ON
  264. questions.id=quizz_rel_questions.question_id AND
  265. questions.c_id = " . api_get_course_int_id()."
  266. WHERE
  267. attempts.exe_id = " . intval($id)." $user_restriction
  268. GROUP BY quizz_rel_questions.question_order, attempts.question_id";
  269. $result = Database::query($sql);
  270. $question_list_from_database = array();
  271. $exerciseResult = array();
  272. while ($row = Database::fetch_array($result)) {
  273. $question_list_from_database[] = $row['question_id'];
  274. $exerciseResult[$row['question_id']] = $row['answer'];
  275. }
  276. //Fixing #2073 Fixing order of questions
  277. if (!empty($track_exercise_info['data_tracking'])) {
  278. $temp_question_list = explode(',', $track_exercise_info['data_tracking']);
  279. // Getting question list from data_tracking
  280. if (!empty($temp_question_list)) {
  281. $questionList = $temp_question_list;
  282. }
  283. // If for some reason data_tracking is empty we select the question list from db
  284. if (empty($questionList)) {
  285. $questionList = $question_list_from_database;
  286. }
  287. } else {
  288. $questionList = $question_list_from_database;
  289. }
  290. // Display the text when finished message if we are on a LP #4227
  291. $end_of_message = $objExercise->selectTextWhenFinished();
  292. if (!empty($end_of_message) && ($origin == 'learnpath')) {
  293. echo Display::return_message($end_of_message, 'normal', false);
  294. echo "<div class='clear'>&nbsp;</div>";
  295. }
  296. // for each question
  297. $total_weighting = 0;
  298. foreach ($questionList as $questionId) {
  299. $objQuestionTmp = Question::read($questionId);
  300. $total_weighting += $objQuestionTmp->selectWeighting();
  301. }
  302. $counter = 1;
  303. $exercise_content = null;
  304. $category_list = array();
  305. $useAdvancedEditor = true;
  306. if (!empty($maxEditors) && count($questionList) > $maxEditors) {
  307. $useAdvancedEditor = false;
  308. }
  309. foreach ($questionList as $questionId) {
  310. $choice = $exerciseResult[$questionId];
  311. // destruction of the Question object
  312. unset($objQuestionTmp);
  313. // creates a temporary Question object
  314. $objQuestionTmp = Question::read($questionId);
  315. $questionWeighting = $objQuestionTmp->selectWeighting();
  316. $answerType = $objQuestionTmp->selectType();
  317. // Start buffer
  318. ob_start();
  319. if ($answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE) {
  320. $choice = array();
  321. }
  322. $relPath = api_get_path(WEB_CODE_PATH);
  323. switch ($answerType) {
  324. case MULTIPLE_ANSWER_COMBINATION:
  325. //no break
  326. case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE:
  327. //no break
  328. case UNIQUE_ANSWER:
  329. //no break;
  330. case UNIQUE_ANSWER_NO_OPTION:
  331. //no break
  332. case UNIQUE_ANSWER_IMAGE:
  333. //no break
  334. case MULTIPLE_ANSWER:
  335. //no break
  336. case MULTIPLE_ANSWER_TRUE_FALSE:
  337. //no break
  338. case FILL_IN_BLANKS:
  339. //no break
  340. case CALCULATED_ANSWER:
  341. //no break
  342. case GLOBAL_MULTIPLE_ANSWER:
  343. //no break
  344. case FREE_ANSWER:
  345. //no break
  346. case ORAL_EXPRESSION:
  347. //no break
  348. case MATCHING:
  349. //no break
  350. case DRAGGABLE:
  351. //no break
  352. case READING_COMPREHENSION:
  353. //no break
  354. case MATCHING_DRAGGABLE:
  355. $question_result = $objExercise->manage_answer(
  356. $id,
  357. $questionId,
  358. $choice,
  359. 'exercise_show',
  360. array(),
  361. false,
  362. true,
  363. $show_results,
  364. $objExercise->selectPropagateNeg(),
  365. [],
  366. $showTotalScoreAndUserChoicesInLastAttempt
  367. );
  368. $questionScore = $question_result['score'];
  369. $totalScore += $question_result['score'];
  370. break;
  371. case HOT_SPOT:
  372. if ($show_results || $showTotalScoreAndUserChoicesInLastAttempt) {
  373. echo '<table width="500" border="0"><tr>
  374. <td valign="top" align="center" style="padding-left:0px;" >
  375. <table border="1" bordercolor="#A4A4A4" style="border-collapse: collapse;" width="552">';
  376. }
  377. $question_result = $objExercise->manage_answer(
  378. $id,
  379. $questionId,
  380. $choice,
  381. 'exercise_show',
  382. array(),
  383. false,
  384. true,
  385. $show_results,
  386. $objExercise->selectPropagateNeg(),
  387. [],
  388. $showTotalScoreAndUserChoicesInLastAttempt
  389. );
  390. $questionScore = $question_result['score'];
  391. $totalScore += $question_result['score'];
  392. if ($show_results) {
  393. echo '</table></td></tr>';
  394. echo "
  395. <tr>
  396. <td colspan=\"2\">
  397. <div id=\"hotspot-solution-$questionId-$id\"></div>
  398. <script>
  399. $(document).on('ready', function () {
  400. new HotspotQuestion({
  401. questionId: $questionId,
  402. exerciseId: $id,
  403. selector: '#hotspot-solution-$questionId-$id',
  404. for: 'solution',
  405. relPath: '$relPath'
  406. });
  407. });
  408. </script>
  409. </td>
  410. </tr>
  411. </table>
  412. <br>
  413. ";
  414. }
  415. break;
  416. case HOT_SPOT_DELINEATION:
  417. $question_result = $objExercise->manage_answer(
  418. $id,
  419. $questionId,
  420. $choice,
  421. 'exercise_show',
  422. array(),
  423. false,
  424. true,
  425. $show_results,
  426. $objExercise->selectPropagateNeg(),
  427. 'database',
  428. [],
  429. $showTotalScoreAndUserChoicesInLastAttempt
  430. );
  431. $questionScore = $question_result['score'];
  432. $totalScore += $question_result['score'];
  433. $final_overlap = $question_result['extra']['final_overlap'];
  434. $final_missing = $question_result['extra']['final_missing'];
  435. $final_excess = $question_result['extra']['final_excess'];
  436. $overlap_color = $question_result['extra']['overlap_color'];
  437. $missing_color = $question_result['extra']['missing_color'];
  438. $excess_color = $question_result['extra']['excess_color'];
  439. $threadhold1 = $question_result['extra']['threadhold1'];
  440. $threadhold2 = $question_result['extra']['threadhold2'];
  441. $threadhold3 = $question_result['extra']['threadhold3'];
  442. if ($show_results) {
  443. if ($overlap_color) {
  444. $overlap_color = 'green';
  445. } else {
  446. $overlap_color = 'red';
  447. }
  448. if ($missing_color) {
  449. $missing_color = 'green';
  450. } else {
  451. $missing_color = 'red';
  452. }
  453. if ($excess_color) {
  454. $excess_color = 'green';
  455. } else {
  456. $excess_color = 'red';
  457. }
  458. if (!is_numeric($final_overlap)) {
  459. $final_overlap = 0;
  460. }
  461. if (!is_numeric($final_missing)) {
  462. $final_missing = 0;
  463. }
  464. if (!is_numeric($final_excess)) {
  465. $final_excess = 0;
  466. }
  467. if ($final_excess > 100) {
  468. $final_excess = 100;
  469. }
  470. $table_resume = '<table class="data_table">
  471. <tr class="row_odd" >
  472. <td></td>
  473. <td ><b>' . get_lang('Requirements').'</b></td>
  474. <td><b>' . get_lang('YourAnswer').'</b></td>
  475. </tr>
  476. <tr class="row_even">
  477. <td><b>' . get_lang('Overlap').'</b></td>
  478. <td>' . get_lang('Min').' '.$threadhold1.'</td>
  479. <td><div style="color:' . $overlap_color.'">'.(($final_overlap < 0) ? 0 : intval($final_overlap)).'</div></td>
  480. </tr>
  481. <tr>
  482. <td><b>' . get_lang('Excess').'</b></td>
  483. <td>' . get_lang('Max').' '.$threadhold2.'</td>
  484. <td><div style="color:' . $excess_color.'">'.(($final_excess < 0) ? 0 : intval($final_excess)).'</div></td>
  485. </tr>
  486. <tr class="row_even">
  487. <td><b>' . get_lang('Missing').'</b></td>
  488. <td>' . get_lang('Max').' '.$threadhold3.'</td>
  489. <td><div style="color:' . $missing_color.'">'.(($final_missing < 0) ? 0 : intval($final_missing)).'</div></td>
  490. </tr></table>';
  491. if ($answerType != HOT_SPOT_DELINEATION) {
  492. $item_list = explode('@@', $destination);
  493. $try = $item_list[0];
  494. $lp = $item_list[1];
  495. $destinationid = $item_list[2];
  496. $url = $item_list[3];
  497. $table_resume = '';
  498. } else {
  499. if ($next == 0) {
  500. $try = $try_hotspot;
  501. $lp = $lp_hotspot;
  502. $destinationid = $select_question_hotspot;
  503. $url = $url_hotspot;
  504. } else {
  505. //show if no error
  506. $comment = $answerComment = $objAnswerTmp->selectComment($nbrAnswers);
  507. $answerDestination = $objAnswerTmp->selectDestination($nbrAnswers);
  508. }
  509. }
  510. echo '<h1><div style="color:#333;">'.get_lang('Feedback').'</div></h1>';
  511. if ($answerType == HOT_SPOT_DELINEATION) {
  512. if ($organs_at_risk_hit > 0) {
  513. $message = '<br />'.get_lang('ResultIs').' <b>'.$result_comment.'</b><br />';
  514. $message .= '<p style="color:#DC0A0A;"><b>'.get_lang('OARHit').'</b></p>';
  515. } else {
  516. $message = '<p>'.get_lang('YourDelineation').'</p>';
  517. $message .= $table_resume;
  518. $message .= '<br />'.get_lang('ResultIs').' <b>'.$result_comment.'</b><br />';
  519. }
  520. $message .= '<p>'.$comment.'</p>';
  521. echo $message;
  522. } else {
  523. echo '<p>'.$comment.'</p>';
  524. }
  525. //showing the score
  526. $queryfree = "SELECT marks from ".$TBL_TRACK_ATTEMPT."
  527. WHERE exe_id = " . intval($id)." AND question_id= ".intval($questionId)."";
  528. $resfree = Database::query($queryfree);
  529. $questionScore = Database::result($resfree, 0, "marks");
  530. $totalScore += $questionScore;
  531. $relPath = api_get_path(REL_PATH);
  532. echo '</table></td></tr>';
  533. echo "
  534. <tr>
  535. <td colspan=\"2\">
  536. <div id=\"hotspot-solution\"></div>
  537. <script>
  538. $(document).on('ready', function () {
  539. new HotspotQuestion({
  540. questionId: $questionId,
  541. exerciseId: $id,
  542. selector: '#hotspot-solution',
  543. for: 'solution',
  544. relPath: '$relPath'
  545. });
  546. });
  547. </script>
  548. </td>
  549. </tr>
  550. </table>
  551. ";
  552. }
  553. break;
  554. case ANNOTATION:
  555. $question_result = $objExercise->manage_answer(
  556. $id,
  557. $questionId,
  558. $choice,
  559. 'exercise_show',
  560. array(),
  561. false,
  562. true,
  563. $show_results,
  564. $objExercise->selectPropagateNeg(),
  565. [],
  566. $showTotalScoreAndUserChoicesInLastAttempt
  567. );
  568. $questionScore = $question_result['score'];
  569. $totalScore += $question_result['score'];
  570. if ($show_results) {
  571. echo '
  572. <div id="annotation-canvas-'.$questionId.'"></div>
  573. <script>
  574. AnnotationQuestion({
  575. questionId: '.(int) $questionId.',
  576. exerciseId: '.(int) $id.',
  577. relPath: \''.$relPath.'\'
  578. });
  579. </script>
  580. ';
  581. }
  582. break;
  583. }
  584. if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) {
  585. echo '</table>';
  586. }
  587. if ($show_results && $answerType != HOT_SPOT) {
  588. echo '</table>';
  589. }
  590. $comnt = null;
  591. if ($show_results) {
  592. if ($is_allowedToEdit &&
  593. $locked == false &&
  594. !api_is_drh() &&
  595. $isCoachAllowedToEdit
  596. ) {
  597. $isFeedbackAllowed = true;
  598. } elseif (!$isCoachAllowedToEdit && $allowCoachFeedbackExercises) {
  599. $isFeedbackAllowed = true;
  600. }
  601. // Boss cannot edit exercise result
  602. if ($isBossOfStudent) {
  603. $isFeedbackAllowed = false;
  604. }
  605. $marksname = '';
  606. if ($isFeedbackAllowed) {
  607. $name = "fckdiv".$questionId;
  608. $marksname = "marksName".$questionId;
  609. if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION, ANNOTATION))) {
  610. $url_name = get_lang('EditCommentsAndMarks');
  611. } else {
  612. if ($action == 'edit') {
  613. $url_name = get_lang('EditIndividualComment');
  614. } else {
  615. $url_name = get_lang('AddComments');
  616. }
  617. }
  618. echo '<br />';
  619. echo Display::url(
  620. $url_name,
  621. 'javascript://',
  622. array(
  623. 'class' => 'btn',
  624. 'onclick' => "showfck('".$name."', '".$marksname."');",
  625. )
  626. );
  627. echo '<br />';
  628. echo '<div id="feedback_'.$name.'" style="width:100%">';
  629. $comnt = trim(Event::get_comments($id, $questionId));
  630. if (empty($comnt)) {
  631. echo '<br />';
  632. } else {
  633. echo '<div id="question_feedback">'.$comnt.'</div>';
  634. }
  635. echo '</div>';
  636. echo '<div id="'.$name.'" style="display:none">';
  637. $arrid[] = $questionId;
  638. $feedback_form = new FormValidator('frmcomments'.$questionId, 'post', '');
  639. $feedback_form->addElement('html', '<br>');
  640. $renderer = & $feedback_form->defaultRenderer();
  641. $renderer->setFormTemplate('<form{attributes}><div align="left">{content}</div></form>');
  642. $renderer->setCustomElementTemplate('<div align="left">{element}</div>');
  643. $comnt = Event::get_comments($id, $questionId);
  644. $default = array('comments_'.$questionId => $comnt);
  645. if ($useAdvancedEditor) {
  646. $feedback_form->addElement(
  647. 'html_editor',
  648. 'comments_'.$questionId,
  649. null,
  650. null,
  651. array(
  652. 'ToolbarSet' => 'TestAnswerFeedback',
  653. 'Width' => '100%',
  654. 'Height' => '120'
  655. )
  656. );
  657. } else {
  658. $feedback_form->addElement('textarea', 'comments_'.$questionId);
  659. }
  660. $feedback_form->addElement('html', '<br>');
  661. $feedback_form->setDefaults($default);
  662. $feedback_form->display();
  663. echo '</div>';
  664. } else {
  665. $comnt = Event::get_comments($id, $questionId);
  666. echo '<br />';
  667. if (!empty($comnt)) {
  668. echo '<b>'.get_lang('Feedback').'</b>';
  669. echo '<div id="question_feedback">'.$comnt.'</div>';
  670. }
  671. }
  672. if ($is_allowedToEdit && $isFeedbackAllowed) {
  673. if (in_array($answerType, array(FREE_ANSWER, ORAL_EXPRESSION, ANNOTATION))) {
  674. $marksname = "marksName".$questionId;
  675. echo '<div id="'.$marksname.'" style="display:none">';
  676. echo '<form name="marksform_'.$questionId.'" method="post" action="">';
  677. $arrmarks[] = $questionId;
  678. echo get_lang("AssignMarks");
  679. echo "&nbsp;<select name='marks' id='marks'>";
  680. for ($i = 0; $i <= $questionWeighting; $i++) {
  681. echo '<option '.(($i == $questionScore) ? "selected='selected'" : '').'>'.$i.'</option>';
  682. }
  683. echo '</select>';
  684. echo '</form><br /></div>';
  685. if ($questionScore == -1) {
  686. $questionScore = 0;
  687. echo Display::return_message(get_lang('notCorrectedYet'));
  688. }
  689. } else {
  690. $arrmarks[] = $questionId;
  691. echo '<div id="'.$marksname.'" style="display:none"><form name="marksform_'.$questionId.'" method="post" action="">
  692. <select name="marks" id="marks" style="display:none;"><option>' . $questionScore.'</option></select></form><br/ ></div>';
  693. }
  694. } else {
  695. if ($questionScore == -1) {
  696. $questionScore = 0;
  697. }
  698. }
  699. }
  700. $my_total_score = $questionScore;
  701. $my_total_weight = $questionWeighting;
  702. $totalWeighting += $questionWeighting;
  703. $category_was_added_for_this_test = false;
  704. if (isset($objQuestionTmp->category) && !empty($objQuestionTmp->category)) {
  705. if (!isset($category_list[$objQuestionTmp->category]['score'])) {
  706. $category_list[$objQuestionTmp->category]['score'] = 0;
  707. }
  708. if (!isset($category_list[$objQuestionTmp->category]['total'])) {
  709. $category_list[$objQuestionTmp->category]['total'] = 0;
  710. }
  711. $category_list[$objQuestionTmp->category]['score'] += $my_total_score;
  712. $category_list[$objQuestionTmp->category]['total'] += $my_total_weight;
  713. $category_was_added_for_this_test = true;
  714. }
  715. if (isset($objQuestionTmp->category_list) && !empty($objQuestionTmp->category_list)) {
  716. foreach ($objQuestionTmp->category_list as $category_id) {
  717. $category_list[$category_id]['score'] += $my_total_score;
  718. $category_list[$category_id]['total'] += $my_total_weight;
  719. $category_was_added_for_this_test = true;
  720. }
  721. }
  722. // No category for this question!
  723. if (!isset($category_list['none']['score'])) {
  724. $category_list['none']['score'] = 0;
  725. }
  726. if (!isset($category_list['none']['total'])) {
  727. $category_list['none']['total'] = 0;
  728. }
  729. if ($category_was_added_for_this_test == false) {
  730. $category_list['none']['score'] += $my_total_score;
  731. $category_list['none']['total'] += $my_total_weight;
  732. }
  733. if ($objExercise->selectPropagateNeg() == 0 && $my_total_score < 0) {
  734. $my_total_score = 0;
  735. }
  736. $score = array();
  737. if ($show_results) {
  738. $score['result'] = get_lang('Score')." : ".ExerciseLib::show_score(
  739. $my_total_score,
  740. $my_total_weight,
  741. false,
  742. false
  743. );
  744. $score['pass'] = $my_total_score >= $my_total_weight ? true : false;
  745. $score['type'] = $answerType;
  746. $score['score'] = $my_total_score;
  747. $score['weight'] = $my_total_weight;
  748. $score['comments'] = isset($comnt) ? $comnt : null;
  749. }
  750. unset($objAnswerTmp);
  751. $i++;
  752. $contents = ob_get_clean();
  753. $question_content = '<div class="question_row">';
  754. if ($show_results) {
  755. //Shows question title an description
  756. $question_content .= $objQuestionTmp->return_header(null, $counter, $score);
  757. }
  758. $counter++;
  759. $question_content .= $contents;
  760. $question_content .= '</div>';
  761. $exercise_content .= $question_content;
  762. } // end of large foreach on questions
  763. $total_score_text = null;
  764. //Total score
  765. if ($origin != 'learnpath' || ($origin == 'learnpath' && isset($_GET['fb_type']))) {
  766. if ($show_results || $show_only_total_score || $showTotalScoreAndUserChoicesInLastAttempt) {
  767. $total_score_text .= '<div class="question_row">';
  768. $my_total_score_temp = $totalScore;
  769. if ($objExercise->selectPropagateNeg() == 0 && $my_total_score_temp < 0) {
  770. $my_total_score_temp = 0;
  771. }
  772. $total_score_text .= ExerciseLib::get_question_ribbon(
  773. $objExercise,
  774. $my_total_score_temp,
  775. $totalWeighting,
  776. true
  777. );
  778. $total_score_text .= '</div>';
  779. }
  780. }
  781. if (!empty($category_list) && ($show_results || $show_only_total_score || $showTotalScoreAndUserChoicesInLastAttempt)) {
  782. // Adding total
  783. $category_list['total'] = array(
  784. 'score' => $my_total_score_temp,
  785. 'total' => $totalWeighting
  786. );
  787. echo TestCategory::get_stats_table_by_attempt($objExercise->id, $category_list);
  788. }
  789. echo $total_score_text;
  790. echo $exercise_content;
  791. echo $total_score_text;
  792. if ($isFeedbackAllowed) {
  793. if (is_array($arrid) && is_array($arrmarks)) {
  794. $strids = implode(",", $arrid);
  795. $marksid = implode(",", $arrmarks);
  796. }
  797. }
  798. if ($isFeedbackAllowed && $origin != 'learnpath' && $origin != 'student_progress') {
  799. if (in_array($origin, array('tracking_course', 'user_course', 'correct_exercise_in_lp'))) {
  800. $formUrl = api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&';
  801. $formUrl .= http_build_query([
  802. 'exerciseId' => $exercise_id,
  803. 'filter' => 2,
  804. 'comments' => 'update',
  805. 'exeid' => $id,
  806. 'origin' => $origin,
  807. 'details' => 'true',
  808. 'course' => Security::remove_XSS($_GET['cidReq'])
  809. ]);
  810. $emailForm = new FormValidator('myform', 'post', $formUrl, '', ['id' => 'myform']);
  811. $emailForm->addHidden('lp_item_id', $learnpath_id);
  812. $emailForm->addHidden('lp_item_view_id', $lp_item_view_id);
  813. $emailForm->addHidden('student_id', $student_id);
  814. $emailForm->addHidden('total_score', $totalScore);
  815. $emailForm->addHidden('my_exe_exo_id', $exercise_id);
  816. } else {
  817. $formUrl = api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&';
  818. $formUrl .= http_build_query([
  819. 'exerciseId' => $exercise_id,
  820. 'filter' => 1,
  821. 'comments' => 'update',
  822. 'exeid' => $id
  823. ]);
  824. $emailForm = new FormValidator('myform', 'post', $formUrl, '', ['id' => 'myform']);
  825. }
  826. $emailForm->addCheckBox(
  827. 'send_notification',
  828. get_lang('SendEmail'),
  829. get_lang('SendEmail'),
  830. ['onclick' => 'openEmailWrapper();']
  831. );
  832. $emailForm->addHtml('<span id="email_content_wrapper" style="display:none">');
  833. $emailForm->addHtmlEditor(
  834. 'notification_content',
  835. get_lang('Content'),
  836. false
  837. );
  838. $emailForm->addHtml('</span>');
  839. if (empty($track_exercise_info['orig_lp_id']) || empty($track_exercise_info['orig_lp_item_id'])) {
  840. // Default url
  841. $url = api_get_path(WEB_CODE_PATH).'exercise/result.php?id='.$track_exercise_info['exe_id'].'&'.api_get_cidreq().'&show_headers=1&id_session='.api_get_session_id();
  842. } else {
  843. $url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?action=view&item_id='.$track_exercise_info['orig_lp_item_id'].'&lp_id='.$track_exercise_info['orig_lp_id'].'&'.api_get_cidreq().'&id_session='.api_get_session_id();
  844. }
  845. $content = ExerciseLib::getEmailNotification(
  846. $currentUserId,
  847. api_get_course_info(),
  848. $track_exercise_info['title'],
  849. $url
  850. );
  851. $emailForm->setDefaults(['notification_content' => $content]);
  852. $emailForm->addButtonSend(
  853. get_lang('CorrectTest'),
  854. 'submit',
  855. false,
  856. ['onclick' => "getFCK('$strids', '$marksid')"]
  857. );
  858. echo $emailForm->returnForm();
  859. }
  860. //Came from lpstats in a lp
  861. if ($origin == 'student_progress') { ?>
  862. <button type="button" class="back" onclick="window.history.go(-1);" value="<?php echo get_lang('Back'); ?>">
  863. <?php echo get_lang('Back'); ?>
  864. </button>
  865. <?php
  866. } elseif ($origin == 'myprogress') {
  867. ?>
  868. <button type="button" class="save"
  869. onclick="top.location.href='../auth/my_progress.php?course=<?php echo api_get_course_id() ?>'"
  870. value="<?php echo get_lang('Finish'); ?>">
  871. <?php echo get_lang('Finish'); ?>
  872. </button>
  873. <?php
  874. }
  875. if ($origin != 'learnpath') {
  876. //we are not in learnpath tool
  877. Display::display_footer();
  878. } else {
  879. if (!isset($_GET['fb_type'])) {
  880. $lp_mode = $_SESSION['lp_mode'];
  881. $url = '../lp/lp_controller.php?'.api_get_cidreq().'&';
  882. $url .= http_build_url([
  883. 'action' => 'view',
  884. 'lp_id' => $learnpath_id,
  885. 'lp_item_id' => $learnpath_item_id,
  886. 'exeId' => $exeId,
  887. 'fb_type' => $feedback_type
  888. ]);
  889. $href = ($lp_mode == 'fullscreen') ? ' window.opener.location.href="'.$url.'" ' : ' top.location.href="'.$url.'" ';
  890. echo '<script type="text/javascript">'.$href.'</script>';
  891. // Record the results in the learning path, using the SCORM interface (API)
  892. echo "<script>window.parent.API.void_save_asset('$totalScore', '$totalWeighting', 0, 'completed'); </script>";
  893. echo '</body></html>';
  894. } else {
  895. echo Display::return_message(get_lang('ExerciseFinished').' '.get_lang('ToContinueUseMenu'), 'normal');
  896. echo '<br />';
  897. }
  898. }
  899. // Destroying the session
  900. Session::erase('questionList');
  901. unset($questionList);
  902. Session::erase('exerciseResult');
  903. unset($exerciseResult);