PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.0.9
Tutor LMS – eLearning and online course solution v1.0.9
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 TutorEDD.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 WooCommerce.php 7 years ago init.php 7 years ago
Quiz.php
1069 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_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('attempt_id', $_POST));
320 $attempt = tutor_utils()->get_attempt($attempt_id);
321
322 $attempt_answers = isset($_POST['attempt']) ? $_POST['attempt'] : false;
323 if ( ! is_user_logged_in()){
324 die('Please sign in to do this operation');
325 }
326
327 global $wpdb;
328 $user_id = get_current_user_id();
329
330 if ($attempt_answers && is_array($attempt_answers) && count($attempt_answers)){
331 foreach ($attempt_answers as $attempt_id => $attempt_answers){
332
333 /**
334 * Get total marks of all question comes
335 */
336 $question_ids = tutor_utils()->avalue_dot('quiz_question_ids', $attempt_answers);
337 if (is_array($question_ids) && count($question_ids)){
338 $question_ids_string = "'".implode("','", $question_ids)."'";
339 $total_question_marks = $wpdb->get_var("SELECT SUM(question_mark) FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN({$question_ids_string}) ;");
340 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', array('total_marks' =>$total_question_marks ), array('attempt_id' => $attempt_id ));
341 }
342
343 if ( ! $attempt || $user_id != $attempt->user_id){
344 die('Operation not allowed, attempt not found or permission denied');
345 }
346
347 $quiz_answers = tutor_utils()->avalue_dot('quiz_question', $attempt_answers);
348 $total_marks = 0;
349 foreach ($quiz_answers as $question_id => $answers){
350 $question = tutor_utils()->get_quiz_question_by_id($question_id);
351 $question_type = $question->question_type;
352
353 $is_answer_was_correct = false;
354 $given_answer = '';
355
356 if ($question_type === 'true_false' || $question_type === 'single_choice'){
357
358 $given_answer = $answers;
359 $is_answer_was_correct = (bool) $wpdb->get_var("SELECT is_correct FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answers} ");
360
361 }elseif ($question_type === 'multiple_choice'){
362
363 $given_answer = maybe_serialize($answers);
364 $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 ;");
365 if (maybe_serialize($get_original_answers) == $given_answer){
366 $is_answer_was_correct = true;
367 }
368
369 }elseif ($question_type === 'fill_in_the_blank'){
370
371 $given_answer = (array) array_map('sanitize_text_field', $answers);
372 $given_answer = maybe_serialize($given_answer);
373
374 $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}' ;");
375 $gap_answer = (array) explode('|', $get_original_answer->answer_two_gap_match);
376
377 $gap_answer = array_map('sanitize_text_field', $gap_answer);
378 if ($given_answer == maybe_serialize($gap_answer)){
379 $is_answer_was_correct = true;
380 }
381
382 }elseif ($question_type === 'open_ended' || $question_type === 'short_answer'){
383
384 $given_answer = wp_kses_post($answers);
385
386 }elseif ($question_type === 'ordering' || $question_type === 'matching'|| $question_type === 'image_matching' ){
387
388 $given_answer = (array) array_map('sanitize_text_field', tutor_utils()->avalue_dot('answers', $answers));
389 $given_answer = maybe_serialize($given_answer);
390
391 $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 ;");
392 $get_original_answers = array_map('sanitize_text_field', $get_original_answers);
393
394 if ($given_answer == maybe_serialize($get_original_answers)){
395 $is_answer_was_correct = true;
396 }
397
398 }elseif ($question_type === 'image_answering'){
399 echo '<pre>';
400
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("SELECT answer_title FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id = {$question_id} AND belongs_question_type =
407 'image_answering' ORDER BY answer_order asc ;");
408
409 if (is_array($db_answer) && count($db_answer)){
410 $is_answer_was_correct = (strtolower(maybe_serialize(array_values($image_inputs))) == strtolower(maybe_serialize($db_answer)) );
411 }
412 }
413
414 $question_mark = $is_answer_was_correct ? $question->question_mark : 0;
415 $total_marks += $question_mark;
416
417 $answers_data = array(
418 'user_id' => $user_id,
419 'quiz_id' => $attempt->quiz_id,
420 'question_id' => $question_id,
421 'quiz_attempt_id' => $attempt_id,
422 'given_answer' => $given_answer,
423 'question_mark' => $question->question_mark,
424 'achieved_mark' => $question_mark,
425 'minus_mark' => 0,
426 'is_correct' => $is_answer_was_correct ? 1 : 0,
427 );
428 $wpdb->insert($wpdb->prefix.'tutor_quiz_attempt_answers', $answers_data);
429 }
430
431 $attempt_info = array(
432 'total_answered_questions' => count($quiz_answers),
433 'earned_marks' => $total_marks,
434 'attempt_status' => 'attempt_ended',
435 'attempt_ended_at' => date("Y-m-d H:i:s"),
436 );
437 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
438 }
439 }
440
441 wp_redirect(get_the_permalink($attempt->quiz_id));
442 die();
443 }
444
445
446 /**
447 * Quiz attempt will be finish here
448 *
449 */
450
451 public function finishing_quiz_attempt(){
452 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_finish_quiz_attempt' ){
453 return;
454 }
455 //Checking nonce
456 tutor_utils()->checking_nonce();
457
458 if ( ! is_user_logged_in()){
459 die('Please sign in to do this operation');
460 }
461
462 global $wpdb;
463
464 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
465 $attempt = tutor_utils()->is_started_quiz($quiz_id);
466
467 $attempt_info = array(
468 'total_answered_questions' => 0,
469 'earned_marks' => 0,
470 'attempt_status' => 'attempt_ended',
471 'attempt_ended_at' => date("Y-m-d H:i:s"),
472 );
473 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt->attempt_id));
474
475 wp_redirect(tutor_utils()->input_old('_wp_http_referer'));
476 }
477
478 /**
479 * Quiz timeout by ajax
480 */
481 public function tutor_quiz_timeout(){
482 global $wpdb;
483
484 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
485 $attempt = tutor_utils()->is_started_quiz($quiz_id);
486
487 if ($attempt) {
488 $data = array(
489 'attempt_status' => 'attempt_timeout',
490 'attempt_ended_at' => date("Y-m-d H:i:s"),
491 );
492 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $data, array('attempt_id' => $attempt->attempt_id));
493 wp_send_json_success();
494 }
495
496 wp_send_json_error(__('Quiz has been timeout already', 'tutor'));
497 }
498
499 /**
500 * Review the answer and change individual answer result
501 */
502
503 public function review_quiz_answer(){
504 global $wpdb;
505
506 $attempt_id = (int) sanitize_text_field($_GET['attempt_id']);
507 $attempt_answer_id = (int) sanitize_text_field($_GET['attempt_answer_id']);
508 $mark_as = sanitize_text_field($_GET['mark_as']);
509
510
511 $attempt_answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers WHERE attempt_answer_id = {$attempt_answer_id} ");
512 $attempt = tutor_utils()->get_attempt($attempt_id);
513
514 $is_correct = (int) $attempt_answer->is_correct;
515
516 if ($mark_as === 'correct' && ! $is_correct){
517
518 $answer_update_data = array(
519 'achieved_mark' => $attempt_answer->question_mark,
520 'is_correct' => 1,
521 );
522 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
523
524 $attempt_update_data = array(
525 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
526 'is_manually_reviewed' => 1,
527 'manually_reviewed_at' => date("Y-m-d H:i:s"),
528 );
529
530 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
531
532 }elseif($mark_as === 'incorrect' && $is_correct){
533
534 $answer_update_data = array(
535 'achieved_mark' => '0.00',
536 'is_correct' => 0,
537 );
538 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
539
540 $attempt_update_data = array(
541 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
542 'is_manually_reviewed' => 1,
543 'manually_reviewed_at' => date("Y-m-d H:i:s"),
544 );
545
546 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
547 }
548
549 wp_redirect(admin_url("admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=".$attempt_id));
550 die();
551 }
552
553
554 /**
555 * New Design Quiz
556 */
557 public function tutor_create_quiz_and_load_modal(){
558 $topic_id = sanitize_text_field($_POST['topic_id']);
559 $quiz_title = sanitize_text_field($_POST['quiz_title']);
560 $quiz_description = sanitize_text_field($_POST['quiz_description']);
561 $next_order_id = tutor_utils()->get_next_course_content_order_id($topic_id);
562
563 $post_arr = array(
564 'post_type' => 'tutor_quiz',
565 'post_title' => $quiz_title,
566 'post_content' => $quiz_description,
567 'post_status' => 'publish',
568 'post_author' => get_current_user_id(),
569 'post_parent' => $topic_id,
570 'menu_order' => $next_order_id,
571 );
572 $quiz_id = wp_insert_post( $post_arr );
573
574 ob_start();
575 include tutor()->path.'views/modal/edit_quiz.php';
576 $output = ob_get_clean();
577
578 ob_start();
579 ?>
580 <div id="tutor-quiz-<?php echo $quiz_id; ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
581 <div class="tutor-lesson-top">
582 <i class="tutor-icon-move"></i>
583 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
584 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
585 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
586 </div>
587 </div>
588 <?php
589 $output_quiz_row = ob_get_clean();
590
591 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
592 }
593
594 public function tutor_delete_quiz_by_id(){
595 global $wpdb;
596
597 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
598 $post = get_post($quiz_id);
599
600 if ( $post->post_type === 'tutor_quiz'){
601 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
602 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
603
604 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$quiz_id} ");
605 if (is_array($questions_ids) && count($questions_ids)){
606 $in_question_ids = "'".implode("','", $questions_ids)."'";
607 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
608 }
609 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
610
611 wp_delete_post($quiz_id, true);
612 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
613 wp_send_json_success();
614 }
615
616 wp_send_json_error();
617 }
618
619 /**
620 * Update Quiz from quiz builder modal
621 *
622 * @since v.1.0.0
623 */
624 public function tutor_quiz_builder_quiz_update(){
625 $quiz_id = sanitize_text_field($_POST['quiz_id']);
626 $topic_id = sanitize_text_field($_POST['topic_id']);
627 $quiz_title = sanitize_text_field($_POST['quiz_title']);
628 $quiz_description = sanitize_text_field($_POST['quiz_description']);
629
630 $post_arr = array(
631 'ID' => $quiz_id,
632 'post_title' => $quiz_title,
633 'post_content' => $quiz_description,
634
635 );
636 $quiz_id = wp_update_post( $post_arr );
637
638 ob_start();
639 ?>
640 <div class="tutor-lesson-top">
641 <i class="tutor-icon-move"></i>
642 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
643 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
644 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
645 </div>
646 <?php
647 $output_quiz_row = ob_get_clean();
648
649 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
650 }
651
652 /**
653 * Load quiz Modal for edit quiz
654 *
655 * @since v.1.0.0
656 */
657 public function tutor_load_edit_quiz_modal(){
658 $quiz_id = sanitize_text_field($_POST['quiz_id']);
659
660 ob_start();
661 include tutor()->path.'views/modal/edit_quiz.php';
662 $output = ob_get_clean();
663
664 wp_send_json_success(array('output' => $output));
665 }
666
667 /**
668 * Load quiz question form for quiz
669 *
670 * @since v.1.0.0
671 */
672 public function tutor_quiz_builder_get_question_form(){
673 global $wpdb;
674 $quiz_id = sanitize_text_field($_POST['quiz_id']);
675 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
676
677 if ( ! $question_id){
678 $next_question_id = tutor_utils()->quiz_next_question_id();
679 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
680
681 $new_question_data = array(
682 'quiz_id' => $quiz_id,
683 'question_title' => __('Question ').$next_question_id,
684 'question_description' => '',
685 'question_type' => 'true_false',
686 'question_mark' => 1,
687 'question_settings' => maybe_serialize(array()),
688 'question_order' => $next_question_order,
689 );
690
691 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
692 $question_id = $wpdb->insert_id;
693 }
694
695 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = {$question_id} ");
696
697 ob_start();
698 include tutor()->path.'views/modal/question_form.php';
699 $output = ob_get_clean();
700
701 wp_send_json_success(array('output' => $output));
702 }
703
704 public function tutor_quiz_modal_update_question(){
705 global $wpdb;
706
707 $question_data = $_POST['tutor_quiz_question'];
708
709 foreach ($question_data as $question_id => $question){
710 $question_title = $question['question_title'];
711 $question_description = $question['question_description'];
712 $question_type = $question['question_type'];
713 $question_mark = $question['question_mark'];
714
715 unset($question['question_title']);
716 unset($question['question_description']);
717
718 $data = array(
719 'question_title' => $question_title,
720 'question_description' => $question_description,
721 'question_type' => $question_type,
722 'question_mark' => $question_mark,
723 'question_settings' => maybe_serialize($question),
724 );
725
726 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
727 }
728
729 wp_send_json_success();
730 }
731
732 public function tutor_quiz_builder_question_delete(){
733 global $wpdb;
734
735 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
736 if ($question_id){
737 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => $question_id));
738 }
739
740 wp_send_json_success();
741 }
742
743 /**
744 * Get answers options form for quiz question
745 *
746 * @since v.1.0.0
747 */
748 public function tutor_quiz_add_question_answers(){
749 $question_id = sanitize_text_field($_POST['question_id']);
750 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
751 $question_type = $question['question_type'];
752
753 ob_start();
754 include tutor()->path.'views/modal/question_answer_form.php';
755 $output = ob_get_clean();
756
757 wp_send_json_success(array('output' => $output));
758 }
759
760 /**
761 * Edit Answer Form
762 *
763 * @since v.1.0.0
764 */
765 public function tutor_quiz_edit_question_answer(){
766 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
767 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
768 foreach ($old_answer as $old_answer);
769 $question_id = $old_answer->belongs_question_id;
770 $question_type = $old_answer->belongs_question_type;
771
772 ob_start();
773 include tutor()->path.'views/modal/question_answer_edit_form.php';
774 $output = ob_get_clean();
775
776 wp_send_json_success(array('output' => $output));
777 }
778
779 public function tutor_save_quiz_answer_options(){
780 global $wpdb;
781
782 $questions = $_POST['tutor_quiz_question'];
783 $answers = $_POST['quiz_answer'];
784
785 foreach ($answers as $question_id => $answer){
786 $question = tutor_utils()->avalue_dot($question_id, $questions);
787 $question_type = $question['question_type'];
788
789 //Getting next sorting order
790 $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}' ");
791 $next_order_id = $next_order_id + 1;
792
793 if ($question){
794 if ($question_type === 'true_false'){
795 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
796 $data_true_false = array(
797 array(
798 'belongs_question_id' => $question_id,
799 'belongs_question_type' => $question_type,
800 'answer_title' => __('True', 'tutor'),
801 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
802 'answer_two_gap_match' => 'true',
803 ),
804 array(
805 'belongs_question_id' => $question_id,
806 'belongs_question_type' => $question_type,
807 'answer_title' => __('False', 'tutor'),
808 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
809 'answer_two_gap_match' => 'false',
810 ),
811 );
812
813 foreach ($data_true_false as $true_false_data){
814 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
815 }
816
817 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
818 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
819
820 $answer_data = array(
821 'belongs_question_id' => $question_id,
822 'belongs_question_type' => $question_type,
823 'answer_title' => $answer['answer_title'],
824 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
825 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
826 'answer_order' => $next_order_id,
827 );
828 if (isset($answer['matched_answer_title'])){
829 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
830 }
831
832 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
833
834 }elseif($question_type === 'fill_in_the_blank'){
835 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
836 $answer_data = array(
837 'belongs_question_id' => $question_id,
838 'belongs_question_type' => $question_type,
839 'answer_title' => $answer['answer_title'],
840 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null,
841 );
842 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
843 }
844 }
845 }
846
847 wp_send_json_success();
848 }
849
850 /**
851 * Tutor Update Answer
852 *
853 * @since v.1.0.0
854 */
855 public function tutor_update_quiz_answer_options(){
856 global $wpdb;
857
858 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
859 $questions = $_POST['tutor_quiz_question'];
860 $answers = $_POST['quiz_answer'];
861
862 foreach ($answers as $question_id => $answer){
863 $question = tutor_utils()->avalue_dot($question_id, $questions);
864 $question_type = $question['question_type'];
865
866 if ($question){
867 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' ){
868
869 $answer_data = array(
870 'belongs_question_id' => $question_id,
871 'belongs_question_type' => $question_type,
872 'answer_title' => $answer['answer_title'],
873 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
874 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : '',
875 );
876 if (isset($answer['matched_answer_title'])){
877 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
878 }
879
880 if ($question_type === 'fill_in_the_blank'){
881 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null;
882 }
883
884 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
885 }
886 }
887 }
888
889 //die(print_r($_POST));
890 wp_send_json_success();
891 }
892
893 public function tutor_quiz_builder_get_answers_by_question(){
894 global $wpdb;
895 $question_id = sanitize_text_field($_POST['question_id']);
896 $question_type = sanitize_text_field($_POST['question_type']);
897
898 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = {$question_id} ");
899 $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 ;");
900
901 ob_start();
902
903 switch ($question_type){
904 case 'true_false':
905 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
906 break;
907 case 'ordering':
908 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>';
909 break;
910 }
911
912 if (is_array($answers) && count($answers)){
913 foreach ($answers as $answer){
914 ?>
915 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
916 <div class="tutor-quiz-answer">
917 <span class="tutor-quiz-answer-title">
918 <?php
919 echo $answer->answer_title;
920 if ($answer->belongs_question_type === 'fill_in_the_blank'){
921 echo ' ('.__('Answer', 'tutor').' : ';
922 echo "<strong>{$answer->answer_two_gap_match} </strong>)";
923 }
924 if ($answer->belongs_question_type === 'matching'){
925 echo " - {$answer->answer_two_gap_match}";
926 }
927 ?>
928 </span>
929
930 <?php
931 if ($answer->image_id){
932 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
933 }
934 if ($question_type === 'true_false' || $question_type === 'single_choice'){
935 ?>
936 <span class="tutor-quiz-answers-mark-correct-wrap">
937 <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); ?> >
938 </span>
939 <?php
940 }elseif ($question_type === 'multiple_choice'){
941 ?>
942 <span class="tutor-quiz-answers-mark-correct-wrap">
943 <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); ?> >
944 </span>
945 <?php
946 }
947 ?>
948 <span class="tutor-quiz-answer-edit">
949 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
950 </span>
951 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
952 </div>
953
954 <div class="tutor-quiz-answer-trash-wrap">
955 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
956 </div>
957 </div>
958 <?php
959 }
960 }
961 $output = ob_get_clean();
962
963 wp_send_json_success(array('output' => $output));
964 }
965
966 public function tutor_quiz_builder_delete_answer(){
967 global $wpdb;
968 $answer_id = sanitize_text_field($_POST['answer_id']);
969
970 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => $answer_id));
971 wp_send_json_success();
972 }
973
974 /**
975 * Save quiz questions sorting
976 */
977 public function tutor_quiz_question_sorting(){
978 global $wpdb;
979
980 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
981 if (is_array($question_ids) && count($question_ids) ){
982 $i = 0;
983 foreach ($question_ids as $key => $question_id){
984 $i++;
985 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
986 }
987 }
988 }
989
990 /**
991 * Save sorting data for quiz answers
992 */
993 public function tutor_quiz_answer_sorting(){
994 global $wpdb;
995
996 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
997 $answer_ids = $_POST['sorted_answer_ids'];
998 $i = 0;
999 foreach ($answer_ids as $key => $answer_id){
1000 $i++;
1001 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
1002 }
1003 }
1004
1005 }
1006
1007 /**
1008 * Mark answer as correct
1009 */
1010
1011 public function tutor_mark_answer_as_correct(){
1012 global $wpdb;
1013
1014 $answer_id = sanitize_text_field($_POST['answer_id']);
1015 $inputValue = sanitize_text_field($_POST['inputValue']);
1016
1017 $answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} LIMIT 0,1 ;");
1018 if ($answer->belongs_question_type === 'single_choice'){
1019 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => $answer->belongs_question_id));
1020 }
1021 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => $inputValue), array('answer_id' => $answer_id));
1022 }
1023
1024 /**
1025 * Update quiz settings from modal
1026 *
1027 * @since : v.1.0.0
1028 */
1029 public function tutor_quiz_modal_update_settings(){
1030 $quiz_id = sanitize_text_field($_POST['quiz_id']);
1031
1032 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
1033 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
1034
1035 wp_send_json_success();
1036 }
1037
1038
1039 //=========================//
1040 // Front end stuffs
1041 //=========================//
1042
1043 /**
1044 * Rendering quiz for frontend
1045 *
1046 * @since v.1.0.0
1047 */
1048
1049 public function tutor_render_quiz_content(){
1050 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
1051
1052 ob_start();
1053 global $post;
1054
1055 $post = get_post($quiz_id);
1056 setup_postdata($post);
1057 //tutor_lesson_content();
1058
1059 single_quiz_contents();
1060
1061 wp_reset_postdata();
1062
1063
1064 $html = ob_get_clean();
1065 wp_send_json_success(array('html' => $html));
1066 }
1067
1068
1069 }