PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 1.5.9
Tutor LMS – eLearning and online course solution v1.5.9
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
983 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 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
473 </div>
474 </div>
475 <?php
476 $output_quiz_row = ob_get_clean();
477
478 wp_send_json_success(array('output' => $output, 'output_quiz_row' => $output_quiz_row));
479 }
480
481 public function tutor_delete_quiz_by_id(){
482 global $wpdb;
483
484 $quiz_id = (int) sanitize_text_field($_POST['quiz_id']);
485 $post = get_post($quiz_id);
486
487 if ( $post->post_type === 'tutor_quiz'){
488 do_action('tutor_delete_quiz_before', $quiz_id);
489
490 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempts', array('quiz_id' => $quiz_id));
491 $wpdb->delete($wpdb->prefix.'tutor_quiz_attempt_answers', array('quiz_id' => $quiz_id));
492
493 $questions_ids = $wpdb->get_col("SELECT question_id FROM {$wpdb->prefix}tutor_quiz_questions WHERE quiz_id = {$quiz_id} ");
494 if (is_array($questions_ids) && count($questions_ids)){
495 $in_question_ids = "'".implode("','", $questions_ids)."'";
496 $wpdb->query("DELETE FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE belongs_question_id IN({$in_question_ids}) ");
497 }
498 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('quiz_id' => $quiz_id));
499
500 wp_delete_post($quiz_id, true);
501 delete_post_meta($quiz_id, '_tutor_course_id_for_lesson');
502
503 do_action('tutor_delete_quiz_after', $quiz_id);
504
505
506 wp_send_json_success();
507 }
508
509 wp_send_json_error();
510 }
511
512 /**
513 * Update Quiz from quiz builder modal
514 *
515 * @since v.1.0.0
516 */
517 public function tutor_quiz_builder_quiz_update(){
518 $quiz_id = sanitize_text_field($_POST['quiz_id']);
519 $topic_id = sanitize_text_field($_POST['topic_id']);
520 $quiz_title = sanitize_text_field($_POST['quiz_title']);
521 $quiz_description = sanitize_text_field($_POST['quiz_description']);
522
523 $post_arr = array(
524 'ID' => $quiz_id,
525 'post_title' => $quiz_title,
526 'post_content' => $quiz_description,
527
528 );
529 $quiz_id = wp_update_post( $post_arr );
530
531 do_action('tutor_quiz_updated', $quiz_id);
532
533 ob_start();
534 ?>
535 <div class="tutor-lesson-top">
536 <i class="tutor-icon-move"></i>
537 <a href="javascript:;" class="open-tutor-quiz-modal" data-quiz-id="<?php echo $quiz_id; ?>" data-topic-id="<?php echo $topic_id;
538 ?>"> <i class=" tutor-icon-doubt"></i>[QUIZ] <?php echo $quiz_title; ?> </a>
539 <a href="javascript:;" class="tutor-delete-quiz-btn" data-quiz-id="<?php echo $quiz_id; ?>"><i class="tutor-icon-garbage"></i></a>
540 </div>
541 <?php
542 $output_quiz_row = ob_get_clean();
543
544 wp_send_json_success(array('output_quiz_row' => $output_quiz_row));
545 }
546
547 /**
548 * Load quiz Modal for edit quiz
549 *
550 * @since v.1.0.0
551 */
552 public function tutor_load_edit_quiz_modal(){
553 $quiz_id = sanitize_text_field($_POST['quiz_id']);
554
555 ob_start();
556 include tutor()->path.'views/modal/edit_quiz.php';
557 $output = ob_get_clean();
558
559 wp_send_json_success(array('output' => $output));
560 }
561
562 /**
563 * Load quiz question form for quiz
564 *
565 * @since v.1.0.0
566 */
567 public function tutor_quiz_builder_get_question_form(){
568 global $wpdb;
569 $quiz_id = sanitize_text_field($_POST['quiz_id']);
570 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
571
572 if ( ! $question_id){
573 $next_question_id = tutor_utils()->quiz_next_question_id();
574 $next_question_order = tutor_utils()->quiz_next_question_order_id($quiz_id);
575
576 $new_question_data = array(
577 'quiz_id' => $quiz_id,
578 'question_title' => __('Question ').$next_question_id,
579 'question_description' => '',
580 'question_type' => 'true_false',
581 'question_mark' => 1,
582 'question_settings' => maybe_serialize(array()),
583 'question_order' => $next_question_order,
584 );
585
586 $wpdb->insert($wpdb->prefix.'tutor_quiz_questions', $new_question_data);
587 $question_id = $wpdb->insert_id;
588 }
589
590 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions where question_id = {$question_id} ");
591
592 ob_start();
593 include tutor()->path.'views/modal/question_form.php';
594 $output = ob_get_clean();
595
596 wp_send_json_success(array('output' => $output));
597 }
598
599 public function tutor_quiz_modal_update_question(){
600 global $wpdb;
601
602 $question_data = $_POST['tutor_quiz_question'];
603
604 foreach ($question_data as $question_id => $question){
605 $question_title = sanitize_text_field($question['question_title']);
606 $question_description = $question['question_description'];
607 $question_type = $question['question_type'];
608 $question_mark = $question['question_mark'];
609
610 unset($question['question_title']);
611 unset($question['question_description']);
612
613 $data = array(
614 'question_title' => $question_title,
615 'question_description' => $question_description,
616 'question_type' => $question_type,
617 'question_mark' => $question_mark,
618 'question_settings' => maybe_serialize($question),
619 );
620
621 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', $data, array('question_id' => $question_id) );
622
623
624 /**
625 * Validation
626 */
627 if ($question_type === 'true_false' || $question_type === 'single_choice'){
628 $question_options = tutils()->get_answers_by_quiz_question($question_id);
629 if (tutils()->count($question_options)){
630 $required_validate = true;
631 foreach ($question_options as $question_option){
632 if ($question_option->is_correct){
633 $required_validate = false;
634 }
635 }
636 if ($required_validate){
637 $validation_msg = "<p class='tutor-error-msg'>".__('Please select the correct answer', 'tutor')."</p>";
638 wp_send_json_error(array('validation_msg' => $validation_msg ));
639 }
640 }else{
641 $validation_msg = "<p class='tutor-error-msg'>".__('Please make sure you have added more than one option and saved them', 'tutor')."</p>";
642 wp_send_json_error(array('validation_msg' => $validation_msg ));
643 }
644 }
645 }
646
647 wp_send_json_success();
648 }
649
650 public function tutor_quiz_builder_question_delete(){
651 global $wpdb;
652
653 $question_id = sanitize_text_field(tutor_utils()->avalue_dot('question_id', $_POST));
654 if ($question_id){
655 $wpdb->delete($wpdb->prefix.'tutor_quiz_questions', array('question_id' => $question_id));
656 }
657
658 wp_send_json_success();
659 }
660
661 /**
662 * Get answers options form for quiz question
663 *
664 * @since v.1.0.0
665 */
666 public function tutor_quiz_add_question_answers(){
667 $question_id = sanitize_text_field($_POST['question_id']);
668 $question = tutor_utils()->avalue_dot($question_id, $_POST['tutor_quiz_question']);
669 $question_type = $question['question_type'];
670
671 ob_start();
672 include tutor()->path.'views/modal/question_answer_form.php';
673 $output = ob_get_clean();
674
675 wp_send_json_success(array('output' => $output));
676 }
677
678 /**
679 * Edit Answer Form
680 *
681 * @since v.1.0.0
682 */
683 public function tutor_quiz_edit_question_answer(){
684 $answer_id = (int) sanitize_text_field($_POST['answer_id']);
685 $old_answer = tutor_utils()->get_answer_by_id($answer_id);
686 foreach ($old_answer as $old_answer);
687 $question_id = $old_answer->belongs_question_id;
688 $question_type = $old_answer->belongs_question_type;
689
690 ob_start();
691 include tutor()->path.'views/modal/question_answer_edit_form.php';
692 $output = ob_get_clean();
693
694 wp_send_json_success(array('output' => $output));
695 }
696
697 public function tutor_save_quiz_answer_options(){
698 global $wpdb;
699
700 $questions = $_POST['tutor_quiz_question'];
701 $answers = $_POST['quiz_answer'];
702
703 foreach ($answers as $question_id => $answer){
704 $question = tutor_utils()->avalue_dot($question_id, $questions);
705 $question_type = $question['question_type'];
706
707 //Getting next sorting order
708 $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}' ");
709 $next_order_id = $next_order_id + 1;
710
711 if ($question){
712 if ($question_type === 'true_false'){
713 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
714 $data_true_false = array(
715 array(
716 'belongs_question_id' => $question_id,
717 'belongs_question_type' => $question_type,
718 'answer_title' => __('True', 'tutor'),
719 'is_correct' => $answer['true_false'] == 'true' ? 1 : 0,
720 'answer_two_gap_match' => 'true',
721 ),
722 array(
723 'belongs_question_id' => $question_id,
724 'belongs_question_type' => $question_type,
725 'answer_title' => __('False', 'tutor'),
726 'is_correct' => $answer['true_false'] == 'false' ? 1 : 0,
727 'answer_two_gap_match' => 'false',
728 ),
729 );
730
731 foreach ($data_true_false as $true_false_data){
732 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $true_false_data);
733 }
734
735 }elseif($question_type === 'multiple_choice' || $question_type === 'single_choice' || $question_type === 'ordering' ||
736 $question_type === 'matching' || $question_type === 'image_matching' || $question_type === 'image_answering' ){
737
738 $answer_data = array(
739 'belongs_question_id' => $question_id,
740 'belongs_question_type' => $question_type,
741 'answer_title' => $answer['answer_title'],
742 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
743 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : 0,
744 'answer_order' => $next_order_id,
745 );
746 if (isset($answer['matched_answer_title'])){
747 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
748 }
749
750 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
751
752 }elseif($question_type === 'fill_in_the_blank'){
753 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('belongs_question_id' => $question_id, 'belongs_question_type' => $question_type));
754 $answer_data = array(
755 'belongs_question_id' => $question_id,
756 'belongs_question_type' => $question_type,
757 'answer_title' => $answer['answer_title'],
758 'answer_two_gap_match' => isset($answer['answer_two_gap_match']) ? trim($answer['answer_two_gap_match']) : null,
759 );
760 $wpdb->insert($wpdb->prefix.'tutor_quiz_question_answers', $answer_data);
761 }
762 }
763 }
764
765 wp_send_json_success();
766 }
767
768 /**
769 * Tutor Update Answer
770 *
771 * @since v.1.0.0
772 */
773 public function tutor_update_quiz_answer_options(){
774 global $wpdb;
775
776 $answer_id = (int) sanitize_text_field($_POST['tutor_quiz_answer_id']);
777 $questions = $_POST['tutor_quiz_question'];
778 $answers = $_POST['quiz_answer'];
779
780 foreach ($answers as $question_id => $answer){
781 $question = tutor_utils()->avalue_dot($question_id, $questions);
782 $question_type = $question['question_type'];
783
784 if ($question){
785 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' ){
786
787 $answer_data = array(
788 'belongs_question_id' => $question_id,
789 'belongs_question_type' => $question_type,
790 'answer_title' => $answer['answer_title'],
791 'image_id' => isset($answer['image_id']) ? $answer['image_id'] : 0,
792 'answer_view_format' => isset($answer['answer_view_format']) ? $answer['answer_view_format'] : '',
793 );
794 if (isset($answer['matched_answer_title'])){
795 $answer_data['answer_two_gap_match'] = $answer['matched_answer_title'];
796 }
797
798 if ($question_type === 'fill_in_the_blank'){
799 $answer_data['answer_two_gap_match'] = isset($answer['answer_two_gap_match']) ? trim($answer['answer_two_gap_match']) : null;
800 }
801
802 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', $answer_data, array('answer_id' => $answer_id));
803 }
804 }
805 }
806
807 //die(print_r($_POST));
808 wp_send_json_success();
809 }
810
811 public function tutor_quiz_builder_get_answers_by_question(){
812 global $wpdb;
813 $question_id = sanitize_text_field($_POST['question_id']);
814 $question_type = sanitize_text_field($_POST['question_type']);
815
816 $question = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_questions WHERE question_id = {$question_id} ");
817 $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 ;");
818
819 ob_start();
820
821 switch ($question_type){
822 case 'true_false':
823 echo '<label>'.__('Answer options &amp; mark correct', 'tutor').'</label>';
824 break;
825 case 'ordering':
826 echo '<label>'.__('Make sure you’re saving the answers in the right order. Students will have to match this order exactly.', 'tutor').'</label>';
827 break;
828 }
829
830 if (is_array($answers) && count($answers)){
831 foreach ($answers as $answer){
832 ?>
833 <div class="tutor-quiz-answer-wrap" data-answer-id="<?php echo $answer->answer_id; ?>">
834 <div class="tutor-quiz-answer">
835 <span class="tutor-quiz-answer-title">
836 <?php
837 echo stripslashes($answer->answer_title);
838 if ($answer->belongs_question_type === 'fill_in_the_blank'){
839 echo ' ('.__('Answer', 'tutor').' : ';
840 echo "<strong>{$answer->answer_two_gap_match} </strong>)";
841 }
842 if ($answer->belongs_question_type === 'matching'){
843 echo " - {$answer->answer_two_gap_match}";
844 }
845 ?>
846 </span>
847
848 <?php
849 if ($answer->image_id){
850 echo '<span class="tutor-question-answer-image"><img src="'.wp_get_attachment_image_url($answer->image_id).'" /> </span>';
851 }
852 if ($question_type === 'true_false' || $question_type === 'single_choice'){
853 ?>
854 <span class="tutor-quiz-answers-mark-correct-wrap">
855 <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); ?> >
856 </span>
857 <?php
858 }elseif ($question_type === 'multiple_choice'){
859 ?>
860 <span class="tutor-quiz-answers-mark-correct-wrap">
861 <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); ?> >
862 </span>
863 <?php
864 }
865 ?>
866 <span class="tutor-quiz-answer-edit">
867 <a href="javascript:;"><i class="tutor-icon-pencil"></i> </a>
868 </span>
869 <span class="tutor-quiz-answer-sort-icon"><i class="tutor-icon-menu-2"></i> </span>
870 </div>
871
872 <div class="tutor-quiz-answer-trash-wrap">
873 <a href="javascript:;" class="answer-trash-btn" data-answer-id="<?php echo $answer->answer_id; ?>"><i class="tutor-icon-garbage"></i> </a>
874 </div>
875 </div>
876 <?php
877 }
878 }
879 $output = ob_get_clean();
880
881 wp_send_json_success(array('output' => $output));
882 }
883
884 public function tutor_quiz_builder_delete_answer(){
885 global $wpdb;
886 $answer_id = sanitize_text_field($_POST['answer_id']);
887
888 $wpdb->delete($wpdb->prefix.'tutor_quiz_question_answers', array('answer_id' => $answer_id));
889 wp_send_json_success();
890 }
891
892 /**
893 * Save quiz questions sorting
894 */
895 public function tutor_quiz_question_sorting(){
896 global $wpdb;
897
898 $question_ids = tutor_utils()->avalue_dot('sorted_question_ids', $_POST);
899 if (is_array($question_ids) && count($question_ids) ){
900 $i = 0;
901 foreach ($question_ids as $key => $question_id){
902 $i++;
903 $wpdb->update($wpdb->prefix.'tutor_quiz_questions', array('question_order' => $i), array('question_id' => $question_id));
904 }
905 }
906 }
907
908 /**
909 * Save sorting data for quiz answers
910 */
911 public function tutor_quiz_answer_sorting(){
912 global $wpdb;
913
914 if ( ! empty($_POST['sorted_answer_ids']) && is_array($_POST['sorted_answer_ids']) && count($_POST['sorted_answer_ids']) ){
915 $answer_ids = $_POST['sorted_answer_ids'];
916 $i = 0;
917 foreach ($answer_ids as $key => $answer_id){
918 $i++;
919 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('answer_order' => $i), array('answer_id' => $answer_id));
920 }
921 }
922 }
923
924 /**
925 * Mark answer as correct
926 */
927
928 public function tutor_mark_answer_as_correct(){
929 global $wpdb;
930
931 $answer_id = sanitize_text_field($_POST['answer_id']);
932 $inputValue = sanitize_text_field($_POST['inputValue']);
933
934 $answer = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}tutor_quiz_question_answers WHERE answer_id = {$answer_id} LIMIT 0,1 ;");
935 if ($answer->belongs_question_type === 'single_choice'){
936 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => 0), array('belongs_question_id' => $answer->belongs_question_id));
937 }
938 $wpdb->update($wpdb->prefix.'tutor_quiz_question_answers', array('is_correct' => $inputValue), array('answer_id' => $answer_id));
939 }
940
941 /**
942 * Update quiz settings from modal
943 *
944 * @since : v.1.0.0
945 */
946 public function tutor_quiz_modal_update_settings(){
947 $quiz_id = sanitize_text_field($_POST['quiz_id']);
948 $quiz_option = tutor_utils()->sanitize_array($_POST['quiz_option']);
949
950 update_post_meta($quiz_id, 'tutor_quiz_option', $quiz_option);
951 do_action('tutor_quiz_settings_updated', $quiz_id);
952 wp_send_json_success();
953 }
954
955
956 //=========================//
957 // Front end stuffs
958 //=========================//
959
960 /**
961 * Rendering quiz for frontend
962 *
963 * @since v.1.0.0
964 */
965
966 public function tutor_render_quiz_content(){
967 $quiz_id = (int) sanitize_text_field(tutor_utils()->avalue_dot('quiz_id', $_POST));
968
969 ob_start();
970 global $post;
971
972 $post = get_post($quiz_id);
973 setup_postdata($post);
974 //tutor_lesson_content();
975
976 single_quiz_contents();
977 wp_reset_postdata();
978
979 $html = ob_get_clean();
980 wp_send_json_success(array('html' => $html));
981 }
982
983 }