PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.9.12
Tutor LMS – eLearning and online course solution v1.9.12
3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / 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 4 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 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 5 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 5 years ago Tutor_Base.php 5 years ago Tutor_List_Table.php 4 years ago Tutor_Setup.php 4 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
1309 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 $course_id = tutor_utils()->get_course_by_quiz($attempt->quiz_id)->ID;
288
289 $attempt_answers = isset($_POST['attempt']) ? $_POST['attempt'] : false;
290 if ( ! is_user_logged_in()){
291 die('Please sign in to do this operation');
292 }
293
294 global $wpdb;
295 $user_id = get_current_user_id();
296
297 do_action('tutor_quiz/attempt_analysing/before', $attempt_id);
298
299 if ($attempt_answers && is_array($attempt_answers) && count($attempt_answers)){
300 foreach ($attempt_answers as $attempt_id => $attempt_answer){
301
302 /**
303 * Get total marks of all question comes
304 */
305 $question_ids = tutor_utils()->avalue_dot('quiz_question_ids', $attempt_answer);
306 if (is_array($question_ids) && count($question_ids)){
307 $question_ids_string = "'".implode("','", $question_ids)."'";
308 $total_question_marks = $wpdb->get_var("SELECT SUM(question_mark) FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN({$question_ids_string}) ;");
309 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', array('total_marks' =>$total_question_marks ), array('attempt_id' => $attempt_id ));
310 }
311
312 if ( ! $attempt || $user_id != $attempt->user_id){
313 die('Operation not allowed, attempt not found or permission denied');
314 }
315
316 $quiz_answers = tutor_utils()->avalue_dot('quiz_question', $attempt_answer);
317
318 $total_marks = 0;
319 $review_required = false;
320
321 if ( tutils()->count($quiz_answers)) {
322
323 foreach ( $quiz_answers as $question_id => $answers ) {
324 $question = tutor_utils()->get_quiz_question_by_id( $question_id );
325 $question_type = $question->question_type;
326
327 $is_answer_was_correct = false;
328 $given_answer = '';
329
330 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
331
332 if(!is_numeric($answers) || !$answers) {
333 wp_send_json_error();
334 exit;
335 }
336
337 $given_answer = $answers;
338 $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 ) );
339
340 } elseif ( $question_type === 'multiple_choice' ) {
341
342 $given_answer = (array) ( $answers );
343
344 $given_answer = array_filter( $given_answer, function($id) {
345 return is_numeric($id) && $id>0;
346 } );
347
348 $get_original_answers = (array) $wpdb->get_col($wpdb->prepare(
349 "SELECT
350 answer_id
351 FROM
352 {$wpdb->prefix}tutor_quiz_question_answers
353 WHERE
354 belongs_question_id = %d
355 AND belongs_question_type = %s
356 AND is_correct = 1 ;
357 ",
358 $question->question_id,
359 $question_type
360 ) );
361
362
363 if (count(array_diff($get_original_answers, $given_answer)) === 0 && count($get_original_answers) === count($given_answer)) {
364 $is_answer_was_correct = true;
365 }
366 $given_answer = maybe_serialize( $answers );
367
368 } elseif ( $question_type === 'fill_in_the_blank' ) {
369
370 $given_answer = (array) array_map( 'sanitize_text_field', $answers );
371 $given_answer = maybe_serialize( $given_answer );
372
373 $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 ) );
374 $gap_answer = (array) explode( '|', $get_original_answer->answer_two_gap_match );
375
376 $gap_answer = array_map( 'sanitize_text_field', $gap_answer );
377 if ( strtolower($given_answer) == strtolower(maybe_serialize( $gap_answer )) ) {
378 $is_answer_was_correct = true;
379 }
380 } elseif ( $question_type === 'open_ended' || $question_type === 'short_answer' ) {
381 $review_required = true;
382 $given_answer = wp_kses_post( $answers );
383
384 } elseif ( $question_type === 'ordering' || $question_type === 'matching' || $question_type === 'image_matching' ) {
385
386 $given_answer = (array) array_map( 'sanitize_text_field', tutor_utils()->avalue_dot( 'answers', $answers ) );
387 $given_answer = maybe_serialize( $given_answer );
388
389 $get_original_answers = (array) $wpdb->get_col($wpdb->prepare(
390 "SELECT answer_id
391 FROM {$wpdb->prefix}tutor_quiz_question_answers
392 WHERE belongs_question_id = %d AND belongs_question_type = %s ORDER BY answer_order ASC ;", $question->question_id, $question_type));
393
394 $get_original_answers = array_map( 'sanitize_text_field', $get_original_answers );
395
396 if ( $given_answer == maybe_serialize( $get_original_answers ) ) {
397 $is_answer_was_correct = true;
398 }
399
400 } elseif ( $question_type === 'image_answering' ) {
401 $image_inputs = tutor_utils()->avalue_dot( 'answer_id', $answers );
402 $image_inputs = (array) array_map( 'sanitize_text_field', $image_inputs );
403 $given_answer = maybe_serialize( $image_inputs );
404 $is_answer_was_correct = false;
405
406 $db_answer = $wpdb->get_col($wpdb->prepare(
407 "SELECT answer_title
408 FROM {$wpdb->prefix}tutor_quiz_question_answers
409 WHERE belongs_question_id = %d AND belongs_question_type = 'image_answering' ORDER BY answer_order asc ;", $question_id));
410
411 if ( is_array( $db_answer ) && count( $db_answer ) ) {
412 $is_answer_was_correct = ( strtolower( maybe_serialize( array_values( $image_inputs ) ) ) == strtolower( maybe_serialize( $db_answer ) ) );
413 }
414 }
415
416 $question_mark = $is_answer_was_correct ? $question->question_mark : 0;
417 $total_marks += $question_mark;
418
419 $answers_data = array(
420 'user_id' => $user_id,
421 'quiz_id' => $attempt->quiz_id,
422 'question_id' => $question_id,
423 'quiz_attempt_id' => $attempt_id,
424 'given_answer' => $given_answer,
425 'question_mark' => $question->question_mark,
426 'achieved_mark' => $question_mark,
427 'minus_mark' => 0,
428 'is_correct' => $is_answer_was_correct ? 1 : 0,
429 );
430
431 /*
432 check if question_type open ended or short ans the set is_correct default value null before saving
433 */
434 if($question_type==="open_ended" || $question_type ==="short_answer")
435 {
436 $answers_data['is_correct'] = NULL;
437 }
438
439 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_attempt_answers', $answers_data );
440 }
441 }
442
443 $attempt_info = array(
444 'total_answered_questions' => tutils()->count($quiz_answers),
445 'earned_marks' => $total_marks,
446 'attempt_status' => 'attempt_ended',
447 'attempt_ended_at' => date("Y-m-d H:i:s", tutor_time()),
448 );
449
450 if ($review_required){
451 $attempt_info['attempt_status'] = 'review_required';
452 }
453
454 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
455 }
456
457 do_action('tutor_quiz/attempt_ended', $attempt_id, $course_id, $user_id);
458 return true;
459 }
460 return false;
461 }
462
463
464 /**
465 * Quiz attempt will be finish here
466 *
467 */
468
469 public function finishing_quiz_attempt(){
470
471 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_finish_quiz_attempt' ){
472 return;
473 }
474 //Checking nonce
475 tutor_utils()->checking_nonce();
476
477 if ( ! is_user_logged_in()){
478 die('Please sign in to do this operation');
479 }
480
481 global $wpdb;
482
483 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
484 $attempt = tutor_utils()->is_started_quiz($quiz_id);
485 $attempt_id = $attempt->attempt_id;
486
487 $attempt_info = array(
488 'total_answered_questions' => 0,
489 'earned_marks' => 0,
490 'attempt_status' => 'attempt_ended',
491 'attempt_ended_at' => date("Y-m-d H:i:s", tutor_time()),
492 );
493
494 do_action('tutor_quiz_before_finish', $attempt_id, $quiz_id, $attempt->user_id);
495 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
496 do_action('tutor_quiz_finished', $attempt_id, $quiz_id, $attempt->user_id);
497
498 wp_redirect(tutor_utils()->input_old('_wp_http_referer'));
499 }
500
501 /**
502 * Quiz timeout by ajax
503 */
504 public function tutor_quiz_timeout(){
505 tutils()->checking_nonce();
506
507 global $wpdb;
508
509 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
510
511 // if(!tutils()->can_user_manage('quiz', $quiz_id)) {
512 // wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
513 // }
514
515 $attempt = tutor_utils()->is_started_quiz($quiz_id);
516
517 if ($attempt) {
518 $attempt_id = $attempt->attempt_id;
519
520 $data = array(
521 'attempt_status' => 'attempt_timeout',
522 'attempt_ended_at' => date("Y-m-d H:i:s", tutor_time()),
523 );
524 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $data, array('attempt_id' => $attempt->attempt_id));
525
526 do_action('tutor_quiz_timeout', $attempt_id, $quiz_id, $attempt->user_id);
527
528 wp_send_json_success();
529 }
530
531 wp_send_json_error(__('Quiz has been timeout already', 'tutor'));
532 }
533
534 /**
535 * Review the answer and change individual answer result
536 */
537
538 public function review_quiz_answer() {
539
540 tutils()->checking_nonce(strtolower($_SERVER['REQUEST_METHOD']));
541
542 global $wpdb;
543
544 $attempt_id = (int) sanitize_text_field($_GET['attempt_id']);
545 $attempt_answer_id = (int) sanitize_text_field($_GET['attempt_answer_id']);
546 $mark_as = sanitize_text_field($_GET['mark_as']);
547
548 if(!tutils()->can_user_manage('attempt', $attempt_id) || !tutils()->can_user_manage('attempt_answer', $attempt_answer_id)) {
549 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
550 }
551
552 $attempt_answer = $wpdb->get_row($wpdb->prepare(
553 "SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers
554 WHERE attempt_answer_id = %d ",
555 $attempt_answer_id
556 ));
557
558 $attempt = tutor_utils()->get_attempt($attempt_id);
559 $question = tutils()->get_quiz_question_by_id($attempt_answer->question_id);
560 $course_id = $attempt->course_id;
561 $student_id = $attempt->user_id;
562 $previous_ans = $attempt_answer->is_correct;
563
564 do_action('tutor_quiz_review_answer_before', $attempt_answer_id, $attempt_id, $mark_as);
565
566 if ($mark_as === 'correct'){
567
568 $answer_update_data = array(
569 'achieved_mark' => $attempt_answer->question_mark,
570 'is_correct' => 1,
571 );
572 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
573 if($previous_ans ==0 OR $previous_ans ==null)
574 {
575
576 //if previous answer was wrong or in review then add point as correct
577 $attempt_update_data = array(
578 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
579 'is_manually_reviewed' => 1,
580 'manually_reviewed_at' => date("Y-m-d H:i:s", tutor_time()),
581 );
582
583
584 }
585
586 if ($question->question_type === 'open_ended' || $question->question_type === 'short_answer' ){
587 $attempt_update_data['attempt_status'] = 'attempt_ended';
588 }
589 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
590 }
591 elseif($mark_as === 'incorrect')
592 {
593
594 $answer_update_data = array(
595 'achieved_mark' => '0.00',
596 'is_correct' => 0,
597 );
598 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
599
600
601 if($previous_ans ==1)
602 {
603
604 //if previous ans was right then mynus
605 $attempt_update_data = array(
606 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
607 'is_manually_reviewed' => 1,
608 'manually_reviewed_at' => date("Y-m-d H:i:s", tutor_time()),
609 );
610
611 }
612 if ($question->question_type === 'open_ended' || $question->question_type === 'short_answer' ){
613 $attempt_update_data['attempt_status'] = 'attempt_ended';
614 }
615
616 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
617 }
618 do_action('tutor_quiz_review_answer_after', $attempt_answer_id, $attempt_id, $mark_as);
619 do_action('tutor_quiz/answer/review/after', $attempt_answer_id, $course_id, $student_id);
620
621 if (wp_doing_ajax())
622 {
623 wp_send_json_success();
624 }
625 else{
626 wp_redirect(admin_url("admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=".$attempt_id));
627 }
628
629 die();
630 }
631
632
633 /**
634 * New Design Quiz
635 */
636 public function tutor_create_quiz_and_load_modal(){
637 tutils()->checking_nonce();
638
639 $topic_id = sanitize_text_field($_POST['topic_id']);
640 $quiz_title = sanitize_text_field($_POST['quiz_title']);
641 $quiz_description = wp_kses( $_POST['quiz_description'], $this->allowed_html );
642 $next_order_id = tutor_utils()->get_next_course_content_order_id($topic_id);
643
644 if(!tutils()->can_user_manage('topic', $topic_id)) {
645 wp_send_json_error( array('message'=>__('Access Denied', 'tutor'), 'data'=>$_POST) );
646 }
647
648 $post_arr = array(
649 'post_type' => 'tutor_quiz',
650 'post_title' => $quiz_title,
651 'post_content' => $quiz_description,
652 'post_status' => 'publish',
653 'post_author' => get_current_user_id(),
654 'post_parent' => $topic_id,
655 'menu_order' => $next_order_id,
656 );
657 $quiz_id = wp_insert_post( $post_arr );
658 do_action('tutor_initial_quiz_created', $quiz_id);
659
660 ob_start();
661 include tutor()->path.'views/modal/edit_quiz.php';
662 $output = ob_get_clean();
663
664 ob_start();
665 ?>
666 <div id="tutor-quiz-<?php echo $quiz_id; ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
667 <div class="tutor-lesson-top">
668 <i class="tutor-icon-move"></i>
669 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
670 ?>"> <i class=" tutor-icon-doubt"></i>[<?php _e('QUIZ', 'tutor'); ?>] <?php echo stripslashes($quiz_title); ?> </a>
671 <?php do_action('tutor_course_builder_before_quiz_btn_action', $quiz_id); ?>
672 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
673 </div>
674 </div>
675 <?php
676 $output_quiz_row = ob_get_clean();
677
678 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
679 }
680
681 public function tutor_delete_quiz_by_id(){
682 tutils()->checking_nonce();
683
684 global $wpdb;
685
686 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
687 $post = get_post($quiz_id);
688
689
690 if(!tutils()->can_user_manage('quiz', $quiz_id)) {
691 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
692 }
693
694 if ( $post->post_type === 'tutor_quiz'){
695 do_action('tutor_delete_quiz_before', $quiz_id);
696
697 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
698 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
699
700 $questions_ids = $wpdb->get_col($wpdb->prepare("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = %d ", $quiz_id));
701
702 if (is_array($questions_ids) && count($questions_ids)){
703 $in_question_ids = "'".implode("','", $questions_ids)."'";
704 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
705 }
706
707 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
708
709 wp_delete_post($quiz_id, true);
710 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
711
712 do_action('tutor_delete_quiz_after', $quiz_id);
713
714
715 wp_send_json_success();
716 }
717
718 wp_send_json_error();
719 }
720
721 /**
722 * Update Quiz from quiz builder modal
723 *
724 * @since v.1.0.0
725 */
726 public function tutor_quiz_builder_quiz_update(){
727 tutils()->checking_nonce();
728
729 $quiz_id = sanitize_text_field($_POST['quiz_id']);
730 $topic_id = sanitize_text_field($_POST['topic_id']);
731 $quiz_title = sanitize_text_field($_POST['quiz_title']);
732 $quiz_description = wp_kses( $_POST['quiz_description'], $this->allowed_html );
733
734 if(!tutils()->can_user_manage('quiz', $quiz_id)) {
735 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
736 }
737
738 $post_arr = array(
739 'ID' => $quiz_id,
740 'post_title' => $quiz_title,
741 'post_content' => $quiz_description,
742
743 );
744 $quiz_id = wp_update_post( $post_arr );
745
746 do_action('tutor_quiz_updated', $quiz_id);
747
748 ob_start();
749 ?>
750 <div class="tutor-lesson-top">
751 <i class="tutor-icon-move"></i>
752 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
753 ?>"> <i class=" tutor-icon-doubt"></i>[<?php _e('QUIZ', 'tutor'); ?>] <?php echo stripslashes($quiz_title); ?> </a>
754 <?php do_action('tutor_course_builder_before_quiz_btn_action', $quiz_id); ?>
755 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
756 </div>
757 <?php
758 $output_quiz_row = ob_get_clean();
759
760 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
761 }
762
763 /**
764 * Load quiz Modal for edit quiz
765 *
766 * @since v.1.0.0
767 */
768 public function tutor_load_edit_quiz_modal(){
769 tutils()->checking_nonce();
770
771 $quiz_id = sanitize_text_field ($_POST['quiz_id'] );
772 $topic_id = sanitize_text_field( $_POST['topic_id'] );
773
774 if(!tutils()->can_user_manage('quiz', $quiz_id)) {
775 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
776 }
777
778 ob_start();
779 include tutor()->path.'views/modal/edit_quiz.php';
780 $output = ob_get_clean();
781
782 wp_send_json_success(array('output' => $output));
783 }
784
785 /**
786 * Load quiz question form for quiz
787 *
788 * @since v.1.0.0
789 */
790 public function tutor_quiz_builder_get_question_form(){
791 tutils()->checking_nonce();
792
793 global $wpdb;
794 $quiz_id = sanitize_text_field($_POST['quiz_id']);
795 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
796
797 if(!tutils()->can_user_manage('quiz', $quiz_id)) {
798 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
799 }
800
801 if ( ! $question_id){
802 $next_question_id = tutor_utils()->quiz_next_question_id();
803 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
804
805 $new_question_data = array(
806 'quiz_id' => $quiz_id,
807 'question_title' => __('Question', 'tutor').' '.$next_question_id,
808 'question_description' => '',
809 'question_type' => 'true_false',
810 'question_mark' => 1,
811 'question_settings' => maybe_serialize(array()),
812 'question_order' => esc_sql( $next_question_order ) ,
813 );
814
815 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
816 $question_id = $wpdb->insert_id;
817 }
818
819 $question = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = %d ", $question_id));
820
821 ob_start();
822 include tutor()->path.'views/modal/question_form.php';
823 $output = ob_get_clean();
824
825 wp_send_json_success(array('output' => $output));
826 }
827
828 public function tutor_quiz_modal_update_question(){
829 tutils()->checking_nonce();
830
831 global $wpdb;
832
833 $question_data = $_POST['tutor_quiz_question'];
834
835 foreach ($question_data as $question_id => $question) {
836
837 if(!tutils()->can_user_manage('question', $question_id)) {
838 continue;
839 }
840
841 $question_title = sanitize_text_field($question['question_title']);
842 $question_description = wp_kses( $question['question_description'], $this->allowed_html ); // sanitize_text_field($question['question_description']);
843 $question_type = sanitize_text_field($question['question_type']);
844 $question_mark = sanitize_text_field($question['question_mark']);
845
846 unset($question['question_title']);
847 unset($question['question_description']);
848
849 $data = array(
850 'question_title' => $question_title,
851 'question_description' => $question_description,
852 'question_type' => $question_type,
853 'question_mark' => $question_mark,
854 'question_settings' => maybe_serialize($question),
855 );
856
857 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
858
859
860 /**
861 * Validation
862 */
863 if ($question_type === 'true_false' || $question_type === 'single_choice'){
864 $question_options = tutils()->get_answers_by_quiz_question($question_id);
865 if (tutils()->count($question_options)){
866 $required_validate = true;
867 foreach ($question_options as $question_option){
868 if ($question_option->is_correct){
869 $required_validate = false;
870 }
871 }
872 if ($required_validate){
873 $validation_msg = "<p class='tutor-error-msg'>".__('Please select the correct answer', 'tutor')."</p>";
874 wp_send_json_error(array('validation_msg' => $validation_msg ));
875 }
876 }else{
877 $validation_msg = "<p class='tutor-error-msg'>".__('Please make sure you have added more than one option and saved them', 'tutor')."</p>";
878 wp_send_json_error(array('validation_msg' => $validation_msg ));
879 }
880 }
881 }
882
883 wp_send_json_success();
884 }
885
886 public function tutor_quiz_builder_question_delete(){
887 tutils()->checking_nonce();
888
889 global $wpdb;
890
891 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
892
893 if(!tutils()->can_user_manage('question', $question_id)) {
894 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
895 }
896
897 if ($question_id){
898 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => esc_sql( $question_id ) ));
899 }
900
901 wp_send_json_success();
902 }
903
904 /**
905 * Get answers options form for quiz question
906 *
907 * @since v.1.0.0
908 */
909 public function tutor_quiz_add_question_answers(){
910 tutils()->checking_nonce();
911
912 $question_id = sanitize_text_field($_POST['question_id']);
913 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
914 $question_type = $question['question_type'];
915
916 if(!tutils()->can_user_manage('question', $question_id)) {
917 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
918 }
919
920 ob_start();
921 include tutor()->path.'views/modal/question_answer_form.php';
922 $output = ob_get_clean();
923
924 wp_send_json_success(array('output' => $output));
925 }
926
927 /**
928 * Edit Answer Form
929 *
930 * @since v.1.0.0
931 */
932 public function tutor_quiz_edit_question_answer(){
933 tutils()->checking_nonce();
934
935 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
936
937 if(!tutils()->can_user_manage('quiz_answer', $answer_id)) {
938 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
939 }
940
941 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
942 foreach ($old_answer as $old_answer);
943 $question_id = $old_answer->belongs_question_id;
944 $question_type = $old_answer->belongs_question_type;
945
946 ob_start();
947 include tutor()->path.'views/modal/question_answer_edit_form.php';
948 $output = ob_get_clean();
949
950 wp_send_json_success(array('output' => $output));
951 }
952
953 public function tutor_save_quiz_answer_options(){
954 tutils()->checking_nonce();
955
956 global $wpdb;
957
958 $questions = $_POST['tutor_quiz_question'];
959 $answers = $_POST['quiz_answer'];
960
961 foreach ($answers as $question_id => $answer){
962
963 if(!tutils()->can_user_manage('question', $question_id)) {
964 continue;
965 }
966
967 $question = tutor_utils()->avalue_dot($question_id, $questions);
968 $question_type = $question['question_type'];
969
970 //Getting next sorting order
971 $next_order_id = (int) $wpdb->get_var($wpdb->prepare(
972 "SELECT MAX(answer_order)
973 FROM {$wpdb->prefix}tutor_quiz_question_answers
974 where belongs_question_id = %d
975 AND belongs_question_type = %s ", $question_id, esc_sql( $question_type )));
976
977 $next_order_id = $next_order_id + 1;
978
979 if ($question){
980 if ($question_type === 'true_false'){
981 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
982 $data_true_false = array(
983 array(
984 'belongs_question_id' => esc_sql( $question_id ) ,
985 'belongs_question_type' => $question_type,
986 'answer_title' => __('True', 'tutor'),
987 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
988 'answer_two_gap_match' => 'true',
989 ),
990 array(
991 'belongs_question_id' => esc_sql( $question_id ) ,
992 'belongs_question_type' => $question_type,
993 'answer_title' => __('False', 'tutor'),
994 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
995 'answer_two_gap_match' => 'false',
996 ),
997 );
998
999 foreach ($data_true_false as $true_false_data){
1000 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
1001 }
1002
1003 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
1004 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
1005
1006 $answer_data = array(
1007 'belongs_question_id' => sanitize_text_field( $question_id ),
1008 'belongs_question_type' => $question_type,
1009 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1010 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
1011 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
1012 'answer_order' => $next_order_id,
1013 );
1014 if (isset($answer['matched_answer_title'])){
1015 $answer_data['answer_two_gap_match'] = sanitize_text_field( $answer['matched_answer_title'] );
1016 }
1017
1018 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
1019
1020 }elseif($question_type === 'fill_in_the_blank'){
1021 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
1022 $answer_data = array(
1023 'belongs_question_id' => sanitize_text_field( $question_id ) ,
1024 'belongs_question_type' => $question_type,
1025 'answer_title' => sanitize_text_field( $answer['answer_title'] ),
1026 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? sanitize_text_field( trim($answer['answer_two_gap_match']) ) : null,
1027 );
1028 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
1029 }
1030 }
1031 }
1032
1033 wp_send_json_success();
1034 }
1035
1036 /**
1037 * Tutor Update Answer
1038 *
1039 * @since v.1.0.0
1040 */
1041 public function tutor_update_quiz_answer_options(){
1042 tutils()->checking_nonce();
1043
1044 global $wpdb;
1045
1046 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
1047
1048 if(!tutils()->can_user_manage('quiz_answer', $answer_id)) {
1049 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
1050 }
1051
1052 $questions = $_POST['tutor_quiz_question'];
1053 $answers = $_POST['quiz_answer'];
1054
1055 foreach ($answers as $question_id => $answer){
1056 $question = tutor_utils()->avalue_dot($question_id, $questions);
1057 $question_type = $question['question_type'];
1058
1059 if ($question){
1060 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' ){
1061
1062 $answer_data = array(
1063 'belongs_question_id' => $question_id,
1064 'belongs_question_type' => $question_type,
1065 'answer_title' => sanitize_text_field( $answer['answer_title'] ) ,
1066 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
1067 'answer_view_format' => isset($answer['answer_view_format']) ? sanitize_text_field( $answer['answer_view_format'] ) : '',
1068 );
1069 if (isset($answer['matched_answer_title'])){
1070 $answer_data['answer_two_gap_match'] = sanitize_text_field( $answer['matched_answer_title'] ) ;
1071 }
1072
1073 if ($question_type === 'fill_in_the_blank'){
1074 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? sanitize_text_field(trim($answer['answer_two_gap_match'])) : null;
1075 }
1076
1077 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
1078 }
1079 }
1080 }
1081
1082 //die(print_r($_POST));
1083 wp_send_json_success();
1084 }
1085
1086 public function tutor_quiz_builder_get_answers_by_question(){
1087 tutils()->checking_nonce();
1088
1089 global $wpdb;
1090 $question_id = sanitize_text_field($_POST['question_id']);
1091 $question_type = sanitize_text_field($_POST['question_type']);
1092
1093 if(!tutils()->can_user_manage('question', $question_id)) {
1094 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
1095 }
1096
1097 $question = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = %d ", $question_id));
1098 $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 ) ));
1099
1100 ob_start();
1101
1102 switch ($question_type){
1103 case 'true_false':
1104 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
1105 break;
1106 case 'ordering':
1107 echo '<label>'.__('Make sure you’re saving the answers in the right order. Students will have to match this order exactly.', 'tutor').'</label>';
1108 break;
1109 }
1110
1111 if (is_array($answers) && count($answers)){
1112 foreach ($answers as $answer){
1113 ?>
1114 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
1115 <div class="tutor-quiz-answer">
1116 <span class="tutor-quiz-answer-title">
1117 <?php
1118 echo stripslashes($answer->answer_title);
1119 if ($answer->belongs_question_type === 'fill_in_the_blank'){
1120 echo ' ('.__('Answer', 'tutor').' : ';
1121 echo '<strong>'. stripslashes($answer->answer_two_gap_match). '</strong>)';
1122 }
1123 if ($answer->belongs_question_type === 'matching'){
1124 echo ' - '.stripslashes($answer->answer_two_gap_match);
1125 }
1126 ?>
1127 </span>
1128
1129 <?php
1130 if ($answer->image_id){
1131 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
1132 }
1133 if ($question_type === 'true_false' || $question_type === 'single_choice'){
1134 ?>
1135 <span class="tutor-quiz-answers-mark-correct-wrap">
1136 <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); ?> >
1137 </span>
1138 <?php
1139 }elseif ($question_type === 'multiple_choice'){
1140 ?>
1141 <span class="tutor-quiz-answers-mark-correct-wrap">
1142 <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); ?> >
1143 </span>
1144 <?php
1145 }
1146 ?>
1147 <?php if( "true_false" != $question_type ):?>
1148 <span class="tutor-quiz-answer-edit">
1149 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
1150 </span>
1151 <?php endif;?>
1152 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
1153 </div>
1154 <?php if( "true_false" != $question_type ):?>
1155 <div class="tutor-quiz-answer-trash-wrap">
1156 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
1157 </div>
1158 <?php endif;?>
1159 </div>
1160 <?php
1161 }
1162 }
1163 $output = ob_get_clean();
1164
1165 wp_send_json_success(array('output' => $output));
1166 }
1167
1168 public function tutor_quiz_builder_delete_answer(){
1169 tutils()->checking_nonce();
1170
1171 global $wpdb;
1172 $answer_id = sanitize_text_field($_POST['answer_id']);
1173
1174 if(!tutils()->can_user_manage('quiz_answer', $answer_id)) {
1175 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
1176 }
1177
1178 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => esc_sql( $answer_id ) ));
1179 wp_send_json_success();
1180 }
1181
1182 /**
1183 * Save quiz questions sorting
1184 */
1185 public function tutor_quiz_question_sorting(){
1186 tutils()->checking_nonce();
1187
1188 global $wpdb;
1189
1190 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
1191 if (is_array($question_ids) && count($question_ids) ){
1192 $i = 0;
1193 foreach ($question_ids as $key => $question_id){
1194 if(tutils()->can_user_manage('question', $question_id)) {
1195 $i++;
1196 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
1197 }
1198 }
1199 }
1200 }
1201
1202 /**
1203 * Save sorting data for quiz answers
1204 */
1205 public function tutor_quiz_answer_sorting(){
1206 tutils()->checking_nonce();
1207
1208 global $wpdb;
1209
1210 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
1211 $answer_ids = $_POST['sorted_answer_ids'];
1212 $i = 0;
1213 foreach ($answer_ids as $key => $answer_id){
1214 if(tutils()->can_user_manage('quiz_answer', $answer_id)) {
1215 $i++;
1216 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
1217 }
1218 }
1219 }
1220 }
1221
1222 /**
1223 * Mark answer as correct
1224 */
1225
1226 public function tutor_mark_answer_as_correct(){
1227 tutils()->checking_nonce();
1228
1229 global $wpdb;
1230
1231 $answer_id = sanitize_text_field($_POST['answer_id']);
1232 $inputValue = sanitize_text_field($_POST['inputValue']);
1233
1234 if(!tutils()->can_user_manage('quiz_answer', $answer_id)) {
1235 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
1236 }
1237
1238 $answer = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = %d LIMIT 0,1 ;", $answer_id));
1239 if ($answer->belongs_question_type === 'single_choice') {
1240 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => esc_sql( $answer->belongs_question_id ) ));
1241 }
1242 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => esc_sql( $inputValue ) ), array('answer_id' => esc_sql( $answer_id ) ));
1243 }
1244
1245 /**
1246 * Update quiz settings from modal
1247 *
1248 * @since : v.1.0.0
1249 */
1250 public function tutor_quiz_modal_update_settings(){
1251 tutils()->checking_nonce();
1252 //while creating quiz if creating step is not follow then it may throw error that why check added
1253 $quiz_id = ( isset( $_POST['quiz_id'] ) ) ? sanitize_text_field( $_POST['quiz_id'] ) : '' ;
1254 $current_topic_id = sanitize_text_field( $_POST['topic_id'] );
1255 $course_id = tutor_utils()->get_course_id_by('topic', sanitize_textarea_field( $current_topic_id ) );
1256
1257 $quiz_option = tutor_utils()->sanitize_array( $_POST['quiz_option'] );
1258
1259 if( !tutils()->can_user_manage('quiz', $quiz_id) ) {
1260 wp_send_json_error( array('message'=>__('Access Denied', 'tutor')) );
1261 }
1262
1263 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
1264 do_action('tutor_quiz_settings_updated', $quiz_id);
1265
1266 //@since 1.9.6
1267 ob_start();
1268 include tutor()->path.'views/metabox/course-contents.php';
1269 $course_contents = ob_get_clean();
1270
1271 wp_send_json_success( array( 'course_contents' => $course_contents ) );
1272 }
1273
1274
1275 //=========================//
1276 // Front end stuffs
1277 //=========================//
1278
1279 /**
1280 * Rendering quiz for frontend
1281 *
1282 * @since v.1.0.0
1283 */
1284
1285 public function tutor_render_quiz_content(){
1286
1287 tutils()->checking_nonce();
1288
1289 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
1290
1291 if(!tutils()->has_enrolled_content_access('quiz', $quiz_id)) {
1292 wp_send_json_error( array('message'=>__('Access Denied.', 'tutor')) );
1293 }
1294
1295 ob_start();
1296 global $post;
1297
1298 $post = get_post($quiz_id);
1299 setup_postdata($post);
1300 //tutor_lesson_content();
1301
1302 single_quiz_contents();
1303 wp_reset_postdata();
1304
1305 $html = ob_get_clean();
1306 wp_send_json_success(array('html' => $html));
1307 }
1308
1309 }