1<?php 2 3// version 1.0 First release 4 5class quiz { 6// Quiz plugin for Dokuwiki 7// Copyright (C) 2010 Luigi Micco 8// based on osQuest5 PHP/XML based Quiz software (and associated Text to XML module) 9// Copyright (C) 2009 Jon-Michael C. Brook 10// based on osQuest4 PHP/XML based Quiz software (and associated Text to XML module) 11// Copyright (C) 2007 Jay Banks 12// 13// These program are free software: you can redistribute them and/or modify 14// them under the terms of the GNU Affero General Public License as 15// published by the Free Software Foundation, either version 3 of the 16// License, or (at your option) any later version. 17// 18// This program is distributed in the hope that it will be useful, 19// but WITHOUT ANY WARRANTY; without even the implied warranty of 20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21// GNU Affero General Public License for more details. 22// 23// A copy of the GNU Affero General Public License is provided below 24// and it applies to all programs in this archive. 25// See also: <http://www.gnu.org/licenses/> or 26// http://www.fsf.org/licensing/licenses/agpl-3.0.html. 27// 28// 29 30 // change below values to adjust preferences 31 // 32 33 var $quizID; 34 var $mode; 35 var $rndquest; 36 var $rndchoice; 37 var $highscore; 38 var $showintro; 39 var $current_question = 1; 40 var $error = ''; 41 42 var $in_question = 0; 43 var $in_section = ''; 44 45 var $parseQuiz_cache = null; 46 var $quizobj; 47 48 function quiz($quizID, &$param) { 49 $this->quizID = $quizID; 50 $this->mode = $param['learning']; 51 $this->rndquest = $param['rndquest']; 52 $this->rndchoice = $param['rndchoice']; 53 $this->highscore = $param['highscores']; 54 $this->showintro = $param['showintro']; 55 56 $this->quizobj = plugin_load('syntax','quiz'); 57 58 if (!($this->mode < 0 || $this->mode > 1 )) { 59 $this->error = 'Error: "' . $this->mode .'" is not a valid quiz mode. Valid modes are "test (0)" and "learning (1)"'; 60 } 61 } 62 63 function do_quiz_here() { 64 if (isset($_POST['next_question'])) { 65 $next = $_POST['next_question']; // + 1; 66 if ((!isset($_POST['answers'][$next - 1])) && ($next > 1)) { $next = $next - 1; } 67 } else { 68 unset($_POST); 69 $next = 0; 70 } 71 return $this->print_question($next); 72 } 73 74 function print_question($question_num) { 75 76 $html = ""; 77 $question_num = (int)$question_num; 78 $data = $this->parseQuiz(); 79 if ($data !== false) { 80 $a = $data['questions'][$question_num]; 81 $count_a = count($data['questions']); 82 83 if ($question_num <= $count_a) { 84 if ($question_num <= 0) { 85 $html .= '<h1>'.$data['title'].'</h1>'; 86 87 $html .= $data['intro']; 88 if (($question_num != 0) && ($this->highscore )) $html .= $this->print_highscore(); 89 90 $html .= '<div class="quiz-block" id="quiz-form__'.$this->quizID.'">'; 91 } else { 92 $html .= '<h1>'.$data['title'].' - '.$question_num.'/'.$count_a.'</h1>'; 93 if ($this->showintro) $html .= $data['intro']; 94 $html .= '<div class="quiz-block" id="quiz-form__'.$this->quizID.'">'; 95 $html .= '<div class="quiz-question">'.$a['question'].'</div>'; 96 } 97 98 $html .= '<form action="getform.php" method="post" onsubmit="plgQuiz_send(\''.$this->quizID.'\'); return false;">'; 99 $html .= '<input type="hidden" name="quiz_id" value="' . $this->quizID. '">' . "\n"; 100 $html .= '<input type="hidden" name="quiz_learning" value="' . $this->mode. '">' . "\n"; 101 $html .= '<input type="hidden" name="quiz_showintro" value="' . $this->showintro. '">' . "\n"; 102 $html .= '<input type="hidden" name="quiz_rndquest" value="' . $this->rndquest. '">' . "\n"; 103 $html .= '<input type="hidden" name="quiz_rndchoice" value="' . $this->rndchoice. '">' . "\n"; 104 $html .= '<input type="hidden" name="quiz_highscore" value="' . $this->highscore. '">' . "\n"; 105 106 if ($question_num <= 0) { 107 $html .= '<div class="quiz-button">'; 108 $html .= '<button class="button" type="submit" onClick="plgQuiz_send(\''.$this->quizID.'\',1); return false;" > '.$this->quizobj->getLang('quiz_start').' </button>'; 109 110 if (($question_num == 0) && ($this->highscore )) { 111 $html .= '<button class="button" type="submit" onClick="plgQuiz_send(\''.$this->quizID.'\',-1); return false;" > '.$this->quizobj->getLang('quiz_highscore').' </button>'; 112 } 113 114 $html .= '</div>'; 115 $html .= '</form>'; 116 $html .= '</div>'; 117 118 } else { 119 120 $html .= '<div class="quiz-choice">'; 121 122 // se si torna alla domanda precedente, cancella i dati doppi 123 $temp = $_POST['answers'][$question_num]; 124 if (isset($_POST['prev'])) { 125 unset($_POST['answers'][$question_num]); 126 } 127 // 128 129 // se presenti, aggiunge in POST i dati delle risposte precedenti 130 if (isset($_POST) && isset($_POST['answers'])) { 131 foreach ($_POST['answers'] as $key => $value) { 132 if (!is_array($value)) { 133 $html .= '<input type="hidden" name="answers[' . $key . ']" value="' . $value .'">' . "\n"; 134 } else { 135 foreach ($value as $literal_key => $valueison) { 136 $html .= '<input type="hidden" name="answers[' . $key . '][' . $literal_key .']" value="on">' . "\n"; 137 } 138 } 139 } 140 } 141 // 142 143 if ($a['type'] == 'single') { 144 for($n=1; $n<=count($a['choices']); $n++) { 145 $state = ''; 146 if ($temp == $n) $state = ' checked'; 147 $html .= '<input '.$state.' type="radio" name="answers[' . $question_num . ']" id="'.$n.'" value="'.$n.'"> <label for="' . $n . 148 '">' . $a['choices'][$n]['text'] . '</label><br>' . "\n"; 149 } 150 } else { 151 if ($a['type'] == 'text') { 152 $html .= '<input type="text" name="answers[' . $question_num . 153 ']" value="'.$temp.'" size="50"><br>' . "\n"; 154 } else { 155 if ($a['type'] == 'multi') { 156 for($n=1; $n<=count($a['choices']); $n++) { 157 $state = ''; 158 if (isset($temp[$n]) && ($temp[$n] =='on')) $state = ' checked'; 159 $html .='<input '.$state.' type="checkbox" id="answers[' . $question_num . '][' . $n . ']" name="answers[' . $question_num . '][' . $n .']"> 160 <label for="answers[' . $question_num . '][' . $n . ']">' . $a['choices'][$n]['text'] . 161 '</label><br>' . "\n"; 162 } 163 } 164 } 165 } 166 $html .= '</div>'; 167 $html .= '<div class="quiz-button">'; 168 if ($question_num > 1) { 169 $html .= '<button class="button" name="next_question" value="'.($question_num-1).'" type="submit" onClick="plgQuiz_send(\''.$this->quizID.'\','.($question_num-1).'); return false;" > '.$this->quizobj->getLang('quiz_prevquest').' </button>'; 170 } 171 $html .= '<button class="button" name="next_question" value="'.($question_num+1).'" type="submit" onClick="plgQuiz_send(\''.$this->quizID.'\','.($question_num+1).'); return false;"> ' . $this->quizobj->getLang(($count_a == $question_num ?'quiz_finish':'quiz_nextquest')) . ' </button>'; 172 $html .= '</form>'; 173 $html .= '</div>'; 174 $html .= '</div>'; 175 } 176 } else { 177 178 // mostra le risposte giuste 179 //display test results 180 $total_answers = count($_POST['answers']); 181 $total_correct_answers = 0; 182 $total_score = 0; 183 foreach ($_POST['answers'] as $_question_num => $_users_answer) { 184 if ($this->checkanswer($_question_num)) { 185 $total_correct_answers++; 186 $total_score = $total_score + $data['questions'][$_question_num]['score']; 187 } 188 } 189 190 $html .= '<h1>'.$data['title'].' - '.$this->quizobj->getLang('quiz_result').'</h1>'; 191 if ($this->showintro) $html .= $data['intro']; 192 $html .= '<div class="quiz-block">'; 193 $html .= '<div class="quiz-result">'; 194 $percent = round(($total_correct_answers / $total_answers) * 100, 2); 195 $html .= sprintf($this->quizobj->getLang('quiz_resulttext'), $total_correct_answers, $total_answers, $percent); 196 $html .= '<br>'; 197 $html .= $this->quizobj->getLang('quiz_score').$total_score . '<br>'; 198 $html .= '</div>'; 199 200 // descrizione del risultato per scaglioni 201 if ($data['scores']) { 202 foreach ($data['scores'] as $scores) { 203 if (($total_score >= $scores['range'][0]) && ($total_score <= $scores['range'][1])) { 204 $html .= '<div class="quiz-report">'; 205 $html .= $scores['text']; 206 $html .= '</div>'; 207 } 208 } 209 } 210 211 $html .= '<div class="quiz-button">'; 212 $html .= '<form action="" method="post">'; 213 $html .= '<BUTTON class="button" name="again" type="submit" > '.$this->quizobj->getLang('quiz_tryagain').' </BUTTON>'; 214 $html .= '</form>'; 215 $html .= '</div>'; 216 217 218 if ($this->mode == 1 && $question_num > 1) { 219 $html .= "<hr>"; 220 for ($i = 1; $i <= $count_a; $i++) { 221 $html .= $this->learning_mode_print_answer($i ); 222 } 223 } 224 225 $scorelist = $this->updateScore($total_score, $percent); 226 $html .= '</div>'; 227 } 228 } else { 229 $html .= $this->quiz_error(); 230 } 231 return $html; 232 } 233 234 function learning_mode_print_answer($question_num) { 235 $ok = false; 236 237 if ($this->checkanswer($question_num, &$answers)) { 238 $ok = true; 239 $div = "correct"; 240 } else { 241 $div = "wrong"; 242 } 243 244 $a = $this->parseQuiz(); 245 $a = $a['questions'][$question_num]; 246 247 $html = ""; 248 249 $html .= '<div class="quiz-block">'; 250 $html .= '<div class="quiz-question">'; 251 $html .= '<div class="quiz-'.$div.'">'; 252 $html .= $question_num.") ".$a['question']; //." ".$img; 253 $html .= '</div>'; 254 $html .= '</div>'; 255 $html .= '<div class="quiz-choice">'; 256 $html .= '<ul>'; 257 258 if ($a['type'] == 'text') { 259 if (!$ok) 260 $html .= '<div class="quiz-wrong">'. $answers[1].'</div>'; 261 262 if (!$ok) 263 $html .= '<div class="quiz-missing">'. $a['answers'][0] .'</div>'; 264 else 265 $html .= '<div class="quiz-correct">'. $a['answers'][0] .'</div>'; 266 } else { 267 for($n=1; $n<=count($a['choices']); $n++) { 268 269 if (($answers[$n] == 1) && $a['choices'][$n]['correct']) { 270 $div = "correct"; 271 } elseif (($answers[$n] == 1) && !$a['choices'][$n]['correct']) { 272 $div = "wrong"; 273 } elseif (($answers[$n] == 0) && $a['choices'][$n]['correct']) { 274 $div = "missing"; 275 } else { 276 $div = ""; 277 } 278 279 $html .= '<div class="quiz-'.$div.'">'; 280 $html .= '<li class="level1">'.$a['choices'][$n]['text'].'</li>'; 281 $html .= '</div>' . "\n"; 282 } 283 } 284 $html .= '</ul>'; 285 286 if ($a['explanation'] != '') { 287 $html .= '<hr><b>'.$this->quizobj->getLang('quiz_explanation').'</b><br>' . $a['explanation'].''; 288 } 289 $html .= '</div>'; 290 $html .= '</div>'; 291 return $html ; 292 } 293 294 function checkanswer($question, $answer_array = null) { 295 $result = false; 296 if ($this->parseQuiz() !== false && $question > 0) { 297 $data = $this->parseQuiz(); 298 $a = $data['questions'][$question]; 299 300 $your_answer = ''; 301 $correct_answer = ''; 302 if ($a['type'] == 'single') { 303 $your_answer = $_POST['answers'][$question]; 304 for($n=1; $n<=count($a['choices']); $n++) { 305 $answer_array[$n] = ($n == $your_answer?1:0); 306 } 307 $result = $a['choices'][$your_answer]['correct']; 308 309 } else { 310 if ($a['type'] == 'text') { 311 $your_answer = $_POST['answers'][$question]; 312 $answer_array[1] = $your_answer; 313 $result = (strtolower($your_answer) == strtolower($a['answers'][0])); 314 315 } else { 316 if ($a['type'] == 'multi') { 317 $your_answers = $_POST['answers'][$question]; 318 ksort($your_answers); 319 320 $result = true; 321 for($n=1; $n<=count($a['choices']); $n++) { 322 $answer_array[$n] = (isset($your_answers[$n])? 1:0); 323 if ( (($answer_array[$n] == 1) && !$a['choices'][$n]['correct']) || 324 (($answer_array[$n] == 0) && $a['choices'][$n]['correct']) ) { 325 $result = false; 326 } 327 } 328 } 329 } 330 } 331 } 332 return $result; 333 } 334 335 function quiz_error() { 336 return $this->error; 337 } 338 339 340 function parseQuiz() { 341 if ($this->parseQuiz_cache === null) { 342 $page = $this->quizID; 343 $quizz = array(); 344 $pos = strrpos(utf8_decode($page), ':'); 345 $pageName = p_get_first_heading($page); 346 if($pageName == NULL) { 347 if($pos != FALSE) { 348 $pageName = utf8_substr($page, $pos+1, utf8_strlen($page)); 349 } else { 350 $pageName = $page; 351 } 352 $pageName = str_replace('_', ' ', $pageName); 353 } 354 $quizz['title'] = $pageName; 355 $select= rawWiki($page); 356 $lines = explode("\n", $select); 357 $nr = count($lines); 358 359 $hRender = new Doku_Renderer_xhtml(); 360 361 $nquest = 0; 362 $quizz['questions'] = array(); 363 364 for($n=0; $n<$nr; $n++) { 365 366 if (substr($lines[$n], 0, 6) == "======") continue; 367 368 if (substr($lines[$n], 0, 5) == "=====") { 369 $temp = str_replace("=====",'',$lines[$n]); 370 $tmp_array = array(); 371 $tmp_array['range']= explode("-", $temp); 372 for($k=$n+1; $k<$nr; $k++) { 373 if ((trim($lines[$k]) == '') || (substr($lines[$k], 0, 5) != "=====")) { 374 $tmp_array['text'] .= $lines[$k]."\n"; 375 } else { 376 $n = $k - 1; 377 $k = $nr + 1; 378 } 379 }; 380 $tmp_array['text'] = $hRender->render($tmp_array['text']); 381 $quizz['scores'][]= $tmp_array; 382 383 }; 384 385 if ((trim($lines[$n]) == '') || (substr($lines[$n], 0, 4) != " * ")) { 386 if ($nquest == 0) $quizz['intro'] .= $lines[$n]."\n"; 387 continue; 388 } 389 390 391 392 $nquest++;; 393 $tmp = $hRender->render(str_replace(" * ",'',$lines[$n])); 394 $tmp = substr($tmp, 4); 395 $tmp = substr($tmp, 0, -5); 396 $quizz['questions'][$nquest]['question'] = $tmp; 397 398 $nchoices = 0; 399 $quizz['questions'][$nquest]['choices'] = array(); 400 $quizz['questions'][$nquest]['answers'] = array(); 401 $quizz['questions'][$nquest]['score']= 1; 402 $quizz['questions'][$nquest]['explanation'] = ''; 403 404 $temp_choices = array(); 405 for($k=$n+1; $k<$nr; $k++) { 406 if (trim($lines[$k]) == '') continue; 407 if (substr($lines[$k], 0, 6) == " - ") { 408 $nchoices++; 409 $tmp = $hRender->render(str_replace(" - ",'',$lines[$k])); 410 $tmp = substr($tmp, 4); 411 $tmp = substr($tmp, 0, -5); 412 413 $quizz['questions'][$nquest]['choices'][$nchoices]['text'] = $tmp; 414 $quizz['questions'][$nquest]['choices'][$nchoices]['correct'] = false; 415 416 } elseif (substr($lines[$k], 0, 7) == " * [") { 417 $temp = str_replace(" * [",'',$lines[$k]); 418 $temp = ereg_replace("].*", "", $temp); 419 420 $quizz['questions'][$nquest]['answers'] = explode("|", $temp); 421 sort($quizz['questions'][$nquest]['answers']); 422 423 for($i=0; $i<count($quizz['questions'][$nquest]['answers']); $i++) { 424 if (isset($quizz['questions'][$nquest]['choices'][$quizz['questions'][$nquest]['answers'][$i]]['text'])) { 425 $quizz['questions'][$nquest]['choices'][$quizz['questions'][$nquest]['answers'][$i]]['correct'] = true; 426 } 427 } 428 429 } elseif (substr($lines[$k], 0, 7) == " * (") { 430 $temp = str_replace(" * (",'',$lines[$k]); 431 $temp = ereg_replace(").*", "", $temp); 432 433 $quizz['questions'][$nquest]['score']= $temp; 434 435 } elseif (substr($lines[$k], 0, 6) == " * ") { 436 $tmp = $hRender->render(str_replace(" * ",'',$lines[$k])); 437 $tmp = substr($tmp, 4); 438 $tmp = substr($tmp, 0, -5); 439 440 $quizz['questions'][$nquest]['explanation'] = $tmp; 441 } else { 442 $n = $k - 1; 443 $k = $nr + 1; 444 } 445 } 446 447 if ($this->rndchoice) $this->kshuffle($quizz['questions'][$nquest]['choices']); 448 449 if (count($quizz['questions'][$nquest]['choices']) == 0) { 450 $quizz['questions'][$nquest]['type'] = 'text'; 451 } elseif (count($quizz['questions'][$nquest]['answers']) > 1) { 452 $quizz['questions'][$nquest]['type'] = 'multi'; 453 } else { 454 $quizz['questions'][$nquest]['type'] = 'single'; 455 } 456 457 } 458 459 if ($this->rndquest) $this->kshuffle($quizz['questions']); 460 $quizz['intro'] = $hRender->render($quizz['intro']); 461 462 463 $this->parseQuiz_cache = $quizz; 464 } 465 return $this->parseQuiz_cache; 466 467 } 468 469 // sequenza casuale, fissa per sessione 470 function kshuffle(&$items) { 471 $temp = $items; 472 srand(hexdec(session_id())); 473 shuffle($temp); 474 for($k=1; $k<=count($temp); $k++) { 475 $items[$k] = $temp[$k-1]; 476 } 477 } 478 479 // visualizza i migliori 10 punteggi 480 function print_highscore() { 481 // get score file contents 482 $pfile = metaFN(md5($this->quizID), '.quiz'); 483 $scorelist = unserialize(@file_get_contents($pfile)); 484 485 $temp = $scorelist['rows']; 486 usort($temp, array($this,'cmp')); 487 $temp = array_slice($temp, 0, 10); 488 489 $html = ""; 490 $html .= '<h2>10 '.$this->quizobj->getLang('quiz_highscore').' ('.$scorelist['total'].')</h2><br>'; 491 492 $html .= '<table class="inline">'; 493 $html .= '<tr class="row0">'; 494 $html .= '<th class="col0 centeralign"> User </th> 495 <th class="col1 centeralign"> IP </th> 496 <th class="col2 centeralign"> Score </th> 497 <th class="col3 centeralign"> % </th> 498 <th class="col3 centeralign"> Date </th>'; 499 $html .= '</tr>'; 500 foreach ($temp as $row) { 501 $html .= "<tr>"; 502 $html .= '<td class="col0">'.$row['user'].'</td>'; 503 $html .= '<td class="col1">'.$row['ip'].'</td>'; 504 $html .= '<td class="col2">'.$row['score'].'</td>'; 505 $html .= '<td class="col3">'.$row['percent'].'</td>'; 506 $html .= '<td class="col3">'.dformat($row['timestamp']).'</td>'; 507 $html .= "</tr>"; 508 } 509 510 $html .= "</table>"; 511 512 return $html; 513 } 514 515 // Comparison function 516 function cmp($a, $b) { 517 if ($a['score'] == $b['score']) { 518 return 0; 519 } 520 return ($a['score'] > $b['score']) ? -1 : 1; 521 } 522 523 // aggiorna il file dei risultati 524 function updateScore($userscore, $percent) { 525 526 // get score file contents 527 $pfile = metaFN(md5($this->quizID), '.quiz'); 528 $score = unserialize(@file_get_contents($pfile)); 529 530 if(!isset($score['total'])) $score['total'] = 0; 531 if(!isset($score['rows'])) { 532 $score['rows'] = array(); 533 } 534 535 if(!isset($score['ips'])) $score['ips'] = array(); 536 if(!isset($score['users'])) $score['users'] = array(); 537 538 $ip = clientIP(true); 539 540 $score['total']++; 541 $tmp = '(anonymous)'; 542 if($_SERVER['REMOTE_USER']) $tmp = $_SERVER['REMOTE_USER']; 543 $score['users'][] =$tmp; 544 $score['ips'][] = $ip; 545 546 $score['rows'][$score['total']]['percent'] = $percent; 547 $score['rows'][$score['total']]['score'] = $userscore; 548 $score['rows'][$score['total']]['user'] = $tmp; 549 $score['rows'][$score['total']]['ip'] = $ip; 550 $score['rows'][$score['total']]['timestamp'] = time(); 551 552 if($fh = fopen($pfile, 'w')) { 553 fwrite($fh, serialize($score)); 554 fclose($fh); 555 } 556 return $score; 557 } 558 559 560} 561 562?> 563 564