Talk:MW: quizzes
From ChekMate Security Group
I've made a modified version of this that is an extension
Hi, I stumbled across your quizzes hack and I really liked the idea. The only problem was it requires access to the quiz.xml file which my users won't have access to. I decided to learn how to make MediaWiki extensions, and after a couple weeks of fiddling around I've been able to make a modified version of quizzes. My version has a couple differences, which I thought would be best to preserve the editability of the wiki. First, instead of requiring a new folder with an index.php which requires an external link, I changed it to be a special page. This took minimal effort, but the initial learning curve to make it an extension was somewhat high. Then, I embarked upon making it use articles as its input instead of XML files, since these can be edited by users. This also took a lot of research and minimal changes to the code. After this, I decided that it should be able to take in multiple quizzes, because having access to only one quiz would be very limiting. This I accomplished using GET arguments (not sure what the proper term is), like Special:Quiz?quiz=myquiz. This worked great, until I realized that I couldn't link to the page with an argument because MediaWiki turns the ? into an html entity or something. Due to this, I had to add a new wiki tag called <showquiz> which basically runs the php script for parsing a quiz article with the text between the tags as the quiz article name. This version works wonderfully, except for the use of a couple hard-coded paths, which I can fix now that I know some more PHP. Once I finish making this usable on other sites, do you think you could put my modified version on your page? It does say you might get around to making it a proper extension eventually, which is basically what I did. If you want to see an example of what I did, you'll have to send me an email or something because my wiki has restricted access. --Ibrahim 21:21, 1 November 2006 (MST)
- I would appreciate to see the new extension files that you have created. This was one of my first hacks to MediaWiki and as such it is completely wrong in the design and development but it met the requirement to get it functioning. In the near future I will look into this and configure the quizzes to use a proper extension build. (smcnaught - 08:43, 2 November 2006 (MST))
My edited version
here you go, this is in a file called quiz.php in the extensions directory. I've changed it so that it uses | instead of , to separate choices, in case you need a comma in a choice. I also changed the tags to lower case. What this does is anything that has a <quiz> tag in it will be parsed as a quiz. This way you can have quizzes anywhere, even as part of a page. I'm going to work on making it so all the questions appear on one page.
<?php
#install extension hook
$wgExtensionFunctions[] = "wfQuizExtension";
#extension hook callback function
function wfQuizExtension() {
global $wgParser;
#install parser hook for <rss> tags
$wgParser->setHook( "quiz", "renderQuiz" );
}
#parser hook callback function
function renderQuiz($input, $args, &$parser) {
global $wgOut;
////////////////////////////////////////////////////////////
//
// xmlQuiz v1.1 - a simple quiz script
//
////////////////////////////////////////////////////////////
//
// This script allows you to quiz users on any number of
// questions and calculate the score.
//
// See readme.txt for more information.
//
// Author: Jon Thomas <http://www.fromthedesk.com/>
// Last Modified: 12/18/2005
//
// You may freely use, modify, and distribute this script.
//
////////////////////////////////////////////////////////////
//
// GET QUIZ DATA
// Add xml opening and closing tags to data since $input is the stuff between <quiz> tags
$data="<?xml version=\"1.0\"?> <quiz>".$input."</quiz>";
// create XML parser
$xParser = xml_parser_create();
// set parser options
xml_parser_set_option($xParser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($xParser, XML_OPTION_SKIP_WHITE, 1);
// parse XML data into arrays
xml_parse_into_struct($xParser, $data, $values, $tags);
// free parser
xml_parser_free($xParser);
//
// STRUCTURE XML DATA INTO ARRAY
//
// set counter variable for to-be-created questions array
$questionNo = 0;
// cycle through parsed XML data to look for text and answer tags
foreach ($values as $key=>$val) {
// save value to "questions" array if this is a TEXT tag
if ($val[tag] == "text") {
$questions[$questionNo]['text'] = $val[value];
}
// save value to "questions" array if this is a CHOICES tag
if ($val[tag] == "choices") {
$questions[$questionNo]['choices'] = $val[value];
}
// save value to "questions" array if this is an ANSWER tag
if ($val[tag] == "answer") {
$questions[$questionNo]['answer'] = $val[value];
// increment question counter variable
$questionNo++;
}
}
//
// IMPORT POST VARIABLES
// doesn't seem to work inside MediaWiki, I could be wrong.
// In any case, it's not really essential or even that helpful.
import_request_variables("p", "post_");
//
// PRINT FIRST QUESTION
//
if (!isset($_POST['answers'])) {
$wtext .= "== Question 1 of " . ($questionNo) . ": ==\n\n";
$wtext .= "<b>" . $questions[0]['text'] . "</b>\n";
$text .= "<form action=\"".$_SERVER['REQUEST_URI']."\" method=\"post\">\n";
// split choices into "choices" array
$choices = explode("|", $questions[0]['choices']);
// print text field if there are no choices
if (count($choices) == 1) {
$text .= "<input type=\"text\" name=\"answers[0]\" size=10>\n";
}
// print radio fields if there are multiple choices
else {
// print a radio button for each choice
for ($i = 0; $i < count($choices); $i++) {
$text .= "<input type=\"radio\" name=\"answers[0]\" value=\"" . $choices[$i] . "\"> " . $choices[$i] . "<br>\n";
}
}
$text .= "<input type=\"submit\" value=\"Next Question\">\n";
$text .= "</form>\n";
}
//
// PRINT NEXT QUESTION
//
elseif (count($questions) > count($_POST['answers'])) {
// get number of next question
$nextQuestion = count($_POST['answers']);
// print question
$wtext .= "== Question " . ($nextQuestion + 1) . " of " . ($questionNo) . " ==\n\n";
$wtext .= "<b>" . $questions[$nextQuestion]['text'] . "</b>\n";
$text .= "<form action=\"".$_SERVER['REQUEST_URI']."\" method=\"post\">\n";
// print answers to previous questions as hidden form fields
for ($i = 0; $i < count($_POST['answers']); $i++) {
$text .= "<input type=\"hidden\" name=\"answers[$i]\" value=\"".$_POST['answers'][$i]."\">\n";
}
// split choices into "choices" array
$choices = explode("|", $questions[$nextQuestion]['choices']);
// print text field if there are no choices
if (count($choices) == 1) {
$text .= "<input type=\"text\" name=\"answers[$nextQuestion]\" size=10>\n";
}
// print radio fields if there are multiple choices
else {
// print a radio button for each choice
for ($i = 0; $i < count($choices); $i++) {
$text .= "<input type=\"radio\" name=\"answers[$nextQuestion]\" value=\"" . $choices[$i] . "\">" . $choices[$i] . "<br>\n";
}
}
// print appropriate button label
if (count($questions) == count($_POST['answers']) + 1) {
$text .= "<input type=\"submit\" value=\"Calculate Score\">\n";
}
else {
$text .= "<input type=\"submit\" value=\"Next Question\">\n";
}
$text .= "</form>\n";
}
//
// CALCULATE AND PRINT SCORE
//
else {
// get number of questions
$noQuestions = count($questions);
// get number of correct answers
for ($i = 0; $i < $noQuestions; $i++) {
// increment "noCorrectAnswers" variable if user has correct answer
if ($questions[$i]['answer'] == $_POST['answers'][$i]) {
$noCorrectAnswers++;
}
}
// calculate score
$score = ($noCorrectAnswers / $noQuestions) * 100;
// round score to nearest whole precentage point
$score = round($score);
// print score
$wtext .= "__NOTOC__\n== Quiz Results ==\n";
$wtext .= "=== You have scored $score% on this quiz.===\n";
if ($noCorrectAnswers == 0) {
$text .= "<p>You answered no questions correctly. <a href=" . $PHP_SELF . ">Try again.</a></p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
if ($noCorrectAnswers == 1) {
$text .= "<p>You answered 1 out of $noQuestions questions correctly. <a href=" . $PHP_SELF . ">Try again.</a></p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
if ($noCorrectAnswers > 1 && $noCorrectAnswers < $noQuestions) {
$text .= "<p>You answered $noCorrectAnswers out of $noQuestions questions correctly. <a href=" . $PHP_SELF . ">Try again.</a></p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
if ($noCorrectAnswers == $noQuestions) {
$text .= "<p>You answered all questions correctly!</p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
for ($i = 0; $i < $noQuestions; $i++) {
// print question
$wtext .= "== Question " . ($i + 1) . " ==\n\n";
$wtext .= "<b>" . $questions[$i]['text'] . "</b>\n";
// split choices into "choices" array
$choices = explode("|", $questions[$i]['choices']);
// print radio fields if there are multiple choices
// print text field if there are no choices
if (count($choices) == 1) {
if ($_POST['answers'][$i] == $questions[$i]['answer']) {
$wtext .= "<b>" . $_POST['answers'][$i]. "</b> <font color=blue><b>CORRECT</b></font><br>\n";
} else {
$wtext .= "<b>" . $_POST['answers'][$i]. "</b> <font color=red><b>INCORRECT</b></font><br>\n";
}
} else {
$wtext .= "\n";
// print a radio button for each choice
for ($x = 0; $x < count($choices); $x++) {
if (($questions[$i]['answer'] == $_POST['answers'][$i]) && ($_POST['answers'][$i] == $choices[$x])) {
$wtext .= " <b> " . $choices[$x] . "</b> <font color=blue><b>CORRECT</b></font><br>\n";
} else {
if ($_POST['answers'][$i] == $choices[$x]) {
$wtext .= " <b> " . $choices[$x] . "</b> <font color=red><b>INCORRECT</b></font><br>\n";
} else {
$wtext .= " " . $choices[$x] . "<br>\n";
}
}
}
$wtext .= "\n";
}
}
}
//Not using $wgOut because categories don't work with it.
$parsedStuff=$parser->parse("[[Category:Quizzes]]<br clear=all>\n".$wtext."<br clear=all>\n", $parser->mTitle, $parser->mOptions, false, false);
return $parsedStuff->getText() . $text;
}
?>
And here is the default quiz edited to match this format. This would go in any wiki page.
<quiz> <question> <text>A set of guidelines that allow different types of devices to communicate with each is called a:</text> <choices>Modem|Protocol|Language|Process</choices> <answer>Protocol</answer> </question> <question> <text>The TCP in TCP/IP stands for:</text> <choices>Transport Control Protocol|Transmission Control Process|Transport Command Process|Transmission Control Protocol</choices> <answer>Transmission Control Protocol</answer> </question> <question> <text>DHCP is used to automatically assign _____ to each device.</text> <choices>MAC Address|Host Name|IP Address|URL</choices> <answer>IP Address</answer> </question> <question> <text>The term NAT stands for:</text> <choices>National Application Transport|Network Advanced Translation|Network Address Translation|New Address Translation</choices> <answer>Network Address Translation</answer> </question> <question> <text>The standard protocol for Internet communication is:</text> <choices>TCP/IP|HTTP|FTP|SMTP</choices> <answer>TCP/IP</answer> </question> <question> <text>In order to communicate on the Internet each Host must have a unique:</text> <choices>Host Name|IP Address|MAC Address|Signature</choices> <answer>IP Address</answer> </question> <question> <text>The emerging IP standard which will help solve the shortage of IP addresses is:</text> <choices>New IP|IPv4|IP2003|IPv6</choices> <answer>IPv6</answer> </question> <question> <text>Using NAT allows more than one computer to connect to the Internet using only one:</text> <choices>IP Address|Web Browser|Host Name|Proxy Server</choices> <answer>IP Address</answer> </question> <question> <text>The term DHCP stands for:</text> <choices>Delta Host Connection Packet|Dynamic High-Speed Connection Protocol|Distributed Host Configuration Process|Dynamic Host Configuration Protocol</choices> <answer>Dynamic Host Configuration Protocol</answer> </question> <question> <text>The IP in TCP/IP stands for:</text> <choices>Internet Protocol</choices> <answer>Internet Protocol</answer> </question> </quiz>
Sorry if my comments are distracting, a lot of it is stuff I tried before I got to this version. --Ibrahim 16:24, 11 November 2006 (MST) Forgot to sign it, oops. Used the time from the history page.
Version that puts all the questions on one page
Here's the version that puts them all on one page. Took a while to get to work. Also, I'm not sure whether it's bad to keep calling $parser->parse() like that since it asks for a title each time. And for some reason the __NOEDITSECTION__ on the questions page doesn't work, but the one on the results page does. Aha, never mind. I found that I need to tell it to not add the edit links on each parse. This version is pretty much final. I'll make the default quiz on my wiki readable anonymously, so you can see how it looks. Default Quiz I made a custom namespace for quizzes, but it's entirely unnecessary. It's just so quizzes don't show up in search results basically.
<?php
#install extension hook
$wgExtensionFunctions[] = "wfQuizExtension";
#extension hook callback function
function wfQuizExtension() {
global $wgParser;
#install parser hook for <rss> tags
$wgParser->setHook( "quiz", "renderQuiz" );
}
#parser hook callback function
function renderQuiz($input, $args, &$parser) {
$parser->disableCache();
global $wgOut;
////////////////////////////////////////////////////////////
//
// xmlQuiz v1.1 - a simple quiz script
//
////////////////////////////////////////////////////////////
//
// This script allows you to quiz users on any number of
// questions and calculate the score.
//
// See readme.txt for more information.
//
// Author: Jon Thomas <http://www.fromthedesk.com/>
// Last Modified: 12/18/2005
//
// You may freely use, modify, and distribute this script.
//
////////////////////////////////////////////////////////////
//
// GET QUIZ DATA
// Add xml opening and closing tags to data since $input is the stuff between <quiz> tags
$data="<?xml version=\"1.0\"?> <quiz>".$input."</quiz>";
// create XML parser
$xParser = xml_parser_create();
// set parser options
xml_parser_set_option($xParser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($xParser, XML_OPTION_SKIP_WHITE, 1);
// parse XML data into arrays
xml_parse_into_struct($xParser, $data, $values, $tags);
// free parser
xml_parser_free($xParser);
//
// STRUCTURE XML DATA INTO ARRAY
//
// set counter variable for to-be-created questions array
$questionNo = 0;
// cycle through parsed XML data to look for text and answer tags
foreach ($values as $key=>$val) {
// save value to "questions" array if this is a TEXT tag
if ($val[tag] == "text") {
$questions[$questionNo]['text'] = $val[value];
}
// save value to "questions" array if this is a CHOICES tag
if ($val[tag] == "choices") {
$questions[$questionNo]['choices'] = $val[value];
}
// save value to "questions" array if this is an ANSWER tag
if ($val[tag] == "answer") {
$questions[$questionNo]['answer'] = $val[value];
// increment question counter variable
$questionNo++;
}
}
//
// IMPORT POST VARIABLES
// doesn't seem to work inside MediaWiki, I could be wrong.
// In any case, it's not really essential or even that helpful.
import_request_variables("p", "post_");
$parsedStuff=$parser->parse("[[Category:Quizzes]] __NOEDITSECTION__ __NOTOC__\n", $parser->mTitle, $parser->mOptions, false, false);
$output = $parsedStuff->getText();
//
// Print all questions in one go, because it's a lot easier with longer quizzes
//
if (!isset($_POST['answers'])) {
for($n=0;$n<$questionNo;$n++){
// number of next question = $n
// print question
$wtext = "== Question " . ($n + 1) . " of " . ($questionNo) . " ==\n\n";
$wtext .= "<b>" . $questions[$n]['text'] . "</b>\n";
$text="";
if($n==0)
$text = "<form action=\"".$PHP_SELF."\" method=\"post\">\n";
// split choices into "choices" array
$choices = explode("|", $questions[$n]['choices']);
// print text field if there are no choices
if (count($choices) == 1) {
$text .= "<input type=\"text\" name=\"answers[$n]\" size=10>\n";
}
// print radio fields if there are multiple choices
else {
// print a radio button for each choice
for ($i = 0; $i < count($choices); $i++) {
$text .= "<input type=\"radio\" name=\"answers[$n]\" value=\"" . $choices[$i] . "\">" . $choices[$i] . "<br>\n";
}
}
// print appropriate button label
if($n+1==$questionNo){
$text .= "\n<br />\n<input type=\"submit\" value=\"Calculate Score\">\n";
$text .= "</form>\n";
}
$parsedStuff=$parser->parse($wtext."__NOEDITSECTION__", $parser->mTitle, $parser->mOptions, false, false);
$output.= $parsedStuff->getText() . $text;
}
}
//
// CALCULATE AND PRINT SCORE
//
else {
// get number of questions
$noQuestions = count($questions);
// get number of correct answers
for ($i = 0; $i < $noQuestions; $i++) {
// increment "noCorrectAnswers" variable if user has correct answer
if ($questions[$i]['answer'] == $_POST['answers'][$i]) {
$noCorrectAnswers++;
}
}
// calculate score
$score = ($noCorrectAnswers / $noQuestions) * 100;
// round score to nearest whole precentage point
$score = round($score);
$wtext="";
$text="";
// print score
$wtext .= "__NOTOC__ \n __NOEDITSECTION__ \n== Quiz Results ==\n";
$wtext .= "=== You have scored $score% on this quiz.===\n";
if ($noCorrectAnswers == 0) {
$text .= "<p>You answered no questions correctly. <a href=" . $PHP_SELF . ">Try again.</a></p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
if ($noCorrectAnswers > 0 && $noCorrectAnswers < $noQuestions) {
$text .= "<p>You answered $noCorrectAnswers out of $noQuestions questions correctly. <a href=" . $PHP_SELF . ">Try again.</a></p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
if ($noCorrectAnswers == $noQuestions) {
$text .= "<p>You answered all questions correctly!</p>";
$text .= "<p>Return to the <a href=\"/wiki/index.php\">Main Page</a></p>";
}
for ($i = 0; $i < $noQuestions; $i++) {
// print question
$wtext .= "== Question " . ($i + 1) . " ==\n\n";
$wtext .= "<b>" . $questions[$i]['text'] . "</b>\n";
// split choices into "choices" array
$choices = explode("|", $questions[$i]['choices']);
// print radio fields if there are multiple choices
// print text field if there are no choices
if (count($choices) == 1) {
if ($_POST['answers'][$i] == $questions[$i]['answer']) {
$wtext .= "<b>" . $_POST['answers'][$i]. "</b> <font color=blue><b>CORRECT</b></font><br>\n";
} else {
$wtext .= "<b>" . $_POST['answers'][$i]. "</b> <font color=red><b>INCORRECT</b></font><br>\n";
}
} else {
$wtext .= "\n";
// print a radio button for each choice
for ($x = 0; $x < count($choices); $x++) {
if (($questions[$i]['answer'] == $_POST['answers'][$i]) && ($_POST['answers'][$i] == $choices[$x])) {
$wtext .= " <b> " . $choices[$x] . "</b> <font color=blue><b>CORRECT</b></font><br>\n";
} else {
if ($_POST['answers'][$i] == $choices[$x]) {
$wtext .= " <b> " . $choices[$x] . "</b> <font color=red><b>INCORRECT</b></font><br>\n";
} else {
$wtext .= " " . $choices[$x] . "<br>\n";
}
}
}
$wtext .= "\n";
}
}
$parsedStuff=$parser->parse($wtext, $parser->mTitle, $parser->mOptions, false, false);
$output.= $parsedStuff->getText() . $text;
}
return $output;
}
?>
--Ibrahim 20:00, 11 November 2006 (MST)
|
-- (smcnaught - Smcnaught - I am also available on irc.chekmate.org #MediaWiki: You have done a wonderful job. I just returned from the UK so over the next few days I will incorporate your version into the pages and publish it with your credits. |




