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