1 | <?php |
||
2 | |||
3 | /* For licensing terms, see /license.txt */ |
||
4 | |||
5 | use Chamilo\CoreBundle\Entity\TrackEExerciseConfirmation; |
||
6 | use Chamilo\CoreBundle\Entity\TrackEExercises; |
||
7 | use Chamilo\CourseBundle\Entity\CQuizQuestion; |
||
8 | use ChamiloSession as Session; |
||
9 | |||
10 | require_once __DIR__.'/../global.inc.php'; |
||
11 | $current_course_tool = TOOL_QUIZ; |
||
12 | $debug = false; |
||
13 | |||
14 | ExerciseLib::logPingForCheckingConnection(); |
||
15 | |||
16 | // Check if the user has access to the contextual course/session |
||
17 | api_protect_course_script(true); |
||
18 | |||
19 | $action = $_REQUEST['a']; |
||
20 | $course_id = api_get_course_int_id(); |
||
21 | $session_id = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : api_get_session_id(); |
||
22 | $course_code = isset($_REQUEST['cidReq']) ? $_REQUEST['cidReq'] : api_get_course_id(); |
||
23 | $currentUserId = api_get_user_id(); |
||
24 | $exeId = isset($_REQUEST['exe_id']) ? $_REQUEST['exe_id'] : 0; |
||
25 | |||
26 | switch ($action) { |
||
27 | case 'get_exercise_by_course': |
||
28 | $course_id = (isset($_GET['course_id']) && !empty($_GET['course_id'])) ? (int) $_GET['course_id'] : 0; |
||
29 | $session_id = (!empty($_GET['session_id'])) ? (int) $_GET['session_id'] : 0; |
||
30 | $data = []; |
||
31 | $onlyActiveExercises = !(api_is_platform_admin(true) || api_is_course_admin()); |
||
32 | $results = ExerciseLib::get_all_exercises_for_course_id( |
||
33 | null, |
||
34 | $session_id, |
||
35 | $course_id, |
||
36 | $onlyActiveExercises |
||
37 | ); |
||
38 | |||
39 | if (!empty($results)) { |
||
40 | foreach ($results as $exercise) { |
||
41 | $data[] = ['id' => $exercise['iid'], 'text' => html_entity_decode($exercise['title'])]; |
||
42 | } |
||
43 | } |
||
44 | |||
45 | echo json_encode($data); |
||
46 | break; |
||
47 | case 'update_duration': |
||
48 | if (Session::read('login_as')) { |
||
49 | if ($debug) { |
||
50 | error_log("User is 'login as' don't update duration time."); |
||
51 | } |
||
52 | exit; |
||
53 | } |
||
54 | |||
55 | if (empty($exeId)) { |
||
56 | if ($debug) { |
||
57 | error_log('Exe id not provided.'); |
||
58 | } |
||
59 | exit; |
||
60 | } |
||
61 | |||
62 | /** @var Exercise $exerciseInSession */ |
||
63 | $exerciseInSession = Session::read('objExercise'); |
||
64 | |||
65 | if (empty($exerciseInSession)) { |
||
66 | if ($debug) { |
||
67 | error_log('Exercise obj not provided.'); |
||
68 | } |
||
69 | exit; |
||
70 | } |
||
71 | |||
72 | // If exercise was updated x seconds before, then don't updated duration. |
||
73 | $onlyUpdateValue = 10; |
||
74 | |||
75 | $em = Database::getManager(); |
||
76 | /** @var TrackEExercises $attempt */ |
||
77 | $attempt = $em->getRepository('ChamiloCoreBundle:TrackEExercises')->find($exeId); |
||
78 | |||
79 | if (empty($attempt)) { |
||
80 | if ($debug) { |
||
81 | error_log("Attempt #$exeId doesn't exists."); |
||
82 | } |
||
83 | exit; |
||
84 | } |
||
85 | |||
86 | $nowObject = api_get_utc_datetime(null, false, true); |
||
87 | $now = $nowObject->getTimestamp(); |
||
88 | $exerciseId = $attempt->getExeExoId(); |
||
89 | $userId = $attempt->getExeUserId(); |
||
90 | |||
91 | if ($userId != $currentUserId) { |
||
92 | if ($debug) { |
||
93 | error_log("User $currentUserId trying to change time for user $userId"); |
||
94 | } |
||
95 | exit; |
||
96 | } |
||
97 | |||
98 | if ($exerciseInSession->iid != $exerciseId) { |
||
99 | if ($debug) { |
||
100 | error_log("Cannot update, exercise are different."); |
||
101 | } |
||
102 | exit; |
||
103 | } |
||
104 | |||
105 | if ($attempt->getStatus() != 'incomplete') { |
||
106 | if ($debug) { |
||
107 | error_log('Cannot update exercise is already completed.'); |
||
108 | } |
||
109 | exit; |
||
110 | } |
||
111 | |||
112 | // Check if we are dealing with the same exercise. |
||
113 | $timeWithOutUpdate = $now - $attempt->getExeDate()->getTimestamp(); |
||
114 | if ($timeWithOutUpdate > $onlyUpdateValue) { |
||
115 | $key = ExerciseLib::get_time_control_key( |
||
116 | $exerciseId, |
||
117 | $attempt->getOrigLpId(), |
||
118 | $attempt->getOrigLpItemId() |
||
119 | ); |
||
120 | $durationFromObject = $attempt->getExeDuration(); |
||
121 | $previousTime = Session::read('duration_time_previous'); |
||
122 | if (isset($previousTime[$key]) && |
||
123 | !empty($previousTime[$key]) |
||
124 | ) { |
||
125 | $sessionTime = $previousTime[$key]; |
||
126 | $duration = $sessionTime = $now - $sessionTime; |
||
127 | if (!empty($durationFromObject)) { |
||
128 | $duration += $durationFromObject; |
||
129 | } |
||
130 | $duration = (int) $duration; |
||
131 | if (!empty($duration)) { |
||
132 | if ($debug) { |
||
133 | error_log("Exe_id: #".$exeId); |
||
134 | error_log("Key: $key"); |
||
135 | error_log("Exercise to update: #$exerciseId of user: #$userId"); |
||
136 | error_log("Duration time found in DB before update: $durationFromObject"); |
||
137 | error_log("Current spent time $sessionTime before an update"); |
||
138 | error_log("Accumulate duration to save in DB: $duration"); |
||
139 | error_log("End date (UTC) before update: ".$attempt->getExeDate()->format('Y-m-d H:i:s')); |
||
140 | error_log("End date (UTC) to be save in DB: ".$nowObject->format('Y-m-d H:i:s')); |
||
141 | } |
||
142 | $attempt |
||
143 | ->setExeDuration($duration) |
||
144 | ->setExeDate($nowObject); |
||
145 | $em->merge($attempt); |
||
146 | $em->flush(); |
||
147 | } |
||
148 | } else { |
||
149 | if ($debug) { |
||
150 | error_log("Nothing to update, 'duration_time_previous' session not set"); |
||
151 | error_log("Key: $key"); |
||
152 | } |
||
153 | } |
||
154 | } else { |
||
155 | if ($debug) { |
||
156 | error_log("Can't update, time was already updated $timeWithOutUpdate seconds ago"); |
||
157 | } |
||
158 | } |
||
159 | |||
160 | break; |
||
161 | case 'get_live_stats': |
||
162 | if (!api_is_allowed_to_edit(null, true)) { |
||
163 | break; |
||
164 | } |
||
165 | |||
166 | // 1. Setting variables needed by jqgrid |
||
167 | $exercise_id = (int) $_GET['exercise_id']; |
||
168 | $page = (int) $_REQUEST['page']; //page |
||
169 | $limit = (int) $_REQUEST['rows']; //quantity of rows |
||
170 | $sidx = $_REQUEST['sidx']; //index to filter |
||
171 | $sord = $_REQUEST['sord']; //asc or desc |
||
172 | |||
173 | if (!in_array($sidx, ['firstname', 'lastname', 'start_date'])) { |
||
174 | $sidx = 1; |
||
175 | } |
||
176 | |||
177 | if (!in_array($sord, ['asc', 'desc'])) { |
||
178 | $sord = 'desc'; |
||
179 | } |
||
180 | // get index row - i.e. user click to sort $sord = $_GET['sord']; |
||
181 | // get the direction |
||
182 | if (!$sidx) { |
||
183 | $sidx = 1; |
||
184 | } |
||
185 | |||
186 | $track_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
||
187 | $user_table = Database::get_main_table(TABLE_MAIN_USER); |
||
188 | $track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); |
||
189 | |||
190 | $minutes = (int) $_REQUEST['minutes']; |
||
191 | $now = time() - 60 * $minutes; |
||
192 | $now = api_get_utc_datetime($now); |
||
193 | |||
194 | $where_condition = " orig_lp_id = 0 AND exe_exo_id = $exercise_id AND start_date > '$now' "; |
||
195 | $sql = "SELECT COUNT(DISTINCT exe_id) |
||
196 | FROM $track_exercise |
||
197 | WHERE $where_condition "; |
||
198 | $result = Database::query($sql); |
||
199 | $count = Database::fetch_row($result); |
||
200 | $count = $count[0]; |
||
201 | |||
202 | //3. Calculating first, end, etc |
||
203 | $total_pages = 0; |
||
204 | if ($count > 0) { |
||
205 | if (!empty($limit)) { |
||
206 | $total_pages = ceil($count / $limit); |
||
207 | } |
||
208 | } |
||
209 | |||
210 | if ($page > $total_pages) { |
||
211 | $page = $total_pages; |
||
212 | } |
||
213 | |||
214 | $start = $limit * $page - $limit; |
||
215 | if ($start < 0) { |
||
216 | $start = 0; |
||
217 | } |
||
218 | |||
219 | $sql = "SELECT |
||
220 | exe_id, |
||
221 | exe_user_id, |
||
222 | firstname, |
||
223 | lastname, |
||
224 | aa.status, |
||
225 | start_date, |
||
226 | exe_result, |
||
227 | exe_weighting, |
||
228 | exe_result/exe_weighting as score, |
||
229 | exe_duration, |
||
230 | questions_to_check, |
||
231 | orig_lp_id |
||
232 | FROM $user_table u |
||
233 | INNER JOIN ( |
||
234 | SELECT |
||
235 | t.exe_id, |
||
236 | t.exe_user_id, |
||
237 | status, |
||
238 | start_date, |
||
239 | exe_result, |
||
240 | exe_weighting, |
||
241 | exe_result/exe_weighting as score, |
||
242 | exe_duration, |
||
243 | questions_to_check, |
||
244 | orig_lp_id |
||
245 | FROM $track_exercise t |
||
246 | LEFT JOIN $track_attempt a |
||
247 | ON (a.exe_id = t.exe_id AND t.exe_user_id = a.user_id) |
||
248 | WHERE t.status = 'incomplete' AND $where_condition |
||
249 | GROUP BY exe_user_id |
||
250 | ) as aa |
||
251 | ON aa.exe_user_id = user_id |
||
252 | ORDER BY $sidx $sord |
||
253 | LIMIT $start, $limit"; |
||
254 | |||
255 | $result = Database::query($sql); |
||
256 | $results = []; |
||
257 | while ($row = Database::fetch_array($result, 'ASSOC')) { |
||
258 | $results[] = $row; |
||
259 | } |
||
260 | |||
261 | $oExe = new Exercise(); |
||
262 | $oExe->read($exercise_id); |
||
263 | |||
264 | $response = new stdClass(); |
||
265 | $response->page = $page; |
||
266 | $response->total = $total_pages; |
||
267 | $response->records = $count; |
||
268 | $i = 0; |
||
269 | |||
270 | if (!empty($results)) { |
||
271 | foreach ($results as $row) { |
||
272 | $sql = "SELECT SUM(count_question_id) as count_question_id |
||
273 | FROM ( |
||
274 | SELECT 1 as count_question_id |
||
275 | FROM $track_attempt a |
||
276 | WHERE |
||
277 | user_id = {$row['exe_user_id']} AND |
||
278 | exe_id = {$row['exe_id']} |
||
279 | GROUP by question_id |
||
280 | ) as count_table"; |
||
281 | $result_count = Database::query($sql); |
||
282 | $count_questions = Database::fetch_array( |
||
283 | $result_count, |
||
284 | 'ASSOC' |
||
285 | ); |
||
286 | $count_questions = $count_questions['count_question_id']; |
||
287 | |||
288 | $row['count_questions'] = $count_questions; |
||
289 | |||
290 | $response->rows[$i]['id'] = $row['exe_id']; |
||
291 | if (!empty($oExe->expired_time)) { |
||
292 | $remaining = strtotime($row['start_date']) + |
||
293 | ($oExe->expired_time * 60) - |
||
294 | strtotime(api_get_utc_datetime(time())); |
||
295 | $h = floor($remaining / 3600); |
||
296 | $m = floor(($remaining - ($h * 3600)) / 60); |
||
297 | $s = ($remaining - ($h * 3600) - ($m * 60)); |
||
298 | $timeInfo = api_convert_and_format_date( |
||
299 | $row['start_date'], |
||
300 | DATE_TIME_FORMAT_LONG |
||
301 | ).' ['.($h > 0 ? $h.':' : '').sprintf("%02d", $m).':'.sprintf("%02d", $s).']'; |
||
302 | } else { |
||
303 | $timeInfo = api_convert_and_format_date( |
||
304 | $row['start_date'], |
||
305 | DATE_TIME_FORMAT_LONG |
||
306 | ); |
||
307 | } |
||
308 | $array = [ |
||
309 | $row['firstname'], |
||
310 | $row['lastname'], |
||
311 | $timeInfo, |
||
312 | $row['count_questions'], |
||
313 | round($row['score'] * 100).'%', |
||
314 | ]; |
||
315 | $response->rows[$i]['cell'] = $array; |
||
316 | $i++; |
||
317 | } |
||
318 | } |
||
319 | echo json_encode($response); |
||
320 | break; |
||
321 | case 'update_exercise_list_order': |
||
322 | if (api_is_allowed_to_edit(null, true)) { |
||
323 | $new_list = $_REQUEST['exercise_list']; |
||
324 | $table = Database::get_course_table(TABLE_QUIZ_ORDER); |
||
325 | $counter = 1; |
||
326 | //Drop all |
||
327 | $sql = "DELETE FROM $table WHERE session_id = $session_id AND c_id = $course_id"; |
||
328 | Database::query($sql); |
||
329 | // Insert all |
||
330 | foreach ($new_list as $new_order_id) { |
||
331 | Database::insert( |
||
332 | $table, |
||
333 | [ |
||
334 | 'exercise_order' => $counter, |
||
335 | 'session_id' => $session_id, |
||
336 | 'exercise_id' => (int) $new_order_id, |
||
337 | 'c_id' => $course_id, |
||
338 | ] |
||
339 | ); |
||
340 | $counter++; |
||
341 | } |
||
342 | echo Display::return_message(get_lang('Saved'), 'confirmation'); |
||
343 | } |
||
344 | break; |
||
345 | case 'update_question_order': |
||
346 | $course_info = api_get_course_info_by_id($course_id); |
||
347 | $course_id = $course_info['real_id']; |
||
348 | $exercise_id = isset($_REQUEST['exercise_id']) ? (int) $_REQUEST['exercise_id'] : null; |
||
349 | |||
350 | if (empty($exercise_id)) { |
||
351 | return Display::return_message(get_lang('Error'), 'error'); |
||
352 | } |
||
353 | if (api_is_allowed_to_edit(null, true)) { |
||
354 | $new_question_list = $_POST['question_id_list']; |
||
355 | $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION); |
||
356 | $counter = 1; |
||
357 | foreach ($new_question_list as $new_order_id) { |
||
358 | Database::update( |
||
359 | $TBL_QUESTIONS, |
||
360 | ['question_order' => $counter], |
||
361 | [ |
||
362 | 'question_id = ? AND c_id = ? AND exercice_id = ? ' => [ |
||
363 | (int) $new_order_id, |
||
364 | $course_id, |
||
365 | $exercise_id, |
||
366 | ], |
||
367 | ] |
||
368 | ) |
||
369 | ; |
||
370 | $counter++; |
||
371 | } |
||
372 | echo Display::return_message(get_lang('Saved'), 'confirmation'); |
||
373 | } |
||
374 | break; |
||
375 | case 'add_question_to_reminder': |
||
376 | /** @var Exercise $objExercise */ |
||
377 | $objExercise = Session::read('objExercise'); |
||
378 | |||
379 | if (empty($objExercise) || empty($exeId)) { |
||
380 | echo 0; |
||
381 | exit; |
||
382 | } |
||
383 | |||
384 | $option = isset($_GET['option']) ? $_GET['option'] : ''; |
||
385 | switch ($option) { |
||
386 | case 'add_all': |
||
387 | $questionListInSession = Session::read('questionList'); |
||
388 | $objExercise->addAllQuestionToRemind($exeId, $questionListInSession); |
||
389 | break; |
||
390 | case 'remove_all': |
||
391 | $objExercise->removeAllQuestionToRemind($exeId); |
||
392 | break; |
||
393 | default: |
||
394 | $objExercise->editQuestionToRemind( |
||
395 | $exeId, |
||
396 | $_REQUEST['question_id'], |
||
397 | $_REQUEST['action'] |
||
398 | ); |
||
399 | break; |
||
400 | } |
||
401 | |||
402 | echo 1; |
||
403 | exit; |
||
404 | |||
405 | break; |
||
406 | case 'check_answers': |
||
407 | if (false === api_is_allowed_to_session_edit()) { |
||
408 | echo 'error'; |
||
409 | exit; |
||
410 | } |
||
411 | |||
412 | /** @var Exercise $objExercise */ |
||
413 | $objExercise = Session::read('objExercise'); |
||
414 | $questionList = Session::read('questionList'); |
||
415 | $exeId = Session::read('exe_id'); |
||
416 | |||
417 | // If exercise or question is not set then exit. |
||
418 | if (empty($questionList) || empty($objExercise)) { |
||
419 | echo 'error'; |
||
420 | exit; |
||
421 | } |
||
422 | |||
423 | $statInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId); |
||
424 | |||
425 | echo Display::page_subheader(get_lang('VerificationOfAnsweredQuestions')); |
||
426 | echo $objExercise->getReminderTable($questionList, $statInfo, true); |
||
427 | break; |
||
428 | case 'save_question_description': |
||
429 | if (!api_get_configuration_value('allow_quick_question_description_popup')) { |
||
430 | exit; |
||
431 | } |
||
432 | if (!api_is_allowed_to_edit(null, true)) { |
||
433 | exit; |
||
434 | } |
||
435 | |||
436 | /** @var \Exercise $objExercise */ |
||
437 | $objExercise = Session::read('objExercise'); |
||
438 | if (empty($objExercise)) { |
||
439 | exit; |
||
440 | } |
||
441 | |||
442 | $questionId = isset($_REQUEST['question_id']) ? (int) $_REQUEST['question_id'] : null; |
||
443 | $image = isset($_REQUEST['image']) ? $_REQUEST['image'] : ''; |
||
444 | |||
445 | $questionList = $objExercise->getQuestionList(); |
||
446 | |||
447 | if (!in_array($questionId, $questionList)) { |
||
448 | echo '0'; |
||
449 | exit; |
||
450 | } |
||
451 | |||
452 | $em = Database::getManager(); |
||
453 | $repo = $em->getRepository(CQuizQuestion::class); |
||
454 | /** @var CQuizQuestion $question */ |
||
455 | $question = $repo->find($questionId); |
||
456 | if (null !== $question) { |
||
457 | $question->setDescription('<img src="'.$image.'" />'); |
||
458 | $em->persist($question); |
||
459 | $em->flush(); |
||
460 | echo 1; |
||
461 | exit; |
||
462 | } |
||
463 | echo 0; |
||
464 | exit; |
||
465 | break; |
||
466 | case 'save_exercise_by_now': |
||
467 | header('Content-Type: application/json'); |
||
468 | |||
469 | $course_info = api_get_course_info_by_id($course_id); |
||
470 | |||
471 | if (empty($course_info)) { |
||
472 | echo json_encode(['error' => true]); |
||
473 | exit; |
||
474 | } |
||
475 | |||
476 | $course_id = $course_info['real_id']; |
||
477 | |||
478 | // Use have permissions to edit exercises results now? |
||
479 | if (false === api_is_allowed_to_session_edit()) { |
||
480 | echo json_encode(['error' => true]); |
||
481 | if ($debug) { |
||
482 | error_log( |
||
483 | 'Exercises attempt '.$exeId.': Failed saving question(s) in course/session '. |
||
484 | $course_id.'/'.$session_id. |
||
485 | ': The user ('.api_get_user_id().') does not have the permission to access this session now' |
||
486 | ); |
||
487 | } |
||
488 | exit; |
||
489 | } |
||
490 | |||
491 | // "all" or "simple" strings means that there's one or all questions exercise type |
||
492 | $type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null; |
||
493 | |||
494 | // Questions choices. |
||
495 | $choice = isset($_REQUEST['choice']) ? $_REQUEST['choice'] : []; |
||
496 | |||
497 | // certainty degree choice |
||
498 | $choiceDegreeCertainty = isset($_REQUEST['choiceDegreeCertainty']) ? $_REQUEST['choiceDegreeCertainty'] : []; |
||
499 | |||
500 | // Hot spot coordinates from all questions. |
||
501 | $hot_spot_coordinates = isset($_REQUEST['hotspot']) ? $_REQUEST['hotspot'] : []; |
||
502 | |||
503 | // the filenames in upload answer type |
||
504 | $uploadAnswerFileNames = isset($_REQUEST['uploadChoice']) ? $_REQUEST['uploadChoice'] : []; |
||
505 | |||
506 | // There is a reminder? |
||
507 | $remind_list = isset($_REQUEST['remind_list']) && !empty($_REQUEST['remind_list']) |
||
508 | ? array_keys($_REQUEST['remind_list']) : []; |
||
509 | |||
510 | // Needed in manage_answer. |
||
511 | $learnpath_id = isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : 0; |
||
512 | $learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? (int) $_REQUEST['learnpath_item_id'] : 0; |
||
513 | |||
514 | if ($debug) { |
||
515 | error_log("exe_id = $exeId"); |
||
516 | error_log("type = $type"); |
||
517 | error_log("choice = ".print_r($choice, 1)." "); |
||
518 | error_log("hot_spot_coordinates = ".print_r($hot_spot_coordinates, 1)); |
||
519 | error_log("remind_list = ".print_r($remind_list, 1)); |
||
520 | error_log("uploadAnswerFileNames = ".print_r($uploadAnswerFileNames, 1)); |
||
521 | error_log("--------------------------------"); |
||
522 | } |
||
523 | |||
524 | /** @var Exercise $objExercise */ |
||
525 | $objExercise = Session::read('objExercise'); |
||
526 | |||
527 | // Question info. |
||
528 | $question_id = isset($_REQUEST['question_id']) ? (int) $_REQUEST['question_id'] : null; |
||
529 | $question_list = Session::read('questionList'); |
||
530 | |||
531 | // If exercise or question is not set then exit. |
||
532 | if (empty($question_list) || empty($objExercise)) { |
||
533 | echo json_encode(['error' => true]); |
||
534 | if ($debug) { |
||
535 | if (empty($question_list)) { |
||
536 | error_log("question_list is empty"); |
||
537 | } |
||
538 | if (empty($objExercise)) { |
||
539 | error_log("objExercise is empty"); |
||
540 | } |
||
541 | } |
||
542 | exit; |
||
543 | } |
||
544 | |||
545 | if (WhispeakAuthPlugin::questionRequireAuthentify($question_id)) { |
||
546 | if ($objExercise->type == ONE_PER_PAGE) { |
||
547 | echo json_encode(['type' => 'one_per_page']); |
||
548 | exit; |
||
549 | } |
||
550 | |||
551 | echo json_encode(['ok' => true]); |
||
552 | exit; |
||
553 | } else { |
||
554 | ChamiloSession::erase(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION); |
||
555 | } |
||
556 | |||
557 | // Getting information of the current exercise. |
||
558 | $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId); |
||
559 | $exercise_id = $exercise_stat_info['exe_exo_id']; |
||
560 | $attemptList = []; |
||
561 | // First time here we create an attempt (getting the exe_id). |
||
562 | if (!empty($exercise_stat_info)) { |
||
563 | // We know the user we get the exe_id. |
||
564 | $exeId = $exercise_stat_info['exe_id']; |
||
565 | $total_score = $exercise_stat_info['exe_result']; |
||
566 | // Getting the list of attempts |
||
567 | $attemptList = Event::getAllExerciseEventByExeId($exeId); |
||
568 | } |
||
569 | |||
570 | // No exe id? Can't save answer. |
||
571 | if (empty($exeId)) { |
||
572 | // Fires an error. |
||
573 | echo json_encode(['error' => true]); |
||
574 | if ($debug) { |
||
575 | error_log('exe_id is empty'); |
||
576 | } |
||
577 | exit; |
||
578 | } |
||
579 | |||
580 | Session::write('exe_id', $exeId); |
||
581 | |||
582 | // Updating Reminder algorithm. |
||
583 | if ($objExercise->type == ONE_PER_PAGE) { |
||
584 | $bd_reminder_list = explode(',', $exercise_stat_info['questions_to_check']); |
||
585 | if (empty($remind_list)) { |
||
586 | $remind_list = $bd_reminder_list; |
||
587 | $new_list = []; |
||
588 | foreach ($bd_reminder_list as $item) { |
||
589 | if ($item != $question_id) { |
||
590 | $new_list[] = $item; |
||
591 | } |
||
592 | } |
||
593 | $remind_list = $new_list; |
||
594 | } else { |
||
595 | if (isset($remind_list[0])) { |
||
596 | if (!in_array($remind_list[0], $bd_reminder_list)) { |
||
597 | array_push($bd_reminder_list, $remind_list[0]); |
||
598 | } |
||
599 | $remind_list = $bd_reminder_list; |
||
600 | } |
||
601 | } |
||
602 | } |
||
603 | |||
604 | // Getting the total weight if the request is simple. |
||
605 | $total_weight = 0; |
||
606 | if ($type === 'simple') { |
||
607 | foreach ($question_list as $my_question_id) { |
||
608 | $objQuestionTmp = Question::read($my_question_id, $objExercise->course); |
||
609 | $total_weight += $objQuestionTmp->selectWeighting(); |
||
610 | } |
||
611 | } |
||
612 | unset($objQuestionTmp); |
||
613 | |||
614 | if ($debug) { |
||
615 | error_log('Starting questions loop in save_exercise_by_now'); |
||
616 | } |
||
617 | |||
618 | $now = time(); |
||
619 | if ('all' === $type) { |
||
620 | // Check we have at least one non-empty answer in the array |
||
621 | // provided by the user's click on the "Finish test" button. |
||
622 | $atLeastOneAnswer = false; |
||
623 | foreach ($question_list as $my_question_id) { |
||
624 | if (!empty($choice[$my_question_id])) { |
||
625 | $atLeastOneAnswer = true; |
||
626 | break; |
||
627 | } |
||
628 | } |
||
629 | |||
630 | if (!$atLeastOneAnswer) { |
||
631 | // Check if time is over. |
||
632 | if ($objExercise->expired_time != 0) { |
||
633 | $clockExpiredTime = ExerciseLib::get_session_time_control_key( |
||
634 | $objExercise->iid, |
||
635 | $learnpath_id, |
||
636 | $learnpath_item_id |
||
637 | ); |
||
638 | if (!empty($clockExpiredTime)) { |
||
639 | $timeLeft = api_strtotime($clockExpiredTime, 'UTC') - $now; |
||
640 | if ($timeLeft <= 0) { |
||
641 | // There's no time, but still no answers ... |
||
642 | echo json_encode(['ok' => true, 'savedAnswerMessage' => '']); |
||
643 | exit; |
||
644 | } |
||
645 | } |
||
646 | } |
||
647 | |||
648 | error_log( |
||
649 | 'In '.__FILE__.'::action save_exercise_by_now,'. |
||
650 | ' from user '.api_get_user_id(). |
||
651 | ' for track_e_exercises.exe_id = '.$exeId. |
||
652 | ', we received an empty set of answers.'. |
||
653 | 'Preventing submission to avoid overwriting w/ null.' |
||
654 | ); |
||
655 | echo json_encode(['error' => true]); |
||
656 | exit; |
||
657 | } |
||
658 | } |
||
659 | |||
660 | // Looping the question list from database (not from the user answer) |
||
661 | foreach ($question_list as $my_question_id) { |
||
662 | if ($type === 'simple' && $question_id != $my_question_id) { |
||
663 | continue; |
||
664 | } |
||
665 | $my_choice = isset($choice[$my_question_id]) ? $choice[$my_question_id] : null; |
||
666 | $objQuestionTmp = Question::read($my_question_id, $objExercise->course); |
||
667 | $myChoiceDegreeCertainty = null; |
||
668 | if ($objQuestionTmp->type === MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) { |
||
669 | if (isset($choiceDegreeCertainty[$my_question_id])) { |
||
670 | $myChoiceDegreeCertainty = $choiceDegreeCertainty[$my_question_id]; |
||
671 | } |
||
672 | } |
||
673 | if ($objQuestionTmp->type === UPLOAD_ANSWER) { |
||
674 | $my_choice = ''; |
||
675 | if (!empty($uploadAnswerFileNames)) { |
||
676 | // Clean user upload_answer folder |
||
677 | $userUploadAnswerSyspath = UserManager::getUserPathById(api_get_user_id(), 'system').'my_files'.'/upload_answer/'.$exeId.'/'.$my_question_id.'/*'; |
||
678 | foreach (glob($userUploadAnswerSyspath) as $file) { |
||
679 | $filename = basename($file); |
||
680 | if (!in_array($filename, $uploadAnswerFileNames[$my_question_id])) { |
||
681 | unlink($file); |
||
682 | } |
||
683 | } |
||
684 | $my_choice = implode('|', $uploadAnswerFileNames[$my_question_id]); |
||
685 | } |
||
686 | } |
||
687 | // Getting free choice data. |
||
688 | if ('all' === $type && in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION])) { |
||
689 | $my_choice = isset($_REQUEST['free_choice'][$my_question_id]) && !empty($_REQUEST['free_choice'][$my_question_id]) |
||
690 | ? $_REQUEST['free_choice'][$my_question_id] |
||
691 | : null; |
||
692 | } |
||
693 | |||
694 | if ($type === 'all') { |
||
695 | // If saving the whole exercise (not only one question), |
||
696 | // record the sum of individual max scores (called |
||
697 | // "exe_weighting" in track_e_exercises) |
||
698 | $total_weight += $objQuestionTmp->selectWeighting(); |
||
699 | } |
||
700 | |||
701 | // This variable came from exercise_submit_modal.php. |
||
702 | $hotspot_delineation_result = null; |
||
703 | if (isset($_SESSION['hotspot_delineation_result']) && |
||
704 | isset($_SESSION['hotspot_delineation_result'][$objExercise->selectId()]) |
||
705 | ) { |
||
706 | $hotspot_delineation_result = $_SESSION['hotspot_delineation_result'][$objExercise->selectId()][$my_question_id]; |
||
707 | } |
||
708 | |||
709 | if ('simple' === $type) { |
||
710 | // Getting old attempt in order to decrease the total score. |
||
711 | $old_result = $objExercise->manage_answer( |
||
712 | $exeId, |
||
713 | $my_question_id, |
||
714 | null, |
||
715 | 'exercise_show', |
||
716 | [], |
||
717 | false, |
||
718 | true, |
||
719 | false, |
||
720 | $objExercise->selectPropagateNeg() |
||
721 | ); |
||
722 | // Removing old score. |
||
723 | $total_score = $total_score - $old_result['score']; |
||
724 | } |
||
725 | |||
726 | $questionDuration = 0; |
||
727 | if (api_get_configuration_value('allow_time_per_question')) { |
||
728 | $extraFieldValue = new ExtraFieldValue('question'); |
||
729 | $value = $extraFieldValue->get_values_by_handler_and_field_variable($objQuestionTmp->iid, 'time'); |
||
730 | if (!empty($value) && isset($value['value']) && !empty($value['value'])) { |
||
731 | $questionDuration = Event::getAttemptQuestionDuration($exeId, $objQuestionTmp->iid); |
||
732 | if (empty($questionDuration)) { |
||
733 | echo json_encode(['error' => true]); |
||
734 | if ($debug) { |
||
735 | error_log("Question duration = 0, in exeId: $exeId, question_id: $my_question_id"); |
||
736 | } |
||
737 | exit; |
||
738 | } |
||
739 | } |
||
740 | } |
||
741 | |||
742 | // Deleting old attempt. |
||
743 | if (isset($attemptList) && !empty($attemptList[$my_question_id])) { |
||
744 | if ($debug) { |
||
745 | error_log("delete_attempt exe_id : $exeId, my_question_id: $my_question_id"); |
||
746 | } |
||
747 | Event::delete_attempt( |
||
748 | $exeId, |
||
749 | api_get_user_id(), |
||
750 | $course_id, |
||
751 | $session_id, |
||
752 | $my_question_id |
||
753 | ); |
||
754 | if (in_array($objQuestionTmp->type, [HOT_SPOT, HOT_SPOT_COMBINATION])) { |
||
755 | Event::delete_attempt_hotspot( |
||
756 | $exeId, |
||
757 | api_get_user_id(), |
||
758 | $course_id, |
||
759 | $session_id, |
||
760 | $my_question_id |
||
761 | ); |
||
762 | } |
||
763 | |||
764 | if (isset($attemptList[$my_question_id]) && |
||
765 | isset($attemptList[$my_question_id]['marks']) |
||
766 | ) { |
||
767 | $total_score -= $attemptList[$my_question_id]['marks']; |
||
768 | } |
||
769 | } |
||
770 | |||
771 | // We're inside *one* question. Go through each possible answer for this question |
||
772 | if ($objQuestionTmp->type === MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) { |
||
773 | $myChoiceTmp = []; |
||
774 | $myChoiceTmp['choice'] = $my_choice; |
||
775 | $myChoiceTmp['choiceDegreeCertainty'] = $myChoiceDegreeCertainty; |
||
776 | $result = $objExercise->manage_answer( |
||
777 | $exeId, |
||
778 | $my_question_id, |
||
779 | $myChoiceTmp, |
||
780 | 'exercise_result', |
||
781 | $hot_spot_coordinates, |
||
782 | true, |
||
783 | false, |
||
784 | false, |
||
785 | $objExercise->selectPropagateNeg(), |
||
786 | $hotspot_delineation_result, |
||
787 | true, |
||
788 | false, |
||
789 | false, |
||
790 | $questionDuration |
||
791 | ); |
||
792 | } else { |
||
793 | $result = $objExercise->manage_answer( |
||
794 | $exeId, |
||
795 | $my_question_id, |
||
796 | $my_choice, |
||
797 | 'exercise_result', |
||
798 | $hot_spot_coordinates, |
||
799 | true, |
||
800 | false, |
||
801 | false, |
||
802 | $objExercise->selectPropagateNeg(), |
||
803 | $hotspot_delineation_result, |
||
804 | true, |
||
805 | false, |
||
806 | false, |
||
807 | $questionDuration |
||
808 | ); |
||
809 | } |
||
810 | |||
811 | // Adding the new score. |
||
812 | $total_score += $result['score']; |
||
813 | |||
814 | if ($debug) { |
||
815 | error_log("total_score: $total_score "); |
||
816 | error_log("total_weight: $total_weight "); |
||
817 | } |
||
818 | |||
819 | $duration = 0; |
||
820 | if ($type === 'all') { |
||
821 | $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId); |
||
822 | } |
||
823 | |||
824 | $key = ExerciseLib::get_time_control_key( |
||
825 | $exercise_id, |
||
826 | $exercise_stat_info['orig_lp_id'], |
||
827 | $exercise_stat_info['orig_lp_item_id'] |
||
828 | ); |
||
829 | |||
830 | $durationTime = Session::read('duration_time'); |
||
831 | if (isset($durationTime[$key]) && !empty($durationTime[$key])) { |
||
832 | if ($debug) { |
||
833 | error_log('Session time: '.$durationTime[$key]); |
||
834 | } |
||
835 | $duration = $now - $durationTime[$key]; |
||
836 | if (!empty($exercise_stat_info['exe_duration'])) { |
||
837 | $duration += $exercise_stat_info['exe_duration']; |
||
838 | } |
||
839 | $duration = (int) $duration; |
||
840 | } else { |
||
841 | if (!empty($exercise_stat_info['exe_duration'])) { |
||
842 | $duration = $exercise_stat_info['exe_duration']; |
||
843 | } |
||
844 | } |
||
845 | |||
846 | if ($debug) { |
||
847 | error_log('duration to save in DB:'.$duration); |
||
848 | } |
||
849 | Session::write('duration_time', [$key => $now]); |
||
850 | Event::updateEventExercise( |
||
851 | $exeId, |
||
852 | $objExercise->selectId(), |
||
853 | $total_score, |
||
854 | $total_weight, |
||
855 | $session_id, |
||
856 | $exercise_stat_info['orig_lp_id'], |
||
857 | $exercise_stat_info['orig_lp_item_id'], |
||
858 | $exercise_stat_info['orig_lp_item_view_id'], |
||
859 | $duration, |
||
860 | $question_list, |
||
861 | 'incomplete', |
||
862 | $remind_list |
||
863 | ); |
||
864 | |||
865 | if (api_get_configuration_value('allow_time_per_question')) { |
||
866 | $questionStart = Session::read('question_start', []); |
||
867 | if (!empty($questionStart)) { |
||
868 | if (isset($questionStart[$my_question_id])) { |
||
869 | unset($questionStart[$my_question_id]); |
||
870 | } |
||
871 | array_filter($questionStart); |
||
872 | Session::write('question_start', $questionStart); |
||
873 | } |
||
874 | } |
||
875 | |||
876 | HookQuizQuestionAnswered::create() |
||
877 | ->setEventData( |
||
878 | [ |
||
879 | 'exe_id' => (int) $exeId, |
||
880 | 'quiz' => [ |
||
881 | 'id' => (int) $objExercise->iid, |
||
882 | 'title' => $objExercise->selectTitle(true), |
||
883 | ], |
||
884 | 'question' => [ |
||
885 | 'id' => (int) $my_question_id, |
||
886 | 'weight' => (float) $result['weight'], |
||
887 | ], |
||
888 | ] |
||
889 | ) |
||
890 | ->notifyQuizQuestionAnswered(); |
||
891 | |||
892 | // Destruction of the Question object |
||
893 | unset($objQuestionTmp); |
||
894 | if ($debug) { |
||
895 | error_log("---------- end question ------------"); |
||
896 | } |
||
897 | } |
||
898 | if ($debug) { |
||
899 | error_log('Finished questions loop in save_exercise_by_now'); |
||
900 | } |
||
901 | |||
902 | $questionsCount = count(explode(',', $exercise_stat_info['data_tracking'])); |
||
903 | $savedAnswersCount = $objExercise->countUserAnswersSavedInExercise($exeId); |
||
904 | |||
905 | if ($savedAnswersCount !== $questionsCount) { |
||
906 | $savedQuestionsMessage = Display::span( |
||
907 | sprintf(get_lang('XAnswersSavedByUsersFromXTotal'), $savedAnswersCount, $questionsCount), |
||
908 | ['class' => 'text-warning'] |
||
909 | ); |
||
910 | } else { |
||
911 | $savedQuestionsMessage = Display::span( |
||
912 | sprintf(get_lang('XAnswersSavedByUsersFromXTotal'), $savedAnswersCount, $questionsCount), |
||
913 | ['class' => 'text-success'] |
||
914 | ); |
||
915 | } |
||
916 | |||
917 | if ($type === 'all') { |
||
918 | if ($debug) { |
||
919 | error_log("result: ok - all"); |
||
920 | error_log(" ------ end ajax call ------- "); |
||
921 | } |
||
922 | echo json_encode(['ok' => true, 'savedAnswerMessage' => $savedQuestionsMessage]); |
||
923 | exit; |
||
924 | } |
||
925 | |||
926 | if ($objExercise->type == ONE_PER_PAGE) { |
||
927 | if ($debug) { |
||
928 | error_log("result: one_per_page"); |
||
929 | error_log(" ------ end ajax call ------- "); |
||
930 | } |
||
931 | echo json_encode(['type' => 'one_per_page', 'savedAnswerMessage' => $savedQuestionsMessage]); |
||
932 | exit; |
||
933 | } |
||
934 | if ($debug) { |
||
935 | error_log("result: ok"); |
||
936 | error_log(" ------ end ajax call ------- "); |
||
937 | } |
||
938 | echo json_encode(['ok' => true, 'savedAnswerMessage' => $savedQuestionsMessage]); |
||
939 | break; |
||
940 | case 'show_question_attempt': |
||
941 | $isAllowedToEdit = api_is_allowed_to_edit(null, true, false, false); |
||
942 | |||
943 | if (!$isAllowedToEdit) { |
||
944 | api_not_allowed(true); |
||
945 | exit; |
||
946 | } |
||
947 | |||
948 | $questionId = isset($_GET['question']) ? (int) $_GET['question'] : 0; |
||
949 | $exerciseId = isset($_REQUEST['exercise']) ? (int) $_REQUEST['exercise'] : 0; |
||
950 | |||
951 | if (!$questionId || !$exerciseId) { |
||
952 | break; |
||
953 | } |
||
954 | |||
955 | $objExercise = new Exercise(); |
||
956 | $objExercise->read($exerciseId); |
||
957 | $objQuestion = Question::read($questionId); |
||
958 | $id = ''; |
||
959 | if (api_get_configuration_value('show_question_id')) { |
||
960 | $id = '<h4>#'.$objQuestion->course['code'].'-'.$objQuestion->iid.'</h4>'; |
||
961 | } |
||
962 | echo $id; |
||
963 | echo '<p class="lead">'.$objQuestion->get_question_type_name().'</p>'; |
||
964 | if (in_array($objQuestion->type, [FILL_IN_BLANKS, FILL_IN_BLANKS_COMBINATION])) { |
||
965 | echo '<script> |
||
966 | $(function() { |
||
967 | $(".selectpicker").selectpicker({}); |
||
968 | }); |
||
969 | </script>'; |
||
970 | } |
||
971 | |||
972 | // Allows render MathJax elements in a ajax call |
||
973 | if (api_get_setting('include_asciimathml_script') === 'true') { |
||
974 | echo '<script> MathJax.Hub.Queue(["Typeset",MathJax.Hub]);</script>'; |
||
975 | } |
||
976 | |||
977 | if (in_array($objQuestion->type, [HOT_SPOT, HOT_SPOT_COMBINATION])) { |
||
978 | echo '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>'; |
||
979 | } |
||
980 | |||
981 | $attemptList = []; |
||
982 | if (!empty($exeId)) { |
||
983 | $attemptList = Event::getAllExerciseEventByExeId($exeId); |
||
984 | } |
||
985 | |||
986 | $userChoice = isset($attemptList[$questionId]) ? $attemptList[$questionId] : null; |
||
987 | |||
988 | ExerciseLib::showQuestion( |
||
989 | $objExercise, |
||
990 | $questionId, |
||
991 | false, |
||
992 | null, |
||
993 | null, |
||
994 | false, |
||
995 | true, |
||
996 | $userChoice, |
||
997 | true |
||
998 | ); |
||
999 | break; |
||
1000 | case 'show_question': |
||
1001 | $isAllowedToEdit = api_is_allowed_to_edit(null, true, false, false); |
||
1002 | |||
1003 | if (!$isAllowedToEdit) { |
||
1004 | api_not_allowed(true); |
||
1005 | exit; |
||
1006 | } |
||
1007 | |||
1008 | $questionId = isset($_GET['question']) ? (int) $_GET['question'] : 0; |
||
1009 | $exerciseId = isset($_REQUEST['exercise']) ? (int) $_REQUEST['exercise'] : 0; |
||
1010 | |||
1011 | if (!$questionId || !$exerciseId) { |
||
1012 | break; |
||
1013 | } |
||
1014 | |||
1015 | $objExercise = new Exercise(); |
||
1016 | $objExercise->read($exerciseId); |
||
1017 | $objQuestion = Question::read($questionId); |
||
1018 | $id = ''; |
||
1019 | if (api_get_configuration_value('show_question_id')) { |
||
1020 | $id = '<h4>#'.$objQuestion->course['code'].'-'.$objQuestion->iid.'</h4>'; |
||
1021 | } |
||
1022 | echo $id; |
||
1023 | echo '<p class="lead">'.$objQuestion->get_question_type_name().'</p>'; |
||
1024 | if (in_array($objQuestion->type, [FILL_IN_BLANKS, FILL_IN_BLANKS_COMBINATION])) { |
||
1025 | echo '<script> |
||
1026 | $(function() { |
||
1027 | $(".selectpicker").selectpicker({}); |
||
1028 | }); |
||
1029 | </script>'; |
||
1030 | } |
||
1031 | |||
1032 | // Allows render MathJax elements in a ajax call |
||
1033 | if (api_get_setting('include_asciimathml_script') === 'true') { |
||
1034 | echo '<script> MathJax.Hub.Queue(["Typeset",MathJax.Hub]);</script>'; |
||
1035 | } |
||
1036 | |||
1037 | ExerciseLib::showQuestion( |
||
1038 | $objExercise, |
||
1039 | $questionId, |
||
1040 | false, |
||
1041 | null, |
||
1042 | null, |
||
1043 | false, |
||
1044 | true, |
||
1045 | false, |
||
1046 | true, |
||
1047 | true |
||
1048 | ); |
||
1049 | break; |
||
1050 | case 'get_quiz_embeddable': |
||
1051 | $exercises = ExerciseLib::get_all_exercises_for_course_id( |
||
1052 | api_get_course_info(), |
||
1053 | api_get_session_id(), |
||
1054 | api_get_course_int_id(), |
||
1055 | false |
||
1056 | ); |
||
1057 | |||
1058 | $exercises = array_filter( |
||
1059 | $exercises, |
||
1060 | function (array $exercise) { |
||
1061 | return ExerciseLib::isQuizEmbeddable($exercise); |
||
1062 | } |
||
1063 | ); |
||
1064 | |||
1065 | $result = []; |
||
1066 | |||
1067 | $codePath = api_get_path(WEB_CODE_PATH); |
||
1068 | |||
1069 | foreach ($exercises as $exercise) { |
||
1070 | $title = Security::remove_XSS(api_html_entity_decode($exercise['title'])); |
||
1071 | |||
1072 | $result[] = [ |
||
1073 | 'id' => $exercise['iid'], |
||
1074 | 'title' => strip_tags($title), |
||
1075 | ]; |
||
1076 | } |
||
1077 | |||
1078 | header('Content-Type: application/json'); |
||
1079 | echo json_encode($result); |
||
1080 | break; |
||
1081 | case 'browser_test': |
||
1082 | $quizCheckButtonEnabled = api_get_configuration_value('quiz_check_button_enable'); |
||
1083 | |||
1084 | if ($quizCheckButtonEnabled) { |
||
1085 | if (isset($_POST['sleep'])) { |
||
1086 | sleep(2); |
||
1087 | } |
||
1088 | |||
1089 | echo 'ok'; |
||
1090 | } |
||
1091 | |||
1092 | break; |
||
1093 | case 'quiz_confirm_saved_answers': |
||
1094 | if (false === api_get_configuration_value('quiz_confirm_saved_answers')) { |
||
1095 | break; |
||
1096 | } |
||
1097 | |||
1098 | $trackConfirmationId = isset($_POST['tc_id']) ? (int) $_POST['tc_id'] : 0; |
||
1099 | $cId = api_get_course_int_id(); |
||
1100 | $sessionId = api_get_session_id(); |
||
1101 | $userId = api_get_user_id(); |
||
1102 | $confirmed = !empty($_POST['quiz_confirm_saved_answers_check']); |
||
1103 | |||
1104 | $em = Database::getManager(); |
||
1105 | $repo = $em->getRepository('ChamiloCoreBundle:TrackEExerciseConfirmation'); |
||
1106 | |||
1107 | try { |
||
1108 | if (!$trackConfirmationId) { |
||
1109 | throw new Exception(get_lang('ErrorOccurred')); |
||
1110 | } |
||
1111 | |||
1112 | /** @var TrackEExerciseConfirmation $trackConfirmation */ |
||
1113 | $trackConfirmation = $repo->findOneBy( |
||
1114 | [ |
||
1115 | 'id' => $trackConfirmationId, |
||
1116 | 'userId' => $userId, |
||
1117 | 'courseId' => $cId, |
||
1118 | 'sessionId' => $sessionId, |
||
1119 | ], |
||
1120 | ['createdAt' => 'DESC'] |
||
1121 | ); |
||
1122 | |||
1123 | if (!$trackConfirmation) { |
||
1124 | throw new Exception(get_lang('NotFound')); |
||
1125 | } |
||
1126 | |||
1127 | $trackConfirmation |
||
1128 | ->setConfirmed($confirmed) |
||
1129 | ->setUpdatedAt(api_get_utc_datetime(null, false, true)); |
||
1130 | |||
1131 | $em->persist($trackConfirmation); |
||
1132 | $em->flush(); |
||
1133 | |||
1134 | http_response_code(200); |
||
1135 | } catch (Exception $exception) { |
||
1136 | http_response_code(500); |
||
1137 | |||
1138 | echo Display::return_message($exception->getMessage(), 'error'); |
||
1139 | } |
||
1140 | |||
1141 | break; |
||
1142 | case 'sign_attempt': |
||
1143 | api_block_anonymous_users(); |
||
1144 | if ('true' !== api_get_plugin_setting('exercise_signature', 'tool_enable')) { |
||
1145 | exit; |
||
1146 | } |
||
1147 | |||
1148 | $file = isset($_REQUEST['file']) ? $_REQUEST['file'] : ''; |
||
1149 | if (empty($exeId) || empty($file)) { |
||
1150 | echo 0; |
||
1151 | exit; |
||
1152 | } |
||
1153 | |||
1154 | $file = str_replace(' ', '+', $file); |
||
1155 | $track = ExerciseLib::get_exercise_track_exercise_info($exeId); |
||
1156 | if ($track) { |
||
1157 | $result = ExerciseSignaturePlugin::saveSignature($currentUserId, $track, $file); |
||
1158 | if ($result) { |
||
1159 | echo 1; |
||
1160 | exit; |
||
1161 | } |
||
1162 | } |
||
1163 | echo 0; |
||
1164 | break; |
||
1165 | case 'upload_answer': |
||
1166 | api_block_anonymous_users(); |
||
1167 | |||
1168 | if (isset($_REQUEST['chunkAction']) && 'send' === $_REQUEST['chunkAction']) { |
||
1169 | // It uploads the files in chunks |
||
1170 | if (!empty($_FILES)) { |
||
1171 | $tempDirectory = api_get_path(SYS_ARCHIVE_PATH); |
||
1172 | $files = $_FILES['files']; |
||
1173 | $fileList = []; |
||
1174 | foreach ($files as $name => $array) { |
||
1175 | $counter = 0; |
||
1176 | foreach ($array as $data) { |
||
1177 | $fileList[$counter][$name] = $data; |
||
1178 | $counter++; |
||
1179 | } |
||
1180 | } |
||
1181 | if (!empty($fileList)) { |
||
1182 | foreach ($fileList as $n => $file) { |
||
1183 | $tmpFile = disable_dangerous_file( |
||
1184 | api_replace_dangerous_char($file['name']) |
||
1185 | ); |
||
1186 | |||
1187 | file_put_contents( |
||
1188 | $tempDirectory.$tmpFile, |
||
1189 | fopen($file['tmp_name'], 'r'), |
||
1190 | FILE_APPEND |
||
1191 | ); |
||
1192 | } |
||
1193 | } |
||
1194 | } |
||
1195 | echo json_encode([ |
||
1196 | 'files' => $_FILES, |
||
1197 | 'errorStatus' => 0, |
||
1198 | ]); |
||
1199 | exit; |
||
1200 | } else { |
||
1201 | if (!empty($_FILES)) { |
||
1202 | $currentDirectory = Security::remove_XSS($_REQUEST['curdirpath']); |
||
1203 | $userId = api_get_user_id(); |
||
1204 | |||
1205 | // Upload answer path is created inside user personal folder my_files/upload_answer/[exe_id]/[question_id] |
||
1206 | $syspath = UserManager::getUserPathById($userId, 'system').'my_files'.$currentDirectory; |
||
1207 | @mkdir($syspath, api_get_permissions_for_new_directories(), true); |
||
1208 | $webpath = UserManager::getUserPathById($userId, 'web').'my_files'.$currentDirectory; |
||
1209 | |||
1210 | $files = $_FILES['files']; |
||
1211 | $fileList = []; |
||
1212 | foreach ($files as $name => $array) { |
||
1213 | $counter = 0; |
||
1214 | foreach ($array as $data) { |
||
1215 | $fileList[$counter][$name] = $data; |
||
1216 | $counter++; |
||
1217 | } |
||
1218 | } |
||
1219 | $resultList = []; |
||
1220 | foreach ($fileList as $file) { |
||
1221 | $json = []; |
||
1222 | |||
1223 | if (isset($_REQUEST['chunkAction']) && 'done' === $_REQUEST['chunkAction']) { |
||
1224 | // to rename and move the finished file |
||
1225 | $chunkedFile = api_get_path(SYS_ARCHIVE_PATH).$file['name']; |
||
1226 | $file['tmp_name'] = $chunkedFile; |
||
1227 | $file['size'] = filesize($chunkedFile); |
||
1228 | $file['copy_file'] = true; |
||
1229 | } |
||
1230 | |||
1231 | $filename = api_replace_dangerous_char($file['name']); |
||
1232 | $filename = disable_dangerous_file($filename); |
||
1233 | |||
1234 | if (isset($file['copy_file']) && $file['copy_file']) { |
||
1235 | $uploaded = copy($file['tmp_name'], $syspath.$filename); |
||
1236 | @unlink($file['tmp_name']); |
||
0 ignored issues
–
show
|
|||
1237 | } else { |
||
1238 | $uploaded = move_uploaded_file($file['tmp_name'], $syspath.$filename); |
||
1239 | } |
||
1240 | |||
1241 | if ($uploaded) { |
||
1242 | $title = $filename; |
||
1243 | $url = $webpath.$filename; |
||
1244 | $json['name'] = api_htmlentities($title); |
||
1245 | $json['link'] = Display::url( |
||
1246 | api_htmlentities($title), |
||
1247 | api_htmlentities($url), |
||
1248 | ['target' => '_blank'] |
||
1249 | ); |
||
1250 | $json['url'] = $url; |
||
1251 | $json['size'] = format_file_size($file['size']); |
||
1252 | $json['type'] = api_htmlentities($file['type']); |
||
1253 | $json['result'] = Display::return_icon( |
||
1254 | 'accept.png', |
||
1255 | get_lang('Uploaded') |
||
1256 | ); |
||
1257 | } else { |
||
1258 | $json['name'] = isset($file['name']) ? $filename : get_lang('Unknown'); |
||
1259 | $json['url'] = ''; |
||
1260 | $json['error'] = get_lang('Error'); |
||
1261 | } |
||
1262 | $resultList[] = $json; |
||
1263 | } |
||
1264 | echo json_encode(['files' => $resultList]); |
||
1265 | exit; |
||
1266 | } |
||
1267 | } |
||
1268 | break; |
||
1269 | default: |
||
1270 | echo ''; |
||
1271 | } |
||
1272 | exit; |
||
1273 |
If you suppress an error, we recommend checking for the error condition explicitly: