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