PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.1
Tutor LMS – eLearning and online course solution v4.0.1
4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / views / quiz / attempt-details.php
tutor / views / quiz Last commit date
header-context 4 days ago attempt-details.php 4 days ago attempt-table.php 4 days ago contexts.php 3 years ago header.php 4 days ago instructor-feedback.php 11 months ago
attempt-details.php
850 lines
1 <?php
2 /**
3 * Attempt details page
4 *
5 * @package Tutor\Views
6 * @subpackage Tutor\Quiz
7 * @author Themeum <support@themeum.com>
8 * @link https://themeum.com
9 * @since 1.0.0
10 */
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 use Tutor\Models\QuizModel;
17 use Tutor\Components\SvgIcon;
18 use TUTOR\Icon;
19
20 $enabled_hide_quiz_details = tutor_utils()->get_option( 'hide_quiz_details' );
21 if ( ! is_admin() && ! current_user_can( 'tutor_instructor' ) && true === $enabled_hide_quiz_details ) {
22 exit;
23 }
24
25 //phpcs:ignore
26 extract( $data ); // $user_id, $attempt_id, $attempt_data(nullable), $context(nullable)
27
28 ! isset( $attempt_data ) ? $attempt_data = tutor_utils()->get_attempt( $attempt_id ) : 0;
29 ! isset( $context ) ? $context = null : 0;
30
31 if ( ! $attempt_id || ! $attempt_data || $user_id != $attempt_data->user_id ) {
32 tutor_utils()->tutor_empty_state( __( 'Attempt not found or access permission denied', 'tutor' ) );
33 return;
34 }
35
36 if ( isset( $user_id ) && $user_id > 0 ) {
37 $user = get_userdata( $user_id );
38 if ( ! $user ) {
39 return;
40 }
41 }
42
43 /**
44 * Render answer list
45 *
46 * @param array $answers answers.
47 * @param boolean $dump_data dump data.
48 *
49 * @return void
50 */
51 function tutor_render_answer_list( $answers = array(), $dump_data = false ) {
52 if ( ! empty( $answers ) ) {
53
54 echo '<div class="correct-answer-wrap">';
55
56 $multi_texts = array();
57 foreach ( $answers as $key => $ans ) {
58 $type = isset( $ans->answer_view_format ) ? $ans->answer_view_format : 'text_image';
59
60 if ( ! empty( $ans->answer_two_gap_match ) ) {
61 echo '<div class="matching-type">';
62 }
63
64 switch ( $type ) {
65 case 'text_image':
66 echo '<div class="text-image-type tutor-mb-4">';
67 if ( isset( $ans->image_id ) ) {
68 $img_url = wp_get_attachment_image_url( $ans->image_id );
69 if ( $img_url ) {
70 echo '<span class="image"><img src="' . esc_url( $img_url ) . '" /></span>';
71 }
72 }
73 if ( isset( $ans->answer_title ) ) {
74 echo '<span class="caption">' . esc_html( stripslashes( $ans->answer_title ) ) . '</span>';
75 }
76 echo '</div>';
77 break;
78
79 case 'text':
80 $ans_string = '<span class="tutor-fs-7 tutor-fw-medium tutor-color-black">'
81 . esc_html( stripslashes( $ans->answer_title ) ) .
82 '</span>';
83
84 if ( isset( $ans->answer_title ) && empty( $ans->answer_two_gap_match ) ) {
85 $multi_texts[ $ans->answer_title ] = $ans_string;
86 } else {
87 echo $ans_string; //phpcs:ignore -- contain safe data
88 }
89 break;
90
91 case 'image':
92 echo '<div class="image-type">';
93 if ( isset( $ans->image_id ) ) {
94 $img_url = wp_get_attachment_image_url( $ans->image_id );
95 if ( $img_url ) {
96 echo '
97 <span class="image">
98 <img src="' . esc_url( $img_url ) . '" />
99 <span>';
100 }
101 }
102 echo '</div>';
103 break;
104 }
105
106 if ( ! empty( $ans->answer_two_gap_match ) ) {
107 echo '<div class="image-match">' . esc_html( stripslashes( $ans->answer_two_gap_match ) ) . '</div>';
108 echo '</div>';
109 }
110 }
111 //phpcs:ignore
112 echo count( $multi_texts ) ? implode( ', ', $multi_texts ) : '';
113
114 echo '</div>';
115 }
116 }
117
118 /**
119 * Render fill in the blank answer
120 *
121 * @param mixed $get_db_answers_by_question get db answers by question.
122 * @param mixed $answer_titles ans titles.
123 *
124 * @return void
125 */
126 function tutor_render_fill_in_the_blank_answer( $get_db_answers_by_question, $answer_titles ) {
127
128 $spaces = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
129
130 // Loop through the answers.
131 foreach ( $get_db_answers_by_question as $db_answer ) {
132 $count_dash_fields = substr_count( $db_answer->answer_title, '{dash}' );
133
134 if ( $count_dash_fields ) {
135 $dash_string = array();
136 $input_data = array();
137 for ( $i = 0; $i < $count_dash_fields; $i++ ) {
138 $ans_title = ( ! empty( $answer_titles[ $i ] ) && ! ctype_space( $answer_titles[ $i ] ) ) ? $answer_titles[ $i ] : null;
139 $input_data[] = $ans_title ? "<span class='filled_dash_unser'>{$ans_title}</span>" : $spaces;
140 }
141
142 $answer_title = $db_answer->answer_title;
143
144 foreach ( $input_data as $index => $replace ) {
145 $replace = '<span style="text-decoration:underline;">' . $replace . '</span>';
146 $answer_title = preg_replace( '/{dash}/i', $replace, $answer_title, 1 );
147 }
148 echo wp_kses(
149 str_replace( '{dash}', "<span class='filled_dash_unser'>{$spaces}</span>", stripslashes( $answer_title ) ),
150 array(
151 'span' => array(
152 'style' => true,
153 'class' => true,
154 ),
155 )
156 );
157 }
158 }
159 }
160
161 /**
162 * Render question type icon using v4 icon component.
163 *
164 * @since 4.0.0
165 *
166 * @param string $question_type Question type slug.
167 *
168 * @return string
169 */
170 if ( ! function_exists( 'tutor_render_question_type_icon' ) ) {
171 /**
172 * Render question type icon using v4 icon component.
173 *
174 * @since 4.0.0
175 *
176 * @param string $question_type Question type slug.
177 *
178 * @return string
179 */
180 function tutor_render_question_type_icon( $question_type ) {
181 $normalized_type = (string) $question_type;
182
183 if ( 'single_choice' === $normalized_type ) {
184 $normalized_type = 'multiple_choice';
185 }
186
187 if ( 'image_matching' === $normalized_type ) {
188 $normalized_type = 'matching';
189 }
190
191 $question_type_icon_map = array(
192 'true_false' => Icon::QUIZ_TRUE_FALSE,
193 'multiple_choice' => Icon::QUIZ_MULTI_CHOICE,
194 'open_ended' => Icon::QUIZ_ESSAY,
195 'fill_in_the_blank' => Icon::QUIZ_FILL_IN_THE_BLANKS,
196 'short_answer' => Icon::QUIZ_SHORT_ANSWER,
197 'matching' => Icon::QUIZ_IMAGE_MATCHING,
198 'image_answering' => Icon::QUIZ_IMAGE_ANSWER,
199 'ordering' => Icon::QUIZ_ORDERING,
200 'draw_image' => Icon::QUIZ_MARK_IN_THE_IMAGE,
201 'scale' => Icon::QUIZ_RANGE,
202 'pin_image' => Icon::QUIZ_PIN,
203 'puzzle' => Icon::QUIZ_PUZZLE,
204 'coordinates' => Icon::QUIZ_GRAPH,
205 'h5p' => Icon::QUIZ_H5P,
206 );
207
208 return $question_type_icon_map[ $normalized_type ] ?? '';
209 }
210 }
211
212 // Prepare student data.
213 if ( ! isset( $user_data ) ) {
214 $user_data = get_userdata( $user_id );
215 }
216
217 //phpcs:ignore
218 extract( QuizModel::get_quiz_attempt_timing( $attempt_data ) ); // $attempt_duration, $attempt_duration_taken;
219
220 // Prepare the correct/incorrect answer count for the first summary table.
221 $answers = QuizModel::get_quiz_answers_by_attempt_id( $attempt_id );
222 $correct = 0;
223 $incorrect = 0;
224 if ( is_array( $answers ) && count( $answers ) > 0 ) {
225 foreach ( $answers as $answer ) {
226 if ( (bool) isset( $answer->is_correct ) ? $answer->is_correct : '' ) {
227 $correct++;
228 } elseif ( 'open_ended' === $answer->question_type || 'short_answer' === $answer->question_type ) {
229 } else {
230 $incorrect++;
231 }
232 }
233 }
234
235 // Prepare the column list for the first summary table.
236 $page_key = 'attempt-details-summary';
237 $table_1_columns = include __DIR__ . '/contexts.php';
238
239 // Prepare the column list for the second table (eery single answer list).
240 $page_key = 'attempt-details-answers';
241 $table_2_columns = include __DIR__ . '/contexts.php';
242
243 require __DIR__ . '/header.php';
244
245 $attempt_info = @unserialize( $attempt_data->attempt_info );
246
247 if ( is_array( $attempt_info ) ) {
248 $attempt_type = '';
249 // Allowed duration.
250 if ( isset( $attempt_info['time_limit'] ) ) {
251 $attempt_duration = tutor_utils()->second_to_formated_time( $attempt_info['time_limit']['time_limit_seconds'], $attempt_info['time_limit']['time_type'] );
252 }
253 if ( 'days' === $attempt_info['time_limit']['time_type'] ) {
254 $attempt_type = 'hours';
255 }
256 if ( 'hours' === $attempt_info['time_limit']['time_type'] ) {
257 $attempt_type = 'minutes';
258 }
259 if ( 'minutes' === $attempt_info['time_limit']['time_type'] ) {
260 $attempt_type = 'minutes';
261 }
262
263 // Taken duration.
264 $seconds = strtotime( $attempt_data->attempt_ended_at ) - strtotime( $attempt_data->attempt_started_at );
265 $attempt_duration_taken = tutor_utils()->second_to_formated_time( $seconds, $attempt_type );
266 }
267 ?>
268
269 <?php echo is_admin() ? '<div class="tutor-admin-body">' : ''; ?>
270 <div class="tutor-table-responsive tutor-table-mobile tutor-mb-32">
271 <table class="tutor-table tutor-quiz-attempt-details">
272 <thead>
273 <tr>
274 <?php foreach ( $table_1_columns as $key => $column ) : ?>
275 <th><?php echo $column; //phpcs:ignore --contain safe data ?></th>
276 <?php endforeach; ?>
277 </tr>
278 </thead>
279
280 <tbody>
281 <tr>
282 <?php foreach ( $table_1_columns as $key => $column ) : ?>
283 <td data-title="<?php echo esc_attr( $column ); ?>">
284 <?php if ( 'user' == $key ) : ?>
285 <div class="tutor-d-flex tutor-align-center">
286 <?php
287 echo wp_kses(
288 tutor_utils()->get_tutor_avatar( $user_id ),
289 tutor_utils()->allowed_avatar_tags()
290 );
291 ?>
292 <div class="tutor-ml-16">
293 <div>
294 <?php
295 echo esc_html(
296 $user_data ? $user_data->display_name : ''
297 );
298 ?>
299 </div>
300 <a href="<?php echo esc_url( tutor_utils()->profile_url( $user_id, false ) ); ?>" class="tutor-iconic-btn">
301 <span class="tutor-icon-external-link"></span>
302 </a>
303 </div>
304 </div>
305
306 <?php elseif ( 'date' == $key ) : ?>
307 <?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $attempt_data->attempt_started_at ) ) ); ?>
308 <?php elseif ( 'qeustion_count' === $key ) : ?>
309 <?php echo esc_html( $attempt_data->total_questions ); ?>
310 <?php elseif ( 'quiz_time' === $key ) : ?>
311 <?php echo esc_html( $attempt_duration ); ?>
312 <?php elseif ( 'attempt_time' === $key ) : ?>
313 <?php echo esc_html( $attempt_duration_taken ); ?>
314 <?php elseif ( 'total_marks' === $key ) : ?>
315 <?php echo esc_html( $attempt_data->total_marks ); ?>
316 <?php elseif ( 'pass_marks' === $key ) : ?>
317 <?php
318 $pass_marks = ( $total_marks * $passing_grade ) / 100;
319 echo esc_html( number_format_i18n( $pass_marks, 2 ) );
320
321 $pass_mark_percent = $passing_grade;
322 echo esc_html( ' (' . $pass_mark_percent . '%)' );
323 ?>
324 <?php elseif ( 'correct_answer' === $key ) : ?>
325 <?php echo esc_html( $correct ); ?>
326 <?php elseif ( 'incorrect_answer' === $key ) : ?>
327 <?php echo esc_html( $incorrect ); ?>
328 <?php elseif ( 'earned_marks' === $key ) : ?>
329 <?php
330 $earned_percentage = QuizModel::calculate_attempt_earned_percentage( $attempt_data );
331 echo esc_html( $attempt_data->earned_marks );
332 echo esc_html( ' (' . $earned_percentage . '%)' );
333 ?>
334 <?php elseif ( 'result' === $key ) : ?>
335 <?php
336 $attempt_result = QuizModel::get_attempt_result( $attempt_data->attempt_id );
337
338 if ( QuizModel::RESULT_PENDING === $attempt_result ) {
339 echo '<span class="tutor-badge-label label-warning">' . esc_html__( 'Pending', 'tutor' ) . '</span>';
340 } elseif ( QuizModel::RESULT_PASS === $attempt_result ) {
341 echo '<span class="tutor-badge-label label-success">' . esc_html__( 'Pass', 'tutor' ) . '</span>';
342 } else {
343 echo '<span class="tutor-badge-label label-danger">' . esc_html__( 'Fail', 'tutor' ) . '</span>';
344 }
345 ?>
346 <?php endif; ?>
347 </td>
348 <?php endforeach; ?>
349 </tr>
350 </tbody>
351 </table>
352 </div>
353
354 <?php
355 // instructor feedback.
356 global $wp_query;
357 $query_vars = $wp_query->query_vars;
358 $page_name = isset( $query_vars['tutor_dashboard_page'] ) ? $query_vars['tutor_dashboard_page'] : '';
359 $attempt_info = maybe_unserialize( $attempt_data->attempt_info );
360 $feedback = is_array( $attempt_info ) && isset( $attempt_info['instructor_feedback'] ) ? $attempt_info['instructor_feedback'] : '';
361 // don't show on instructor quiz attempt since below already have feedback box area.
362 if ( '' !== $feedback && 'my-quiz-attempts' === $page_name ) {
363 ?>
364 <div class="tutor-quiz-attempt-note tutor-instructor-note tutor-my-32 tutor-py-20 tutor-px-24 tutor-py-sm-32 tutor-px-sm-36">
365 <div class="tutor-in-title tutor-fs-6 tutor-fw-medium tutor-color-black">
366 <?php esc_html_e( 'Instructor Note', 'tutor' ); ?>
367 </div>
368 <div class="tutor-in-body tutor-fs-6 tutor-color-secondary tutor-pt-12 tutor-pt-sm-16">
369 <?php echo wp_kses_post( $feedback ); ?>
370 </div>
371 </div>
372 <?php } ?>
373
374 <?php
375 if ( is_array( $answers ) && count( $answers ) ) {
376 // Filter out not needed columns based on question type.
377 $table_2_columns = apply_filters( 'tutor_filter_attempt_answer_column', $table_2_columns, $answers );
378 $answers = apply_filters( 'tutor_filter_attempt_answers', $answers );
379 echo 'course-single-previous-attempts' !== $context ? '<div class="tutor-fs-6 tutor-fw-medium tutor-color-black tutor-mt-24">' . esc_html__( 'Quiz Overview', 'tutor' ) . '</div>' : '';
380 ?>
381 <div class="tutor-table-responsive tutor-table-mobile tutor-mt-16">
382 <table class="tutor-table tutor-quiz-attempt-details tutor-mb-32 tutor-table-data-td-target">
383 <thead>
384 <tr>
385 <?php foreach ( $table_2_columns as $key => $column ) : ?>
386 <th><?php echo $column; //phpcs:ignore --contain safe data ?></th>
387 <?php endforeach; ?>
388 </tr>
389 </thead>
390
391 <tbody>
392 <?php
393 $answer_i = 0;
394 foreach ( $answers as $answer ) {
395 $answer_i++;
396 $question_type = QuizModel::get_question_types( $answer->question_type );
397 $question_settings = maybe_unserialize( $answer->question_settings );
398 $is_image_matching = isset( $question_settings['is_image_matching'] ) && '1' === $question_settings['is_image_matching'];
399 $answer_status = 'wrong';
400
401 // If already correct, then show it.
402 if ( (bool) $answer->is_correct ) {
403 $answer_status = 'correct';
404 }
405
406 // Image answering also needs review since the answer texts are not meant to match exactly.
407 elseif ( in_array( $answer->question_type, array( 'open_ended', 'short_answer', 'image_answering' ), true ) ) {
408 $answer_status = null === $answer->is_correct ? 'pending' : 'wrong';
409 }
410
411 // Allow Pro and add-ons to set answer status for custom question types.
412 /**
413 * Filter to set answer status for custom question types.
414 * Pro handles draw_image via this filter.
415 *
416 * @param string|null $answer_status Current answer status (null if not set).
417 * @param object $answer Answer object.
418 *
419 * @return string|null Answer status or null to use default.
420 */
421 $custom_status = apply_filters( 'tutor_quiz_answer_status_for_question_type', null, $answer );
422 if ( null !== $custom_status ) {
423 $answer_status = $custom_status;
424 }
425 ?>
426
427 <tr class="tutor-quiz-answer-status-<?php echo esc_html( $answer_status ); ?>">
428 <?php foreach ( $table_2_columns as $key => $column ) : ?>
429 <?php
430 switch ( $key ) {
431 case 'no':
432 ?>
433 <td class="no" data-title="<?php echo esc_attr( $column ); ?>">
434 <span class="tutor-fs-7 tutor-fw-medium tutor-color-black">
435 <?php echo esc_html( $answer_i ); ?>
436 </span>
437 </td>
438 <?php
439 break;
440
441 case 'type':
442 ?>
443 <td class="type" data-title="<?php echo esc_attr( $column ); ?>">
444 <div class="tooltip-wrap tutor-d-inline-flex tutor-align-center">
445 <?php
446 $question_icon_name = tutor_render_question_type_icon( $answer->question_type );
447 if ( ! empty( $question_icon_name ) ) {
448 SvgIcon::make()
449 ->name( $question_icon_name )
450 ->size( 32 )
451 ->render();
452 }
453 ?>
454 <span class="tooltip-txt tooltip-top">
455 <?php echo esc_html( QuizModel::get_question_types( $answer->question_type )['name'] ?? '' ); ?>
456 </span>
457 </div>
458 </td>
459 <?php
460 break;
461
462 case 'questions':
463 ?>
464 <td class="questions" data-title="<?php echo esc_attr( $column ); ?>">
465 <span class="tutor-fs-7 tutor-fw-medium tutor-d-flex tutor-align-center">
466 <?php echo esc_html( stripslashes( $answer->question_title ) ); ?>
467 </span>
468 </td>
469 <?php
470 break;
471
472 case 'given_answer':
473 ?>
474 <td class="given-answer" data-title="<?php echo esc_attr( $column ); ?>">
475 <div>
476 <?php
477 // Single choice.
478 if ( 'single_choice' === $answer->question_type ) {
479 $get_answers = tutor_utils()->get_answer_by_id( $answer->given_answer );
480 tutor_render_answer_list( $get_answers );
481 }
482
483
484 // True false or single choice.
485 if ( 'true_false' === $answer->question_type ) {
486 $get_answers = tutor_utils()->get_answer_by_id( $answer->given_answer );
487 $answer_titles = wp_list_pluck( $get_answers, 'answer_title' );
488 $answer_titles = array_map( 'stripslashes', $answer_titles );
489
490 echo '<span class="tutor-fs-7 tutor-fw-medium tutor-color-black">' .
491 implode( '</p><p>', $answer_titles ) . //phpcs:ignore
492 '</span>';
493 }
494
495 // Multiple choice.
496 elseif ( 'multiple_choice' === $answer->question_type ) {
497 $get_answers = tutor_utils()->get_answer_by_id( maybe_unserialize( $answer->given_answer ) );
498 tutor_render_answer_list( $get_answers );
499 }
500
501 // Fill in the blank.
502 elseif ( 'fill_in_the_blank' === $answer->question_type ) {
503 $answer_titles = maybe_unserialize( $answer->given_answer );
504 $get_db_answers_by_question = QuizModel::get_answers_by_quiz_question( $answer->question_id );
505
506 echo tutor_render_fill_in_the_blank_answer( $get_db_answers_by_question, $answer_titles ); //phpcs:ignore --contain safe data
507 }
508
509 // Open ended or short answer.
510 elseif ( 'open_ended' === $answer->question_type || 'short_answer' === $answer->question_type ) {
511 if ( $answer->given_answer ) {
512 echo wp_kses(
513 wpautop( stripslashes( $answer->given_answer ) ),
514 array(
515 'p' => true,
516 'span' => true,
517 )
518 );
519 }
520 }
521
522 // Ordering.
523 elseif ( 'ordering' === $answer->question_type ) {
524 $ordering_ids = maybe_unserialize( $answer->given_answer );
525 foreach ( $ordering_ids as $ordering_id ) {
526 $get_answers = tutor_utils()->get_answer_by_id( $ordering_id );
527 tutor_render_answer_list( $get_answers );
528 }
529 }
530
531 // Matching.
532 elseif ( 'matching' === $answer->question_type ) {
533
534 $ordering_ids = maybe_unserialize( $answer->given_answer );
535 $original_saved_answers = QuizModel::get_answers_by_quiz_question( $answer->question_id );
536
537 $answers = array();
538
539 foreach ( $original_saved_answers as $key => $original_saved_answer ) {
540 $provided_answer_order_id = isset( $ordering_ids[ $key ] ) ? $ordering_ids[ $key ] : 0;
541 $provided_answer_order = tutor_utils()->get_answer_by_id( $provided_answer_order_id );
542 if ( tutor_utils()->count( $provided_answer_order ) ) {
543 foreach ( $provided_answer_order as $provided_answer_order ) {
544 if ( $is_image_matching ) {
545 $original_saved_answer->answer_view_format = 'text_image';
546 $original_saved_answer->answer_title = $provided_answer_order->answer_title;
547 $original_saved_answer->answer_two_gap_match = '';
548 $answers[] = $original_saved_answer;
549 } else {
550 $original_saved_answer->answer_two_gap_match = $provided_answer_order->answer_two_gap_match;
551 $answers[] = $original_saved_answer;
552 }
553 }
554 }
555 }
556
557 tutor_render_answer_list( $answers );
558 } elseif ( 'image_matching' === $answer->question_type ) {
559
560 $ordering_ids = maybe_unserialize( $answer->given_answer );
561 $original_saved_answers = QuizModel::get_answers_by_quiz_question( $answer->question_id );
562
563 $answers = array();
564
565 foreach ( $original_saved_answers as $key => $original_saved_answer ) {
566 $provided_answer_order_id = isset( $ordering_ids[ $key ] ) ? $ordering_ids[ $key ] : 0;
567 $provided_answer_order = tutor_utils()->get_answer_by_id( $provided_answer_order_id );
568 foreach ( $provided_answer_order as $p_answer ) {
569 if ( $p_answer->answer_title ) {
570 $original_saved_answer->answer_view_format = 'text_image';
571 $original_saved_answer->answer_title = $p_answer->answer_title;
572 $answers[] = $original_saved_answer;
573 }
574 }
575 }
576
577 tutor_render_answer_list( $answers );
578 } elseif ( 'image_answering' === $answer->question_type ) {
579
580 $ordering_ids = maybe_unserialize( $answer->given_answer );
581
582 $answers = array();
583
584 foreach ( $ordering_ids as $answer_id => $image_answer ) {
585 $db_answers = tutor_utils()->get_answer_by_id( $answer_id );
586 foreach ( $db_answers as $db_answer ) {
587 }
588 $db_answer->answer_title = $image_answer;
589 $db_answer->answer_view_format = 'text_image';
590 $answers[] = $db_answer;
591
592 }
593
594 tutor_render_answer_list( $answers );
595 } else {
596 /**
597 * Allow Pro and add-ons to render given answer for custom question types.
598 * Pro handles draw_image and pin_image via this action.
599 *
600 * @param object $answer Answer object.
601 */
602 do_action( 'tutor_quiz_render_given_answer_for_question_type', $answer );
603 }
604 ?>
605 </div>
606 </td>
607 <?php
608 break;
609
610 case 'correct_answer':
611 ?>
612 <td class="correct-answer" data-title="<?php echo esc_attr( $column ); ?>">
613 <div>
614 <?php
615 if ( ( $answer->question_type != 'open_ended' && $answer->question_type != 'short_answer' ) ) {
616
617 global $wpdb;
618
619 // True false.
620 if ( 'true_false' === $answer->question_type ) {
621 $correct_answer = $wpdb->get_var(
622 $wpdb->prepare(
623 "SELECT answer_title FROM {$wpdb->prefix}tutor_quiz_question_answers
624 WHERE belongs_question_id = %d
625 AND belongs_question_type='true_false'
626 AND is_correct = 1",
627 $answer->question_id
628 )
629 );
630
631 echo '<span class="tutor-fs-7 tutor-fw-medium tutor-color-black">' .
632 esc_html( $correct_answer ) .
633 '</span>';
634 }
635
636 // Single choice.
637 elseif ( 'single_choice' === $answer->question_type ) {
638 $correct_answer = $wpdb->get_results(
639 $wpdb->prepare(
640 "SELECT answer_title, image_id, answer_view_format
641 FROM {$wpdb->prefix}tutor_quiz_question_answers
642 WHERE belongs_question_id = %d
643 AND belongs_question_type='single_choice' AND
644 is_correct = 1",
645 $answer->question_id
646 )
647 );
648
649 tutor_render_answer_list( $correct_answer );
650 }
651
652 // Multiple choice.
653 elseif ( 'multiple_choice' === $answer->question_type ) {
654 $correct_answer = $wpdb->get_results(
655 $wpdb->prepare(
656 "SELECT answer_title, image_id, answer_view_format
657 FROM {$wpdb->prefix}tutor_quiz_question_answers
658 WHERE belongs_question_id = %d
659 AND belongs_question_type='multiple_choice'
660 AND is_correct = 1 ;",
661 $answer->question_id
662 )
663 );
664
665 tutor_render_answer_list( $correct_answer );
666 }
667
668 // Fill in the blanks.
669 elseif ( 'fill_in_the_blank' === $answer->question_type ) {
670 $correct_answer = $wpdb->get_var(
671 $wpdb->prepare(
672 "SELECT answer_two_gap_match FROM {$wpdb->prefix}tutor_quiz_question_answers
673 WHERE belongs_question_id = %d
674 AND belongs_question_type='fill_in_the_blank'",
675 $answer->question_id
676 )
677 );
678
679 $answer_titles = explode( '|', stripslashes( $correct_answer ) );
680 $get_db_answers_by_question = QuizModel::get_answers_by_quiz_question( $answer->question_id );
681
682 echo tutor_render_fill_in_the_blank_answer( $get_db_answers_by_question, $answer_titles ); //phpcs:ignore --contain safe data
683 }
684
685 // Ordering.
686 elseif ( 'ordering' === $answer->question_type ) {
687 $correct_answer = $wpdb->get_results(
688 $wpdb->prepare(
689 "SELECT answer_title, image_id, answer_view_format
690 FROM {$wpdb->prefix}tutor_quiz_question_answers
691 WHERE belongs_question_id = %d
692 AND belongs_question_type='ordering'
693 ORDER BY answer_order ASC;",
694 $answer->question_id
695 )
696 );
697
698 foreach ( $correct_answer as $ans ) {
699 tutor_render_answer_list( array( $ans ) );
700 }
701 }
702
703 // Matching.
704 elseif ( 'matching' === $answer->question_type ) {
705 $correct_answer = $wpdb->get_results(
706 $wpdb->prepare(
707 "SELECT answer_title, image_id, answer_two_gap_match, answer_view_format
708 FROM {$wpdb->prefix}tutor_quiz_question_answers
709 WHERE belongs_question_id = %d
710 AND belongs_question_type='matching'
711 ORDER BY answer_order ASC;",
712 $answer->question_id
713 )
714 );
715
716 if ( $is_image_matching ) {
717 array_map(
718 function( $ans ) {
719 $ans->answer_view_format = 'text_image';
720 $ans->answer_two_gap_match = '';
721 },
722 $correct_answer
723 );
724 }
725
726 tutor_render_answer_list( $correct_answer );
727 }
728
729 // Image matching.
730 elseif ( 'image_matching' === $answer->question_type ) {
731 $correct_answer = $wpdb->get_results(
732 $wpdb->prepare(
733 "SELECT answer_title, image_id, answer_two_gap_match
734 FROM {$wpdb->prefix}tutor_quiz_question_answers
735 WHERE belongs_question_id = %d
736 AND belongs_question_type='image_matching'
737 ORDER BY answer_order ASC;",
738 $answer->question_id
739 )
740 );
741
742 tutor_render_answer_list( $correct_answer, true );
743 }
744
745 // Image Answering.
746 elseif ( 'image_answering' === $answer->question_type ) {
747
748 $correct_answer = $wpdb->get_results(
749 $wpdb->prepare(
750 "SELECT answer_title, image_id, answer_two_gap_match
751 FROM {$wpdb->prefix}tutor_quiz_question_answers
752 WHERE belongs_question_id = %d
753 AND belongs_question_type='image_answering'
754 ORDER BY answer_order ASC;",
755 $answer->question_id
756 )
757 );
758
759 ! is_array( $correct_answer ) ? $correct_answer = array() : 0;
760
761 echo '<div class="answer-image-matched-wrap">';
762 foreach ( $correct_answer as $image_answer ) {
763 ?>
764 <div class="image-matching-item">
765 <p class="dragged-img-rap"><img src="<?php echo esc_url( wp_get_attachment_image_url( $image_answer->image_id ) ); ?>" /> </p>
766 <p class="dragged-caption"><?php echo esc_html( $image_answer->answer_title ); ?></p>
767 </div>
768 <?php
769 }
770 echo '</div>';
771 } else {
772 /**
773 * Allow Pro and add-ons to render correct answer for custom question types.
774 * Pro handles draw_image and pin_image via this action.
775 *
776 * @param object $answer Answer object.
777 */
778 do_action( 'tutor_quiz_render_correct_answer_for_question_type', $answer );
779 }
780 }
781 ?>
782 </div>
783 </td>
784 <?php
785 break;
786
787 case 'result':
788 ?>
789 <td class="result" data-title="<?php echo esc_attr( $column ); ?>">
790 <div class="tutor-d-flex tutor-align-center tutor-justify-between tutor-gap-4px">
791 <?php do_action( 'tutor_quiz_attempt_after_result_column', $answer, $answer_status ); ?>
792
793 <?php
794 if ( 'h5p' !== $answer->question_type ) {
795 switch ( $answer_status ) {
796 case 'correct':
797 echo '<span class="tutor-badge-label label-success">' . esc_html__( 'Correct', 'tutor' ) . '</span>';
798 break;
799
800 case 'pending':
801 echo '<span class="tutor-badge-label label-warning">' . esc_html__( 'Pending', 'tutor' ) . '</span>';
802 break;
803
804 case 'wrong':
805 echo '<span class="tutor-badge-label label-danger">' . esc_html__( 'Incorrect', 'tutor' ) . '</span>';
806 break;
807 }
808 }
809 ?>
810
811 <?php do_action( 'tutor_quiz_attempt_details_after_result', $answer, $answer_status ); ?>
812 </div>
813 </td>
814 <?php
815 break;
816
817 case 'manual_review':
818 ?>
819 <td class="tutor-text-center tutor-nowrap-ellipsis" data-title="<?php echo esc_attr( $column ); ?>">
820 <div class="tutor-manual-review-wrapper">
821 <a href="javascript:;" data-back-url="<?php echo esc_url( $back_url ); ?>" data-attempt-id="<?php echo esc_attr( $attempt_id ); ?>" data-attempt-answer-id="<?php echo esc_attr( $answer->attempt_answer_id ); ?>" data-question-id="<?php echo esc_attr( $answer->question_id ); ?>" data-mark-as="correct" data-context="<?php echo esc_attr( $context ); ?>" title="<?php esc_attr_e( 'Mark as correct', 'tutor' ); ?>" class="quiz-manual-review-action tutor-mr-12 tutor-icon-rounded tutor-color-success">
822 <i class="tutor-icon-mark"></i>
823 </a>
824
825 <a href="javascript:;" data-back-url="<?php echo esc_url( $back_url ); ?>" data-attempt-id="<?php echo esc_attr( $attempt_id ); ?>" data-attempt-answer-id="<?php echo esc_attr( $answer->attempt_answer_id ); ?>" data-question-id="<?php echo esc_attr( $answer->question_id ); ?>" data-mark-as="incorrect" data-context="<?php echo esc_attr( $context ); ?>" title="<?php esc_attr_e( 'Mark as In correct', 'tutor' ); ?>" class="quiz-manual-review-action tutor-icon-rounded tutor-color-danger">
826 <i class="tutor-icon-times"></i>
827 </a>
828 </div>
829 </td>
830 <?php
831 }
832 ?>
833 <?php endforeach; ?>
834 </tr>
835
836 <?php do_action( 'tutor_quiz_attempt_details_loop_after_row', $answer, $answer_status, $table_2_columns ); ?>
837
838 <?php
839 }
840 ?>
841 </tbody>
842 </table>
843 </div>
844 <?php
845 do_action( 'tutor_quiz_attempt_details_loop_after' );
846 }
847 ?>
848
849 <?php echo is_admin() ? '</div>' : ''; ?>
850