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