PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.0.6
Tutor LMS – eLearning and online course solution v1.0.6
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Quiz.php
tutor / classes Last commit date
Addons.php 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
1067 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 echo '<pre>';
398
399 $image_inputs = tutor_utils()->avalue_dot('answer_id', $answers);
400 $image_inputs = (array) array_map('sanitize_text_field', $image_inputs);
401 $given_answer = maybe_serialize($image_inputs);
402 $is_answer_was_correct = false;
403
404 $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 =
405 'image_answering' ORDER BY answer_order asc ;");
406
407 if (is_array($db_answer) && count($db_answer)){
408 $is_answer_was_correct = (strtolower(maybe_serialize(array_values($image_inputs))) == strtolower(maybe_serialize($db_answer)) );
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
509 $attempt_answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers WHERE attempt_answer_id = {$attempt_answer_id} ");
510 $attempt = tutor_utils()->get_attempt($attempt_id);
511
512 $is_correct = (int) $attempt_answer->is_correct;
513
514 if ($mark_as === 'correct' && ! $is_correct){
515
516 $answer_update_data = array(
517 'achieved_mark' => $attempt_answer->question_mark,
518 'is_correct' => 1,
519 );
520 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
521
522 $attempt_update_data = array(
523 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
524 'is_manually_reviewed' => 1,
525 'manually_reviewed_at' => date("Y-m-d H:i:s"),
526 );
527
528 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
529
530 }elseif($mark_as === 'incorrect' && $is_correct){
531
532 $answer_update_data = array(
533 'achieved_mark' => '0.00',
534 'is_correct' => 0,
535 );
536 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
537
538 $attempt_update_data = array(
539 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
540 'is_manually_reviewed' => 1,
541 'manually_reviewed_at' => date("Y-m-d H:i:s"),
542 );
543
544 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
545 }
546
547 wp_redirect(admin_url("admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=".$attempt_id));
548 die();
549 }
550
551
552 /**
553 * New Design Quiz
554 */
555 public function tutor_create_quiz_and_load_modal(){
556 $topic_id = sanitize_text_field($_POST['topic_id']);
557 $quiz_title = sanitize_text_field($_POST['quiz_title']);
558 $quiz_description = sanitize_text_field($_POST['quiz_description']);
559 $next_order_id = tutor_utils()->get_next_course_content_order_id($topic_id);
560
561 $post_arr = array(
562 'post_type' => 'tutor_quiz',
563 'post_title' => $quiz_title,
564 'post_content' => $quiz_description,
565 'post_status' => 'publish',
566 'post_author' => get_current_user_id(),
567 'post_parent' => $topic_id,
568 'menu_order' => $next_order_id,
569 );
570 $quiz_id = wp_insert_post( $post_arr );
571
572 ob_start();
573 include tutor()->path.'views/modal/edit_quiz.php';
574 $output = ob_get_clean();
575
576 ob_start();
577 ?>
578 <div id="tutor-quiz-<?php echo $quiz_id; ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
579 <div class="tutor-lesson-top">
580 <i class="tutor-icon-move"></i>
581 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
582 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
583 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
584 </div>
585 </div>
586 <?php
587 $output_quiz_row = ob_get_clean();
588
589 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
590 }
591
592 public function tutor_delete_quiz_by_id(){
593 global $wpdb;
594
595 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
596 $post = get_post($quiz_id);
597
598 if ( $post->post_type === 'tutor_quiz'){
599 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
600 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
601
602 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$quiz_id} ");
603 if (is_array($questions_ids) && count($questions_ids)){
604 $in_question_ids = "'".implode("','", $questions_ids)."'";
605 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
606 }
607 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
608
609 wp_delete_post($quiz_id, true);
610 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
611 wp_send_json_success();
612 }
613
614 wp_send_json_error();
615 }
616
617 /**
618 * Update Quiz from quiz builder modal
619 *
620 * @since v.1.0.0
621 */
622 public function tutor_quiz_builder_quiz_update(){
623 $quiz_id = sanitize_text_field($_POST['quiz_id']);
624 $topic_id = sanitize_text_field($_POST['topic_id']);
625 $quiz_title = sanitize_text_field($_POST['quiz_title']);
626 $quiz_description = sanitize_text_field($_POST['quiz_description']);
627
628 $post_arr = array(
629 'ID' => $quiz_id,
630 'post_title' => $quiz_title,
631 'post_content' => $quiz_description,
632
633 );
634 $quiz_id = wp_update_post( $post_arr );
635
636 ob_start();
637 ?>
638 <div class="tutor-lesson-top">
639 <i class="tutor-icon-move"></i>
640 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
641 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
642 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
643 </div>
644 <?php
645 $output_quiz_row = ob_get_clean();
646
647 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
648 }
649
650 /**
651 * Load quiz Modal for edit quiz
652 *
653 * @since v.1.0.0
654 */
655 public function tutor_load_edit_quiz_modal(){
656 $quiz_id = sanitize_text_field($_POST['quiz_id']);
657
658 ob_start();
659 include tutor()->path.'views/modal/edit_quiz.php';
660 $output = ob_get_clean();
661
662 wp_send_json_success(array('output' => $output));
663 }
664
665 /**
666 * Load quiz question form for quiz
667 *
668 * @since v.1.0.0
669 */
670 public function tutor_quiz_builder_get_question_form(){
671 global $wpdb;
672 $quiz_id = sanitize_text_field($_POST['quiz_id']);
673 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
674
675 if ( ! $question_id){
676 $next_question_id = tutor_utils()->quiz_next_question_id();
677 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
678
679 $new_question_data = array(
680 'quiz_id' => $quiz_id,
681 'question_title' => __('Question ').$next_question_id,
682 'question_description' => '',
683 'question_type' => 'true_false',
684 'question_mark' => 1,
685 'question_settings' => maybe_serialize(array()),
686 'question_order' => $next_question_order,
687 );
688
689 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
690 $question_id = $wpdb->insert_id;
691 }
692
693 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = {$question_id} ");
694
695 ob_start();
696 include tutor()->path.'views/modal/question_form.php';
697 $output = ob_get_clean();
698
699 wp_send_json_success(array('output' => $output));
700 }
701
702 public function tutor_quiz_modal_update_question(){
703 global $wpdb;
704
705 $question_data = $_POST['tutor_quiz_question'];
706
707 foreach ($question_data as $question_id => $question){
708 $question_title = $question['question_title'];
709 $question_description = $question['question_description'];
710 $question_type = $question['question_type'];
711 $question_mark = $question['question_mark'];
712
713 unset($question['question_title']);
714 unset($question['question_description']);
715
716 $data = array(
717 'question_title' => $question_title,
718 'question_description' => $question_description,
719 'question_type' => $question_type,
720 'question_mark' => $question_mark,
721 'question_settings' => maybe_serialize($question),
722 );
723
724 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
725 }
726
727 wp_send_json_success();
728 }
729
730 public function tutor_quiz_builder_question_delete(){
731 global $wpdb;
732
733 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
734 if ($question_id){
735 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => $question_id));
736 }
737
738 wp_send_json_success();
739 }
740
741 /**
742 * Get answers options form for quiz question
743 *
744 * @since v.1.0.0
745 */
746 public function tutor_quiz_add_question_answers(){
747 $question_id = sanitize_text_field($_POST['question_id']);
748 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
749 $question_type = $question['question_type'];
750
751 ob_start();
752 include tutor()->path.'views/modal/question_answer_form.php';
753 $output = ob_get_clean();
754
755 wp_send_json_success(array('output' => $output));
756 }
757
758 /**
759 * Edit Answer Form
760 *
761 * @since v.1.0.0
762 */
763 public function tutor_quiz_edit_question_answer(){
764 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
765 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
766 foreach ($old_answer as $old_answer);
767 $question_id = $old_answer->belongs_question_id;
768 $question_type = $old_answer->belongs_question_type;
769
770 ob_start();
771 include tutor()->path.'views/modal/question_answer_edit_form.php';
772 $output = ob_get_clean();
773
774 wp_send_json_success(array('output' => $output));
775 }
776
777 public function tutor_save_quiz_answer_options(){
778 global $wpdb;
779
780 $questions = $_POST['tutor_quiz_question'];
781 $answers = $_POST['quiz_answer'];
782
783 foreach ($answers as $question_id => $answer){
784 $question = tutor_utils()->avalue_dot($question_id, $questions);
785 $question_type = $question['question_type'];
786
787 //Getting next sorting order
788 $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}' ");
789 $next_order_id = $next_order_id + 1;
790
791 if ($question){
792 if ($question_type === 'true_false'){
793 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
794 $data_true_false = array(
795 array(
796 'belongs_question_id' => $question_id,
797 'belongs_question_type' => $question_type,
798 'answer_title' => __('True', 'tutor'),
799 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
800 'answer_two_gap_match' => 'true',
801 ),
802 array(
803 'belongs_question_id' => $question_id,
804 'belongs_question_type' => $question_type,
805 'answer_title' => __('False', 'tutor'),
806 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
807 'answer_two_gap_match' => 'false',
808 ),
809 );
810
811 foreach ($data_true_false as $true_false_data){
812 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
813 }
814
815 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
816 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
817
818 $answer_data = array(
819 'belongs_question_id' => $question_id,
820 'belongs_question_type' => $question_type,
821 'answer_title' => $answer['answer_title'],
822 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
823 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
824 'answer_order' => $next_order_id,
825 );
826 if (isset($answer['matched_answer_title'])){
827 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
828 }
829
830 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
831
832 }elseif($question_type === 'fill_in_the_blank'){
833 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
834 $answer_data = array(
835 'belongs_question_id' => $question_id,
836 'belongs_question_type' => $question_type,
837 'answer_title' => $answer['answer_title'],
838 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null,
839 );
840 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
841 }
842 }
843 }
844
845 wp_send_json_success();
846 }
847
848 /**
849 * Tutor Update Answer
850 *
851 * @since v.1.0.0
852 */
853 public function tutor_update_quiz_answer_options(){
854 global $wpdb;
855
856 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
857 $questions = $_POST['tutor_quiz_question'];
858 $answers = $_POST['quiz_answer'];
859
860 foreach ($answers as $question_id => $answer){
861 $question = tutor_utils()->avalue_dot($question_id, $questions);
862 $question_type = $question['question_type'];
863
864 if ($question){
865 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' ){
866
867 $answer_data = array(
868 'belongs_question_id' => $question_id,
869 'belongs_question_type' => $question_type,
870 'answer_title' => $answer['answer_title'],
871 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
872 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : '',
873 );
874 if (isset($answer['matched_answer_title'])){
875 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
876 }
877
878 if ($question_type === 'fill_in_the_blank'){
879 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null;
880 }
881
882 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
883 }
884 }
885 }
886
887 //die(print_r($_POST));
888 wp_send_json_success();
889 }
890
891 public function tutor_quiz_builder_get_answers_by_question(){
892 global $wpdb;
893 $question_id = sanitize_text_field($_POST['question_id']);
894 $question_type = sanitize_text_field($_POST['question_type']);
895
896 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = {$question_id} ");
897 $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 ;");
898
899 ob_start();
900
901 switch ($question_type){
902 case 'true_false':
903 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
904 break;
905 case 'ordering':
906 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>';
907 break;
908 }
909
910 if (is_array($answers) && count($answers)){
911 foreach ($answers as $answer){
912 ?>
913 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
914 <div class="tutor-quiz-answer">
915 <span class="tutor-quiz-answer-title">
916 <?php
917 echo $answer->answer_title;
918 if ($answer->belongs_question_type === 'fill_in_the_blank'){
919 echo ' ('.__('Answer', 'tutor').' : ';
920 echo "<strong>{$answer->answer_two_gap_match} </strong>)";
921 }
922 if ($answer->belongs_question_type === 'matching'){
923 echo " - {$answer->answer_two_gap_match}";
924 }
925 ?>
926 </span>
927
928 <?php
929 if ($answer->image_id){
930 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
931 }
932 if ($question_type === 'true_false' || $question_type === 'single_choice'){
933 ?>
934 <span class="tutor-quiz-answers-mark-correct-wrap">
935 <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); ?> >
936 </span>
937 <?php
938 }elseif ($question_type === 'multiple_choice'){
939 ?>
940 <span class="tutor-quiz-answers-mark-correct-wrap">
941 <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); ?> >
942 </span>
943 <?php
944 }
945 ?>
946 <span class="tutor-quiz-answer-edit">
947 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
948 </span>
949 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
950 </div>
951
952 <div class="tutor-quiz-answer-trash-wrap">
953 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
954 </div>
955 </div>
956 <?php
957 }
958 }
959 $output = ob_get_clean();
960
961 wp_send_json_success(array('output' => $output));
962 }
963
964 public function tutor_quiz_builder_delete_answer(){
965 global $wpdb;
966 $answer_id = sanitize_text_field($_POST['answer_id']);
967
968 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => $answer_id));
969 wp_send_json_success();
970 }
971
972 /**
973 * Save quiz questions sorting
974 */
975 public function tutor_quiz_question_sorting(){
976 global $wpdb;
977
978 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
979 if (is_array($question_ids) && count($question_ids) ){
980 $i = 0;
981 foreach ($question_ids as $key => $question_id){
982 $i++;
983 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
984 }
985 }
986 }
987
988 /**
989 * Save sorting data for quiz answers
990 */
991 public function tutor_quiz_answer_sorting(){
992 global $wpdb;
993
994 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
995 $answer_ids = $_POST['sorted_answer_ids'];
996 $i = 0;
997 foreach ($answer_ids as $key => $answer_id){
998 $i++;
999 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
1000 }
1001 }
1002
1003 }
1004
1005 /**
1006 * Mark answer as correct
1007 */
1008
1009 public function tutor_mark_answer_as_correct(){
1010 global $wpdb;
1011
1012 $answer_id = sanitize_text_field($_POST['answer_id']);
1013 $inputValue = sanitize_text_field($_POST['inputValue']);
1014
1015 $answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} LIMIT 0,1 ;");
1016 if ($answer->belongs_question_type === 'single_choice'){
1017 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => $answer->belongs_question_id));
1018 }
1019 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => $inputValue), array('answer_id' => $answer_id));
1020 }
1021
1022 /**
1023 * Update quiz settings from modal
1024 *
1025 * @since : v.1.0.0
1026 */
1027 public function tutor_quiz_modal_update_settings(){
1028 $quiz_id = sanitize_text_field($_POST['quiz_id']);
1029
1030 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
1031 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
1032
1033 wp_send_json_success();
1034 }
1035
1036
1037 //=========================//
1038 // Front end stuffs
1039 //=========================//
1040
1041 /**
1042 * Rendering quiz for frontend
1043 *
1044 * @since v.1.0.0
1045 */
1046
1047 public function tutor_render_quiz_content(){
1048 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
1049
1050 ob_start();
1051 global $post;
1052
1053 $post = get_post($quiz_id);
1054 setup_postdata($post);
1055 //tutor_lesson_content();
1056
1057 single_quiz_contents();
1058
1059 wp_reset_postdata();
1060
1061
1062 $html = ob_get_clean();
1063 wp_send_json_success(array('html' => $html));
1064 }
1065
1066
1067 }