PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.0.2
Tutor LMS – eLearning and online course solution v1.0.2
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 7 years ago Admin.php 7 years ago Ajax.php 7 years ago Assets.php 7 years ago Course.php 7 years ago Gutenberg.php 7 years ago Instructor.php 7 years ago Instructors_List.php 7 years ago Lesson.php 7 years ago Options.php 7 years ago Post_types.php 7 years ago Q_and_A.php 7 years ago Question.php 7 years ago Question_Answers_List.php 7 years ago Quiz.php 7 years ago Quiz_Attempts_List.php 7 years ago Rewrite_Rules.php 7 years ago Shortcode.php 7 years ago Student.php 7 years ago Students_List.php 7 years ago Template.php 7 years ago Theme_Compatibility.php 7 years ago Tools.php 7 years ago Tutor_Base.php 7 years ago Tutor_List_Table.php 7 years ago User.php 7 years ago Utils.php 7 years ago Video_Stream.php 7 years ago init.php 7 years ago
Quiz.php
1066 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 public function __construct() {
20 add_filter( "manage_tutor_quiz_posts_columns", array($this, 'add_column'), 10,1 );
21 add_action( "manage_tutor_quiz_posts_custom_column" , array($this, 'custom_question_column'), 10, 2 );
22
23 add_action( 'add_meta_boxes', array($this, 'register_meta_box') );
24 add_action('save_post_tutor_quiz', array($this, 'save_quiz_meta'));
25
26 //Depricated at alpha version
27 add_action('wp_ajax_tutor_load_quiz_modal', array($this, 'tutor_load_quiz_modal'));
28
29 add_action('wp_ajax_tutor_load_quiz_builder_modal', array($this, 'tutor_load_quiz_builder_modal'));
30 add_action('wp_ajax_tutor_add_quiz_to_post', array($this, 'tutor_add_quiz_to_post'));
31 add_action('wp_ajax_remove_quiz_from_post', array($this, 'remove_quiz_from_post'));
32
33 add_action('wp_ajax_tutor_quiz_timeout', array($this, 'tutor_quiz_timeout'));
34
35 //User take the quiz
36 add_action('template_redirect', array($this, 'start_the_quiz'));
37 add_action('template_redirect', array($this, 'answering_quiz'));
38 add_action('template_redirect', array($this, 'finishing_quiz_attempt'));
39
40 add_action('admin_action_review_quiz_answer', array($this, 'review_quiz_answer'));
41
42 /**
43 * New Design Quiz
44 */
45
46 add_action('wp_ajax_tutor_create_quiz_and_load_modal', array($this, 'tutor_create_quiz_and_load_modal'));
47 add_action('wp_ajax_tutor_delete_quiz_by_id', array($this, 'tutor_delete_quiz_by_id'));
48 add_action('wp_ajax_tutor_quiz_builder_quiz_update', array($this, 'tutor_quiz_builder_quiz_update'));
49 add_action('wp_ajax_tutor_load_edit_quiz_modal', array($this, 'tutor_load_edit_quiz_modal'));
50 add_action('wp_ajax_tutor_quiz_builder_get_question_form', array($this, 'tutor_quiz_builder_get_question_form'));
51 add_action('wp_ajax_tutor_quiz_modal_update_question', array($this, 'tutor_quiz_modal_update_question'));
52 add_action('wp_ajax_tutor_quiz_builder_question_delete', array($this, 'tutor_quiz_builder_question_delete'));
53 add_action('wp_ajax_tutor_quiz_add_question_answers', array($this, 'tutor_quiz_add_question_answers'));
54 add_action('wp_ajax_tutor_quiz_edit_question_answer', array($this, 'tutor_quiz_edit_question_answer'));
55 add_action('wp_ajax_tutor_save_quiz_answer_options', array($this, 'tutor_save_quiz_answer_options'));
56 add_action('wp_ajax_tutor_update_quiz_answer_options', array($this, 'tutor_update_quiz_answer_options'));
57 add_action('wp_ajax_tutor_quiz_builder_get_answers_by_question', array($this, 'tutor_quiz_builder_get_answers_by_question'));
58 add_action('wp_ajax_tutor_quiz_builder_delete_answer', array($this, 'tutor_quiz_builder_delete_answer'));
59 add_action('wp_ajax_tutor_quiz_question_sorting', array($this, 'tutor_quiz_question_sorting'));
60 add_action('wp_ajax_tutor_quiz_answer_sorting', array($this, 'tutor_quiz_answer_sorting'));
61 add_action('wp_ajax_tutor_mark_answer_as_correct', array($this, 'tutor_mark_answer_as_correct'));
62 add_action('wp_ajax_tutor_quiz_modal_update_settings', array($this, 'tutor_quiz_modal_update_settings'));
63
64
65
66 /**
67 * Frontend Stuff
68 */
69
70 add_action('wp_ajax_tutor_render_quiz_content', array($this, 'tutor_render_quiz_content'));
71
72 }
73
74 public function add_column($columns){
75 $date_col = $columns['date'];
76 unset($columns['date']);
77 $columns['quiz'] = __('Course', 'tutor');
78 $columns['questions'] = __('Questions', 'tutor');
79 $columns['date'] = $date_col;
80
81 return $columns;
82 }
83
84 public function custom_question_column($column, $post_id ){
85 if ($column === 'quiz'){
86 $quiz = tutor_utils()->get_course_by_quiz($post_id);
87
88 if ($quiz){
89 echo '<a href="'.admin_url('post.php?post='.$quiz->ID.'&action=edit').'">'.get_the_title($quiz->ID).'</a>';
90 }
91 }
92
93 if ($column === 'questions'){
94 echo tutor_utils()->total_questions_for_student_by_quiz($post_id);
95 }
96 }
97
98 public function register_meta_box(){
99 add_meta_box( 'tutor-quiz-questions', __( 'Questions', 'tutor' ), array($this, 'quiz_questions'), 'tutor_quiz' );
100 add_meta_box( 'tutor-quiz-settings', __( 'Settings', 'tutor' ), array($this, 'quiz_settings'), 'tutor_quiz' );
101 }
102
103 public function quiz_questions(){
104 include tutor()->path.'views/metabox/quiz_questions.php';
105 }
106
107 public function quiz_settings(){
108 include tutor()->path.'views/metabox/quizzes.php';
109 }
110
111 public function save_quiz_meta($post_ID){
112 if (isset($_POST['quiz_option'])){
113 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
114 update_post_meta($post_ID, 'tutor_quiz_option', $quiz_option);
115 }
116 }
117
118 /**
119 * @depricated at alpha version
120 * Check tutor_load_quiz_builder_modal instead of this method
121 */
122 public function tutor_load_quiz_modal(){
123 $quiz_for_post_id = (int) sanitize_text_field($_POST['quiz_for_post_id']);
124
125 $search_terms = sanitize_text_field(tutor_utils()->avalue_dot('search_terms', $_POST));
126 $quizzes = tutor_utils()->get_unattached_quiz(array('search_term' => $search_terms));
127
128 $output = '';
129 if ($quizzes){
130 foreach ($quizzes as $quiz){
131 $output .= "<p><label><input type='checkbox' name='quiz_for[{$quiz_for_post_id}][quiz_id][]' value='{$quiz->ID}' > {$quiz->post_title} </label></p>";
132 }
133 $output .= '<p class="quiz-search-suggest-text">Search the quiz to get specific quiz</p>';
134 }else{
135 $add_question_url = admin_url('post-new.php?post_type=tutor_quiz');
136 $output .= sprintf('No quiz available right now, please %s add some quiz %s', '<a href="'.$add_question_url.'" target="_blank">', '</a>' );
137 }
138
139 ob_start();
140 ?>
141 <div class="tutor-option-field-row">
142 <div class="tutor-option-field-label">
143 <label for="">
144 <?php _e('New quiz title', 'tutor'); ?>
145 </label>
146 </div>
147 <div class="tutor-option-field">
148 <input type="text" name="quiz_title" placeholder="<?php _e('Place quiz title to create new quiz', 'tutor'); ?>" >
149 <p class="desc"><?php _e('Provide a quiz title to create a quiz from here.'); ?></p>
150 </div>
151 </div>
152
153 <?php
154 $output .= ob_get_clean();
155
156 wp_send_json_success(array('output' => $output));
157 }
158
159 /**
160 * Tutor Quiz Builder Modal
161 */
162 public function tutor_load_quiz_builder_modal(){
163 ob_start();
164 include tutor()->path.'views/modal/add_quiz.php';
165 $output = ob_get_clean();
166
167 wp_send_json_success(array('output' => $output));
168
169 }
170
171 public function tutor_add_quiz_to_post(){
172 global $wpdb;
173
174 $quiz_data = tutor_utils()->avalue_dot('quiz_for', $_POST);
175
176 $output = '';
177 $post_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('parent_post_id', $_POST)) ;
178 if ($quiz_data){
179 foreach ($quiz_data as $post_id => $quiz_ids_a);
180
181 $quiz_ids = tutor_utils()->avalue_dot('quiz_id', $quiz_ids_a);
182 foreach ($quiz_ids as $quiz_id){
183 $wpdb->update($wpdb->posts, array('post_parent' => $post_id), array('ID' => $quiz_id) );
184 }
185 }
186
187 $quiz_title = sanitize_text_field(tutor_utils()->avalue_dot('quiz_title', $_POST));
188 if ($quiz_title){
189 wp_insert_post(array(
190 'post_parent' => $post_id,
191 'post_title' => $quiz_title,
192 'post_type' => 'tutor_quiz',
193 'post_status' => 'publish',
194 ));
195 }
196
197 if ($post_id) {
198 ob_start();
199 $attached_quizzes = tutor_utils()->get_attached_quiz( $post_id );
200 if ( $attached_quizzes ) {
201 foreach ( $attached_quizzes as $attached_quiz ) {
202 ?>
203 <div id="added-quiz-id-<?php echo $attached_quiz->ID; ?>" class="added-quiz-item added-quiz-item-<?php echo $attached_quiz->ID; ?>" data-quiz-id="<?php echo $attached_quiz->ID; ?>">
204 <span class="quiz-icon"><i class="dashicons dashicons-clock"></i></span>
205 <span class="quiz-name">
206 <?php edit_post_link( $attached_quiz->post_title, null, null, $attached_quiz->ID ); ?>
207 </span>
208 <span class="quiz-control">
209 <a href="javascript:;" class="tutor-quiz-delete-btn"><i class="dashicons dashicons-trash"></i></a>
210 </span>
211 </div>
212 <?php
213 }
214 }
215 $output .= ob_get_clean();
216 }
217
218 wp_send_json_success(array('output' => $output));
219 }
220
221 public function remove_quiz_from_post(){
222 global $wpdb;
223 $quiz_id = (int) tutor_utils()->avalue_dot('quiz_id', $_POST);
224 $wpdb->update($wpdb->posts, array('post_parent' => 0), array('ID' => $quiz_id) );
225 wp_send_json_success();
226 }
227
228 /**
229 *
230 * Start Quiz from here...
231 *
232 * @since v.1.0.0
233 */
234
235 public function start_the_quiz(){
236 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_start_quiz' ){
237 return;
238 }
239 //Checking nonce
240 tutor_utils()->checking_nonce();
241
242 if ( ! is_user_logged_in()){
243 //TODO: need to set a view in the next version
244 die('Please sign in to do this operation');
245 }
246
247 global $wpdb;
248
249 $user_id = get_current_user_id();
250 $user = get_userdata($user_id);
251
252 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
253
254 $quiz = get_post($quiz_id);
255 $course = tutor_utils()->get_course_by_quiz($quiz_id);
256 if ( empty($course->ID)){
257 die('There is something went wrong with course, please check if quiz attached with a course');
258 }
259
260 $date = date("Y-m-d H:i:s");
261
262 $tutor_quiz_option = maybe_unserialize(get_post_meta($quiz_id, 'tutor_quiz_option', true));
263 $attempts_allowed = tutor_utils()->get_quiz_option($quiz_id, 'attempts_allowed', 0);
264
265 $time_limit = tutor_utils()->get_quiz_option($quiz_id, 'time_limit.time_value');
266 $time_limit_seconds = 0;
267 $time_type = 'seconds';
268 if ($time_limit){
269 $time_type = tutor_utils()->get_quiz_option($quiz_id, 'time_limit.time_type');
270
271 switch ($time_type){
272 case 'seconds':
273 $time_limit_seconds = $time_limit;
274 break;
275 case 'minutes':
276 $time_limit_seconds = $time_limit * 60;
277 break;
278 case 'hours':
279 $time_limit_seconds = $time_limit * 60 * 60;
280 break;
281 case 'days':
282 $time_limit_seconds = $time_limit * 60 * 60 * 24;
283 break;
284 case 'weeks':
285 $time_limit_seconds = $time_limit * 60 * 60 * 24 * 7;
286 break;
287 }
288 }
289
290 $max_question_allowed = tutor_utils()->max_questions_for_take_quiz($quiz_id);
291 $tutor_quiz_option['time_limit']['time_limit_seconds'] = $time_limit_seconds;
292
293 $attempt_data = array(
294 'course_id' => $course->ID,
295 'quiz_id' => $quiz_id,
296 'user_id' => $user_id,
297 'total_questions' => $max_question_allowed,
298 'total_answered_questions' => 0,
299 'attempt_info' => maybe_serialize($tutor_quiz_option),
300 'attempt_status' => 'attempt_started',
301 'attempt_ip' => tutor_utils()->get_ip(),
302 'attempt_started_at' => $date,
303 );
304
305 $wpdb->insert($wpdb->prefix.'tutor_quiz_attempts', $attempt_data);
306 $attempt_id = (int) $wpdb->insert_id;
307
308 wp_redirect(get_permalink($quiz_id));
309 die();
310 }
311
312 public function answering_quiz(){
313 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_answering_quiz_question' ){
314 return;
315 }
316 //Checking nonce
317 tutor_utils()->checking_nonce();
318
319 $attempt_answers = isset($_POST['attempt']) ? $_POST['attempt'] : false;
320 if ( ! is_user_logged_in()){
321 die('Please sign in to do this operation');
322 }
323
324 global $wpdb;
325 $user_id = get_current_user_id();
326
327 if ($attempt_answers && is_array($attempt_answers) && count($attempt_answers)){
328 foreach ($attempt_answers as $attempt_id => $attempt_answers){
329 $attempt = tutor_utils()->get_attempt($attempt_id);
330
331 /**
332 * Get total marks of all question comes
333 */
334 $question_ids = tutor_utils()->avalue_dot('quiz_question_ids', $attempt_answers);
335 if (is_array($question_ids) && count($question_ids)){
336 $question_ids_string = "'".implode("','", $question_ids)."'";
337 $total_question_marks = $wpdb->get_var("SELECT SUM(question_mark) FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN({$question_ids_string}) ;");
338 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', array('total_marks' =>$total_question_marks ), array('attempt_id' => $attempt_id ));
339 }
340
341 if ( ! $attempt || $user_id != $attempt->user_id){
342 die('Operation not allowed, attempt not found or permission denied');
343 }
344
345 $quiz_answers = tutor_utils()->avalue_dot('quiz_question', $attempt_answers);
346 $total_marks = 0;
347 foreach ($quiz_answers as $question_id => $answers){
348 $question = tutor_utils()->get_quiz_question_by_id($question_id);
349 $question_type = $question->question_type;
350
351 $is_answer_was_correct = false;
352 $given_answer = '';
353
354 if ($question_type === 'true_false' || $question_type === 'single_choice'){
355
356 $given_answer = $answers;
357 $is_answer_was_correct = (bool) $wpdb->get_var("SELECT is_correct FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answers} ");
358
359 }elseif ($question_type === 'multiple_choice'){
360
361 $given_answer = maybe_serialize($answers);
362 $get_original_answers = (array) $wpdb->get_col("SELECT answer_id FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id = {$question->question_id} AND belongs_question_type = '{$question_type}' AND is_correct = 1 ;");
363 if (maybe_serialize($get_original_answers) == $given_answer){
364 $is_answer_was_correct = true;
365 }
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("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id = {$question->question_id} AND belongs_question_type = '{$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 ($given_answer == maybe_serialize($gap_answer)){
377 $is_answer_was_correct = true;
378 }
379
380 }elseif ($question_type === 'open_ended' || $question_type === 'short_answer'){
381
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("SELECT answer_id FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id = {$question->question_id} AND belongs_question_type = '{$question_type}' ORDER BY answer_order ASC ;");
390 $get_original_answers = array_map('sanitize_text_field', $get_original_answers);
391
392 if ($given_answer == maybe_serialize($get_original_answers)){
393 $is_answer_was_correct = true;
394 }
395
396 }elseif ($question_type === 'image_answering'){
397 $image_inputs = tutor_utils()->avalue_dot('answer_id', $answers);
398 $given_answer = maybe_serialize($image_inputs);
399
400 $is_answer_was_correct = false;
401
402 if (is_array($image_inputs) && count($image_inputs)){
403 foreach ($image_inputs as $answer_id => $answer){
404 $db_answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} ;");
405
406 $is_answer_was_correct = sanitize_text_field($answer) == $db_answer->answer_title;
407 }
408 }
409
410 }
411
412 $question_mark = $is_answer_was_correct ? $question->question_mark : 0;
413 $total_marks += $question_mark;
414
415 $answers_data = array(
416 'user_id' => $user_id,
417 'quiz_id' => $attempt->quiz_id,
418 'question_id' => $question_id,
419 'quiz_attempt_id' => $attempt_id,
420 'given_answer' => $given_answer,
421 'question_mark' => $question->question_mark,
422 'achieved_mark' => $question_mark,
423 'minus_mark' => 0,
424 'is_correct' => $is_answer_was_correct ? 1 : 0,
425 );
426 $wpdb->insert($wpdb->prefix.'tutor_quiz_attempt_answers', $answers_data);
427 }
428
429 $attempt_info = array(
430 'total_answered_questions' => count($quiz_answers),
431 'earned_marks' => $total_marks,
432 'attempt_status' => 'attempt_ended',
433 'attempt_ended_at' => date("Y-m-d H:i:s"),
434 );
435 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
436 }
437 }
438
439 wp_redirect(tutor_utils()->input_old('_wp_http_referer'));
440 die();
441 }
442
443
444 /**
445 * Quiz attempt will be finish here
446 *
447 */
448
449 public function finishing_quiz_attempt(){
450 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_finish_quiz_attempt' ){
451 return;
452 }
453 //Checking nonce
454 tutor_utils()->checking_nonce();
455
456 if ( ! is_user_logged_in()){
457 die('Please sign in to do this operation');
458 }
459
460 global $wpdb;
461
462 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
463 $attempt = tutor_utils()->is_started_quiz($quiz_id);
464
465 $attempt_info = array(
466 'total_answered_questions' => 0,
467 'earned_marks' => 0,
468 'attempt_status' => 'attempt_ended',
469 'attempt_ended_at' => date("Y-m-d H:i:s"),
470 );
471 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt->attempt_id));
472
473 wp_redirect(tutor_utils()->input_old('_wp_http_referer'));
474 }
475
476 /**
477 * Quiz timeout by ajax
478 */
479 public function tutor_quiz_timeout(){
480 global $wpdb;
481
482 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
483 $attempt = tutor_utils()->is_started_quiz($quiz_id);
484
485 if ($attempt) {
486 $data = array(
487 'attempt_status' => 'attempt_timeout',
488 'attempt_ended_at' => date("Y-m-d H:i:s"),
489 );
490 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $data, array('attempt_id' => $attempt->attempt_id));
491 wp_send_json_success();
492 }
493
494 wp_send_json_error(__('Quiz has been timeout already', 'tutor'));
495 }
496
497 /**
498 * Review the answer and change individual answer result
499 */
500
501 public function review_quiz_answer(){
502 global $wpdb;
503
504 $attempt_id = (int) sanitize_text_field($_GET['attempt_id']);
505 $attempt_answer_id = (int) sanitize_text_field($_GET['attempt_answer_id']);
506 $mark_as = sanitize_text_field($_GET['mark_as']);
507
508 $attempt_answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers WHERE attempt_answer_id = {$attempt_answer_id} ");
509 $attempt = tutor_utils()->get_attempt($attempt_id);
510
511 $is_correct = (int) $attempt_answer->is_correct;
512
513 if ($mark_as === 'correct' && ! $is_correct){
514
515 $answer_update_data = array(
516 'achieved_mark' => $attempt_answer->question_mark,
517 'is_correct' => 1,
518 );
519 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
520
521 $attempt_update_data = array(
522 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
523 'is_manually_reviewed' => 1,
524 'manually_reviewed_at' => date("Y-m-d H:i:s"),
525 );
526
527 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
528
529 }elseif($mark_as === 'incorrect' && $is_correct){
530
531 $answer_update_data = array(
532 'achieved_mark' => '0.00',
533 'is_correct' => 0,
534 );
535 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_id ));
536
537 $attempt_update_data = array(
538 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
539 'is_manually_reviewed' => 1,
540 'manually_reviewed_at' => date("Y-m-d H:i:s"),
541 );
542
543 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
544 }
545
546 wp_redirect(admin_url("admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=".$attempt_id));
547 die();
548 }
549
550
551 /**
552 * New Design Quiz
553 */
554 public function tutor_create_quiz_and_load_modal(){
555 $topic_id = sanitize_text_field($_POST['topic_id']);
556 $quiz_title = sanitize_text_field($_POST['quiz_title']);
557 $quiz_description = sanitize_text_field($_POST['quiz_description']);
558 $next_order_id = tutor_utils()->get_next_course_content_order_id($topic_id);
559
560 $post_arr = array(
561 'post_type' => 'tutor_quiz',
562 'post_title' => $quiz_title,
563 'post_content' => $quiz_description,
564 'post_status' => 'publish',
565 'post_author' => get_current_user_id(),
566 'post_parent' => $topic_id,
567 'menu_order' => $next_order_id,
568 );
569 $quiz_id = wp_insert_post( $post_arr );
570
571 ob_start();
572 include tutor()->path.'views/modal/edit_quiz.php';
573 $output = ob_get_clean();
574
575 ob_start();
576 ?>
577 <div id="tutor-quiz-<?php echo $quiz_id; ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
578 <div class="tutor-lesson-top">
579 <i class="tutor-icon-move"></i>
580 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
581 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
582 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
583 </div>
584 </div>
585 <?php
586 $output_quiz_row = ob_get_clean();
587
588 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
589 }
590
591 public function tutor_delete_quiz_by_id(){
592 global $wpdb;
593
594 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
595 $post = get_post($quiz_id);
596
597 if ( $post->post_type === 'tutor_quiz'){
598 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
599 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
600
601 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$quiz_id} ");
602 if (is_array($questions_ids) && count($questions_ids)){
603 $in_question_ids = "'".implode("','", $questions_ids)."'";
604 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
605 }
606 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
607
608 wp_delete_post($quiz_id, true);
609 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
610 wp_send_json_success();
611 }
612
613 wp_send_json_error();
614 }
615
616 /**
617 * Update Quiz from quiz builder modal
618 *
619 * @since v.1.0.0
620 */
621 public function tutor_quiz_builder_quiz_update(){
622 $quiz_id = sanitize_text_field($_POST['quiz_id']);
623 $topic_id = sanitize_text_field($_POST['topic_id']);
624 $quiz_title = sanitize_text_field($_POST['quiz_title']);
625 $quiz_description = sanitize_text_field($_POST['quiz_description']);
626
627 $post_arr = array(
628 'ID' => $quiz_id,
629 'post_title' => $quiz_title,
630 'post_content' => $quiz_description,
631
632 );
633 $quiz_id = wp_update_post( $post_arr );
634
635 ob_start();
636 ?>
637 <div class="tutor-lesson-top">
638 <i class="tutor-icon-move"></i>
639 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
640 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
641 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
642 </div>
643 <?php
644 $output_quiz_row = ob_get_clean();
645
646 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
647 }
648
649 /**
650 * Load quiz Modal for edit quiz
651 *
652 * @since v.1.0.0
653 */
654 public function tutor_load_edit_quiz_modal(){
655 $quiz_id = sanitize_text_field($_POST['quiz_id']);
656
657 ob_start();
658 include tutor()->path.'views/modal/edit_quiz.php';
659 $output = ob_get_clean();
660
661 wp_send_json_success(array('output' => $output));
662 }
663
664 /**
665 * Load quiz question form for quiz
666 *
667 * @since v.1.0.0
668 */
669 public function tutor_quiz_builder_get_question_form(){
670 global $wpdb;
671 $quiz_id = sanitize_text_field($_POST['quiz_id']);
672 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
673
674 if ( ! $question_id){
675 $next_question_id = tutor_utils()->quiz_next_question_id();
676 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
677
678 $new_question_data = array(
679 'quiz_id' => $quiz_id,
680 'question_title' => __('Question ').$next_question_id,
681 'question_description' => '',
682 'question_type' => 'true_false',
683 'question_mark' => 1,
684 'question_settings' => maybe_serialize(array()),
685 'question_order' => $next_question_order,
686 );
687
688 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
689 $question_id = $wpdb->insert_id;
690 }
691
692 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = {$question_id} ");
693
694 ob_start();
695 include tutor()->path.'views/modal/question_form.php';
696 $output = ob_get_clean();
697
698 wp_send_json_success(array('output' => $output));
699 }
700
701 public function tutor_quiz_modal_update_question(){
702 global $wpdb;
703
704 $question_data = $_POST['tutor_quiz_question'];
705
706 foreach ($question_data as $question_id => $question){
707 $question_title = $question['question_title'];
708 $question_description = $question['question_description'];
709 $question_type = $question['question_type'];
710 $question_mark = $question['question_mark'];
711
712 unset($question['question_title']);
713 unset($question['question_description']);
714
715 $data = array(
716 'question_title' => $question_title,
717 'question_description' => $question_description,
718 'question_type' => $question_type,
719 'question_mark' => $question_mark,
720 'question_settings' => maybe_serialize($question),
721 );
722
723 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
724 }
725
726 wp_send_json_success();
727 }
728
729 public function tutor_quiz_builder_question_delete(){
730 global $wpdb;
731
732 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
733 if ($question_id){
734 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => $question_id));
735 }
736
737 wp_send_json_success();
738 }
739
740 /**
741 * Get answers options form for quiz question
742 *
743 * @since v.1.0.0
744 */
745 public function tutor_quiz_add_question_answers(){
746 $question_id = sanitize_text_field($_POST['question_id']);
747 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
748 $question_type = $question['question_type'];
749
750 ob_start();
751 include tutor()->path.'views/modal/question_answer_form.php';
752 $output = ob_get_clean();
753
754 wp_send_json_success(array('output' => $output));
755 }
756
757 /**
758 * Edit Answer Form
759 *
760 * @since v.1.0.0
761 */
762 public function tutor_quiz_edit_question_answer(){
763 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
764 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
765 foreach ($old_answer as $old_answer);
766 $question_id = $old_answer->belongs_question_id;
767 $question_type = $old_answer->belongs_question_type;
768
769 ob_start();
770 include tutor()->path.'views/modal/question_answer_edit_form.php';
771 $output = ob_get_clean();
772
773 wp_send_json_success(array('output' => $output));
774 }
775
776 public function tutor_save_quiz_answer_options(){
777 global $wpdb;
778
779 $questions = $_POST['tutor_quiz_question'];
780 $answers = $_POST['quiz_answer'];
781
782 foreach ($answers as $question_id => $answer){
783 $question = tutor_utils()->avalue_dot($question_id, $questions);
784 $question_type = $question['question_type'];
785
786 //Getting next sorting order
787 $next_order_id = (int) $wpdb->get_var("SELECT MAX(answer_order) FROM {$wpdb->prefix}tutor_quiz_question_answers where belongs_question_id = {$question_id} AND belongs_question_type = '{$question_type}' ");
788 $next_order_id = $next_order_id + 1;
789
790 if ($question){
791 if ($question_type === 'true_false'){
792 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
793 $data_true_false = array(
794 array(
795 'belongs_question_id' => $question_id,
796 'belongs_question_type' => $question_type,
797 'answer_title' => __('True', 'tutor'),
798 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
799 'answer_two_gap_match' => 'true',
800 ),
801 array(
802 'belongs_question_id' => $question_id,
803 'belongs_question_type' => $question_type,
804 'answer_title' => __('False', 'tutor'),
805 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
806 'answer_two_gap_match' => 'false',
807 ),
808 );
809
810 foreach ($data_true_false as $true_false_data){
811 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
812 }
813
814 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
815 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
816
817 $answer_data = array(
818 'belongs_question_id' => $question_id,
819 'belongs_question_type' => $question_type,
820 'answer_title' => $answer['answer_title'],
821 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
822 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
823 'answer_order' => $next_order_id,
824 );
825 if (isset($answer['matched_answer_title'])){
826 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
827 }
828
829 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
830
831 }elseif($question_type === 'fill_in_the_blank'){
832 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
833 $answer_data = array(
834 'belongs_question_id' => $question_id,
835 'belongs_question_type' => $question_type,
836 'answer_title' => $answer['answer_title'],
837 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null,
838 );
839 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
840 }
841 }
842 }
843
844 wp_send_json_success();
845 }
846
847 /**
848 * Tutor Update Answer
849 *
850 * @since v.1.0.0
851 */
852 public function tutor_update_quiz_answer_options(){
853 global $wpdb;
854
855 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
856 $questions = $_POST['tutor_quiz_question'];
857 $answers = $_POST['quiz_answer'];
858
859 foreach ($answers as $question_id => $answer){
860 $question = tutor_utils()->avalue_dot($question_id, $questions);
861 $question_type = $question['question_type'];
862
863 if ($question){
864 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' ){
865
866 $answer_data = array(
867 'belongs_question_id' => $question_id,
868 'belongs_question_type' => $question_type,
869 'answer_title' => $answer['answer_title'],
870 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
871 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : '',
872 );
873 if (isset($answer['matched_answer_title'])){
874 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
875 }
876
877 if ($question_type === 'fill_in_the_blank'){
878 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null;
879 }
880
881 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
882 }
883 }
884 }
885
886 //die(print_r($_POST));
887 wp_send_json_success();
888 }
889
890 public function tutor_quiz_builder_get_answers_by_question(){
891 global $wpdb;
892 $question_id = sanitize_text_field($_POST['question_id']);
893 $question_type = sanitize_text_field($_POST['question_type']);
894
895 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = {$question_id} ");
896 $answers = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers where belongs_question_id = {$question_id} AND belongs_question_type = '{$question_type}' order by answer_order asc ;");
897
898 ob_start();
899
900 switch ($question_type){
901 case 'true_false':
902 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
903 break;
904 case 'ordering':
905 echo '<label>'.__('Student should order below items exact this order, make sure your answer is in right order, you can re-order them', 'tutor').'</label>';
906 break;
907 }
908
909 if (is_array($answers) && count($answers)){
910 foreach ($answers as $answer){
911 ?>
912 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
913 <div class="tutor-quiz-answer">
914 <span class="tutor-quiz-answer-title">
915 <?php
916 echo $answer->answer_title;
917 if ($answer->belongs_question_type === 'fill_in_the_blank'){
918 echo ' ('.__('Answer', 'tutor').' : ';
919 echo "<strong>{$answer->answer_two_gap_match} </strong>)";
920 }
921 if ($answer->belongs_question_type === 'matching'){
922 echo " - {$answer->answer_two_gap_match}";
923 }
924 ?>
925 </span>
926
927 <?php
928 if ($answer->image_id){
929 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
930 }
931 if ($question->question_type === 'true_false' || $question->question_type === 'single_choice'){
932 ?>
933 <span class="tutor-quiz-answers-mark-correct-wrap">
934 <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); ?> >
935 </span>
936 <?php
937 }elseif ($question->question_type === 'multiple_choice'){
938 ?>
939 <span class="tutor-quiz-answers-mark-correct-wrap">
940 <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); ?> >
941 </span>
942 <?php
943 }
944 ?>
945 <span class="tutor-quiz-answer-edit">
946 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
947 </span>
948 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
949 </div>
950
951 <div class="tutor-quiz-answer-trash-wrap">
952 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
953 </div>
954 </div>
955 <?php
956 }
957 }
958 $output = ob_get_clean();
959
960 wp_send_json_success(array('output' => $output));
961 }
962
963 public function tutor_quiz_builder_delete_answer(){
964 global $wpdb;
965 $answer_id = sanitize_text_field($_POST['answer_id']);
966
967 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => $answer_id));
968 wp_send_json_success();
969 }
970
971 /**
972 * Save quiz questions sorting
973 */
974 public function tutor_quiz_question_sorting(){
975 global $wpdb;
976
977 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
978 if (is_array($question_ids) && count($question_ids) ){
979 $i = 0;
980 foreach ($question_ids as $key => $question_id){
981 $i++;
982 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
983 }
984 }
985 }
986
987 /**
988 * Save sorting data for quiz answers
989 */
990 public function tutor_quiz_answer_sorting(){
991 global $wpdb;
992
993 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
994 $answer_ids = $_POST['sorted_answer_ids'];
995 $i = 0;
996 foreach ($answer_ids as $key => $answer_id){
997 $i++;
998 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
999 }
1000 }
1001
1002 }
1003
1004 /**
1005 * Mark answer as correct
1006 */
1007
1008 public function tutor_mark_answer_as_correct(){
1009 global $wpdb;
1010
1011 $answer_id = sanitize_text_field($_POST['answer_id']);
1012 $inputValue = sanitize_text_field($_POST['inputValue']);
1013
1014 $answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} LIMIT 0,1 ;");
1015 if ($answer->belongs_question_type === 'single_choice'){
1016 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => $answer->belongs_question_id));
1017 }
1018 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => $inputValue), array('answer_id' => $answer_id));
1019 }
1020
1021 /**
1022 * Update quiz settings from modal
1023 *
1024 * @since : v.1.0.0
1025 */
1026 public function tutor_quiz_modal_update_settings(){
1027 $quiz_id = sanitize_text_field($_POST['quiz_id']);
1028
1029 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
1030 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
1031
1032 wp_send_json_success();
1033 }
1034
1035
1036 //=========================//
1037 // Front end stuffs
1038 //=========================//
1039
1040 /**
1041 * Rendering quiz for frontend
1042 *
1043 * @since v.1.0.0
1044 */
1045
1046 public function tutor_render_quiz_content(){
1047 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
1048
1049 ob_start();
1050 global $post;
1051
1052 $post = get_post($quiz_id);
1053 setup_postdata($post);
1054 //tutor_lesson_content();
1055
1056 single_quiz_contents();
1057
1058 wp_reset_postdata();
1059
1060
1061 $html = ob_get_clean();
1062 wp_send_json_success(array('html' => $html));
1063 }
1064
1065
1066 }