PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.7.1
Tutor LMS – eLearning and online course solution v1.7.1
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Quiz.php
tutor / classes Last commit date
Addons.php 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 Custom_Validation.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
1039 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
188 if ( tutils()->array_get('tutor_action', $_POST) !== 'tutor_answering_quiz_question' ){
189 return;
190 }
191 //Checking nonce
192 tutor_utils()->checking_nonce();
193
194 $attempt_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('attempt_id', $_POST));
195 $attempt = tutor_utils()->get_attempt($attempt_id);
196
197 $attempt_answers = isset($_POST['attempt']) ? $_POST['attempt'] : false;
198 if ( ! is_user_logged_in()){
199 die('Please sign in to do this operation');
200 }
201
202 global $wpdb;
203 $user_id = get_current_user_id();
204
205 do_action('tutor_quiz/attempt_analysing/before', $attempt_id);
206
207 if ($attempt_answers && is_array($attempt_answers) && count($attempt_answers)){
208 foreach ($attempt_answers as $attempt_id => $attempt_answer){
209
210 /**
211 * Get total marks of all question comes
212 */
213 $question_ids = tutor_utils()->avalue_dot('quiz_question_ids', $attempt_answer);
214 if (is_array($question_ids) && count($question_ids)){
215 $question_ids_string = "'".implode("','", $question_ids)."'";
216 $total_question_marks = $wpdb->get_var("SELECT SUM(question_mark) FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id IN({$question_ids_string}) ;");
217 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', array('total_marks' =>$total_question_marks ), array('attempt_id' => $attempt_id ));
218 }
219
220 if ( ! $attempt || $user_id != $attempt->user_id){
221 die('Operation not allowed, attempt not found or permission denied');
222 }
223
224 $quiz_answers = tutor_utils()->avalue_dot('quiz_question', $attempt_answer);
225
226 $total_marks = 0;
227 $review_required = false;
228
229 if ( tutils()->count($quiz_answers)) {
230
231 foreach ( $quiz_answers as $question_id => $answers ) {
232 $question = tutor_utils()->get_quiz_question_by_id( $question_id );
233 $question_type = $question->question_type;
234
235 $is_answer_was_correct = false;
236 $given_answer = '';
237
238 if ( $question_type === 'true_false' || $question_type === 'single_choice' ) {
239
240 $given_answer = $answers;
241 $is_answer_was_correct = (bool) $wpdb->get_var( "SELECT is_correct FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answers} " );
242
243 } elseif ( $question_type === 'multiple_choice' ) {
244
245 $given_answer = (array) ( $answers );
246 $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 ;" );
247 if (count(array_diff($get_original_answers, $given_answer)) === 0 && count($get_original_answers) === count($given_answer)) {
248 $is_answer_was_correct = true;
249 }
250 $given_answer = maybe_serialize( $answers );
251
252 } elseif ( $question_type === 'fill_in_the_blank' ) {
253
254 $given_answer = (array) array_map( 'sanitize_text_field', $answers );
255 $given_answer = maybe_serialize( $given_answer );
256
257 $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}' ;" );
258 $gap_answer = (array) explode( '|', $get_original_answer->answer_two_gap_match );
259
260 $gap_answer = array_map( 'sanitize_text_field', $gap_answer );
261 if ( strtolower($given_answer) == strtolower(maybe_serialize( $gap_answer )) ) {
262 $is_answer_was_correct = true;
263 }
264 } elseif ( $question_type === 'open_ended' || $question_type === 'short_answer' ) {
265 $review_required = true;
266 $given_answer = wp_kses_post( $answers );
267
268 } elseif ( $question_type === 'ordering' || $question_type === 'matching' || $question_type === 'image_matching' ) {
269
270 $given_answer = (array) array_map( 'sanitize_text_field', tutor_utils()->avalue_dot( 'answers', $answers ) );
271 $given_answer = maybe_serialize( $given_answer );
272
273 $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 ;" );
274 $get_original_answers = array_map( 'sanitize_text_field', $get_original_answers );
275
276 if ( $given_answer == maybe_serialize( $get_original_answers ) ) {
277 $is_answer_was_correct = true;
278 }
279
280 } elseif ( $question_type === 'image_answering' ) {
281 $image_inputs = tutor_utils()->avalue_dot( 'answer_id', $answers );
282 $image_inputs = (array) array_map( 'sanitize_text_field', $image_inputs );
283 $given_answer = maybe_serialize( $image_inputs );
284 $is_answer_was_correct = false;
285
286 $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 =
287 'image_answering' ORDER BY answer_order asc ;" );
288
289 if ( is_array( $db_answer ) && count( $db_answer ) ) {
290 $is_answer_was_correct = ( strtolower( maybe_serialize( array_values( $image_inputs ) ) ) == strtolower( maybe_serialize( $db_answer ) ) );
291 }
292 }
293
294 $question_mark = $is_answer_was_correct ? $question->question_mark : 0;
295 $total_marks += $question_mark;
296
297 $answers_data = array(
298 'user_id' => $user_id,
299 'quiz_id' => $attempt->quiz_id,
300 'question_id' => $question_id,
301 'quiz_attempt_id' => $attempt_id,
302 'given_answer' => $given_answer,
303 'question_mark' => $question->question_mark,
304 'achieved_mark' => $question_mark,
305 'minus_mark' => 0,
306 'is_correct' => $is_answer_was_correct ? 1 : 0,
307 );
308
309 /*
310 check if question_type open ended or short ans the set is_correct default value null before saving
311 */
312 if($question_type==="open_ended" || $question_type ==="short_answer")
313 {
314 $answers_data['is_correct'] = NULL;
315 }
316
317 $wpdb->insert( $wpdb->prefix . 'tutor_quiz_attempt_answers', $answers_data );
318 }
319 }
320
321 $attempt_info = array(
322 'total_answered_questions' => tutils()->count($quiz_answers),
323 'earned_marks' => $total_marks,
324 'attempt_status' => 'attempt_ended',
325 'attempt_ended_at' => date("Y-m-d H:i:s", tutor_time()),
326 );
327
328 if ($review_required){
329 $attempt_info['attempt_status'] = 'review_required';
330 }
331
332 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
333 }
334
335 do_action('tutor_quiz/attempt_ended', $attempt_id);
336 }
337
338 wp_redirect(get_the_permalink($attempt->quiz_id));
339 die();
340 }
341
342
343 /**
344 * Quiz attempt will be finish here
345 *
346 */
347
348 public function finishing_quiz_attempt(){
349
350 if ( ! isset($_POST['tutor_action']) || $_POST['tutor_action'] !== 'tutor_finish_quiz_attempt' ){
351 return;
352 }
353 //Checking nonce
354 tutor_utils()->checking_nonce();
355
356 if ( ! is_user_logged_in()){
357 die('Please sign in to do this operation');
358 }
359
360 global $wpdb;
361
362 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
363 $attempt = tutor_utils()->is_started_quiz($quiz_id);
364 $attempt_id = $attempt->attempt_id;
365
366 $attempt_info = array(
367 'total_answered_questions' => 0,
368 'earned_marks' => 0,
369 'attempt_status' => 'attempt_ended',
370 'attempt_ended_at' => date("Y-m-d H:i:s", tutor_time()),
371 );
372
373 do_action('tutor_quiz_before_finish', $attempt_id, $quiz_id, $attempt->user_id);
374 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_info, array('attempt_id' => $attempt_id));
375 do_action('tutor_quiz_finished', $attempt_id, $quiz_id, $attempt->user_id);
376
377 wp_redirect(tutor_utils()->input_old('_wp_http_referer'));
378 }
379
380 /**
381 * Quiz timeout by ajax
382 */
383 public function tutor_quiz_timeout(){
384 global $wpdb;
385
386 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
387 $attempt = tutor_utils()->is_started_quiz($quiz_id);
388
389 if ($attempt) {
390 $attempt_id = $attempt->attempt_id;
391
392 $data = array(
393 'attempt_status' => 'attempt_timeout',
394 'attempt_ended_at' => date("Y-m-d H:i:s", tutor_time()),
395 );
396 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $data, array('attempt_id' => $attempt->attempt_id));
397
398 do_action('tutor_quiz_timeout', $attempt_id, $quiz_id, $attempt->user_id);
399
400 wp_send_json_success();
401 }
402
403 wp_send_json_error(__('Quiz has been timeout already', 'tutor'));
404 }
405
406 /**
407 * Review the answer and change individual answer result
408 */
409
410 public function review_quiz_answer(){
411
412 global $wpdb;
413 $attempt_id = (int) sanitize_text_field($_GET['attempt_id']);
414
415 $attempt_answer_id = (int) sanitize_text_field($_GET['attempt_answer_id']);
416
417 $mark_as = sanitize_text_field($_GET['mark_as']);
418
419 $attempt_answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_attempt_answers WHERE attempt_answer_id = {$attempt_answer_id} ");
420
421 $attempt = tutor_utils()->get_attempt($attempt_id);
422 $question = tutils()->get_quiz_question_by_id($attempt_answer->question_id);
423
424 $previous_ans = $attempt_answer->is_correct;
425
426 do_action('tutor_quiz_review_answer_before', $attempt_answer_id, $attempt_id, $mark_as);
427
428 if ($mark_as === 'correct'){
429
430 $answer_update_data = array(
431 'achieved_mark' => $attempt_answer->question_mark,
432 'is_correct' => 1,
433 );
434 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
435 if($previous_ans ==0 OR $previous_ans ==null)
436 {
437
438 //if previous answer was wrong or in review then add point as correct
439 $attempt_update_data = array(
440 'earned_marks' => $attempt->earned_marks + $attempt_answer->question_mark,
441 'is_manually_reviewed' => 1,
442 'manually_reviewed_at' => date("Y-m-d H:i:s", tutor_time()),
443 );
444
445
446 }
447
448 if ($question->question_type === 'open_ended' || $question->question_type === 'short_answer' ){
449 $attempt_update_data['attempt_status'] = 'attempt_ended';
450 }
451 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
452 }
453 elseif($mark_as === 'incorrect')
454 {
455
456 $answer_update_data = array(
457 'achieved_mark' => '0.00',
458 'is_correct' => 0,
459 );
460 $wpdb->update($wpdb->prefix.'tutor_quiz_attempt_answers', $answer_update_data, array('attempt_answer_id' => $attempt_answer_id ));
461
462
463 if($previous_ans ==1)
464 {
465
466 //if previous ans was right then mynus
467 $attempt_update_data = array(
468 'earned_marks' => $attempt->earned_marks - $attempt_answer->question_mark,
469 'is_manually_reviewed' => 1,
470 'manually_reviewed_at' => date("Y-m-d H:i:s", tutor_time()),
471 );
472
473 }
474 if ($question->question_type === 'open_ended' || $question->question_type === 'short_answer' ){
475 $attempt_update_data['attempt_status'] = 'attempt_ended';
476 }
477
478 $wpdb->update($wpdb->prefix.'tutor_quiz_attempts', $attempt_update_data, array('attempt_id' => $attempt_id ));
479 }
480 do_action('tutor_quiz_review_answer_after', $attempt_answer_id, $attempt_id, $mark_as);
481
482 if (wp_doing_ajax())
483 {
484 wp_send_json_success();
485 }
486 else{
487 wp_redirect(admin_url("admin.php?page=tutor_quiz_attempts&sub_page=view_attempt&attempt_id=".$attempt_id));
488 }
489
490 die();
491 }
492
493
494 /**
495 * New Design Quiz
496 */
497 public function tutor_create_quiz_and_load_modal(){
498 $topic_id = sanitize_text_field($_POST['topic_id']);
499 $quiz_title = sanitize_text_field($_POST['quiz_title']);
500 $quiz_description = sanitize_text_field($_POST['quiz_description']);
501 $next_order_id = tutor_utils()->get_next_course_content_order_id($topic_id);
502
503 $post_arr = array(
504 'post_type' => 'tutor_quiz',
505 'post_title' => $quiz_title,
506 'post_content' => $quiz_description,
507 'post_status' => 'publish',
508 'post_author' => get_current_user_id(),
509 'post_parent' => $topic_id,
510 'menu_order' => $next_order_id,
511 );
512 $quiz_id = wp_insert_post( $post_arr );
513 do_action('tutor_initial_quiz_created', $quiz_id);
514
515 ob_start();
516 include tutor()->path.'views/modal/edit_quiz.php';
517 $output = ob_get_clean();
518
519 ob_start();
520 ?>
521 <div id="tutor-quiz-<?php echo $quiz_id; ?>" class="course-content-item tutor-quiz tutor-quiz-<?php echo $quiz_id; ?>">
522 <div class="tutor-lesson-top">
523 <i class="tutor-icon-move"></i>
524 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
525 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
526 <?php do_action('tutor_course_builder_before_quiz_btn_action', $quiz_id); ?>
527 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
528 </div>
529 </div>
530 <?php
531 $output_quiz_row = ob_get_clean();
532
533 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
534 }
535
536 public function tutor_delete_quiz_by_id(){
537 global $wpdb;
538
539 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
540 $post = get_post($quiz_id);
541
542 if ( $post->post_type === 'tutor_quiz'){
543 do_action('tutor_delete_quiz_before', $quiz_id);
544
545 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
546 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
547
548 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$quiz_id} ");
549 if (is_array($questions_ids) && count($questions_ids)){
550 $in_question_ids = "'".implode("','", $questions_ids)."'";
551 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
552 }
553 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
554
555 wp_delete_post($quiz_id, true);
556 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
557
558 do_action('tutor_delete_quiz_after', $quiz_id);
559
560
561 wp_send_json_success();
562 }
563
564 wp_send_json_error();
565 }
566
567 /**
568 * Update Quiz from quiz builder modal
569 *
570 * @since v.1.0.0
571 */
572 public function tutor_quiz_builder_quiz_update(){
573 $quiz_id = sanitize_text_field($_POST['quiz_id']);
574 $topic_id = sanitize_text_field($_POST['topic_id']);
575 $quiz_title = sanitize_text_field($_POST['quiz_title']);
576 $quiz_description = sanitize_text_field($_POST['quiz_description']);
577
578 $post_arr = array(
579 'ID' => $quiz_id,
580 'post_title' => $quiz_title,
581 'post_content' => $quiz_description,
582
583 );
584 $quiz_id = wp_update_post( $post_arr );
585
586 do_action('tutor_quiz_updated', $quiz_id);
587
588 ob_start();
589 ?>
590 <div class="tutor-lesson-top">
591 <i class="tutor-icon-move"></i>
592 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
593 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
594 <?php do_action('tutor_course_builder_before_quiz_btn_action', $quiz_id); ?>
595 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
596 </div>
597 <?php
598 $output_quiz_row = ob_get_clean();
599
600 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
601 }
602
603 /**
604 * Load quiz Modal for edit quiz
605 *
606 * @since v.1.0.0
607 */
608 public function tutor_load_edit_quiz_modal(){
609 $quiz_id = sanitize_text_field($_POST['quiz_id']);
610
611 ob_start();
612 include tutor()->path.'views/modal/edit_quiz.php';
613 $output = ob_get_clean();
614
615 wp_send_json_success(array('output' => $output));
616 }
617
618 /**
619 * Load quiz question form for quiz
620 *
621 * @since v.1.0.0
622 */
623 public function tutor_quiz_builder_get_question_form(){
624 global $wpdb;
625 $quiz_id = sanitize_text_field($_POST['quiz_id']);
626 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
627
628 if ( ! $question_id){
629 $next_question_id = tutor_utils()->quiz_next_question_id();
630 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
631
632 $new_question_data = array(
633 'quiz_id' => $quiz_id,
634 'question_title' => __('Question ').$next_question_id,
635 'question_description' => '',
636 'question_type' => 'true_false',
637 'question_mark' => 1,
638 'question_settings' => maybe_serialize(array()),
639 'question_order' => $next_question_order,
640 );
641
642 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
643 $question_id = $wpdb->insert_id;
644 }
645
646 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = {$question_id} ");
647
648 ob_start();
649 include tutor()->path.'views/modal/question_form.php';
650 $output = ob_get_clean();
651
652 wp_send_json_success(array('output' => $output));
653 }
654
655 public function tutor_quiz_modal_update_question(){
656 global $wpdb;
657
658 $question_data = $_POST['tutor_quiz_question'];
659
660 foreach ($question_data as $question_id => $question){
661 $question_title = sanitize_text_field($question['question_title']);
662 $question_description = wp_kses_post($question['question_description']);
663 $question_type = $question['question_type'];
664 $question_mark = $question['question_mark'];
665
666 unset($question['question_title']);
667 unset($question['question_description']);
668
669 $data = array(
670 'question_title' => $question_title,
671 'question_description' => $question_description,
672 'question_type' => $question_type,
673 'question_mark' => $question_mark,
674 'question_settings' => maybe_serialize($question),
675 );
676
677 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
678
679
680 /**
681 * Validation
682 */
683 if ($question_type === 'true_false' || $question_type === 'single_choice'){
684 $question_options = tutils()->get_answers_by_quiz_question($question_id);
685 if (tutils()->count($question_options)){
686 $required_validate = true;
687 foreach ($question_options as $question_option){
688 if ($question_option->is_correct){
689 $required_validate = false;
690 }
691 }
692 if ($required_validate){
693 $validation_msg = "<p class='tutor-error-msg'>".__('Please select the correct answer', 'tutor')."</p>";
694 wp_send_json_error(array('validation_msg' => $validation_msg ));
695 }
696 }else{
697 $validation_msg = "<p class='tutor-error-msg'>".__('Please make sure you have added more than one option and saved them', 'tutor')."</p>";
698 wp_send_json_error(array('validation_msg' => $validation_msg ));
699 }
700 }
701 }
702
703 wp_send_json_success();
704 }
705
706 public function tutor_quiz_builder_question_delete(){
707 global $wpdb;
708
709 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
710 if ($question_id){
711 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => $question_id));
712 }
713
714 wp_send_json_success();
715 }
716
717 /**
718 * Get answers options form for quiz question
719 *
720 * @since v.1.0.0
721 */
722 public function tutor_quiz_add_question_answers(){
723 $question_id = sanitize_text_field($_POST['question_id']);
724 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
725 $question_type = $question['question_type'];
726
727 ob_start();
728 include tutor()->path.'views/modal/question_answer_form.php';
729 $output = ob_get_clean();
730
731 wp_send_json_success(array('output' => $output));
732 }
733
734 /**
735 * Edit Answer Form
736 *
737 * @since v.1.0.0
738 */
739 public function tutor_quiz_edit_question_answer(){
740 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
741 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
742 foreach ($old_answer as $old_answer);
743 $question_id = $old_answer->belongs_question_id;
744 $question_type = $old_answer->belongs_question_type;
745
746 ob_start();
747 include tutor()->path.'views/modal/question_answer_edit_form.php';
748 $output = ob_get_clean();
749
750 wp_send_json_success(array('output' => $output));
751 }
752
753 public function tutor_save_quiz_answer_options(){
754 global $wpdb;
755
756 $questions = $_POST['tutor_quiz_question'];
757 $answers = $_POST['quiz_answer'];
758
759 foreach ($answers as $question_id => $answer){
760 $question = tutor_utils()->avalue_dot($question_id, $questions);
761 $question_type = $question['question_type'];
762
763 //Getting next sorting order
764 $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}' ");
765 $next_order_id = $next_order_id + 1;
766
767 if ($question){
768 if ($question_type === 'true_false'){
769 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
770 $data_true_false = array(
771 array(
772 'belongs_question_id' => $question_id,
773 'belongs_question_type' => $question_type,
774 'answer_title' => __('True', 'tutor'),
775 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
776 'answer_two_gap_match' => 'true',
777 ),
778 array(
779 'belongs_question_id' => $question_id,
780 'belongs_question_type' => $question_type,
781 'answer_title' => __('False', 'tutor'),
782 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
783 'answer_two_gap_match' => 'false',
784 ),
785 );
786
787 foreach ($data_true_false as $true_false_data){
788 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
789 }
790
791 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
792 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
793
794 $answer_data = array(
795 'belongs_question_id' => $question_id,
796 'belongs_question_type' => $question_type,
797 'answer_title' => $answer['answer_title'],
798 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
799 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
800 'answer_order' => $next_order_id,
801 );
802 if (isset($answer['matched_answer_title'])){
803 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
804 }
805
806 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
807
808 }elseif($question_type === 'fill_in_the_blank'){
809 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
810 $answer_data = array(
811 'belongs_question_id' => $question_id,
812 'belongs_question_type' => $question_type,
813 'answer_title' => $answer['answer_title'],
814 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? trim($answer['answer_two_gap_match']) : null,
815 );
816 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
817 }
818 }
819 }
820
821 wp_send_json_success();
822 }
823
824 /**
825 * Tutor Update Answer
826 *
827 * @since v.1.0.0
828 */
829 public function tutor_update_quiz_answer_options(){
830 global $wpdb;
831
832 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
833 $questions = $_POST['tutor_quiz_question'];
834 $answers = $_POST['quiz_answer'];
835
836 foreach ($answers as $question_id => $answer){
837 $question = tutor_utils()->avalue_dot($question_id, $questions);
838 $question_type = $question['question_type'];
839
840 if ($question){
841 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' ){
842
843 $answer_data = array(
844 'belongs_question_id' => $question_id,
845 'belongs_question_type' => $question_type,
846 'answer_title' => $answer['answer_title'],
847 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
848 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : '',
849 );
850 if (isset($answer['matched_answer_title'])){
851 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
852 }
853
854 if ($question_type === 'fill_in_the_blank'){
855 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? trim($answer['answer_two_gap_match']) : null;
856 }
857
858 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
859 }
860 }
861 }
862
863 //die(print_r($_POST));
864 wp_send_json_success();
865 }
866
867 public function tutor_quiz_builder_get_answers_by_question(){
868 global $wpdb;
869 $question_id = sanitize_text_field($_POST['question_id']);
870 $question_type = sanitize_text_field($_POST['question_type']);
871
872 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = {$question_id} ");
873 $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 ;");
874
875 ob_start();
876
877 switch ($question_type){
878 case 'true_false':
879 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
880 break;
881 case 'ordering':
882 echo '<label>'.__('Make sure you’re saving the answers in the right order. Students will have to match this order exactly.', 'tutor').'</label>';
883 break;
884 }
885
886 if (is_array($answers) && count($answers)){
887 foreach ($answers as $answer){
888 ?>
889 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
890 <div class="tutor-quiz-answer">
891 <span class="tutor-quiz-answer-title">
892 <?php
893 echo stripslashes($answer->answer_title);
894 if ($answer->belongs_question_type === 'fill_in_the_blank'){
895 echo ' ('.__('Answer', 'tutor').' : ';
896 echo "<strong>{$answer->answer_two_gap_match} </strong>)";
897 }
898 if ($answer->belongs_question_type === 'matching'){
899 echo " - {$answer->answer_two_gap_match}";
900 }
901 ?>
902 </span>
903
904 <?php
905 if ($answer->image_id){
906 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
907 }
908 if ($question_type === 'true_false' || $question_type === 'single_choice'){
909 ?>
910 <span class="tutor-quiz-answers-mark-correct-wrap">
911 <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); ?> >
912 </span>
913 <?php
914 }elseif ($question_type === 'multiple_choice'){
915 ?>
916 <span class="tutor-quiz-answers-mark-correct-wrap">
917 <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); ?> >
918 </span>
919 <?php
920 }
921 ?>
922 <span class="tutor-quiz-answer-edit">
923 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
924 </span>
925 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
926 </div>
927
928 <div class="tutor-quiz-answer-trash-wrap">
929 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
930 </div>
931 </div>
932 <?php
933 }
934 }
935 $output = ob_get_clean();
936
937 wp_send_json_success(array('output' => $output));
938 }
939
940 public function tutor_quiz_builder_delete_answer(){
941 global $wpdb;
942 $answer_id = sanitize_text_field($_POST['answer_id']);
943
944 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => $answer_id));
945 wp_send_json_success();
946 }
947
948 /**
949 * Save quiz questions sorting
950 */
951 public function tutor_quiz_question_sorting(){
952 global $wpdb;
953
954 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
955 if (is_array($question_ids) && count($question_ids) ){
956 $i = 0;
957 foreach ($question_ids as $key => $question_id){
958 $i++;
959 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
960 }
961 }
962 }
963
964 /**
965 * Save sorting data for quiz answers
966 */
967 public function tutor_quiz_answer_sorting(){
968 global $wpdb;
969
970 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
971 $answer_ids = $_POST['sorted_answer_ids'];
972 $i = 0;
973 foreach ($answer_ids as $key => $answer_id){
974 $i++;
975 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
976 }
977 }
978 }
979
980 /**
981 * Mark answer as correct
982 */
983
984 public function tutor_mark_answer_as_correct(){
985 global $wpdb;
986
987 $answer_id = sanitize_text_field($_POST['answer_id']);
988 $inputValue = sanitize_text_field($_POST['inputValue']);
989
990 $answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} LIMIT 0,1 ;");
991 if ($answer->belongs_question_type === 'single_choice'){
992 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => $answer->belongs_question_id));
993 }
994 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => $inputValue), array('answer_id' => $answer_id));
995 }
996
997 /**
998 * Update quiz settings from modal
999 *
1000 * @since : v.1.0.0
1001 */
1002 public function tutor_quiz_modal_update_settings(){
1003 $quiz_id = sanitize_text_field($_POST['quiz_id']);
1004 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
1005
1006 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
1007 do_action('tutor_quiz_settings_updated', $quiz_id);
1008 wp_send_json_success();
1009 }
1010
1011
1012 //=========================//
1013 // Front end stuffs
1014 //=========================//
1015
1016 /**
1017 * Rendering quiz for frontend
1018 *
1019 * @since v.1.0.0
1020 */
1021
1022 public function tutor_render_quiz_content(){
1023 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
1024
1025 ob_start();
1026 global $post;
1027
1028 $post = get_post($quiz_id);
1029 setup_postdata($post);
1030 //tutor_lesson_content();
1031
1032 single_quiz_contents();
1033 wp_reset_postdata();
1034
1035 $html = ob_get_clean();
1036 wp_send_json_success(array('html' => $html));
1037 }
1038
1039 }