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