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