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 / templates / shared / components / quiz / attempt-details / summary.php
tutor / templates / shared / components / quiz / attempt-details Last commit date
questions 2 days ago question-header.php 5 days ago question.php 5 days ago questions-sidebar.php 5 days ago review-answers.php 5 days ago summary.php 5 days ago
summary.php
318 lines
1 <?php
2 /**
3 * Tutor quiz summary.
4 *
5 * @package Tutor\Templates
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 4.0.0
9 */
10
11 defined( 'ABSPATH' ) || exit;
12
13 use TUTOR\Icon;
14 use Tutor\Components\SvgIcon;
15 use Tutor\Components\Button;
16 use Tutor\Components\ConfirmationModal;
17 use Tutor\Components\Constants\Color;
18 use Tutor\Components\Progress;
19 use Tutor\Components\Constants\Variant;
20 use Tutor\Components\PreviewTrigger;
21 use Tutor\Models\QuizModel;
22 use TUTOR\Quiz;
23 use Tutor\Helpers\UrlHelper;
24 use TUTOR\Quiz_Attempts_List;
25
26 if ( ! isset( $attempt_data ) || ! is_object( $attempt_data ) ) {
27 return;
28 }
29
30 $attempt_id = (int) $attempt_data->attempt_id;
31 $quiz_id = (int) $attempt_data->quiz_id;
32 $course_id = (int) $attempt_data->course_id;
33 $topic_id = (int) wp_get_post_parent_id( $quiz_id );
34 $is_instructor_review = ! empty( $is_instructor_review );
35 $student_id = (int) ( $attempt_data->user_id ?? 0 );
36 $student = $student_id > 0 ? get_userdata( $student_id ) : null;
37 $student_name = $student ? $student->display_name : '';
38 $student_profile_url = $student_id > 0 ? tutor_utils()->profile_url( $student_id, false ) : '';
39
40 $attempt_info = maybe_unserialize( $attempt_data->attempt_info );
41 $passing_grade = is_array( $attempt_info ) ? (int) ( $attempt_info['passing_grade'] ?? 0 ) : 0;
42 $instructor_feedback = is_array( $attempt_info ) ? (string) ( $attempt_info['instructor_feedback'] ?? '' ) : '';
43 $total_marks = (float) $attempt_data->total_marks;
44 $earned_marks = (float) $attempt_data->earned_marks;
45 $pass_marks = ( $total_marks * $passing_grade ) / 100;
46 $earned_percentage = (float) QuizModel::calculate_attempt_earned_percentage( $attempt_data );
47 $attempt_result = QuizModel::get_attempt_result( $attempt_id );
48 $attempted_at_label = date_i18n( get_option( 'date_format' ) . ', ' . get_option( 'time_format' ), strtotime( $attempt_data->attempt_started_at ) );
49 $is_manually_reviewed = ! empty( $attempt_data->is_manually_reviewed );
50
51 $timing = QuizModel::get_quiz_attempt_timing( $attempt_data );
52 $attempt_duration = $timing['attempt_duration'] ?? '';
53 $attempt_duration_taken = $timing['attempt_duration_taken'] ?? '';
54
55 $answers = isset( $answers ) ? $answers : QuizModel::get_quiz_answers_by_attempt_id( $attempt_id );
56 $correct = 0;
57 $incorrect = 0;
58
59 if ( is_array( $answers ) ) {
60 foreach ( $answers as $answer ) {
61 if ( ! empty( $answer->is_correct ) ) {
62 ++$correct;
63 } elseif ( ! in_array( $answer->question_type, array( 'open_ended', 'short_answer' ), true ) ) {
64 ++$incorrect;
65 }
66 }
67 }
68
69 $total_questions = (int) $attempt_data->total_questions;
70 $attempts_count = 0;
71
72 $attempts = ( new QuizModel() )->quiz_attempts( $quiz_id, get_current_user_id() );
73 if ( is_array( $attempts ) ) {
74 $attempts_count = count( $attempts );
75 }
76
77 $quiz_settings = tutor_utils()->get_quiz_option( $quiz_id, '', array() );
78 $limit_attempts_allowed = '1' === (string) ( $quiz_settings['limit_attempts_allowed'] ?? '0' );
79 $attempts_allowed = (int) ( $quiz_settings['attempts_allowed'] ?? 0 );
80 $can_retry = Quiz::can_retry_quiz( $limit_attempts_allowed, $attempts_allowed, $attempts_count );
81 $has_instructor_feedback = '' !== trim( wp_strip_all_tags( $instructor_feedback ) );
82 $retry_modal_id = 'tutor-retry-modal-' . $attempt_id;
83
84 $result_badge_class = 'failed';
85 $result_label = __( 'Failed', 'tutor' );
86 $result_icon = Icon::BADGE_INFO;
87
88 if ( QuizModel::RESULT_PASS === $attempt_result ) {
89 $result_badge_class = 'passed';
90 $result_label = __( 'Passed', 'tutor' );
91 $result_icon = Icon::BADGE_CHECK;
92 } elseif ( QuizModel::RESULT_PENDING === $attempt_result ) {
93 $result_badge_class = 'pending';
94 $result_label = __( 'Pending', 'tutor' );
95 }
96 ?>
97 <div class="tutor-quiz-summary" x-data="tutorQuizRetryAttempt()" x-init="init()">
98 <div class="tutor-quiz-summary-overview">
99 <h2 class="tutor-h2 tutor-sm-text-h3 tutor-mb-3 tutor-sm-mb-2 tutor-text-center">
100 <?php echo esc_html( get_the_title( $quiz_id ) ); ?>
101 </h2>
102
103 <div class="tutor-flex tutor-gap-2 tutor-items-center tutor-justify-center tutor-medium tutor-sm-text-tiny tutor-text-subdued tutor-mb-6">
104 <div class="tutor-flex tutor-gap-2 tutor-items-center tutor-overflow-hidden">
105 <?php esc_html_e( 'Topic', 'tutor' ); ?>
106 <i class="tutor-text-secondary tutor-truncate">
107 <?php echo esc_html( get_the_title( $topic_id ? $topic_id : $quiz_id ) ); ?>
108 </i>
109 </div>
110 <div class="tutor-flex tutor-gap-2 tutor-items-center tutor-overflow-hidden">
111 <?php esc_html_e( 'in', 'tutor' ); ?>
112 <?php
113 if ( $course_id ) {
114 PreviewTrigger::make()->id( $course_id )->render();
115 } else {
116 echo esc_html__( 'Course', 'tutor' );
117 }
118 ?>
119 </div>
120 </div>
121
122 <div class="tutor-quiz-result">
123 <?php Quiz_Attempts_List::render_quiz_attempt_marks_percentage( $attempt_result, $earned_percentage, 'large', 'tutor-quiz-result-progress' ); ?>
124
125 <div class="tutor-quiz-result-marks">
126 <div class="tutor-result-badge <?php echo esc_attr( $result_badge_class ); ?>">
127 <?php SvgIcon::make()->name( $result_icon )->size( 32 )->render(); ?>
128 <?php echo esc_html( $result_label ); ?>
129 </div>
130
131 <div class="tutor-flex tutor-flex-column tutor-gap-2 tutor-sm-gap-1">
132 <div class="tutor-flex tutor-items-center tutor-gap-3">
133 <?php esc_html_e( 'Earned Marks', 'tutor' ); ?>
134 <span class="tutor-font-semibold tutor-text-primary">
135 <?php echo esc_html( number_format_i18n( $earned_marks, 2 ) ); ?>
136 </span>
137 <span>
138 <?php echo esc_html( '(' . number_format_i18n( $earned_percentage, 0 ) . '%)' ); ?>
139 </span>
140 </div>
141
142 <div class="tutor-flex tutor-items-center tutor-gap-3">
143 <?php esc_html_e( 'Pass Marks', 'tutor' ); ?>
144 <span class="tutor-font-semibold tutor-text-primary">
145 <?php echo esc_html( number_format_i18n( $pass_marks, 2 ) ); ?>
146 </span>
147 <span>
148 <?php echo esc_html( '(' . number_format_i18n( $passing_grade, 0 ) . '%)' ); ?>
149 </span>
150 </div>
151 </div>
152
153 <div class="tutor-flex tutor-items-center tutor-gap-3">
154 <?php SvgIcon::make()->name( Icon::CLOCK_2 )->size( 24 )->render(); ?>
155 <span class="tutor-font-semibold tutor-text-primary">
156 <?php echo esc_html( $attempt_duration_taken ); ?>
157 </span>
158 <?php if ( (int) $attempt_duration ) : ?>
159 <span>
160 <?php
161 echo esc_html(
162 sprintf(
163 // translators: %s: localized attempt duration.
164 __( 'of %s', 'tutor' ),
165 $attempt_duration
166 )
167 );
168 ?>
169 </span>
170 <?php endif; ?>
171 </div>
172 </div>
173
174 <div class="tutor-quiz-result-statics">
175 <div class="tutor-quiz-result-static-item correct">
176 <?php
177 printf(
178 wp_kses(
179 /* translators: %d: number of correct answers. */
180 __( '<span class="tutor-font-semibold tutor-text-primary">%d</span> correct', 'tutor' ),
181 array(
182 'span' => array(
183 'class' => true,
184 ),
185 )
186 ),
187 esc_html( $correct )
188 );
189 ?>
190 </div>
191
192 <div class="tutor-quiz-result-static-item incorrect">
193 <?php
194 printf(
195 wp_kses(
196 /* translators: %d: number of incorrect answers. */
197 __( '<span class="tutor-font-semibold tutor-text-primary">%d</span> incorrect', 'tutor' ),
198 array(
199 'span' => array(
200 'class' => true,
201 ),
202 )
203 ),
204 esc_html( $incorrect )
205 );
206 ?>
207 </div>
208
209 <div class="tutor-quiz-result-static-item total">
210 <?php
211 printf(
212 wp_kses(
213 /* translators: %d: number of total questions. */
214 __( '<span class="tutor-font-semibold tutor-text-primary">%d</span> total', 'tutor' ),
215 array(
216 'span' => array(
217 'class' => true,
218 ),
219 )
220 ),
221 esc_html( $total_questions )
222 );
223 ?>
224 </div>
225 </div>
226
227 <?php if ( ! $is_instructor_review && $can_retry ) : ?>
228 <div class="tutor-quiz-result-retake">
229 <?php
230 Button::make()
231 ->label( __( 'Retry Quiz', 'tutor' ) )
232 ->variant( Variant::PRIMARY_SOFT )
233 ->icon( Icon::RELOAD_3, 'left', 20 )
234 ->attr( 'type', 'button' )
235 ->attr( 'class', 'tutor-gap-2 tutor-btn-block' )
236 ->attr(
237 '@click',
238 sprintf(
239 'TutorCore.modal.showModal("%s", { data: %s });',
240 $retry_modal_id,
241 wp_json_encode(
242 array(
243 'quizID' => $quiz_id,
244 'redirectURL' => get_post_permalink( $quiz_id ),
245 )
246 )
247 )
248 )
249 ->render();
250 ?>
251 </div>
252 <?php endif; ?>
253 </div>
254 </div>
255
256 <div class="tutor-quiz-result-footer">
257 <div>
258 <?php
259 echo esc_html(
260 sprintf(
261 /* translators: %s: localized attempt date time. */
262 __( 'Attempted on: %s', 'tutor' ),
263 $attempted_at_label
264 )
265 );
266 ?>
267 </div>
268
269 <?php if ( $is_instructor_review && $student_name && $student_profile_url ) : ?>
270 <div>
271 <?php esc_html_e( 'Attempted by:', 'tutor' ); ?>
272 <a
273 href="<?php echo esc_url( $student_profile_url ); ?>"
274 class="tutor-font-semibold tutor-text-brand"
275 >
276 <?php echo esc_html( $student_name ); ?>
277 </a>
278 </div>
279 <?php elseif ( ! $is_instructor_review && $is_manually_reviewed ) : ?>
280 <div class="tutor-quiz-result-footer-note">
281 <?php esc_html_e( 'Edited by Instructor', 'tutor' ); ?>
282 </div>
283 <?php endif; ?>
284 </div>
285
286 <?php if ( ! $is_instructor_review && $has_instructor_feedback ) : ?>
287 <div class="tutor-quiz-summary-feedback">
288 <div data-title>
289 <?php esc_html_e( 'Instructor Feedback', 'tutor' ); ?>
290 </div>
291 <div data-body>
292 <?php echo wp_kses_post( $instructor_feedback ); ?>
293 </div>
294 </div>
295 <?php endif; ?>
296
297 <?php if ( ! $is_instructor_review && $can_retry ) : ?>
298 <?php
299 ConfirmationModal::make()
300 ->id( $retry_modal_id )
301 ->title( __( 'Retake Quiz?', 'tutor' ) )
302 ->icon( tutor_utils()->get_themed_svg( 'images/illustrations/quiz-retry.svg' ), 80, 80, ConfirmationModal::ICON_TYPE_HTML )
303 ->message( __( 'Retrying this quiz will reset your current attempt. Your answers and score from this attempt will be lost.', 'tutor' ) )
304 ->confirm_handler( 'retryMutation?.mutate({...payload?.data})' )
305 ->confirm_text( __( 'Retry Quiz', 'tutor' ) )
306 ->mutation_state( 'retryMutation' )
307 ->render();
308 ?>
309 <?php endif; ?>
310
311 <?php if ( ! $answers ) : ?>
312 <div class="tutor-empty-quiz-details">
313 <?php SvgIcon::make()->name( Icon::WARNING )->color( Color::CRITICAL )->size( 20 )->render(); ?>
314 <p class="error-message"><?php echo esc_html__( 'No Questions Answered.', 'tutor' ); ?></p>
315 </div>
316 <?php endif; ?>
317 </div>
318