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