PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.9.15
Tutor LMS – eLearning and online course solution v1.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 / 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 4 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 4 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
1345 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) $_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) 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) $_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) $_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) $_GET['attempt_id'];
556 $attempt_answer_id = (int) $_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 esc_attr( $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 esc_attr( $quiz_id ); ?>" data-topic-id="<?php echo esc_attr( $topic_id ); ?>">
680 <i class=" tutor-icon-doubt"></i>[<?php _e( 'QUIZ', 'tutor' ); ?>]&nbsp;
681 <?php echo esc_html( 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 esc_attr( $quiz_id ); ?>">
685 <i class="tutor-icon-garbage"></i>
686 </a>
687 </div>
688 </div>
689 <?php
690 $output_quiz_row = ob_get_clean();
691
692 wp_send_json_success(
693 array(
694 'output' => $output,
695 'output_quiz_row' => $output_quiz_row,
696 )
697 );
698 }
699
700 public function tutor_delete_quiz_by_id() {
701 tutils()->checking_nonce();
702
703 global $wpdb;
704
705 $quiz_id = (int) $_POST['quiz_id'];
706 $post = get_post( $quiz_id );
707
708 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
709 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
710 }
711
712 if ( $post->post_type === 'tutor_quiz' ) {
713 do_action( 'tutor_delete_quiz_before', $quiz_id );
714
715 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempts', array( 'quiz_id' => $quiz_id ) );
716 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_attempt_answers', array( 'quiz_id' => $quiz_id ) );
717
718 $questions_ids = $wpdb->get_col( $wpdb->prepare( "SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = %d ", $quiz_id ) );
719
720 if ( is_array( $questions_ids ) && count( $questions_ids ) ) {
721 $in_question_ids = "'" . implode( "','", $questions_ids ) . "'";
722 $wpdb->query( "DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) " );
723 }
724
725 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_questions', array( 'quiz_id' => $quiz_id ) );
726
727 wp_delete_post( $quiz_id, true );
728 delete_post_meta( $quiz_id, '_tutor_course_id_for_lesson' );
729
730 do_action( 'tutor_delete_quiz_after', $quiz_id );
731
732 wp_send_json_success();
733 }
734
735 wp_send_json_error();
736 }
737
738 /**
739 * Update Quiz from quiz builder modal
740 *
741 * @since v.1.0.0
742 */
743 public function tutor_quiz_builder_quiz_update() {
744 tutils()->checking_nonce();
745
746 $quiz_id = sanitize_text_field( $_POST['quiz_id'] );
747 $topic_id = sanitize_text_field( $_POST['topic_id'] );
748 $quiz_title = sanitize_text_field( $_POST['quiz_title'] );
749 $quiz_description = wp_kses( $_POST['quiz_description'], $this->allowed_html );
750
751 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
752 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
753 }
754
755 $post_arr = array(
756 'ID' => $quiz_id,
757 'post_title' => $quiz_title,
758 'post_content' => $quiz_description,
759
760 );
761 $quiz_id = wp_update_post( $post_arr );
762
763 do_action( 'tutor_quiz_updated', $quiz_id );
764
765 ob_start();
766 ?>
767 <div class="tutor-lesson-top">
768 <i class="tutor-icon-move"></i>
769 <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 ); ?>">
770 <i class=" tutor-icon-doubt"></i>[<?php _e( 'QUIZ', 'tutor' ); ?>]&nbsp;
771 <?php echo stripslashes( $quiz_title ); ?>
772 </a>
773 <?php do_action( 'tutor_course_builder_before_quiz_btn_action', $quiz_id ); ?>
774 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo esc_attr( $quiz_id ); ?>"><i class="tutor-icon-garbage"></i></a>
775 </div>
776 <?php
777 $output_quiz_row = ob_get_clean();
778
779 wp_send_json_success( array( 'output_quiz_row' => $output_quiz_row ) );
780 }
781
782 /**
783 * Load quiz Modal for edit quiz
784 *
785 * @since v.1.0.0
786 */
787 public function tutor_load_edit_quiz_modal() {
788 tutils()->checking_nonce();
789
790 $quiz_id = sanitize_text_field( $_POST['quiz_id'] );
791 $topic_id = sanitize_text_field( $_POST['topic_id'] );
792
793 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
794 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
795 }
796
797 ob_start();
798 include tutor()->path . 'views/modal/edit_quiz.php';
799 $output = ob_get_clean();
800
801 wp_send_json_success( array( 'output' => $output ) );
802 }
803
804 /**
805 * Load quiz question form for quiz
806 *
807 * @since v.1.0.0
808 */
809 public function tutor_quiz_builder_get_question_form() {
810 tutils()->checking_nonce();
811
812 global $wpdb;
813 $quiz_id = sanitize_text_field( $_POST['quiz_id'] );
814 $question_id = sanitize_text_field( tutor_utils()->avalue_dot( 'question_id', $_POST ) );
815
816 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
817 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
818 }
819
820 if ( ! $question_id ) {
821 $next_question_id = tutor_utils()->quiz_next_question_id();
822 $next_question_order = tutor_utils()->quiz_next_question_order_id( $quiz_id );
823
824 $new_question_data = array(
825 'quiz_id' => $quiz_id,
826 'question_title' => __( 'Question', 'tutor' ) . ' ' . $next_question_id,
827 'question_description' => '',
828 'question_type' => 'true_false',
829 'question_mark' => 1,
830 'question_settings' => maybe_serialize( array() ),
831 'question_order' => esc_sql( $next_question_order ),
832 );
833
834 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_questions', $new_question_data );
835 $question_id = $wpdb->insert_id;
836 }
837
838 $question = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = %d ", $question_id ) );
839
840 ob_start();
841 include tutor()->path . 'views/modal/question_form.php';
842 $output = ob_get_clean();
843
844 wp_send_json_success( array( 'output' => $output ) );
845 }
846
847 public function tutor_quiz_modal_update_question() {
848 tutils()->checking_nonce();
849
850 global $wpdb;
851
852 $question_data = $_POST['tutor_quiz_question'];
853
854 foreach ( $question_data as $question_id => $question ) {
855
856 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
857 continue;
858 }
859
860 $question_title = sanitize_text_field( $question['question_title'] );
861 $question_description = wp_kses( $question['question_description'], $this->allowed_html ); // sanitize_text_field($question['question_description']);
862 $question_type = sanitize_text_field( $question['question_type'] );
863 $question_mark = sanitize_text_field( $question['question_mark'] );
864
865 unset( $question['question_title'] );
866 unset( $question['question_description'] );
867
868 $data = array(
869 'question_title' => $question_title,
870 'question_description' => $question_description,
871 'question_type' => $question_type,
872 'question_mark' => $question_mark,
873 'question_settings' => maybe_serialize( $question ),
874 );
875
876 $wpdb->update( $wpdb->prefix . 'tutor_quiz_questions', $data, array( 'question_id' => $question_id ) );
877
878 /**
879 * Validation
880 */
881 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
882 $question_options = tutils()->get_answers_by_quiz_question( $question_id );
883 if ( tutils()->count( $question_options ) ) {
884 $required_validate = true;
885 foreach ( $question_options as $question_option ) {
886 if ( $question_option->is_correct ) {
887 $required_validate = false;
888 }
889 }
890 if ( $required_validate ) {
891 $validation_msg = "<p class='tutor-error-msg'>" . __( 'Please select the correct answer', 'tutor' ) . '</p>';
892 wp_send_json_error( array( 'validation_msg' => $validation_msg ) );
893 }
894 } else {
895 $validation_msg = "<p class='tutor-error-msg'>" . __( 'Please make sure you have added more than one option and saved them', 'tutor' ) . '</p>';
896 wp_send_json_error( array( 'validation_msg' => $validation_msg ) );
897 }
898 }
899 }
900
901 wp_send_json_success();
902 }
903
904 public function tutor_quiz_builder_question_delete() {
905 tutils()->checking_nonce();
906
907 global $wpdb;
908
909 $question_id = sanitize_text_field( tutor_utils()->avalue_dot( 'question_id', $_POST ) );
910
911 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
912 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
913 }
914
915 if ( $question_id ) {
916 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_questions', array( 'question_id' => esc_sql( $question_id ) ) );
917 }
918
919 wp_send_json_success();
920 }
921
922 /**
923 * Get answers options form for quiz question
924 *
925 * @since v.1.0.0
926 */
927 public function tutor_quiz_add_question_answers() {
928 tutils()->checking_nonce();
929
930 $question_id = sanitize_text_field( $_POST['question_id'] );
931 $question = tutor_utils()->avalue_dot( $question_id, $_POST['tutor_quiz_question'] );
932 $question_type = $question['question_type'];
933
934 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
935 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
936 }
937
938 ob_start();
939 include tutor()->path . 'views/modal/question_answer_form.php';
940 $output = ob_get_clean();
941
942 wp_send_json_success( array( 'output' => $output ) );
943 }
944
945 /**
946 * Edit Answer Form
947 *
948 * @since v.1.0.0
949 */
950 public function tutor_quiz_edit_question_answer() {
951 tutils()->checking_nonce();
952
953 $answer_id = (int) $_POST['answer_id'];
954
955 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
956 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
957 }
958
959 $old_answer = tutor_utils()->get_answer_by_id( $answer_id );
960 foreach ( $old_answer as $old_answer ) {
961 }
962 $question_id = $old_answer->belongs_question_id;
963 $question_type = $old_answer->belongs_question_type;
964
965 ob_start();
966 include tutor()->path . 'views/modal/question_answer_edit_form.php';
967 $output = ob_get_clean();
968
969 wp_send_json_success( array( 'output' => $output ) );
970 }
971
972 public function tutor_save_quiz_answer_options() {
973 tutils()->checking_nonce();
974
975 global $wpdb;
976
977 $questions = tutor_sanitize_data( $_POST['tutor_quiz_question'] );
978 $answers = tutor_sanitize_data( $_POST['quiz_answer'] );
979
980 foreach ( $answers as $question_id => $answer ) {
981
982 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
983 continue;
984 }
985
986 $question = tutor_utils()->avalue_dot( $question_id, $questions );
987 $question_type = $question['question_type'];
988
989 // Getting next sorting order
990 $next_order_id = (int) $wpdb->get_var(
991 $wpdb->prepare(
992 "SELECT MAX(answer_order)
993 FROM {$wpdb->prefix}tutor_quiz_question_answers
994 where belongs_question_id = %d
995 AND belongs_question_type = %s ",
996 $question_id,
997 esc_sql( $question_type )
998 )
999 );
1000
1001 $next_order_id = $next_order_id + 1;
1002
1003 if ( $question ) {
1004 if ( $question_type === 'true_false' ) {
1005 $wpdb->delete(
1006 $wpdb->prefix . 'tutor_quiz_question_answers',
1007 array(
1008 'belongs_question_id' => $question_id,
1009 'belongs_question_type' => $question_type,
1010 )
1011 );
1012 $data_true_false = array(
1013 array(
1014 'belongs_question_id' => esc_sql( $question_id ),
1015 'belongs_question_type' => $question_type,
1016 'answer_title' => __( 'True', 'tutor' ),
1017 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
1018 'answer_two_gap_match' => 'true',
1019 ),
1020 array(
1021 'belongs_question_id' => esc_sql( $question_id ),
1022 'belongs_question_type' => $question_type,
1023 'answer_title' => __( 'False', 'tutor' ),
1024 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
1025 'answer_two_gap_match' => 'false',
1026 ),
1027 );
1028
1029 foreach ( $data_true_false as $true_false_data ) {
1030 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_question_answers', $true_false_data );
1031 }
1032 } elseif ( $question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
1033 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ) {
1034
1035 $answer_data = array(
1036 'belongs_question_id' => sanitize_text_field( $question_id ),
1037 'belongs_question_type' => $question_type,
1038 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1039 'image_id' => isset( $answer['image_id'] ) ? $answer['image_id'] : 0,
1040 'answer_view_format' => isset( $answer['answer_view_format'] ) ? $answer['answer_view_format'] : 0,
1041 'answer_order' => $next_order_id,
1042 );
1043 if ( isset( $answer['matched_answer_title'] ) ) {
1044 $answer_data['answer_two_gap_match'] = sanitize_text_field( $answer['matched_answer_title'] );
1045 }
1046
1047 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_question_answers', $answer_data );
1048
1049 } elseif ( $question_type === 'fill_in_the_blank' ) {
1050 $wpdb->delete(
1051 $wpdb->prefix . 'tutor_quiz_question_answers',
1052 array(
1053 'belongs_question_id' => $question_id,
1054 'belongs_question_type' => $question_type,
1055 )
1056 );
1057 $answer_data = array(
1058 'belongs_question_id' => sanitize_text_field( $question_id ),
1059 'belongs_question_type' => $question_type,
1060 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1061 'answer_two_gap_match' => isset( $answer['answer_two_gap_match'] ) ? sanitize_text_field( trim( $answer['answer_two_gap_match'] ) ) : null,
1062 );
1063 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_question_answers', $answer_data );
1064 }
1065 }
1066 }
1067
1068 wp_send_json_success();
1069 }
1070
1071 /**
1072 * Tutor Update Answer
1073 *
1074 * @since v.1.0.0
1075 */
1076 public function tutor_update_quiz_answer_options() {
1077 tutils()->checking_nonce();
1078
1079 global $wpdb;
1080
1081 $answer_id = (int) $_POST['tutor_quiz_answer_id'];
1082
1083 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1084 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1085 }
1086
1087 $questions = tutor_sanitize_data( $_POST['tutor_quiz_question'] );
1088 $answers = tutor_sanitize_data( $_POST['quiz_answer'] );
1089
1090 foreach ( $answers as $question_id => $answer ) {
1091 $question = tutor_utils()->avalue_dot( $question_id, $questions );
1092 $question_type = $question['question_type'];
1093
1094 if ( $question ) {
1095 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' ) {
1096
1097 $answer_data = array(
1098 'belongs_question_id' => $question_id,
1099 'belongs_question_type' => $question_type,
1100 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1101 'image_id' => isset( $answer['image_id'] ) ? $answer['image_id'] : 0,
1102 'answer_view_format' => isset( $answer['answer_view_format'] ) ? sanitize_text_field( $answer['answer_view_format'] ) : '',
1103 );
1104 if ( isset( $answer['matched_answer_title'] ) ) {
1105 $answer_data['answer_two_gap_match'] = sanitize_text_field( $answer['matched_answer_title'] );
1106 }
1107
1108 if ( $question_type === 'fill_in_the_blank' ) {
1109 $answer_data['answer_two_gap_match'] = isset( $answer['answer_two_gap_match'] ) ? sanitize_text_field( trim( $answer['answer_two_gap_match'] ) ) : null;
1110 }
1111
1112 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', $answer_data, array( 'answer_id' => $answer_id ) );
1113 }
1114 }
1115 }
1116
1117 // die(print_r($_POST));
1118 wp_send_json_success();
1119 }
1120
1121 public function tutor_quiz_builder_get_answers_by_question() {
1122 tutils()->checking_nonce();
1123
1124 global $wpdb;
1125 $question_id = sanitize_text_field( $_POST['question_id'] );
1126 $question_type = sanitize_text_field( $_POST['question_type'] );
1127
1128 if ( ! tutils()->can_user_manage( 'question', $question_id ) ) {
1129 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1130 }
1131
1132 $question = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = %d ", $question_id ) );
1133 $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 ) ) );
1134
1135 ob_start();
1136
1137 switch ( $question_type ) {
1138 case 'true_false':
1139 echo '<label>' . __( 'Answer options &amp; mark correct', 'tutor' ) . '</label>';
1140 break;
1141 case 'ordering':
1142 echo '<label>' . __( 'Make sure you’re saving the answers in the right order. Students will have to match this order exactly.', 'tutor' ) . '</label>';
1143 break;
1144 }
1145
1146 if ( is_array( $answers ) && count( $answers ) ) {
1147 foreach ( $answers as $answer ) {
1148 ?>
1149 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo esc_attr( $answer->answer_id ); ?>">
1150 <div class="tutor-quiz-answer">
1151 <span class="tutor-quiz-answer-title">
1152 <?php
1153 echo stripslashes( $answer->answer_title );
1154 if ( $answer->belongs_question_type === 'fill_in_the_blank' ) {
1155 echo ' (' . __( 'Answer', 'tutor' ) . ' : ';
1156 echo '<strong>' . stripslashes( $answer->answer_two_gap_match ) . '</strong>)';
1157 }
1158 if ( $answer->belongs_question_type === 'matching' ) {
1159 echo ' - ' . stripslashes( $answer->answer_two_gap_match );
1160 }
1161 ?>
1162 </span>
1163
1164 <?php
1165 if ( $answer->image_id ) {
1166 echo '<span class="tutor-question-answer-image"><img src="' . wp_get_attachment_image_url( $answer->image_id ) . '" /> </span>';
1167 }
1168 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
1169 ?>
1170 <span class="tutor-quiz-answers-mark-correct-wrap">
1171 <input type="radio" name="mark_as_correct[<?php echo esc_attr( $answer->belongs_question_id ); ?>]" value="<?php echo esc_attr( $answer->answer_id ); ?>" title="<?php _e( 'Mark as correct', 'tutor' ); ?>" <?php checked( 1, $answer->is_correct ); ?> >
1172 </span>
1173 <?php
1174 } elseif ( $question_type === 'multiple_choice' ) {
1175 ?>
1176 <span class="tutor-quiz-answers-mark-correct-wrap">
1177 <input type="checkbox" name="mark_as_correct[<?php echo esc_attr( $answer->belongs_question_id ); ?>]" value="<?php echo esc_attr( $answer->answer_id ); ?>" title="<?php _e( 'Mark as correct', 'tutor' ); ?>" <?php checked( 1, $answer->is_correct ); ?> >
1178 </span>
1179 <?php
1180 }
1181 ?>
1182 <?php if ( 'true_false' != $question_type ) : ?>
1183 <span class="tutor-quiz-answer-edit">
1184 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
1185 </span>
1186 <?php endif; ?>
1187 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
1188 </div>
1189 <?php if ( 'true_false' != $question_type ) : ?>
1190 <div class="tutor-quiz-answer-trash-wrap">
1191 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo esc_attr( $answer->answer_id ); ?>"><i class="tutor-icon-garbage"></i> </a>
1192 </div>
1193 <?php endif; ?>
1194 </div>
1195 <?php
1196 }
1197 }
1198 $output = ob_get_clean();
1199
1200 wp_send_json_success( array( 'output' => $output ) );
1201 }
1202
1203 public function tutor_quiz_builder_delete_answer() {
1204 tutils()->checking_nonce();
1205
1206 global $wpdb;
1207 $answer_id = sanitize_text_field( $_POST['answer_id'] );
1208
1209 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1210 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1211 }
1212
1213 $wpdb->delete( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'answer_id' => esc_sql( $answer_id ) ) );
1214 wp_send_json_success();
1215 }
1216
1217 /**
1218 * Save quiz questions sorting
1219 */
1220 public function tutor_quiz_question_sorting() {
1221 tutils()->checking_nonce();
1222
1223 global $wpdb;
1224
1225 $question_ids = tutor_utils()->avalue_dot( 'sorted_question_ids', tutor_sanitize_data($_POST) );
1226 if ( is_array( $question_ids ) && count( $question_ids ) ) {
1227 $i = 0;
1228 foreach ( $question_ids as $key => $question_id ) {
1229 if ( tutils()->can_user_manage( 'question', $question_id ) ) {
1230 $i++;
1231 $wpdb->update( $wpdb->prefix . 'tutor_quiz_questions', array( 'question_order' => $i ), array( 'question_id' => $question_id ) );
1232 }
1233 }
1234 }
1235 }
1236
1237 /**
1238 * Save sorting data for quiz answers
1239 */
1240 public function tutor_quiz_answer_sorting() {
1241 tutils()->checking_nonce();
1242
1243 global $wpdb;
1244
1245 if ( ! empty( $_POST['sorted_answer_ids'] ) && is_array( $_POST['sorted_answer_ids'] ) && count( $_POST['sorted_answer_ids'] ) ) {
1246 $answer_ids = tutor_sanitize_data( $_POST['sorted_answer_ids'] );
1247 $i = 0;
1248 foreach ( $answer_ids as $key => $answer_id ) {
1249 if ( tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1250 $i++;
1251 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'answer_order' => $i ), array( 'answer_id' => $answer_id ) );
1252 }
1253 }
1254 }
1255 }
1256
1257 /**
1258 * Mark answer as correct
1259 */
1260
1261 public function tutor_mark_answer_as_correct() {
1262 tutils()->checking_nonce();
1263
1264 global $wpdb;
1265
1266 $answer_id = sanitize_text_field( $_POST['answer_id'] );
1267 $inputValue = sanitize_text_field( $_POST['inputValue'] );
1268
1269 if ( ! tutils()->can_user_manage( 'quiz_answer', $answer_id ) ) {
1270 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1271 }
1272
1273 $answer = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = %d LIMIT 0,1 ;", $answer_id ) );
1274 if ( $answer->belongs_question_type === 'single_choice' ) {
1275 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'is_correct' => 0 ), array( 'belongs_question_id' => esc_sql( $answer->belongs_question_id ) ) );
1276 }
1277 $wpdb->update( $wpdb->prefix . 'tutor_quiz_question_answers', array( 'is_correct' => esc_sql( $inputValue ) ), array( 'answer_id' => esc_sql( $answer_id ) ) );
1278 }
1279
1280 /**
1281 * Update quiz settings from modal
1282 *
1283 * @since : v.1.0.0
1284 */
1285 public function tutor_quiz_modal_update_settings() {
1286 tutils()->checking_nonce();
1287 // while creating quiz if creating step is not follow then it may throw error that why check added
1288 $quiz_id = ( isset( $_POST['quiz_id'] ) ) ? sanitize_text_field( $_POST['quiz_id'] ) : '';
1289 $current_topic_id = sanitize_text_field( $_POST['topic_id'] );
1290 $course_id = tutor_utils()->get_course_id_by( 'topic', sanitize_textarea_field( $current_topic_id ) );
1291
1292 $quiz_option = tutor_utils()->sanitize_array( $_POST['quiz_option'] );
1293
1294 if ( ! tutils()->can_user_manage( 'quiz', $quiz_id ) ) {
1295 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
1296 }
1297
1298 update_post_meta( $quiz_id, 'tutor_quiz_option', $quiz_option );
1299 do_action( 'tutor_quiz_settings_updated', $quiz_id );
1300
1301 // @since 1.9.6
1302 ob_start();
1303 include tutor()->path . 'views/metabox/course-contents.php';
1304 $course_contents = ob_get_clean();
1305
1306 wp_send_json_success( array( 'course_contents' => $course_contents ) );
1307 }
1308
1309
1310 // =========================//
1311 // Front end stuffs
1312 // =========================//
1313
1314 /**
1315 * Rendering quiz for frontend
1316 *
1317 * @since v.1.0.0
1318 */
1319
1320 public function tutor_render_quiz_content() {
1321
1322 tutils()->checking_nonce();
1323
1324 $quiz_id = (int) tutor_utils()->avalue_dot( 'quiz_id', $_POST );
1325
1326 if ( ! tutils()->has_enrolled_content_access( 'quiz', $quiz_id ) ) {
1327 wp_send_json_error( array( 'message' => __( 'Access Denied.', 'tutor' ) ) );
1328 }
1329
1330 ob_start();
1331 global $post;
1332
1333 $post = get_post( $quiz_id );
1334 setup_postdata( $post );
1335 // tutor_lesson_content();
1336
1337 single_quiz_contents();
1338 wp_reset_postdata();
1339
1340 $html = ob_get_clean();
1341 wp_send_json_success( array( 'html' => $html ) );
1342 }
1343
1344 }
1345