PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.9.13
Tutor LMS – eLearning and online course solution v1.9.13
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 / classes / Quiz.php
tutor / classes Last commit date
Addons.php 4 years ago Admin.php 4 years ago Ajax.php 4 years ago Assets.php 4 years ago Course.php 4 years ago Course_Filter.php 4 years ago Course_Settings_Tabs.php 4 years ago Course_Widget.php 4 years ago Custom_Validation.php 5 years ago Dashboard.php 4 years ago Email.php 5 years ago FormHandler.php 4 years ago Frontend.php 5 years ago Gutenberg.php 4 years ago Instructor.php 5 years ago Instructors_List.php 4 years ago Lesson.php 4 years ago Options.php 4 years ago Post_types.php 4 years ago Private_Course_Access.php 5 years ago Q_and_A.php 5 years ago Question_Answers_List.php 4 years ago Quiz.php 4 years ago Quiz_Attempts_List.php 4 years ago RestAPI.php 4 years ago Rewrite_Rules.php 4 years ago Shortcode.php 4 years ago Student.php 4 years ago Students_List.php 4 years ago Taxonomies.php 4 years ago Template.php 4 years ago Theme_Compatibility.php 5 years ago Tools.php 4 years ago Tutor.php 4 years ago TutorEDD.php 4 years ago Tutor_Base.php 5 years ago Tutor_List_Table.php 4 years ago Tutor_Setup.php 4 years ago Upgrader.php 4 years ago User.php 4 years ago Utils.php 4 years ago Video_Stream.php 4 years ago Withdraw.php 4 years ago Withdraw_Requests_List.php 4 years ago WooCommerce.php 4 years ago
Quiz.php
1343 lines
1 <?php
2
3 /**
4 * Quize class
5 *
6 * @author: themeum
7 * @author_uri: https://themeum.com
8 * @package Tutor
9 * @since v.1.0.0
10 */
11
12 namespace TUTOR;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 class Quiz {
19
20 private $allowed_attributes = array(
21 'src' => array(),
22 'style' => array(),
23 'class' => array(),
24 'id' => array(),
25 'href' => array(),
26 'alt' => array(),
27 'title' => array(),
28 'type' => array(),
29 'controls' => array(),
30 'muted' => array(),
31 'loop' => array(),
32 'poster' => array(),
33 'preload' => array(),
34 'autoplay' => array(),
35 'width' => array(),
36 'height' => array(),
37 );
38
39 private $allowed_html = array( 'img', 'b', 'i', 'br', 'a', 'audio', 'video', 'source' );
40
41 public function __construct() {
42
43 add_action( 'save_post_tutor_quiz', array( $this, 'save_quiz_meta' ) );
44
45 add_action( 'wp_ajax_tutor_load_quiz_builder_modal', array( $this, 'tutor_load_quiz_builder_modal' ) );
46 add_action( 'wp_ajax_remove_quiz_from_post', array( $this, 'remove_quiz_from_post' ) );
47
48 add_action( 'wp_ajax_tutor_quiz_timeout', array( $this, 'tutor_quiz_timeout' ) );
49
50 // User take the quiz
51 add_action( 'template_redirect', array( $this, 'start_the_quiz' ) );
52 add_action( 'template_redirect', array( $this, 'answering_quiz' ) );
53 add_action( 'template_redirect', array( $this, 'finishing_quiz_attempt' ) );
54
55 add_action( 'admin_action_review_quiz_answer', array( $this, 'review_quiz_answer' ) );
56 add_action( 'wp_ajax_review_quiz_answer', array( $this, 'review_quiz_answer' ) );
57 add_action( 'wp_ajax_tutor_instructor_feedback', array( $this, 'tutor_instructor_feedback' ) ); // Instructor Feedback Action
58
59 /**
60 * New Design Quiz
61 */
62
63 add_action( 'wp_ajax_tutor_create_quiz_and_load_modal', array( $this, 'tutor_create_quiz_and_load_modal' ) );
64 add_action( 'wp_ajax_tutor_delete_quiz_by_id', array( $this, 'tutor_delete_quiz_by_id' ) );
65 add_action( 'wp_ajax_tutor_quiz_builder_quiz_update', array( $this, 'tutor_quiz_builder_quiz_update' ) );
66 add_action( 'wp_ajax_tutor_load_edit_quiz_modal', array( $this, 'tutor_load_edit_quiz_modal' ) );
67 add_action( 'wp_ajax_tutor_quiz_builder_get_question_form', array( $this, 'tutor_quiz_builder_get_question_form' ) );
68 add_action( 'wp_ajax_tutor_quiz_modal_update_question', array( $this, 'tutor_quiz_modal_update_question' ) );
69 add_action( 'wp_ajax_tutor_quiz_builder_question_delete', array( $this, 'tutor_quiz_builder_question_delete' ) );
70 add_action( 'wp_ajax_tutor_quiz_add_question_answers', array( $this, 'tutor_quiz_add_question_answers' ) );
71 add_action( 'wp_ajax_tutor_quiz_edit_question_answer', array( $this, 'tutor_quiz_edit_question_answer' ) );
72 add_action( 'wp_ajax_tutor_save_quiz_answer_options', array( $this, 'tutor_save_quiz_answer_options' ) );
73 add_action( 'wp_ajax_tutor_update_quiz_answer_options', array( $this, 'tutor_update_quiz_answer_options' ) );
74 add_action( 'wp_ajax_tutor_quiz_builder_get_answers_by_question', array( $this, 'tutor_quiz_builder_get_answers_by_question' ) );
75 add_action( 'wp_ajax_tutor_quiz_builder_delete_answer', array( $this, 'tutor_quiz_builder_delete_answer' ) );
76 add_action( 'wp_ajax_tutor_quiz_question_sorting', array( $this, 'tutor_quiz_question_sorting' ) );
77 add_action( 'wp_ajax_tutor_quiz_answer_sorting', array( $this, 'tutor_quiz_answer_sorting' ) );
78 add_action( 'wp_ajax_tutor_mark_answer_as_correct', array( $this, 'tutor_mark_answer_as_correct' ) );
79 add_action( 'wp_ajax_tutor_quiz_modal_update_settings', array( $this, 'tutor_quiz_modal_update_settings' ) );
80
81 /**
82 * Frontend Stuff
83 */
84 add_action( 'wp_ajax_tutor_render_quiz_content', array( $this, 'tutor_render_quiz_content' ) );
85
86 /**
87 * Quiz abandon action
88 *
89 * @since 1.9.6
90 */
91 add_action( 'wp_ajax_tutor_quiz_abandon', array( $this, 'tutor_quiz_abandon' ) );
92
93 $this->prepare_allowed_html();
94 }
95
96 private function prepare_allowed_html() {
97
98 $allowed = array();
99
100 foreach ( $this->allowed_html as $tag ) {
101 $allowed[ $tag ] = $this->allowed_attributes;
102 }
103
104 $this->allowed_html = $allowed;
105 }
106
107 public function tutor_instructor_feedback() {
108 tutils()->checking_nonce();
109
110 $feedback = sanitize_text_field( $_POST['feedback'] );
111 $attempt_id = (int) tutor_utils()->avalue_dot( 'attempts_id', tutor_sanitize_data( $_POST ) );
112
113 if ( $attempt_id && tutils()->can_user_manage( 'attempt', $attempt_id ) ) {
114 update_post_meta( $attempt_id, 'instructor_feedback', $feedback );
115 do_action( 'tutor_quiz/attempt/submitted/feedback', $attempt_id );
116
117 wp_send_json_success();
118 }
119 }
120
121 public function save_quiz_meta( $post_ID ) {
122 if ( isset( $_POST['quiz_option'] ) ) {
123 $quiz_option = tutor_utils()->sanitize_array( $_POST['quiz_option'] );
124 update_post_meta( $post_ID, 'tutor_quiz_option', $quiz_option );
125 }
126 }
127
128 /**
129 * Tutor Quiz Builder Modal
130 */
131 public function tutor_load_quiz_builder_modal() {
132 tutils()->checking_nonce();
133
134 ob_start();
135 include tutor()->path . 'views/modal/add_quiz.php';
136 $output = ob_get_clean();
137
138 wp_send_json_success( array( 'output' => $output ) );
139 }
140
141 public function remove_quiz_from_post() {
142 tutils()->checking_nonce();
143
144 global $wpdb;
145 $quiz_id = (int) tutor_utils()->avalue_dot( 'quiz_id', tutor_sanitize_data($_POST) );
146
147 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
148 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
149 }
150
151 $wpdb->update( $wpdb->posts, array( 'post_parent' => 0 ), array( 'ID' => $quiz_id ) );
152 wp_send_json_success();
153 }
154
155 /**
156 *
157 * Start Quiz from here...
158 *
159 * @since v.1.0.0
160 */
161
162 public function start_the_quiz() {
163 if ( ! isset( $_POST['tutor_action'] ) || $_POST['tutor_action'] !== 'tutor_start_quiz' ) {
164 return;
165 }
166 // Checking nonce
167 tutor_utils()->checking_nonce();
168
169 if ( ! is_user_logged_in() ) {
170 // TODO: need to set a view in the next version
171 die( 'Please sign in to do this operation' );
172 }
173
174 global $wpdb;
175
176 $user_id = get_current_user_id();
177 $user = get_userdata( $user_id );
178
179 $quiz_id = (int) sanitize_text_field( $_POST['quiz_id'] );
180
181 $quiz = get_post( $quiz_id );
182 $course = tutor_utils()->get_course_by_quiz( $quiz_id );
183 if ( empty( $course->ID ) ) {
184 die( 'There is something went wrong with course, please check if quiz attached with a course' );
185 }
186
187 do_action( 'tutor_quiz/start/before', $quiz_id, $user_id );
188
189 $date = date( 'Y-m-d H:i:s', tutor_time() );
190
191 $tutor_quiz_option = (array) maybe_unserialize( get_post_meta( $quiz_id, 'tutor_quiz_option', true ) );
192 $attempts_allowed = tutor_utils()->get_quiz_option( $quiz_id, 'attempts_allowed', 0 );
193
194 $time_limit = tutor_utils()->get_quiz_option( $quiz_id, 'time_limit.time_value' );
195 $time_limit_seconds = 0;
196 $time_type = 'seconds';
197 if ( $time_limit ) {
198 $time_type = tutor_utils()->get_quiz_option( $quiz_id, 'time_limit.time_type' );
199
200 switch ( $time_type ) {
201 case 'seconds':
202 $time_limit_seconds = $time_limit;
203 break;
204 case 'minutes':
205 $time_limit_seconds = $time_limit * 60;
206 break;
207 case 'hours':
208 $time_limit_seconds = $time_limit * 60 * 60;
209 break;
210 case 'days':
211 $time_limit_seconds = $time_limit * 60 * 60 * 24;
212 break;
213 case 'weeks':
214 $time_limit_seconds = $time_limit * 60 * 60 * 24 * 7;
215 break;
216 }
217 }
218
219 $max_question_allowed = tutor_utils()->max_questions_for_take_quiz( $quiz_id );
220 $tutor_quiz_option['time_limit']['time_limit_seconds'] = $time_limit_seconds;
221
222 $attempt_data = array(
223 'course_id' => $course->ID,
224 'quiz_id' => $quiz_id,
225 'user_id' => $user_id,
226 'total_questions' => $max_question_allowed,
227 'total_answered_questions' => 0,
228 'attempt_info' => maybe_serialize( $tutor_quiz_option ),
229 'attempt_status' => 'attempt_started',
230 'attempt_ip' => tutor_utils()->get_ip(),
231 'attempt_started_at' => $date,
232 );
233
234 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_attempts', $attempt_data );
235 $attempt_id = (int) $wpdb->insert_id;
236
237 do_action( 'tutor_quiz/start/after', $quiz_id, $user_id, $attempt_id );
238
239 wp_redirect( get_permalink( $quiz_id ) );
240 die();
241 }
242
243 public function answering_quiz() {
244
245 if ( tutils()->array_get( 'tutor_action', $_POST ) !== 'tutor_answering_quiz_question' ) {
246 return;
247 }
248 // submit quiz attempts
249 self::tutor_quiz_attemp_submit();
250
251 wp_redirect( get_the_permalink() );
252 die();
253 }
254
255 /**
256 * Quiz abandon submission handler
257 *
258 * @return JSON response
259 *
260 * @since 1.9.6
261 */
262 public function tutor_quiz_abandon() {
263 if ( tutils()->array_get( 'tutor_action', $_POST ) !== 'tutor_answering_quiz_question' ) {
264 return;
265 }
266 // submit quiz attempts
267 if ( self::tutor_quiz_attemp_submit() ) {
268 wp_send_json_success();
269 } else {
270 wp_send_json_error();
271 }
272 }
273
274 /**
275 * This is a unified method for handling normal quiz submit or abandon submit
276 *
277 * It will handle ajax or normal form submit and can be used with different hooks
278 *
279 * @return true | false
280 *
281 * @since 1.9.6
282 */
283 public static function tutor_quiz_attemp_submit() {
284 tutor_utils()->checking_nonce();
285
286 $attempt_id = (int) sanitize_text_field( tutor_utils()->avalue_dot( 'attempt_id', $_POST ) );
287 $attempt = tutor_utils()->get_attempt( $attempt_id );
288 $course_id = tutor_utils()->get_course_by_quiz( $attempt->quiz_id )->ID;
289
290 $attempt_answers = isset( $_POST['attempt'] ) ? tutor_sanitize_data( $_POST['attempt'] ) : false;
291 if ( ! is_user_logged_in() ) {
292 die( 'Please sign in to do this operation' );
293 }
294
295 global $wpdb;
296 $user_id = get_current_user_id();
297
298 do_action( 'tutor_quiz/attempt_analysing/before', $attempt_id );
299
300 if ( $attempt_answers && is_array( $attempt_answers ) && count( $attempt_answers ) ) {
301 foreach ( $attempt_answers as $attempt_id => $attempt_answer ) {
302
303 /**
304 * Get total marks of all question comes
305 */
306 $question_ids = tutor_utils()->avalue_dot( 'quiz_question_ids', $attempt_answer );
307 if ( is_array( $question_ids ) && count( $question_ids ) ) {
308 $question_ids_string = "'" . implode( "','", $question_ids ) . "'";
309 $total_question_marks = $wpdb->get_var( "SELECT SUM(question_mark) FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN({$question_ids_string}) ;" );
310 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempts', array( 'total_marks' => $total_question_marks ), array( 'attempt_id' => $attempt_id ) );
311 }
312
313 if ( ! $attempt || $user_id != $attempt->user_id ) {
314 die( 'Operation not allowed, attempt not found or permission denied' );
315 }
316
317 $quiz_answers = tutor_utils()->avalue_dot( 'quiz_question', $attempt_answer );
318
319 $total_marks = 0;
320 $review_required = false;
321
322 if ( tutils()->count( $quiz_answers ) ) {
323
324 foreach ( $quiz_answers as $question_id => $answers ) {
325 $question = tutor_utils()->get_quiz_question_by_id( $question_id );
326 $question_type = $question->question_type;
327
328 $is_answer_was_correct = false;
329 $given_answer = '';
330
331 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
332
333 if ( ! is_numeric( $answers ) || ! $answers ) {
334 wp_send_json_error();
335 exit;
336 }
337
338 $given_answer = $answers;
339 $is_answer_was_correct = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT is_correct FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = %d ", $answers ) );
340
341 } elseif ( $question_type === 'multiple_choice' ) {
342
343 $given_answer = (array) ( $answers );
344
345 $given_answer = array_filter(
346 $given_answer,
347 function( $id ) {
348 return is_numeric( $id ) && $id > 0;
349 }
350 );
351
352 $get_original_answers = (array) $wpdb->get_col(
353 $wpdb->prepare(
354 "SELECT
355 answer_id
356 FROM
357 {$wpdb->prefix}tutor_quiz_question_answers
358 WHERE
359 belongs_question_id = %d
360 AND belongs_question_type = %s
361 AND is_correct = 1 ;
362 ",
363 $question->question_id,
364 $question_type
365 )
366 );
367
368 if ( count( array_diff( $get_original_answers, $given_answer ) ) === 0 && count( $get_original_answers ) === count( $given_answer ) ) {
369 $is_answer_was_correct = true;
370 }
371 $given_answer = maybe_serialize( $answers );
372
373 } elseif ( $question_type === 'fill_in_the_blank' ) {
374
375 $given_answer = (array) array_map( 'sanitize_text_field', $answers );
376 $given_answer = maybe_serialize( $given_answer );
377
378 $get_original_answer = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id = %d AND belongs_question_type = %s ;", $question->question_id, $question_type ) );
379 $gap_answer = (array) explode( '|', $get_original_answer->answer_two_gap_match );
380
381 $gap_answer = array_map( 'sanitize_text_field', $gap_answer );
382 if ( strtolower( $given_answer ) == strtolower( maybe_serialize( $gap_answer ) ) ) {
383 $is_answer_was_correct = true;
384 }
385 } elseif ( $question_type === 'open_ended' || $question_type === 'short_answer' ) {
386 $review_required = true;
387 $given_answer = wp_kses_post( $answers );
388
389 } elseif ( $question_type === 'ordering' || $question_type === 'matching' || $question_type === 'image_matching' ) {
390
391 $given_answer = (array) array_map( 'sanitize_text_field', tutor_utils()->avalue_dot( 'answers', $answers ) );
392 $given_answer = maybe_serialize( $given_answer );
393
394 $get_original_answers = (array) $wpdb->get_col(
395 $wpdb->prepare(
396 "SELECT answer_id
397 FROM {$wpdb->prefix}tutor_quiz_question_answers
398 WHERE belongs_question_id = %d AND belongs_question_type = %s ORDER BY answer_order ASC ;",
399 $question->question_id,
400 $question_type
401 )
402 );
403
404 $get_original_answers = array_map( 'sanitize_text_field', $get_original_answers );
405
406 if ( $given_answer == maybe_serialize( $get_original_answers ) ) {
407 $is_answer_was_correct = true;
408 }
409 } elseif ( $question_type === 'image_answering' ) {
410 $image_inputs = tutor_utils()->avalue_dot( 'answer_id', $answers );
411 $image_inputs = (array) array_map( 'sanitize_text_field', $image_inputs );
412 $given_answer = maybe_serialize( $image_inputs );
413 $is_answer_was_correct = false;
414
415 $db_answer = $wpdb->get_col(
416 $wpdb->prepare(
417 "SELECT answer_title
418 FROM {$wpdb->prefix}tutor_quiz_question_answers
419 WHERE belongs_question_id = %d AND belongs_question_type = 'image_answering' ORDER BY answer_order asc ;",
420 $question_id
421 )
422 );
423
424 if ( is_array( $db_answer ) && count( $db_answer ) ) {
425 $is_answer_was_correct = ( strtolower( maybe_serialize( array_values( $image_inputs ) ) ) == strtolower( maybe_serialize( $db_answer ) ) );
426 }
427 }
428
429 $question_mark = $is_answer_was_correct ? $question->question_mark : 0;
430 $total_marks += $question_mark;
431
432 $answers_data = array(
433 'user_id' => $user_id,
434 'quiz_id' => $attempt->quiz_id,
435 'question_id' => $question_id,
436 'quiz_attempt_id' => $attempt_id,
437 'given_answer' => $given_answer,
438 'question_mark' => $question->question_mark,
439 'achieved_mark' => $question_mark,
440 'minus_mark' => 0,
441 'is_correct' => $is_answer_was_correct ? 1 : 0,
442 );
443
444 /*
445 check if question_type open ended or short ans the set is_correct default value null before saving
446 */
447 if ( $question_type === 'open_ended' || $question_type === 'short_answer' ) {
448 $answers_data['is_correct'] = null;
449 }
450
451 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_attempt_answers', $answers_data );
452 }
453 }
454
455 $attempt_info = array(
456 'total_answered_questions' => tutils()->count( $quiz_answers ),
457 'earned_marks' => $total_marks,
458 'attempt_status' => 'attempt_ended',
459 'attempt_ended_at' => date( 'Y-m-d H:i:s', tutor_time() ),
460 );
461
462 if ( $review_required ) {
463 $attempt_info['attempt_status'] = 'review_required';
464 }
465
466 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempts', $attempt_info, array( 'attempt_id' => $attempt_id ) );
467 }
468
469 do_action( 'tutor_quiz/attempt_ended', $attempt_id, $course_id, $user_id );
470 return true;
471 }
472 return false;
473 }
474
475
476 /**
477 * Quiz attempt will be finish here
478 */
479
480 public function finishing_quiz_attempt() {
481
482 if ( ! isset( $_POST['tutor_action'] ) || $_POST['tutor_action'] !== 'tutor_finish_quiz_attempt' ) {
483 return;
484 }
485 // Checking nonce
486 tutor_utils()->checking_nonce();
487
488 if ( ! is_user_logged_in() ) {
489 die( 'Please sign in to do this operation' );
490 }
491
492 global $wpdb;
493
494 $quiz_id = (int) sanitize_text_field( $_POST['quiz_id'] );
495 $attempt = tutor_utils()->is_started_quiz( $quiz_id );
496 $attempt_id = $attempt->attempt_id;
497
498 $attempt_info = array(
499 'total_answered_questions' => 0,
500 'earned_marks' => 0,
501 'attempt_status' => 'attempt_ended',
502 'attempt_ended_at' => date( 'Y-m-d H:i:s', tutor_time() ),
503 );
504
505 do_action( 'tutor_quiz_before_finish', $attempt_id, $quiz_id, $attempt->user_id );
506 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempts', $attempt_info, array( 'attempt_id' => $attempt_id ) );
507 do_action( 'tutor_quiz_finished', $attempt_id, $quiz_id, $attempt->user_id );
508
509 wp_redirect( tutor_utils()->input_old( '_wp_http_referer' ) );
510 }
511
512 /**
513 * Quiz timeout by ajax
514 */
515 public function tutor_quiz_timeout() {
516 tutils()->checking_nonce();
517
518 global $wpdb;
519
520 $quiz_id = (int) sanitize_text_field( $_POST['quiz_id'] );
521
522 // if(!tutils()->can_user_manage('quiz', $quiz_id)) {
523 // wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
524 // }
525
526 $attempt = tutor_utils()->is_started_quiz( $quiz_id );
527
528 if ( $attempt ) {
529 $attempt_id = $attempt->attempt_id;
530
531 $data = array(
532 'attempt_status' => 'attempt_timeout',
533 'attempt_ended_at' => date( 'Y-m-d H:i:s', tutor_time() ),
534 );
535 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempts', $data, array( 'attempt_id' => $attempt->attempt_id ) );
536
537 do_action( 'tutor_quiz_timeout', $attempt_id, $quiz_id, $attempt->user_id );
538
539 wp_send_json_success();
540 }
541
542 wp_send_json_error( __( 'Quiz has been timeout already', 'tutor' ) );
543 }
544
545 /**
546 * Review the answer and change individual answer result
547 */
548
549 public function review_quiz_answer() {
550
551 tutils()->checking_nonce( strtolower( $_SERVER['REQUEST_METHOD'] ) );
552
553 global $wpdb;
554
555 $attempt_id = (int) sanitize_text_field( $_GET['attempt_id'] );
556 $attempt_answer_id = (int) sanitize_text_field( $_GET['attempt_answer_id'] );
557 $mark_as = sanitize_text_field( $_GET['mark_as'] );
558
559 if ( ! tutils()->can_user_manage( 'attempt', $attempt_id ) || ! tutils()->can_user_manage( 'attempt_answer', $attempt_answer_id ) ) {
560 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
561 }
562
563 $attempt_answer = $wpdb->get_row(
564 $wpdb->prepare(
565 "SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers
566 WHERE attempt_answer_id = %d ",
567 $attempt_answer_id
568 )
569 );
570
571 $attempt = tutor_utils()->get_attempt( $attempt_id );
572 $question = tutils()->get_quiz_question_by_id( $attempt_answer->question_id );
573 $course_id = $attempt->course_id;
574 $student_id = $attempt->user_id;
575 $previous_ans = $attempt_answer->is_correct;
576
577 do_action( 'tutor_quiz_review_answer_before', $attempt_answer_id, $attempt_id, $mark_as );
578
579 if ( $mark_as === 'correct' ) {
580
581 $answer_update_data = array(
582 'achieved_mark' => $attempt_answer->question_mark,
583 'is_correct' => 1,
584 );
585 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempt_answers', $answer_update_data, array( 'attempt_answer_id' => $attempt_answer_id ) );
586 if ( $previous_ans == 0 or $previous_ans == null ) {
587
588 // if previous answer was wrong or in review then add point as correct
589 $attempt_update_data = array(
590 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
591 'is_manually_reviewed' => 1,
592 'manually_reviewed_at' => date( 'Y-m-d H:i:s', tutor_time() ),
593 );
594
595 }
596
597 if ( $question->question_type === 'open_ended' || $question->question_type === 'short_answer' ) {
598 $attempt_update_data['attempt_status'] = 'attempt_ended';
599 }
600 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempts', $attempt_update_data, array( 'attempt_id' => $attempt_id ) );
601 } elseif ( $mark_as === 'incorrect' ) {
602
603 $answer_update_data = array(
604 'achieved_mark' => '0.00',
605 'is_correct' => 0,
606 );
607 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempt_answers', $answer_update_data, array( 'attempt_answer_id' => $attempt_answer_id ) );
608
609 if ( $previous_ans == 1 ) {
610
611 // if previous ans was right then mynus
612 $attempt_update_data = array(
613 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
614 'is_manually_reviewed' => 1,
615 'manually_reviewed_at' => date( 'Y-m-d H:i:s', tutor_time() ),
616 );
617
618 }
619 if ( $question->question_type === 'open_ended' || $question->question_type === 'short_answer' ) {
620 $attempt_update_data['attempt_status'] = 'attempt_ended';
621 }
622
623 $wpdb->update( $wpdb->prefix . 'tutor_quiz_attempts', $attempt_update_data, array( 'attempt_id' => $attempt_id ) );
624 }
625 do_action( 'tutor_quiz_review_answer_after', $attempt_answer_id, $attempt_id, $mark_as );
626 do_action( 'tutor_quiz/answer/review/after', $attempt_answer_id, $course_id, $student_id );
627
628 if ( wp_doing_ajax() ) {
629 wp_send_json_success();
630 } else {
631 wp_redirect( admin_url( 'admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=' . $attempt_id ) );
632 }
633
634 die();
635 }
636
637
638 /**
639 * New Design Quiz
640 */
641 public function tutor_create_quiz_and_load_modal() {
642 tutils()->checking_nonce();
643
644 $topic_id = sanitize_text_field( $_POST['topic_id'] );
645 $quiz_title = sanitize_text_field( $_POST['quiz_title'] );
646 $quiz_description = wp_kses( $_POST['quiz_description'], $this->allowed_html );
647 $next_order_id = tutor_utils()->get_next_course_content_order_id( $topic_id );
648
649 if ( ! tutils()->can_user_manage( 'topic', $topic_id ) ) {
650 wp_send_json_error(
651 array(
652 'message' => __( 'Access Denied', 'tutor' ),
653 'data' => tutor_sanitize_data($_POST),
654 )
655 );
656 }
657
658 $post_arr = array(
659 'post_type' => 'tutor_quiz',
660 'post_title' => $quiz_title,
661 'post_content' => $quiz_description,
662 'post_status' => 'publish',
663 'post_author' => get_current_user_id(),
664 'post_parent' => $topic_id,
665 'menu_order' => $next_order_id,
666 );
667 $quiz_id = wp_insert_post( $post_arr );
668 do_action( 'tutor_initial_quiz_created', $quiz_id );
669
670 ob_start();
671 include tutor()->path . 'views/modal/edit_quiz.php';
672 $output = ob_get_clean();
673
674 ob_start();
675 ?>
676 <div id="tutor-quiz-<?php echo esc_attr( $quiz_id ); ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
677 <div class="tutor-lesson-top">
678 <i class="tutor-icon-move"></i>
679 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id; ?>">
680 <i class=" tutor-icon-doubt"></i>[<?php _e( 'QUIZ', 'tutor' ); ?>]&nbsp;
681 <?php echo stripslashes( $quiz_title ); ?>
682 </a>
683 <?php do_action( 'tutor_course_builder_before_quiz_btn_action', $quiz_id ); ?>
684 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
685 </div>
686 </div>
687 <?php
688 $output_quiz_row = ob_get_clean();
689
690 wp_send_json_success(
691 array(
692 'output' => $output,
693 'output_quiz_row' => $output_quiz_row,
694 )
695 );
696 }
697
698 public function tutor_delete_quiz_by_id() {
699 tutils()->checking_nonce();
700
701 global $wpdb;
702
703 $quiz_id = (int) sanitize_text_field( $_POST['quiz_id'] );
704 $post = get_post( $quiz_id );
705
706 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
707 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
708 }
709
710 if ( $post->post_type === 'tutor_quiz' ) {
711 do_action( 'tutor_delete_quiz_before', $quiz_id );
712
713 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempts', array( 'quiz_id' => $quiz_id ) );
714 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempt_answers', array( 'quiz_id' => $quiz_id ) );
715
716 $questions_ids = $wpdb->get_col( $wpdb->prepare( "SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = %d ", $quiz_id ) );
717
718 if ( is_array( $questions_ids ) && count( $questions_ids ) ) {
719 $in_question_ids = "'" . implode( "','", $questions_ids ) . "'";
720 $wpdb->query( "DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) " );
721 }
722
723 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_questions', array( 'quiz_id' => $quiz_id ) );
724
725 wp_delete_post( $quiz_id, true );
726 delete_post_meta( $quiz_id, '_tutor_course_id_for_lesson' );
727
728 do_action( 'tutor_delete_quiz_after', $quiz_id );
729
730 wp_send_json_success();
731 }
732
733 wp_send_json_error();
734 }
735
736 /**
737 * Update Quiz from quiz builder modal
738 *
739 * @since v.1.0.0
740 */
741 public function tutor_quiz_builder_quiz_update() {
742 tutils()->checking_nonce();
743
744 $quiz_id = sanitize_text_field( $_POST['quiz_id'] );
745 $topic_id = sanitize_text_field( $_POST['topic_id'] );
746 $quiz_title = sanitize_text_field( $_POST['quiz_title'] );
747 $quiz_description = wp_kses( $_POST['quiz_description'], $this->allowed_html );
748
749 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
750 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
751 }
752
753 $post_arr = array(
754 'ID' => $quiz_id,
755 'post_title' => $quiz_title,
756 'post_content' => $quiz_description,
757
758 );
759 $quiz_id = wp_update_post( $post_arr );
760
761 do_action( 'tutor_quiz_updated', $quiz_id );
762
763 ob_start();
764 ?>
765 <div class="tutor-lesson-top">
766 <i class="tutor-icon-move"></i>
767 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo esc_attr( $quiz_id ); ?>" data-topic-id="<?php echo esc_attr( $topic_id ); ?>">
768 <i class=" tutor-icon-doubt"></i>[<?php _e( 'QUIZ', 'tutor' ); ?>]&nbsp;
769 <?php echo stripslashes( $quiz_title ); ?>
770 </a>
771 <?php do_action( 'tutor_course_builder_before_quiz_btn_action', $quiz_id ); ?>
772 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo esc_attr( $quiz_id ); ?>"><i class="tutor-icon-garbage"></i></a>
773 </div>
774 <?php
775 $output_quiz_row = ob_get_clean();
776
777 wp_send_json_success( array( 'output_quiz_row' => $output_quiz_row ) );
778 }
779
780 /**
781 * Load quiz Modal for edit quiz
782 *
783 * @since v.1.0.0
784 */
785 public function tutor_load_edit_quiz_modal() {
786 tutils()->checking_nonce();
787
788 $quiz_id = sanitize_text_field( $_POST['quiz_id'] );
789 $topic_id = sanitize_text_field( $_POST['topic_id'] );
790
791 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
792 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
793 }
794
795 ob_start();
796 include tutor()->path . 'views/modal/edit_quiz.php';
797 $output = ob_get_clean();
798
799 wp_send_json_success( array( 'output' => $output ) );
800 }
801
802 /**
803 * Load quiz question form for quiz
804 *
805 * @since v.1.0.0
806 */
807 public function tutor_quiz_builder_get_question_form() {
808 tutils()->checking_nonce();
809
810 global $wpdb;
811 $quiz_id = sanitize_text_field( $_POST['quiz_id'] );
812 $question_id = sanitize_text_field( tutor_utils()->avalue_dot( 'question_id', $_POST ) );
813
814 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
815 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
816 }
817
818 if ( ! $question_id ) {
819 $next_question_id = tutor_utils()->quiz_next_question_id();
820 $next_question_order = tutor_utils()->quiz_next_question_order_id( $quiz_id );
821
822 $new_question_data = array(
823 'quiz_id' => $quiz_id,
824 'question_title' => __( 'Question', 'tutor' ) . ' ' . $next_question_id,
825 'question_description' => '',
826 'question_type' => 'true_false',
827 'question_mark' => 1,
828 'question_settings' => maybe_serialize( array() ),
829 'question_order' => esc_sql( $next_question_order ),
830 );
831
832 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_questions', $new_question_data );
833 $question_id = $wpdb->insert_id;
834 }
835
836 $question = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = %d ", $question_id ) );
837
838 ob_start();
839 include tutor()->path . 'views/modal/question_form.php';
840 $output = ob_get_clean();
841
842 wp_send_json_success( array( 'output' => $output ) );
843 }
844
845 public function tutor_quiz_modal_update_question() {
846 tutils()->checking_nonce();
847
848 global $wpdb;
849
850 $question_data = tutor_sanitize_data( $_POST['tutor_quiz_question'] );
851
852 foreach ( $question_data as $question_id => $question ) {
853
854 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
855 continue;
856 }
857
858 $question_title = sanitize_text_field( $question['question_title'] );
859 $question_description = wp_kses( $question['question_description'], $this->allowed_html ); // sanitize_text_field($question['question_description']);
860 $question_type = sanitize_text_field( $question['question_type'] );
861 $question_mark = sanitize_text_field( $question['question_mark'] );
862
863 unset( $question['question_title'] );
864 unset( $question['question_description'] );
865
866 $data = array(
867 'question_title' => $question_title,
868 'question_description' => $question_description,
869 'question_type' => $question_type,
870 'question_mark' => $question_mark,
871 'question_settings' => maybe_serialize( $question ),
872 );
873
874 $wpdb->update( $wpdb->prefix . 'tutor_quiz_questions', $data, array( 'question_id' => $question_id ) );
875
876 /**
877 * Validation
878 */
879 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
880 $question_options = tutils()->get_answers_by_quiz_question( $question_id );
881 if ( tutils()->count( $question_options ) ) {
882 $required_validate = true;
883 foreach ( $question_options as $question_option ) {
884 if ( $question_option->is_correct ) {
885 $required_validate = false;
886 }
887 }
888 if ( $required_validate ) {
889 $validation_msg = "<p class='tutor-error-msg'>" . __( 'Please select the correct answer', 'tutor' ) . '</p>';
890 wp_send_json_error( array( 'validation_msg' => $validation_msg ) );
891 }
892 } else {
893 $validation_msg = "<p class='tutor-error-msg'>" . __( 'Please make sure you have added more than one option and saved them', 'tutor' ) . '</p>';
894 wp_send_json_error( array( 'validation_msg' => $validation_msg ) );
895 }
896 }
897 }
898
899 wp_send_json_success();
900 }
901
902 public function tutor_quiz_builder_question_delete() {
903 tutils()->checking_nonce();
904
905 global $wpdb;
906
907 $question_id = sanitize_text_field( tutor_utils()->avalue_dot( 'question_id', $_POST ) );
908
909 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
910 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
911 }
912
913 if ( $question_id ) {
914 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_questions', array( 'question_id' => esc_sql( $question_id ) ) );
915 }
916
917 wp_send_json_success();
918 }
919
920 /**
921 * Get answers options form for quiz question
922 *
923 * @since v.1.0.0
924 */
925 public function tutor_quiz_add_question_answers() {
926 tutils()->checking_nonce();
927
928 $question_id = sanitize_text_field( $_POST['question_id'] );
929 $question = tutor_utils()->avalue_dot( $question_id, $_POST['tutor_quiz_question'] );
930 $question_type = $question['question_type'];
931
932 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
933 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
934 }
935
936 ob_start();
937 include tutor()->path . 'views/modal/question_answer_form.php';
938 $output = ob_get_clean();
939
940 wp_send_json_success( array( 'output' => $output ) );
941 }
942
943 /**
944 * Edit Answer Form
945 *
946 * @since v.1.0.0
947 */
948 public function tutor_quiz_edit_question_answer() {
949 tutils()->checking_nonce();
950
951 $answer_id = (int) sanitize_text_field( $_POST['answer_id'] );
952
953 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
954 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
955 }
956
957 $old_answer = tutor_utils()->get_answer_by_id( $answer_id );
958 foreach ( $old_answer as $old_answer ) {
959 }
960 $question_id = $old_answer->belongs_question_id;
961 $question_type = $old_answer->belongs_question_type;
962
963 ob_start();
964 include tutor()->path . 'views/modal/question_answer_edit_form.php';
965 $output = ob_get_clean();
966
967 wp_send_json_success( array( 'output' => $output ) );
968 }
969
970 public function tutor_save_quiz_answer_options() {
971 tutils()->checking_nonce();
972
973 global $wpdb;
974
975 $questions = tutor_sanitize_data( $_POST['tutor_quiz_question'] );
976 $answers = tutor_sanitize_data( $_POST['quiz_answer'] );
977
978 foreach ( $answers as $question_id => $answer ) {
979
980 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
981 continue;
982 }
983
984 $question = tutor_utils()->avalue_dot( $question_id, $questions );
985 $question_type = $question['question_type'];
986
987 // Getting next sorting order
988 $next_order_id = (int) $wpdb->get_var(
989 $wpdb->prepare(
990 "SELECT MAX(answer_order)
991 FROM {$wpdb->prefix}tutor_quiz_question_answers
992 where belongs_question_id = %d
993 AND belongs_question_type = %s ",
994 $question_id,
995 esc_sql( $question_type )
996 )
997 );
998
999 $next_order_id = $next_order_id + 1;
1000
1001 if ( $question ) {
1002 if ( $question_type === 'true_false' ) {
1003 $wpdb->delete(
1004 $wpdb->prefix . 'tutor_quiz_question_answers',
1005 array(
1006 'belongs_question_id' => $question_id,
1007 'belongs_question_type' => $question_type,
1008 )
1009 );
1010 $data_true_false = array(
1011 array(
1012 'belongs_question_id' => esc_sql( $question_id ),
1013 'belongs_question_type' => $question_type,
1014 'answer_title' => __( 'True', 'tutor' ),
1015 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
1016 'answer_two_gap_match' => 'true',
1017 ),
1018 array(
1019 'belongs_question_id' => esc_sql( $question_id ),
1020 'belongs_question_type' => $question_type,
1021 'answer_title' => __( 'False', 'tutor' ),
1022 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
1023 'answer_two_gap_match' => 'false',
1024 ),
1025 );
1026
1027 foreach ( $data_true_false as $true_false_data ) {
1028 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_question_answers', $true_false_data );
1029 }
1030 } elseif ( $question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
1031 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ) {
1032
1033 $answer_data = array(
1034 'belongs_question_id' => sanitize_text_field( $question_id ),
1035 'belongs_question_type' => $question_type,
1036 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1037 'image_id' => isset( $answer['image_id'] ) ? $answer['image_id'] : 0,
1038 'answer_view_format' => isset( $answer['answer_view_format'] ) ? $answer['answer_view_format'] : 0,
1039 'answer_order' => $next_order_id,
1040 );
1041 if ( isset( $answer['matched_answer_title'] ) ) {
1042 $answer_data['answer_two_gap_match'] = sanitize_text_field( $answer['matched_answer_title'] );
1043 }
1044
1045 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_question_answers', $answer_data );
1046
1047 } elseif ( $question_type === 'fill_in_the_blank' ) {
1048 $wpdb->delete(
1049 $wpdb->prefix . 'tutor_quiz_question_answers',
1050 array(
1051 'belongs_question_id' => $question_id,
1052 'belongs_question_type' => $question_type,
1053 )
1054 );
1055 $answer_data = array(
1056 'belongs_question_id' => sanitize_text_field( $question_id ),
1057 'belongs_question_type' => $question_type,
1058 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1059 'answer_two_gap_match' => isset( $answer['answer_two_gap_match'] ) ? sanitize_text_field( trim( $answer['answer_two_gap_match'] ) ) : null,
1060 );
1061 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_question_answers', $answer_data );
1062 }
1063 }
1064 }
1065
1066 wp_send_json_success();
1067 }
1068
1069 /**
1070 * Tutor Update Answer
1071 *
1072 * @since v.1.0.0
1073 */
1074 public function tutor_update_quiz_answer_options() {
1075 tutils()->checking_nonce();
1076
1077 global $wpdb;
1078
1079 $answer_id = (int) sanitize_text_field( $_POST['tutor_quiz_answer_id'] );
1080
1081 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1082 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1083 }
1084
1085 $questions = tutor_sanitize_data( $_POST['tutor_quiz_question'] );
1086 $answers = tutor_sanitize_data( $_POST['quiz_answer'] );
1087
1088 foreach ( $answers as $question_id => $answer ) {
1089 $question = tutor_utils()->avalue_dot( $question_id, $questions );
1090 $question_type = $question['question_type'];
1091
1092 if ( $question ) {
1093 if ( $question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' || $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'fill_in_the_blank' || $question_type === 'image_answering' ) {
1094
1095 $answer_data = array(
1096 'belongs_question_id' => $question_id,
1097 'belongs_question_type' => $question_type,
1098 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1099 'image_id' => isset( $answer['image_id'] ) ? $answer['image_id'] : 0,
1100 'answer_view_format' => isset( $answer['answer_view_format'] ) ? sanitize_text_field( $answer['answer_view_format'] ) : '',
1101 );
1102 if ( isset( $answer['matched_answer_title'] ) ) {
1103 $answer_data['answer_two_gap_match'] = sanitize_text_field( $answer['matched_answer_title'] );
1104 }
1105
1106 if ( $question_type === 'fill_in_the_blank' ) {
1107 $answer_data['answer_two_gap_match'] = isset( $answer['answer_two_gap_match'] ) ? sanitize_text_field( trim( $answer['answer_two_gap_match'] ) ) : null;
1108 }
1109
1110 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', $answer_data, array( 'answer_id' => $answer_id ) );
1111 }
1112 }
1113 }
1114
1115 // die(print_r($_POST));
1116 wp_send_json_success();
1117 }
1118
1119 public function tutor_quiz_builder_get_answers_by_question() {
1120 tutils()->checking_nonce();
1121
1122 global $wpdb;
1123 $question_id = sanitize_text_field( $_POST['question_id'] );
1124 $question_type = sanitize_text_field( $_POST['question_type'] );
1125
1126 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
1127 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1128 }
1129
1130 $question = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = %d ", $question_id ) );
1131 $answers = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers where belongs_question_id = %d AND belongs_question_type = %s order by answer_order asc ;", $question_id, esc_sql( $question_type ) ) );
1132
1133 ob_start();
1134
1135 switch ( $question_type ) {
1136 case 'true_false':
1137 echo '<label>' . __( 'Answer options &amp; mark correct', 'tutor' ) . '</label>';
1138 break;
1139 case 'ordering':
1140 echo '<label>' . __( 'Make sure you’re saving the answers in the right order. Students will have to match this order exactly.', 'tutor' ) . '</label>';
1141 break;
1142 }
1143
1144 if ( is_array( $answers ) && count( $answers ) ) {
1145 foreach ( $answers as $answer ) {
1146 ?>
1147 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo esc_attr( $answer->answer_id ); ?>">
1148 <div class="tutor-quiz-answer">
1149 <span class="tutor-quiz-answer-title">
1150 <?php
1151 echo stripslashes( $answer->answer_title );
1152 if ( $answer->belongs_question_type === 'fill_in_the_blank' ) {
1153 echo ' (' . __( 'Answer', 'tutor' ) . ' : ';
1154 echo '<strong>' . stripslashes( $answer->answer_two_gap_match ) . '</strong>)';
1155 }
1156 if ( $answer->belongs_question_type === 'matching' ) {
1157 echo ' - ' . stripslashes( $answer->answer_two_gap_match );
1158 }
1159 ?>
1160 </span>
1161
1162 <?php
1163 if ( $answer->image_id ) {
1164 echo '<span class="tutor-question-answer-image"><img src="' . wp_get_attachment_image_url( $answer->image_id ) . '" /> </span>';
1165 }
1166 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
1167 ?>
1168 <span class="tutor-quiz-answers-mark-correct-wrap">
1169 <input type="radio" name="mark_as_correct[<?php echo $answer->belongs_question_id; ?>]" value="<?php echo $answer->answer_id; ?>" title="<?php _e( 'Mark as correct', 'tutor' ); ?>" <?php checked( 1, $answer->is_correct ); ?> >
1170 </span>
1171 <?php
1172 } elseif ( $question_type === 'multiple_choice' ) {
1173 ?>
1174 <span class="tutor-quiz-answers-mark-correct-wrap">
1175 <input type="checkbox" name="mark_as_correct[<?php echo $answer->belongs_question_id; ?>]" value="<?php echo $answer->answer_id; ?>" title="<?php _e( 'Mark as correct', 'tutor' ); ?>" <?php checked( 1, $answer->is_correct ); ?> >
1176 </span>
1177 <?php
1178 }
1179 ?>
1180 <?php if ( 'true_false' != $question_type ) : ?>
1181 <span class="tutor-quiz-answer-edit">
1182 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
1183 </span>
1184 <?php endif; ?>
1185 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
1186 </div>
1187 <?php if ( 'true_false' != $question_type ) : ?>
1188 <div class="tutor-quiz-answer-trash-wrap">
1189 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo esc_attr( $answer->answer_id ); ?>"><i class="tutor-icon-garbage"></i> </a>
1190 </div>
1191 <?php endif; ?>
1192 </div>
1193 <?php
1194 }
1195 }
1196 $output = ob_get_clean();
1197
1198 wp_send_json_success( array( 'output' => $output ) );
1199 }
1200
1201 public function tutor_quiz_builder_delete_answer() {
1202 tutils()->checking_nonce();
1203
1204 global $wpdb;
1205 $answer_id = sanitize_text_field( $_POST['answer_id'] );
1206
1207 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1208 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1209 }
1210
1211 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'answer_id' => esc_sql( $answer_id ) ) );
1212 wp_send_json_success();
1213 }
1214
1215 /**
1216 * Save quiz questions sorting
1217 */
1218 public function tutor_quiz_question_sorting() {
1219 tutils()->checking_nonce();
1220
1221 global $wpdb;
1222
1223 $question_ids = tutor_utils()->avalue_dot( 'sorted_question_ids', tutor_sanitize_data($_POST) );
1224 if ( is_array( $question_ids ) && count( $question_ids ) ) {
1225 $i = 0;
1226 foreach ( $question_ids as $key => $question_id ) {
1227 if ( tutils()->can_user_manage( 'question', $question_id ) ) {
1228 $i++;
1229 $wpdb->update( $wpdb->prefix . 'tutor_quiz_questions', array( 'question_order' => $i ), array( 'question_id' => $question_id ) );
1230 }
1231 }
1232 }
1233 }
1234
1235 /**
1236 * Save sorting data for quiz answers
1237 */
1238 public function tutor_quiz_answer_sorting() {
1239 tutils()->checking_nonce();
1240
1241 global $wpdb;
1242
1243 if ( ! empty( $_POST['sorted_answer_ids'] ) && is_array( $_POST['sorted_answer_ids'] ) && count( $_POST['sorted_answer_ids'] ) ) {
1244 $answer_ids = tutor_sanitize_data( $_POST['sorted_answer_ids'] );
1245 $i = 0;
1246 foreach ( $answer_ids as $key => $answer_id ) {
1247 if ( tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1248 $i++;
1249 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'answer_order' => $i ), array( 'answer_id' => $answer_id ) );
1250 }
1251 }
1252 }
1253 }
1254
1255 /**
1256 * Mark answer as correct
1257 */
1258
1259 public function tutor_mark_answer_as_correct() {
1260 tutils()->checking_nonce();
1261
1262 global $wpdb;
1263
1264 $answer_id = sanitize_text_field( $_POST['answer_id'] );
1265 $inputValue = sanitize_text_field( $_POST['inputValue'] );
1266
1267 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1268 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1269 }
1270
1271 $answer = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = %d LIMIT 0,1 ;", $answer_id ) );
1272 if ( $answer->belongs_question_type === 'single_choice' ) {
1273 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'is_correct' => 0 ), array( 'belongs_question_id' => esc_sql( $answer->belongs_question_id ) ) );
1274 }
1275 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'is_correct' => esc_sql( $inputValue ) ), array( 'answer_id' => esc_sql( $answer_id ) ) );
1276 }
1277
1278 /**
1279 * Update quiz settings from modal
1280 *
1281 * @since : v.1.0.0
1282 */
1283 public function tutor_quiz_modal_update_settings() {
1284 tutils()->checking_nonce();
1285 // while creating quiz if creating step is not follow then it may throw error that why check added
1286 $quiz_id = ( isset( $_POST['quiz_id'] ) ) ? sanitize_text_field( $_POST['quiz_id'] ) : '';
1287 $current_topic_id = sanitize_text_field( $_POST['topic_id'] );
1288 $course_id = tutor_utils()->get_course_id_by( 'topic', sanitize_textarea_field( $current_topic_id ) );
1289
1290 $quiz_option = tutor_utils()->sanitize_array( $_POST['quiz_option'] );
1291
1292 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
1293 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1294 }
1295
1296 update_post_meta( $quiz_id, 'tutor_quiz_option', $quiz_option );
1297 do_action( 'tutor_quiz_settings_updated', $quiz_id );
1298
1299 // @since 1.9.6
1300 ob_start();
1301 include tutor()->path . 'views/metabox/course-contents.php';
1302 $course_contents = ob_get_clean();
1303
1304 wp_send_json_success( array( 'course_contents' => $course_contents ) );
1305 }
1306
1307
1308 // =========================//
1309 // Front end stuffs
1310 // =========================//
1311
1312 /**
1313 * Rendering quiz for frontend
1314 *
1315 * @since v.1.0.0
1316 */
1317
1318 public function tutor_render_quiz_content() {
1319
1320 tutils()->checking_nonce();
1321
1322 $quiz_id = (int) sanitize_text_field( tutor_utils()->avalue_dot( 'quiz_id', $_POST ) );
1323
1324 if ( ! tutils()->has_enrolled_content_access( 'quiz', $quiz_id ) ) {
1325 wp_send_json_error( array( 'message' => __( 'Access Denied.', 'tutor' ) ) );
1326 }
1327
1328 ob_start();
1329 global $post;
1330
1331 $post = get_post( $quiz_id );
1332 setup_postdata( $post );
1333 // tutor_lesson_content();
1334
1335 single_quiz_contents();
1336 wp_reset_postdata();
1337
1338 $html = ob_get_clean();
1339 wp_send_json_success( array( 'html' => $html ) );
1340 }
1341
1342 }
1343