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