PluginProbe
Watu Quiz / trunk
Watu Quiz vtrunk
3.4.8 trunk 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.5.3 3.4.6 3.4.7
watu / controllers / show_exam.php

show_exam.php in Watu Quiz trunk, at controllers/show_exam.php

377 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4 if(isset($_REQUEST['do']) and $_REQUEST['do']=='show_exam_result' ) $exam_id = intval($_REQUEST['quiz_id']);
5
6 if(!is_singular() and isset($GLOBALS['watu_client_includes_loaded'])) { #If this is in the listing page - and a quiz is already shown, don't show another.
7 printf(__("Please go to <a href='%s'>%s</a> to view the test", 'watu'), get_permalink(), get_the_title());
8 return false;
9 }
10
11 global $wpdb, $user_ID, $post, $achieved;
12 $taker_email = '';
13 $do_redirect = false;
14
15 $appid = get_option('watuproshare_facebook_appid');
16
17 if(!empty($appid)) {
18 echo "<script>
19 window.fbAsyncInit = function() {
20 FB.init({
21 appId : '".$appid."',
22 autoLogAppEvents : true,
23 xfbml : true,
24 version : 'v3.1'
25 });
26 };
27
28 (function(d, s, id){
29 var js, fjs = d.getElementsByTagName(s)[0];
30 if (d.getElementById(id)) {return;}
31 js = d.createElement(s); js.id = id;
32 js.src = \"https://connect.facebook.net/en_US/sdk.js\";
33 fjs.parentNode.insertBefore(js, fjs);
34 }(document, 'script', 'facebook-jssdk'));
35 </script>";
36 }
37
38 // select exam
39 $exam = $wpdb->get_row($wpdb->prepare("SELECT * FROM ".WATU_EXAMS." WHERE ID=%d", $exam_id));
40 if(empty($exam->ID)) return sprintf(__('%s not found.', 'watu'), ucfirst(WATU_QUIZ_WORD));
41 $advanced_settings = unserialize(stripslashes($exam->advanced_settings ?? ''));
42
43 // limited total number of attempts for this test?
44 if(!empty($advanced_settings['total_attempts_limit'])) {
45 $num_attempts = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM ".WATU_TAKINGS." WHERE exam_id=%d", $exam->ID));
46 if($num_attempts >= $advanced_settings['total_attempts_limit']) {
47 if(empty($advanced_settings['total_attempts_limit_message'])) printf(__('This %s is no longer available.', 'watu'), WATU_QUIZ_WORD);
48 else echo stripslashes(rawurldecode($advanced_settings['total_attempts_limit_message']));
49 return false;
50 }
51 } // end checking the total number of attempts
52
53 // requires login?
54 if(!empty($exam->require_login) and !is_user_logged_in()) {
55 echo "<p><b>".sprintf(__('You need to be registered and logged in to take this %s.', 'watu'), __('quiz', 'watu')).
56 " <a href='".wp_login_url(get_permalink( $post->ID ))."'>".__('Log in', 'watu')."</a>";
57 if(get_option("users_can_register")) {
58 echo " ".__('or', 'watu')." <a href='".add_query_arg(array("redirect_to"=> get_permalink( $post->ID ), 'watu_register'=>1), wp_registration_url())."'>".__('Register', 'watu')."</a></b>";
59 }
60 echo "</p>";
61 return false;
62 }
63
64 // is scheduled?
65 if($exam->is_scheduled == 1) {
66 // global schedule
67 $schedule_from = strtotime($exam->schedule_from);
68 $schedule_to = strtotime($exam->schedule_to);
69 if(!watu_check_schedule($schedule_from, $schedule_to)) return false;
70 }
71
72 // Namaste no access due to lesson restriction
73 // If option namaste_access_exam_started_lesson is true, exam cannot be accessed if it is associated to a lesson and the lesson is not started
74 $namaste_lesson_no_access = false;
75 if(class_exists('NamasteLMS') and get_option('namaste_use_exams') == 'watu' and get_option('namaste_access_exam_started_lesson') == 1) {
76 $has_lesson = $wpdb->get_var($wpdb->prepare("SELECT tP.ID FROM {$wpdb->posts} tP
77 JOIN {$wpdb->postmeta} tM ON tM.post_id=tP.ID
78 WHERE tP.post_type = 'namaste_lesson' AND tM.meta_value=%d AND tM.meta_key = 'namaste_required_exam' ", $exam->ID));
79
80 // was lesson started by the student?
81 $started_lesson = false;
82 if($has_lesson) {
83 $started_lesson = $wpdb->get_var($wpdb->prepare("SELECT id FROM ".NAMASTE_STUDENT_LESSONS."
84 WHERE student_id=%d AND lesson_id=%d", $user_ID, $has_lesson));
85 }
86
87 if($has_lesson and !$started_lesson) {
88 $lesson = get_post($has_lesson);
89 $lesson_link = get_permalink($lesson->ID);
90 echo '<p>';
91 printf(__('To access this %s you need first to read lesson <a href="%s">%s</a>.', 'watu'), WATU_QUIZ_WORD, $lesson_link, stripslashes($lesson->post_title));
92 echo '</p>';
93 return false;
94 }
95 }
96 // End Namaste check
97
98
99 // can re-take?
100 if(!empty($exam->require_login) and (empty($exam->take_again) or !empty($exam->times_to_take))) {
101 $cnt_takings=$wpdb->get_var($wpdb->prepare("SELECT COUNT(ID) FROM ".WATU_TAKINGS."
102 WHERE exam_id=%d AND user_id=%d", $exam->ID, $user_ID));
103
104 if(empty($exam->take_again) and $cnt_takings > 0) {
105 printf(__("Sorry, you can take this %s only once!", 'watu'), __('quiz', 'watu'));
106 return false;
107 }
108
109 // multiple times allowed, but number is specified
110 if($exam->times_to_take and $cnt_takings >= $exam->times_to_take) {
111 echo "<p><b>";
112 printf(__("Sorry, you can take this quiz only %d times.", 'watu'), $exam->times_to_take);
113 echo "</b></p>";
114 return false;
115 }
116 }
117
118 $answer_display = get_option('watu_show_answers');
119 if(!isset($exam->show_answers) or $exam->show_answers == 100) $answer_display = $answer_display; // assign the default
120 else $answer_display = $exam->show_answers;
121
122 $order_sql = ($exam->randomize or $exam->pull_random) ? "ORDER BY RAND()" : "ORDER BY sort_order, ID";
123 $limit_sql = $exam->pull_random ? $wpdb->prepare("LIMIT %d", $exam->pull_random) : "";
124
125 $questions = $wpdb->get_results($wpdb->prepare("SELECT * FROM ".WATU_QUESTIONS."
126 WHERE exam_id=%d AND is_inactive=0 $order_sql $limit_sql", $exam_id));
127 $num_questions = 0;
128 foreach($questions as $question) {
129 if(!$question->is_survey) $num_questions++;
130 }
131
132 $all_questions = $wpdb->get_results($wpdb->prepare("SELECT * FROM ".WATU_QUESTIONS." WHERE exam_id = %d AND is_inactive = 0 ", $exam_id));
133
134 if($questions) {
135 if(!isset($GLOBALS['watu_client_includes_loaded']) and !isset($_REQUEST['do']) ) {
136 $GLOBALS['watu_client_includes_loaded'] = true; // Make sure that this code is not loaded more than once.
137 }
138
139 // honeypot validation?
140 if(!empty($exam->use_honeypot) and !empty($_POST['do'])) {
141 if(@$_POST['h_app_id'] != '__' . md5('honeyforme' . $exam->ID)) die('WATU_CAPTCHA:::'.__('No answer to the verification question.', 'watu'));
142 }
143
144 // text captcha?
145 if(!empty($exam->require_text_captcha)) {
146 $text_captcha_html = WatuTextCaptcha :: generate();
147 $textcaptca_style = $exam->single_page==1?"":"style='display:none;'";
148 $text_captcha_html = "<div id='WatuTextCaptcha' $textcaptca_style>".$text_captcha_html."</div>";
149 // verify captcha
150 if(!empty($_POST['do'])) {
151 if(!WatuTextCaptcha :: verify($_POST['watu_text_captcha_question'], $_POST['watu_text_captcha_answer'])) die('WATU_CAPTCHA:::'.__('Wrong answer to the verification question.', 'watu'));
152 }
153 }
154
155 if(isset($_REQUEST['do']) and $_REQUEST['do']) { // Quiz Reuslts.
156 $achieved = $max_points = $num_correct = $num_wrong = $num_empty = 0;
157 $result = '';
158
159 // we should reorder the questions in the same way they came from POST because exam might be randomized
160 $_exam = new WatuExam();
161 $questions = $_exam->reorder_questions($all_questions, $_POST['question_id']);
162
163 foreach ($questions as $qct => $ques) {
164 $is_empty = false;
165 $qnum = $qct+1;
166 $question_number = empty($exam->dont_display_question_numbers) ? "<span class='watu_num'>$qnum. </span>" : '';
167
168 $result .= "<div class='show-question'>";
169 $result .= "<div class='show-question-content'>". wpautop($question_number . stripslashes($ques->question), true) . "</div>";
170 $all_answers = $ques->answers;
171 $correct = false;
172 $class = $textarea_class = 'answer';
173 $result .= "<ul>";
174 $ansArr = is_array( @$_REQUEST["answer-" . $ques->ID] )? $_POST["answer-" . $ques->ID] : array();
175 foreach ($all_answers as $ans) {
176 $class = 'answer';
177
178 list($points, $correct, $class) = WatuQuestion :: calculate($ques, $ans, $ansArr, $correct, $class);
179
180 if(strstr($class, 'correct-answer')) $textarea_class = $class;
181
182 $achieved += $points;
183 if($ques->answer_type != 'textarea') $result .= wpautop("<li class='$class'><span class='answer'><!--WATUEMAIL".$class."WATUEMAIL-->" . stripslashes($ans->answer) . "</span></li>");
184
185 // for textareas the answer can be correct only once. If the user has wrongly entered same word multiple times (or with case sensitivity in mind) we have to ensure the question won't add
186 // points and num correct answers multiple times.
187 if($ques->answer_type == 'textarea' and $correct) break;
188 }
189
190 // textareas
191 if($ques->answer_type=='textarea' and !empty($_POST["answer-" . $ques->ID][0])) {
192 if(!count($all_answers) and !$question->is_survey) $textarea_class = 'correct-answer';
193 $textarea_answer = wpautop("<li class='user-answer $textarea_class'><span class='answer'><!--WATUEMAIL".$class."WATUEMAIL-->".esc_html(stripslashes($_POST["answer-" . $ques->ID][0]))."</span></li>");
194 if($ques->is_survey) $textarea_answer = str_replace(['user-answer','correct-answer'], ['user-answer-survey',''], $textarea_answer);
195 $result .= $textarea_answer;
196 }
197
198 $result .= "</ul>";
199 if(($ques->answer_type == 'textarea' and empty($_POST["answer-" . $ques->ID][0]))
200 or ($ques->answer_type != 'textarea' and empty($_POST["answer-" . $ques->ID])) ) {
201 $num_empty++;
202 $is_empty = true;
203 $result .= "<p class='unanswered'>" . __('Question was not answered', 'watu') . "</p>";
204 }
205
206 // answer explanation?
207 if(!empty($ques->feedback)) {
208 // has split tag?
209 if(strstr($ques->feedback, '{{{split}}}')) {
210 $parts = explode('{{{split}}}', $ques->feedback);
211 if($correct) $ques->feedback = $parts[0];
212 else $ques->feedback = $parts[1];
213 }
214
215 $result .= "<div class='show-question-feedback'>".wpautop(stripslashes($ques->feedback))."</div>";
216 }
217
218 $result .= "</div>";
219
220 if($correct) $num_correct++;
221 if(!$correct and !$is_empty and !$question->is_survey) $num_wrong++;
222 $max_points += WatuQuestion :: max_points($ques, $all_answers);
223 }
224
225 // percent correct answers
226 $percent_correct = $num_questions ? round(100 * $num_correct / $num_questions) : 0;
227
228 // Find scoring details
229 if($max_points == 0) $percent = 0;
230 else $percent = number_format($achieved / $max_points * 100, 2);
231 //0-9 10-19%, 20-29%, 30-39% 40-49%
232 $all_rating = array(__('Failed', 'watu'), __('Failed', 'watu'), __('Failed', 'watu'), __('Failed', 'watu'), __('Just Passed', 'watu'),
233 // 100% More than 100%?!
234 __('Satisfactory', 'watu'), __('Competent', 'watu'), __('Good', 'watu'), __('Very Good', 'watu'), __('Excellent', 'watu'), __('Unbeatable', 'watu'), __('Cheater', 'watu'));
235 $rate = intval($percent / 10);
236 if($percent == 100) $rate = 9;
237 if($achieved == $max_points) $rate = 10;
238 if($percent>100) $rate = 11;
239 $rating = @$all_rating[$rate];
240
241 $grade = __('None', 'watu');
242 $gtitle = $gdescription="";
243 $g_id = 0;
244 $allGrades = $wpdb->get_results(" SELECT * FROM `".WATU_GRADES."` WHERE exam_id=$exam_id ");
245 if( count($allGrades) ){
246 foreach($allGrades as $grow ) {
247
248 if( $grow->gfrom <= $achieved and $achieved <= $grow->gto ) {
249 $grade = $gtitle = $grow->gtitle;
250 $gdescription = wpautop(stripslashes($grow->gdescription));
251 $g_id = $grow->ID;
252 if(!empty($grow->gdescription)) $grade .= wpautop(stripslashes($grow->gdescription));
253 if(!empty($grow->redirect_url)) $do_redirect = $grow->redirect_url;
254 break;
255 }
256 }
257 }
258
259 ####################### VARIOUS AVERAGE CALCULATIONS (think about placing them in function / method #######################
260 // calculate averages
261 $avg_points = $avg_percent = '';
262 if(strstr($exam->final_screen, '%%AVG-POINTS%%')) {
263 $all_point_rows = $wpdb->get_results($wpdb->prepare("SELECT points FROM ".WATU_TAKINGS."
264 WHERE exam_id=%d", $exam->ID));
265 $all_points = 0;
266 foreach($all_point_rows as $r) $all_points += $r->points;
267 $all_points += $achieved;
268 $avg_points = round($all_points / ($wpdb->num_rows + 1), 1);
269 }
270
271 // better than what %?
272 $better_than = '';
273 if(strstr($exam->final_screen, '%%BETTER-THAN%%')) {
274 // select total completed quizzes
275 $total_takings = $wpdb->get_var($wpdb->prepare("SELECT COUNT(ID) FROM ".WATU_TAKINGS."
276 WHERE exam_id=%d", $exam->ID));
277
278 $num_lower = $wpdb->get_var($wpdb->prepare("SELECT COUNT(ID) FROM ".WATU_TAKINGS."
279 WHERE exam_id=%d AND points < %f", $exam->ID, $achieved));
280
281 $better_than = $total_takings ? round($num_lower * 100 / $total_takings) : 0;
282 }
283
284 // average points per question for this attempt
285 $avg_qpoints = $num_questions ? round($achieved / $num_questions, 2) + 0 : 0;
286 ####################### END VARIOUS AVERAGE CALCULATIONS #######################
287
288
289 $quiz_details = $wpdb->get_row($wpdb->prepare("SELECT name,final_screen, description FROM {$wpdb->prefix}watu_master WHERE ID=%d", $exam_id));
290
291 $quiz_details->final_screen = str_replace('%%TOTAL%%', '%%MAX-POINTS%%', $quiz_details->final_screen);
292 $replace_these = array('%%SCORE%%', '%%MAX-POINTS%%', '%%PERCENTAGE%%', '%%GRADE%%', '%%RATING%%', '%%CORRECT%%', '%%WRONG_ANSWERS%%', '%%QUIZ_NAME%%', '%%DESCRIPTION%%', '%%GRADE-TITLE%%', '%%GRADE-DESCRIPTION%%', '%%POINTS%%', '%%AVG-POINTS%%', '%%BETTER-THAN%%', '%%EMPTY%%', '%%WRONG%%', '%%AVG-QPOINTS%%', '%%TOTAL%%');
293 $with_these = array($achieved, $max_points, $percent_correct, $grade, $rating, $num_correct, $num_wrong, stripslashes($quiz_details->name), wpautop(stripslashes($quiz_details->description)), $gtitle, $gdescription, $achieved, $avg_points, $better_than, $num_empty, $num_wrong, $avg_qpoints, $num_questions);
294
295 if(strstr($exam->final_screen ?? '', '%%TOTAL-ENTRIES%%') or strstr($exam->email_output ?? '', '%%TOTAL-ENTRIES%%')) {
296 $replace_these[] = '%%TOTAL-ENTRIES%%';
297 $total_entries = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM ".WATU_TAKINGS."
298 WHERE exam_id=%d", $exam->ID));
299 $with_these[] = $total_entries + 1;
300 }
301
302 // insert taking
303 $uid = $user_ID ? $user_ID : 0;
304 $taker_email = empty($_POST['watu_taker_email']) ? '' : sanitize_email($_POST['watu_taker_email']);
305 if(empty($exam->dont_store_data)) {
306 if($exam->no_ajax) {
307 $taking_id = $wpdb->get_var($wpdb->prepare("SELECT ID FROM ".WATU_TAKINGS."
308 WHERE ip=%s AND user_id=%d AND exam_id=%d AND points=%d AND grade_id=%d AND start_time=%s",
309 '', $user_ID, $exam->ID, $achieved, $g_id, $_POST['start_time']));
310 }
311 if(empty($taking_id)) {
312 $source_url = empty($exam->save_source_url) ? '' : esc_url_raw($_SERVER['HTTP_REFERER']);
313 $wpdb->query($wpdb->prepare("INSERT INTO ".WATU_TAKINGS." SET exam_id=%d, user_id=%d, ip=%s, date=CURDATE(),
314 points=%d, grade_id=%d, result=%s, snapshot='', start_time=%s, email=%s, percent_correct=%d, source_url=%s,
315 num_correct=%d, num_wrong=%d, num_empty=%d",
316 $exam_id, $uid, '', $achieved, $g_id, $grade, @$_POST['start_time'],
317 $taker_email, $percent, $source_url, $num_correct, $num_wrong, $num_empty));
318 $taking_id = $wpdb->insert_id;
319 }
320 }
321 else $taking_id = 0;
322 $GLOBALS['watu_taking_id'] = $taking_id;
323
324 // Show the results
325 $output = str_replace($replace_these, $with_these, wpautop(stripslashes($quiz_details->final_screen)));
326 if(strstr($output, '%%ANSWERS%%')) {
327 $output = str_replace('%%ANSWERS%%', $result, $output);
328 }
329 $final_output = apply_filters(WATU_CONTENT_FILTER, $output);
330
331 // replace email if entered
332 $final_output = watu_replace_email($taker_email, $final_output);
333
334 // this filter is for third party integrations. Not used in Watu itself.
335 $final_output = apply_filters('watu_final_screen',$final_output, $taking_id, $exam, $questions, $result);
336
337 if(!empty($do_redirect)) {
338 if(empty($exam->no_ajax)) echo "WATU_REDIRECT:::".$do_redirect;
339 }
340 else echo $final_output;
341
342 // update snapshot
343 $wpdb->query($wpdb->prepare("UPDATE ".WATU_TAKINGS." SET snapshot=%s WHERE ID=%d", $final_output, $taking_id));
344
345 $user_name = __('Guest', 'watu');
346 if(is_user_logged_in()) {
347 $current_user = wp_get_current_user();
348 $user_name = $current_user->display_name;
349 }
350 $exam->user_name = $user_name; // to use for email subject in email_results
351
352 // notify admin
353 if(!empty($exam->email_output)) {
354 $email_output = wpautop(stripslashes($exam->email_output));
355 $email_output = str_replace($replace_these, $with_these, $email_output);
356 if(strstr($email_output, '%%ANSWERS%%')) {
357 $email_output = str_replace('%%ANSWERS%%', $result, $email_output);
358 }
359 $email_output = watu_replace_email($taker_email, $email_output);
360 $email_output = apply_filters(WATU_CONTENT_FILTER, $email_output);
361 }
362 else $email_output = $final_output;
363 if(!empty($exam->notify_admin)) watu_notify($exam, $uid, $email_output);
364 if(!empty($exam->notify_user)) watu_notify($exam, $uid, $email_output, 'user');
365
366 do_action('watu_exam_submitted', $taking_id);
367 do_action('watu_exam_submitted_detailed', $taking_id, $exam, $user_ID, $achieved, $g_id);
368 if(empty($exam->no_ajax)) exit;// Exit due to ajax call
369 if(!empty($exam->no_ajax) and !empty($do_redirect)) watu_redirect($do_redirect);
370
371 } else { // Show The Test
372 $single_page = $exam->single_page;
373 if(@file_exists(get_stylesheet_directory().'/watu/show_exam.html.php')) include get_stylesheet_directory().'/watu/show_exam.html.php';
374 else include(WATU_PATH . '/views/show_exam.html.php');
375 }
376 } // end if $questions
377