PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.4.4
Tutor LMS – eLearning and online course solution v1.4.4
4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Quiz.php
tutor / classes Last commit date
Addons.php 6 years ago Admin.php 6 years ago Ajax.php 6 years ago Assets.php 6 years ago Course.php 6 years ago Course_Settings_Tabs.php 6 years ago Course_Widget.php 6 years ago Dashboard.php 6 years ago Email.php 6 years ago FormHandler.php 6 years ago Gutenberg.php 6 years ago Instructor.php 6 years ago Instructors_List.php 6 years ago Lesson.php 6 years ago Options.php 6 years ago Post_types.php 6 years ago Q_and_A.php 6 years ago Question_Answers_List.php 6 years ago Quiz.php 6 years ago Quiz_Attempts_List.php 6 years ago Rewrite_Rules.php 6 years ago Shortcode.php 6 years ago Student.php 6 years ago Students_List.php 6 years ago Taxonomies.php 6 years ago Template.php 6 years ago Theme_Compatibility.php 6 years ago Tools.php 6 years ago Tutor.php 6 years ago TutorEDD.php 6 years ago Tutor_Base.php 6 years ago Tutor_List_Table.php 6 years ago Upgrader.php 6 years ago User.php 6 years ago Utils.php 6 years ago Video_Stream.php 6 years ago Withdraw.php 6 years ago Withdraw_Requests_List.php 6 years ago WooCommerce.php 6 years ago
Quiz.php
995 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_action('save_post_tutor_quiz', array($this, 'save_quiz_meta'));
21
22 add_action('wp_ajax_tutor_load_quiz_builder_modal', array($this, 'tutor_load_quiz_builder_modal'));
23 add_action('wp_ajax_remove_quiz_from_post', array($this, 'remove_quiz_from_post'));
24
25 add_action('wp_ajax_tutor_quiz_timeout', array($this, 'tutor_quiz_timeout'));
26
27 //User take the quiz
28 add_action('template_redirect', array($this, 'start_the_quiz'));
29 add_action('template_redirect', array($this, 'answering_quiz'));
30 add_action('template_redirect', array($this, 'finishing_quiz_attempt'));
31
32 add_action('admin_action_review_quiz_answer', array($this, 'review_quiz_answer'));
33 add_action('wp_ajax_review_quiz_answer', array($this, 'review_quiz_answer'));
34
35 /**
36 * New Design Quiz
37 */
38
39 add_action('wp_ajax_tutor_create_quiz_and_load_modal', array($this, 'tutor_create_quiz_and_load_modal'));
40 add_action('wp_ajax_tutor_delete_quiz_by_id', array($this, 'tutor_delete_quiz_by_id'));
41 add_action('wp_ajax_tutor_quiz_builder_quiz_update', array($this, 'tutor_quiz_builder_quiz_update'));
42 add_action('wp_ajax_tutor_load_edit_quiz_modal', array($this, 'tutor_load_edit_quiz_modal'));
43 add_action('wp_ajax_tutor_quiz_builder_get_question_form', array($this, 'tutor_quiz_builder_get_question_form'));
44 add_action('wp_ajax_tutor_quiz_modal_update_question', array($this, 'tutor_quiz_modal_update_question'));
45 add_action('wp_ajax_tutor_quiz_builder_question_delete', array($this, 'tutor_quiz_builder_question_delete'));
46 add_action('wp_ajax_tutor_quiz_add_question_answers', array($this, 'tutor_quiz_add_question_answers'));
47 add_action('wp_ajax_tutor_quiz_edit_question_answer', array($this, 'tutor_quiz_edit_question_answer'));
48 add_action('wp_ajax_tutor_save_quiz_answer_options', array($this, 'tutor_save_quiz_answer_options'));
49 add_action('wp_ajax_tutor_update_quiz_answer_options', array($this, 'tutor_update_quiz_answer_options'));
50 add_action('wp_ajax_tutor_quiz_builder_get_answers_by_question', array($this, 'tutor_quiz_builder_get_answers_by_question'));
51 add_action('wp_ajax_tutor_quiz_builder_delete_answer', array($this, 'tutor_quiz_builder_delete_answer'));
52 add_action('wp_ajax_tutor_quiz_question_sorting', array($this, 'tutor_quiz_question_sorting'));
53 add_action('wp_ajax_tutor_quiz_answer_sorting', array($this, 'tutor_quiz_answer_sorting'));
54 add_action('wp_ajax_tutor_mark_answer_as_correct', array($this, 'tutor_mark_answer_as_correct'));
55 add_action('wp_ajax_tutor_quiz_modal_update_settings', array($this, 'tutor_quiz_modal_update_settings'));
56
57 /**
58 * Frontend Stuff
59 */
60 add_action('wp_ajax_tutor_render_quiz_content', array($this, 'tutor_render_quiz_content'));
61 }
62
63
64 public function save_quiz_meta($post_ID){
65 if (isset($_POST['quiz_option'])){
66 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
67 update_post_meta($post_ID, 'tutor_quiz_option', $quiz_option);
68 }
69 }
70
71 /**
72 * Tutor Quiz Builder Modal
73 */
74 public function tutor_load_quiz_builder_modal(){
75 ob_start();
76 include tutor()->path.'views/modal/add_quiz.php';
77 $output = ob_get_clean();
78
79 wp_send_json_success(array('output' => $output));
80
81 }
82
83 public function remove_quiz_from_post(){
84 global $wpdb;
85 $quiz_id = (int) tutor_utils()->avalue_dot('quiz_id', $_POST);
86 $wpdb->update($wpdb->posts, array('post_parent' => 0), array('ID' => $quiz_id) );
87 wp_send_json_success();
88 }
89
90 /**
91 *
92 * Start Quiz from here...
93 *
94 * @since v.1.0.0
95 */
96
97 public function start_the_quiz(){
98 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_start_quiz' ){
99 return;
100 }
101 //Checking nonce
102 tutor_utils()->checking_nonce();
103
104 if ( ! is_user_logged_in()){
105 //TODO: need to set a view in the next version
106 die('Please sign in to do this operation');
107 }
108
109 global $wpdb;
110
111 $user_id = get_current_user_id();
112 $user = get_userdata($user_id);
113
114 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
115
116 $quiz = get_post($quiz_id);
117 $course = tutor_utils()->get_course_by_quiz($quiz_id);
118 if ( empty($course->ID)){
119 die('There is something went wrong with course, please check if quiz attached with a course');
120 }
121
122 $date = date("Y-m-d H:i:s");
123
124 $tutor_quiz_option = (array) maybe_unserialize(get_post_meta($quiz_id, 'tutor_quiz_option', true));
125 $attempts_allowed = tutor_utils()->get_quiz_option($quiz_id, 'attempts_allowed', 0);
126
127 $time_limit = tutor_utils()->get_quiz_option($quiz_id, 'time_limit.time_value');
128 $time_limit_seconds = 0;
129 $time_type = 'seconds';
130 if ($time_limit){
131 $time_type = tutor_utils()->get_quiz_option($quiz_id, 'time_limit.time_type');
132
133 switch ($time_type){
134 case 'seconds':
135 $time_limit_seconds = $time_limit;
136 break;
137 case 'minutes':
138 $time_limit_seconds = $time_limit * 60;
139 break;
140 case 'hours':
141 $time_limit_seconds = $time_limit * 60 * 60;
142 break;
143 case 'days':
144 $time_limit_seconds = $time_limit * 60 * 60 * 24;
145 break;
146 case 'weeks':
147 $time_limit_seconds = $time_limit * 60 * 60 * 24 * 7;
148 break;
149 }
150 }
151
152 $max_question_allowed = tutor_utils()->max_questions_for_take_quiz($quiz_id);
153 $tutor_quiz_option['time_limit']['time_limit_seconds'] = $time_limit_seconds;
154
155 $attempt_data = array(
156 'course_id' => $course->ID,
157 'quiz_id' => $quiz_id,
158 'user_id' => $user_id,
159 'total_questions' => $max_question_allowed,
160 'total_answered_questions' => 0,
161 'attempt_info' => maybe_serialize($tutor_quiz_option),
162 'attempt_status' => 'attempt_started',
163 'attempt_ip' => tutor_utils()->get_ip(),
164 'attempt_started_at' => $date,
165 );
166
167 $wpdb->insert($wpdb->prefix.'tutor_quiz_attempts', $attempt_data);
168 $attempt_id = (int) $wpdb->insert_id;
169
170 wp_redirect(get_permalink($quiz_id));
171 die();
172 }
173
174 public function answering_quiz(){
175 if ( tutils()->array_get('tutor_action', $_POST) !== 'tutor_answering_quiz_question' ){
176 return;
177 }
178 //Checking nonce
179 tutor_utils()->checking_nonce();
180
181 $attempt_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('attempt_id', $_POST));
182 $attempt = tutor_utils()->get_attempt($attempt_id);
183
184 $attempt_answers = isset($_POST['attempt']) ? $_POST['attempt'] : false;
185 if ( ! is_user_logged_in()){
186 die('Please sign in to do this operation');
187 }
188
189 global $wpdb;
190 $user_id = get_current_user_id();
191
192 do_action('tutor_quiz/attempt_analysing/before', $attempt_id);
193
194 if ($attempt_answers && is_array($attempt_answers) && count($attempt_answers)){
195 foreach ($attempt_answers as $attempt_id => $attempt_answers){
196
197 /**
198 * Get total marks of all question comes
199 */
200 $question_ids = tutor_utils()->avalue_dot('quiz_question_ids', $attempt_answers);
201 if (is_array($question_ids) && count($question_ids)){
202 $question_ids_string = "'".implode("','", $question_ids)."'";
203 $total_question_marks = $wpdb->get_var("SELECT SUM(question_mark) FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN({$question_ids_string}) ;");
204 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', array('total_marks' =>$total_question_marks ), array('attempt_id' => $attempt_id ));
205 }
206
207 if ( ! $attempt || $user_id != $attempt->user_id){
208 die('Operation not allowed, attempt not found or permission denied');
209 }
210
211 $quiz_answers = tutor_utils()->avalue_dot('quiz_question', $attempt_answers);
212
213 $total_marks = 0;
214
215 if ( tutils()->count($quiz_answers)) {
216
217 foreach ( $quiz_answers as $question_id => $answers ) {
218 $question = tutor_utils()->get_quiz_question_by_id( $question_id );
219 $question_type = $question->question_type;
220
221 $is_answer_was_correct = false;
222 $given_answer = '';
223
224 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
225
226 $given_answer = $answers;
227 $is_answer_was_correct = (bool) $wpdb->get_var( "SELECT is_correct FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answers} " );
228
229 } elseif ( $question_type === 'multiple_choice' ) {
230
231 $given_answer = maybe_serialize( $answers );
232 $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 ;" );
233 if ( maybe_serialize( $get_original_answers ) == $given_answer ) {
234 $is_answer_was_correct = true;
235 }
236
237 } elseif ( $question_type === 'fill_in_the_blank' ) {
238
239 $given_answer = (array) array_map( 'sanitize_text_field', $answers );
240 $given_answer = maybe_serialize( $given_answer );
241
242 $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}' ;" );
243 $gap_answer = (array) explode( '|', $get_original_answer->answer_two_gap_match );
244
245 $gap_answer = array_map( 'sanitize_text_field', $gap_answer );
246 if ( $given_answer == maybe_serialize( $gap_answer ) ) {
247 $is_answer_was_correct = true;
248 }
249
250 } elseif ( $question_type === 'open_ended' || $question_type === 'short_answer' ) {
251
252 $given_answer = wp_kses_post( $answers );
253
254 } elseif ( $question_type === 'ordering' || $question_type === 'matching' || $question_type === 'image_matching' ) {
255
256 $given_answer = (array) array_map( 'sanitize_text_field', tutor_utils()->avalue_dot( 'answers', $answers ) );
257 $given_answer = maybe_serialize( $given_answer );
258
259 $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 ;" );
260 $get_original_answers = array_map( 'sanitize_text_field', $get_original_answers );
261
262 if ( $given_answer == maybe_serialize( $get_original_answers ) ) {
263 $is_answer_was_correct = true;
264 }
265
266 } elseif ( $question_type === 'image_answering' ) {
267 echo '<pre>';
268
269 $image_inputs = tutor_utils()->avalue_dot( 'answer_id', $answers );
270 $image_inputs = (array) array_map( 'sanitize_text_field', $image_inputs );
271 $given_answer = maybe_serialize( $image_inputs );
272 $is_answer_was_correct = false;
273
274 $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 =
275 'image_answering' ORDER BY answer_order asc ;" );
276
277 if ( is_array( $db_answer ) && count( $db_answer ) ) {
278 $is_answer_was_correct = ( strtolower( maybe_serialize( array_values( $image_inputs ) ) ) == strtolower( maybe_serialize( $db_answer ) ) );
279 }
280 }
281
282 $question_mark = $is_answer_was_correct ? $question->question_mark : 0;
283 $total_marks += $question_mark;
284
285 $answers_data = array(
286 'user_id' => $user_id,
287 'quiz_id' => $attempt->quiz_id,
288 'question_id' => $question_id,
289 'quiz_attempt_id' => $attempt_id,
290 'given_answer' => $given_answer,
291 'question_mark' => $question->question_mark,
292 'achieved_mark' => $question_mark,
293 'minus_mark' => 0,
294 'is_correct' => $is_answer_was_correct ? 1 : 0,
295 );
296 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_attempt_answers', $answers_data );
297 }
298 }
299
300 $attempt_info = array(
301 'total_answered_questions' => tutils()->count($quiz_answers),
302 'earned_marks' => $total_marks,
303 'attempt_status' => 'attempt_ended',
304 'attempt_ended_at' => date("Y-m-d H:i:s"),
305 );
306 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
307 }
308
309 do_action('tutor_quiz/attempt_ended', $attempt_id);
310 }
311
312 wp_redirect(get_the_permalink($attempt->quiz_id));
313 die();
314 }
315
316
317 /**
318 * Quiz attempt will be finish here
319 *
320 */
321
322 public function finishing_quiz_attempt(){
323 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_finish_quiz_attempt' ){
324 return;
325 }
326 //Checking nonce
327 tutor_utils()->checking_nonce();
328
329 if ( ! is_user_logged_in()){
330 die('Please sign in to do this operation');
331 }
332
333 global $wpdb;
334
335 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
336 $attempt = tutor_utils()->is_started_quiz($quiz_id);
337 $attempt_id = $attempt->attempt_id;
338
339 $attempt_info = array(
340 'total_answered_questions' => 0,
341 'earned_marks' => 0,
342 'attempt_status' => 'attempt_ended',
343 'attempt_ended_at' => date("Y-m-d H:i:s"),
344 );
345
346 do_action('tutor_quiz_before_finish', $attempt_id, $quiz_id, $attempt->user_id);
347 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
348 do_action('tutor_quiz_finished', $attempt_id, $quiz_id, $attempt->user_id);
349
350 wp_redirect(tutor_utils()->input_old('_wp_http_referer'));
351 }
352
353 /**
354 * Quiz timeout by ajax
355 */
356 public function tutor_quiz_timeout(){
357 global $wpdb;
358
359 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
360 $attempt = tutor_utils()->is_started_quiz($quiz_id);
361
362 if ($attempt) {
363 $attempt_id = $attempt->attempt_id;
364
365 $data = array(
366 'attempt_status' => 'attempt_timeout',
367 'attempt_ended_at' => date("Y-m-d H:i:s"),
368 );
369 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $data, array('attempt_id' => $attempt->attempt_id));
370
371 do_action('tutor_quiz_timeout', $attempt_id, $quiz_id, $attempt->user_id);
372
373 wp_send_json_success();
374 }
375
376 wp_send_json_error(__('Quiz has been timeout already', 'tutor'));
377 }
378
379 /**
380 * Review the answer and change individual answer result
381 */
382
383 public function review_quiz_answer(){
384 global $wpdb;
385 $attempt_id = (int) sanitize_text_field($_GET['attempt_id']);
386 $attempt_answer_id = (int) sanitize_text_field($_GET['attempt_answer_id']);
387 $mark_as = sanitize_text_field($_GET['mark_as']);
388
389 $attempt_answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers WHERE attempt_answer_id = {$attempt_answer_id} ");
390 $attempt = tutor_utils()->get_attempt($attempt_id);
391
392 $is_correct = (int) $attempt_answer->is_correct;
393
394 do_action('tutor_quiz_review_answer_before', $attempt_answer_id, $attempt_id, $mark_as);
395
396 if ($mark_as === 'correct' && ! $is_correct){
397
398 $answer_update_data = array(
399 'achieved_mark' => $attempt_answer->question_mark,
400 'is_correct' => 1,
401 );
402 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
403
404 $attempt_update_data = array(
405 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
406 'is_manually_reviewed' => 1,
407 'manually_reviewed_at' => date("Y-m-d H:i:s"),
408 );
409
410 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
411
412 }elseif($mark_as === 'incorrect' && $is_correct){
413
414 $answer_update_data = array(
415 'achieved_mark' => '0.00',
416 'is_correct' => 0,
417 );
418 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
419
420 $attempt_update_data = array(
421 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
422 'is_manually_reviewed' => 1,
423 'manually_reviewed_at' => date("Y-m-d H:i:s"),
424 );
425
426 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
427 }
428 do_action('tutor_quiz_review_answer_after', $attempt_answer_id, $attempt_id, $mark_as);
429
430 if (wp_doing_ajax()){
431 wp_send_json_success();
432 }else{
433 wp_redirect(admin_url("admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=".$attempt_id));
434 }
435
436 die();
437 }
438
439
440 /**
441 * New Design Quiz
442 */
443 public function tutor_create_quiz_and_load_modal(){
444 $topic_id = sanitize_text_field($_POST['topic_id']);
445 $quiz_title = sanitize_text_field($_POST['quiz_title']);
446 $quiz_description = sanitize_text_field($_POST['quiz_description']);
447 $next_order_id = tutor_utils()->get_next_course_content_order_id($topic_id);
448
449 $post_arr = array(
450 'post_type' => 'tutor_quiz',
451 'post_title' => $quiz_title,
452 'post_content' => $quiz_description,
453 'post_status' => 'publish',
454 'post_author' => get_current_user_id(),
455 'post_parent' => $topic_id,
456 'menu_order' => $next_order_id,
457 );
458 $quiz_id = wp_insert_post( $post_arr );
459 do_action('tutor_initial_quiz_created', $quiz_id);
460
461 ob_start();
462 include tutor()->path.'views/modal/edit_quiz.php';
463 $output = ob_get_clean();
464
465 ob_start();
466 ?>
467 <div id="tutor-quiz-<?php echo $quiz_id; ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
468 <div class="tutor-lesson-top">
469 <i class="tutor-icon-move"></i>
470 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
471 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
472 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
473 </div>
474 </div>
475 <?php
476 $output_quiz_row = ob_get_clean();
477
478 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
479 }
480
481 public function tutor_delete_quiz_by_id(){
482 global $wpdb;
483
484 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
485 $post = get_post($quiz_id);
486
487 if ( $post->post_type === 'tutor_quiz'){
488 do_action('tutor_delete_quiz_before', $quiz_id);
489
490 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
491 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
492
493 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$quiz_id} ");
494 if (is_array($questions_ids) && count($questions_ids)){
495 $in_question_ids = "'".implode("','", $questions_ids)."'";
496 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
497 }
498 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
499
500 wp_delete_post($quiz_id, true);
501 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
502
503 do_action('tutor_delete_quiz_after', $quiz_id);
504
505
506 wp_send_json_success();
507 }
508
509 wp_send_json_error();
510 }
511
512 /**
513 * Update Quiz from quiz builder modal
514 *
515 * @since v.1.0.0
516 */
517 public function tutor_quiz_builder_quiz_update(){
518 $quiz_id = sanitize_text_field($_POST['quiz_id']);
519 $topic_id = sanitize_text_field($_POST['topic_id']);
520 $quiz_title = sanitize_text_field($_POST['quiz_title']);
521 $quiz_description = sanitize_text_field($_POST['quiz_description']);
522
523 $post_arr = array(
524 'ID' => $quiz_id,
525 'post_title' => $quiz_title,
526 'post_content' => $quiz_description,
527
528 );
529 $quiz_id = wp_update_post( $post_arr );
530
531 do_action('tutor_quiz_updated', $quiz_id);
532
533 ob_start();
534 ?>
535 <div class="tutor-lesson-top">
536 <i class="tutor-icon-move"></i>
537 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
538 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
539 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
540 </div>
541 <?php
542 $output_quiz_row = ob_get_clean();
543
544 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
545 }
546
547 /**
548 * Load quiz Modal for edit quiz
549 *
550 * @since v.1.0.0
551 */
552 public function tutor_load_edit_quiz_modal(){
553 $quiz_id = sanitize_text_field($_POST['quiz_id']);
554
555 ob_start();
556 include tutor()->path.'views/modal/edit_quiz.php';
557 $output = ob_get_clean();
558
559 wp_send_json_success(array('output' => $output));
560 }
561
562 /**
563 * Load quiz question form for quiz
564 *
565 * @since v.1.0.0
566 */
567 public function tutor_quiz_builder_get_question_form(){
568 global $wpdb;
569 $quiz_id = sanitize_text_field($_POST['quiz_id']);
570 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
571
572 if ( ! $question_id){
573 $next_question_id = tutor_utils()->quiz_next_question_id();
574 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
575
576 $new_question_data = array(
577 'quiz_id' => $quiz_id,
578 'question_title' => __('Question ').$next_question_id,
579 'question_description' => '',
580 'question_type' => 'true_false',
581 'question_mark' => 1,
582 'question_settings' => maybe_serialize(array()),
583 'question_order' => $next_question_order,
584 );
585
586 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
587 $question_id = $wpdb->insert_id;
588 }
589
590 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = {$question_id} ");
591
592 ob_start();
593 include tutor()->path.'views/modal/question_form.php';
594 $output = ob_get_clean();
595
596 wp_send_json_success(array('output' => $output));
597 }
598
599 public function tutor_quiz_modal_update_question(){
600 global $wpdb;
601
602 $question_data = $_POST['tutor_quiz_question'];
603
604 foreach ($question_data as $question_id => $question){
605 $question_title = sanitize_text_field($question['question_title']);
606 $question_description = $question['question_description'];
607 $question_type = $question['question_type'];
608 $question_mark = $question['question_mark'];
609
610 unset($question['question_title']);
611 unset($question['question_description']);
612
613 $data = array(
614 'question_title' => $question_title,
615 'question_description' => $question_description,
616 'question_type' => $question_type,
617 'question_mark' => $question_mark,
618 'question_settings' => maybe_serialize($question),
619 );
620
621 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
622
623
624 /**
625 * Validation
626 */
627 if ($question_type === 'true_false' || $question_type === 'single_choice'){
628 $question_options = tutils()->get_answers_by_quiz_question($question_id);
629 if (tutils()->count($question_options)){
630 $required_validate = true;
631 foreach ($question_options as $question_option){
632 if ($question_option->is_correct){
633 $required_validate = false;
634 }
635 }
636 if ($required_validate){
637 $validation_msg = "<p class='tutor-error-msg'>".__('Please select the correct answer', 'tutor')."</p>";
638 wp_send_json_error(array('validation_msg' => $validation_msg ));
639 }
640 }else{
641 $validation_msg = "<p class='tutor-error-msg'>".__('Please make sure you have added more than one option and saved them', 'tutor')."</p>";
642 wp_send_json_error(array('validation_msg' => $validation_msg ));
643 }
644 }
645 }
646
647 wp_send_json_success();
648 }
649
650 public function tutor_quiz_builder_question_delete(){
651 global $wpdb;
652
653 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
654 if ($question_id){
655 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => $question_id));
656 }
657
658 wp_send_json_success();
659 }
660
661 /**
662 * Get answers options form for quiz question
663 *
664 * @since v.1.0.0
665 */
666 public function tutor_quiz_add_question_answers(){
667 $question_id = sanitize_text_field($_POST['question_id']);
668 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
669 $question_type = $question['question_type'];
670
671 ob_start();
672 include tutor()->path.'views/modal/question_answer_form.php';
673 $output = ob_get_clean();
674
675 wp_send_json_success(array('output' => $output));
676 }
677
678 /**
679 * Edit Answer Form
680 *
681 * @since v.1.0.0
682 */
683 public function tutor_quiz_edit_question_answer(){
684 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
685 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
686 foreach ($old_answer as $old_answer);
687 $question_id = $old_answer->belongs_question_id;
688 $question_type = $old_answer->belongs_question_type;
689
690 ob_start();
691 include tutor()->path.'views/modal/question_answer_edit_form.php';
692 $output = ob_get_clean();
693
694 wp_send_json_success(array('output' => $output));
695 }
696
697 public function tutor_save_quiz_answer_options(){
698 global $wpdb;
699
700 $questions = $_POST['tutor_quiz_question'];
701 $answers = $_POST['quiz_answer'];
702
703 foreach ($answers as $question_id => $answer){
704 $question = tutor_utils()->avalue_dot($question_id, $questions);
705 $question_type = $question['question_type'];
706
707 //Getting next sorting order
708 $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}' ");
709 $next_order_id = $next_order_id + 1;
710
711 if ($question){
712 if ($question_type === 'true_false'){
713 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
714 $data_true_false = array(
715 array(
716 'belongs_question_id' => $question_id,
717 'belongs_question_type' => $question_type,
718 'answer_title' => __('True', 'tutor'),
719 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
720 'answer_two_gap_match' => 'true',
721 ),
722 array(
723 'belongs_question_id' => $question_id,
724 'belongs_question_type' => $question_type,
725 'answer_title' => __('False', 'tutor'),
726 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
727 'answer_two_gap_match' => 'false',
728 ),
729 );
730
731 foreach ($data_true_false as $true_false_data){
732 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
733 }
734
735 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
736 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
737
738 $answer_data = array(
739 'belongs_question_id' => $question_id,
740 'belongs_question_type' => $question_type,
741 'answer_title' => $answer['answer_title'],
742 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
743 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
744 'answer_order' => $next_order_id,
745 );
746 if (isset($answer['matched_answer_title'])){
747 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
748 }
749
750 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
751
752 }elseif($question_type === 'fill_in_the_blank'){
753 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
754 $answer_data = array(
755 'belongs_question_id' => $question_id,
756 'belongs_question_type' => $question_type,
757 'answer_title' => $answer['answer_title'],
758 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null,
759 );
760 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
761 }
762 }
763 }
764
765 wp_send_json_success();
766 }
767
768 /**
769 * Tutor Update Answer
770 *
771 * @since v.1.0.0
772 */
773 public function tutor_update_quiz_answer_options(){
774 global $wpdb;
775
776 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
777 $questions = $_POST['tutor_quiz_question'];
778 $answers = $_POST['quiz_answer'];
779
780 foreach ($answers as $question_id => $answer){
781 $question = tutor_utils()->avalue_dot($question_id, $questions);
782 $question_type = $question['question_type'];
783
784 if ($question){
785 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' ){
786
787 $answer_data = array(
788 'belongs_question_id' => $question_id,
789 'belongs_question_type' => $question_type,
790 'answer_title' => $answer['answer_title'],
791 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
792 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : '',
793 );
794 if (isset($answer['matched_answer_title'])){
795 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
796 }
797
798 if ($question_type === 'fill_in_the_blank'){
799 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? strtolower(trim($answer['answer_two_gap_match'])) : null;
800 }
801
802 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
803 }
804 }
805 }
806
807 //die(print_r($_POST));
808 wp_send_json_success();
809 }
810
811 public function tutor_quiz_builder_get_answers_by_question(){
812 global $wpdb;
813 $question_id = sanitize_text_field($_POST['question_id']);
814 $question_type = sanitize_text_field($_POST['question_type']);
815
816 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = {$question_id} ");
817 $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 ;");
818
819 ob_start();
820
821 /*
822 * @TODO: Should We remove this text?
823 * Screenshot: http://prntscr.com/ngl2mb
824 * Screenshot: http://prntscr.com/ngl2ak
825 */
826
827 switch ($question_type){
828 case 'true_false':
829 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
830 break;
831 case 'ordering':
832 echo '<label>'.__('Make sure you’re saving the answers in the right order. Students will have to match this order exactly.', 'tutor').'</label>';
833 break;
834 }
835
836 if (is_array($answers) && count($answers)){
837 foreach ($answers as $answer){
838 ?>
839 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
840 <div class="tutor-quiz-answer">
841 <span class="tutor-quiz-answer-title">
842 <?php
843 echo stripslashes($answer->answer_title);
844 if ($answer->belongs_question_type === 'fill_in_the_blank'){
845 echo ' ('.__('Answer', 'tutor').' : ';
846 echo "<strong>{$answer->answer_two_gap_match} </strong>)";
847 }
848 if ($answer->belongs_question_type === 'matching'){
849 echo " - {$answer->answer_two_gap_match}";
850 }
851 ?>
852 </span>
853
854 <?php
855 if ($answer->image_id){
856 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
857 }
858 if ($question_type === 'true_false' || $question_type === 'single_choice'){
859 ?>
860 <span class="tutor-quiz-answers-mark-correct-wrap">
861 <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); ?> >
862 </span>
863 <?php
864 }elseif ($question_type === 'multiple_choice'){
865 ?>
866 <span class="tutor-quiz-answers-mark-correct-wrap">
867 <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); ?> >
868 </span>
869 <?php
870 }
871 ?>
872 <span class="tutor-quiz-answer-edit">
873 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
874 </span>
875 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
876 </div>
877
878 <div class="tutor-quiz-answer-trash-wrap">
879 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
880 </div>
881 </div>
882 <?php
883 }
884 }
885 $output = ob_get_clean();
886
887 wp_send_json_success(array('output' => $output));
888 }
889
890 public function tutor_quiz_builder_delete_answer(){
891 global $wpdb;
892 $answer_id = sanitize_text_field($_POST['answer_id']);
893
894 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => $answer_id));
895 wp_send_json_success();
896 }
897
898 /**
899 * Save quiz questions sorting
900 */
901 public function tutor_quiz_question_sorting(){
902 global $wpdb;
903
904 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
905 if (is_array($question_ids) && count($question_ids) ){
906 $i = 0;
907 foreach ($question_ids as $key => $question_id){
908 $i++;
909 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
910 }
911 }
912 }
913
914 /**
915 * Save sorting data for quiz answers
916 */
917 public function tutor_quiz_answer_sorting(){
918 global $wpdb;
919
920 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
921 $answer_ids = $_POST['sorted_answer_ids'];
922 $i = 0;
923 foreach ($answer_ids as $key => $answer_id){
924 $i++;
925 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
926 }
927 }
928
929 }
930
931 /**
932 * Mark answer as correct
933 */
934
935 public function tutor_mark_answer_as_correct(){
936 global $wpdb;
937
938 $answer_id = sanitize_text_field($_POST['answer_id']);
939 $inputValue = sanitize_text_field($_POST['inputValue']);
940
941 $answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} LIMIT 0,1 ;");
942 if ($answer->belongs_question_type === 'single_choice'){
943 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => $answer->belongs_question_id));
944 }
945 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => $inputValue), array('answer_id' => $answer_id));
946 }
947
948 /**
949 * Update quiz settings from modal
950 *
951 * @since : v.1.0.0
952 */
953 public function tutor_quiz_modal_update_settings(){
954 $quiz_id = sanitize_text_field($_POST['quiz_id']);
955
956 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
957 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
958
959 do_action('tutor_quiz_settings_updated', $quiz_id);
960
961 wp_send_json_success();
962 }
963
964
965 //=========================//
966 // Front end stuffs
967 //=========================//
968
969 /**
970 * Rendering quiz for frontend
971 *
972 * @since v.1.0.0
973 */
974
975 public function tutor_render_quiz_content(){
976 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
977
978 ob_start();
979 global $post;
980
981 $post = get_post($quiz_id);
982 setup_postdata($post);
983 //tutor_lesson_content();
984
985 single_quiz_contents();
986
987 wp_reset_postdata();
988
989
990 $html = ob_get_clean();
991 wp_send_json_success(array('html' => $html));
992 }
993
994
995 }