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