Removed import/export files. Moved to new (question) location

This commit is contained in:
thepurpleblob 2006-02-24 16:01:44 +00:00
parent 7b4ac82a37
commit d8c2c55bf9
33 changed files with 0 additions and 6708 deletions

View File

@ -1,19 +0,0 @@
QUIZ FILE FORMATS FOR IMPORT/EXPORT
------------------------------------
This directory contains plug-in sub-modules to add
import-export formats to Moodle quizzes.
Each sub-module must contain at least a format.php file
containing a class that contains functions for reading,
writing, importing and exporting quiz questions.
For correct operation the class name must be based on the
name of the containing directory, e.g.,
directory: webct
class: class quiz_format_webct extends quiz_default_format {
Most of them are based on the class found in mod/quiz/format.php.
See the comments therein for more information.

View File

@ -1,94 +0,0 @@
<?php // $Id$
///
/// Written by Tom Robb <tom@robb.net> 2 November 2003
///
////////////////////////////////////////////////////////////////////////////
/// AIKEN FORMAT
///
/// This Moodle class provides all functions necessary to import and export
/// one-correct-answer multiple choice questions in this format:
///
/// Question text
/// A) Choice #1
/// B) Choice #2
/// C) Choice #3
/// D) Choice #4
/// ANSWER: B
/// (blank line next not necessary since "AN" at the beginning of a line
/// triggers the question input and causes input to start all over.
///
///Only ONE correct answer is allowed with no feedback responses.
///
///Be sure to reword "All of the above" type questions as "All of these" (etc.) so that choices can
/// be randomized
///
///This should work on WIN, Mac and Unix although only tested on Mac
///
////////////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
class quiz_format_aiken extends quiz_default_format {
function provide_import() {
return true;
}
//will this override default function?
function readquestions($lines){
$questions = array();
$question = $this->defaultquestion();
$endchar = chr(13);
foreach ($lines as $line) {
$stp = strpos($line,$endchar,0);
$newlines = explode($endchar,$line);
$foundQ = 0;
for ($i=0; $i < count($newlines);$i++){
$nowline = addslashes($newlines[$i]);
///Go through the arrage and build an object called $question
///When done, add $question to $questions
if (strlen($nowline)< 2) {
continue;
}
// This will show everyline when file is being processed
// print("$nowline<br />");
$leader = substr(ltrim($nowline),0,2);
if (strpos(".A)B)C)D)E)F)G)H)I)J)A.B.C.D.E.F.G.H.I.J.",$leader)>0){
//trim off the label and space
$question->answer[] = substr($nowline,3);
$question->fraction[] = 0;
$question->feedback[] = '';
continue;
}
if ($leader == "AN"){
$ans = trim(strstr($nowline,":"));
$ans = substr($ans,2,1);
//A becomes 0 since array starts from 0
$rightans = ord($ans) - 65;
$question->fraction[$rightans] = 1;
$questions[] = $question;
//clear array for next question set
$question = NULL;
continue;
} else {
//Must be the first line since no leader
$question->qtype = MULTICHOICE;
$question->name = addslashes( substr($nowline,0,50) );
$question->questiontext = $nowline;
$question->single = 1;
$question->feedback[] = "";
}
}
}
return $questions;
}
function readquestion($lines) {
//this is no longer needed but might still be called by default.php
return;
}
}
?>

View File

@ -1,224 +0,0 @@
<?php // $Id$
////////////////////////////////////////////////////////////////////////////
/// Academy of Nursing format
///
/// Based on missingword.php
///
/// This Moodle class provides all functions necessary to import and export
/// one-correct-answer multiple choice questions in this format:
///
/// As soon as we begin to explore our body parts as infants
/// we become students of {=anatomy and physiology ~reflexology
/// ~science ~experiment}, and in a sense we remain students for life.
///
/// Each answer is separated with a tilde ~, and the correct answer is
/// prefixed with an equals sign =
///
/// Afterwards, all short-answer questions are randomly packed into
/// 4-answer matching questions.
///
////////////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
class quiz_format_aon extends quiz_default_format {
function provide_import() {
return true;
}
function readquestion($lines) {
/// Given an array of lines known to define a question in
/// this format, this function converts it into a question
/// object suitable for processing and insertion into Moodle.
$question = $this->defaultquestion();
$text = implode($lines, " ");
/// Find answer section
$answerstart = strpos($text, "{");
if ($answerstart === false) {
if ($this->displayerrors) {
echo "<p>$text<p>Could not find a {";
}
return false;
}
$answerfinish = strpos($text, "}");
if ($answerfinish === false) {
if ($this->displayerrors) {
echo "<p>$text<p>Could not find a }";
}
return false;
}
$answerlength = $answerfinish - $answerstart;
$answertext = substr($text, $answerstart + 1, $answerlength - 1);
/// Save the new question text
$question->questiontext = addslashes(substr_replace($text, "_____", $answerstart, $answerlength+1));
$question->name = substr($question->questiontext, 0, 60)." ...";
/// Parse the answers
$answers = explode("~", $answertext);
$countanswers = count($answers);
switch ($countanswers) {
case 0: // invalid question
if ($this->displayerrors) {
echo "<p>No answers found in $answertext";
}
return false;
case 1:
$question->qtype = SHORTANSWER;
$answer = trim($answers[0]);
if ($answer[0] == "=") {
$answer = substr($answer, 1);
}
$question->answer[] = addslashes($answer);
$question->fraction[] = 1;
$question->feedback[] = "";
return $question;
default:
$question->qtype = MULTICHOICE;
$answers = swapshuffle($answers);
foreach ($answers as $key => $answer) {
$answer = trim($answer);
if ($answer[0] == "=") {
$question->fraction[$key] = 1;
$answer = substr($answer, 1);
} else {
$question->fraction[$key] = 0;
}
$question->answer[$key] = addslashes($answer);
$question->feedback[$key] = "";
}
$question->single = 1; // Only one answer is allowed
return $question;
}
}
function importpostprocess() {
/// Goes through the questionids, looking for shortanswer questions
/// and converting random groups of 4 into matching questions.
/// Doesn't handle shortanswer questions with more than one answer
global $CFG;
print_heading(count($this->questionids)." ".get_string("questions", "quiz"));
$questionids = implode(',', $this->questionids);
if (!$shortanswers = get_records_select("quiz_questions",
"id IN ($questionids) AND qtype = ".SHORTANSWER,
"", "id,qtype")) {
return true;
}
$shortanswerids = array();
foreach ($shortanswers as $key => $shortanswer) {
$shortanswerids[] = $key;
}
$strmatch = get_string("match", "quiz")." (".$this->category->name.")";
$shortanswerids = swapshuffle($shortanswerids);
$count = $shortanswercount = count($shortanswerids);
$i = 1;
$matchcount = 0;
$question->category = $this->category->id;
$question->qtype = MATCH;
$question->questiontext = get_string("randomsamatchintro", "quiz");
$question->image = "";
while ($count > 4) {
$matchcount++;
$question->name = "$strmatch $i";
$question->subquestions = array();
$question->subanswers = array();
$extractids = implode(',', array_splice($shortanswerids, -4));
$count = count($shortanswerids);
$extracts = get_records_sql("SELECT q.questiontext, a.answer
FROM {$CFG->prefix}quiz_questions q,
{$CFG->prefix}quiz_shortanswer sa,
{$CFG->prefix}quiz_answers a
WHERE q.id in ($extractids)
AND sa.question = q.id
AND a.id = sa.answers");
if (count($extracts) != 4) {
print_object($extracts);
notify("Could not find exactly four shortanswer questions with ids: $extractids");
continue;
}
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
$question->version = 1; // Original version of this question
$question->parent = 0;
if (!$question->id = insert_record("quiz_questions", $question)) {
error("Could not insert new question!");
}
foreach ($extracts as $shortanswer) {
$question->subquestions[] = addslashes($shortanswer->questiontext);
$question->subanswers[] = addslashes($shortanswer->answer);
}
$result = quiz_save_question_options($question);
if (!empty($result->error)) {
notify("Error: $result->error");
}
if (!empty($result->notice)) {
notify($result->notice);
}
/// Delete the old short-answer questions
execute_sql("DELETE FROM {$CFG->prefix}quiz_questions WHERE id IN ($extractids)", false);
execute_sql("DELETE FROM {$CFG->prefix}quiz_shortanswer WHERE question IN ($extractids)", false);
execute_sql("DELETE FROM {$CFG->prefix}quiz_answers WHERE question IN ($extractids)", false);
}
if ($count) { /// Delete the remaining ones
foreach ($shortanswerids as $shortanswerid) {
delete_records("quiz_questions", "id", $shortanswerid);
delete_records("quiz_shortanswer", "question", $shortanswerid);
delete_records("quiz_answers", "question", $shortanswerid);
}
}
$info = "$shortanswercount ".get_string("shortanswer", "quiz").
" => $matchcount ".get_string("match", "quiz");
print_heading($info);
$options['category'] = $this->category->id;
echo "<center>";
print_single_button("multiple.php", $options, get_string("randomcreate", "quiz"));
echo "</center>";
return true;
}
}
?>

View File

@ -1,304 +0,0 @@
<?php // $Id$
////////////////////////////////////////////////////////////////////////////
/// Blackboard 6.0 Format
///
/// This Moodle class provides all functions necessary to import and export
///
///
////////////////////////////////////////////////////////////////////////////
// Based on default.php, included by ../import.php
require_once ("$CFG->libdir/xmlize.php");
class quiz_format_blackboard extends quiz_default_format {
function provide_import() {
return true;
}
/********************************
function readdata($filename) {
/// Returns complete file with an array, one item per line
if (is_readable($filename)) {
$zip = zip_open($filename);
$zip_entry = $zip_read($zip);
if (strstr($zip_entry_name($zip_entry), "imsmanifest") == 0)
$zip_entry = $zip_read($zip); // skip past manifest file
if (zip_entry_open($zip, $zip_entry, "r")) {
$strbuf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
$buf = explode("\n", $strbuf);
zip_entry_close($zip_entry);
zip_close($zip);
return $buf;
} else {
zip_close($zip);
return false;
}
}
return false;
}
********************************/
function readquestions ($lines) {
/// Parses an array of lines into an array of questions,
/// where each item is a question object as defined by
/// readquestion().
$text = implode($lines, " ");
$xml = xmlize($text, 0);
$questions = array();
process_tf($xml, $questions);
process_mc($xml, $questions);
process_ma($xml, $questions);
process_fib($xml, $questions);
process_matching($xml, $questions);
return $questions;
}
}
//----------------------------------------
// Process True / False Questions
//----------------------------------------
function process_tf($xml, &$questions) {
$tfquestions = $xml["POOL"]["#"]["QUESTION_TRUEFALSE"];
for ($i = 0; $i < sizeof ($tfquestions); $i++) {
$question = $this->defaultquestion();
$question->qtype = TRUEFALSE;
$question->single = 1; // Only one answer is allowed
$thisquestion = $tfquestions[$i];
// put questiontext in question object
$question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
// put name in question object
$question->name = $question->questiontext;
$choices = $thisquestion["#"]["ANSWER"];
$correct_answer = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"][0]["@"]["answer_id"];
// first choice is true, second is false.
$id = $choices[0]["@"]["id"];
if (strcmp($id, $correct_answer) == 0) { // true is correct
$question->answer = 1;
$question->feedbacktrue = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]));
$question->feedbackfalse = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]));
} else { // false is correct
$question->answer = 0;
$question->feedbacktrue = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]));
$question->feedbackfalse = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]));
}
$questions[] = $question;
}
}
//----------------------------------------
// Process Multiple Choice Questions
//----------------------------------------
function process_mc($xml, &$questions) {
$mcquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLECHOICE"];
for ($i = 0; $i < sizeof ($mcquestions); $i++) {
$question = $this->defaultquestion();
$question->qtype = MULTICHOICE;
$question->single = 1; // Only one answer is allowed
$thisquestion = $mcquestions[$i];
// put questiontext in question object
$question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
// put name of question in question object
$question->name = $question->questiontext;
$choices = $thisquestion["#"]["ANSWER"];
for ($j = 0; $j < sizeof ($choices); $j++) {
$choice = trim($choices[$j]["#"]["TEXT"][0]["#"]);
// put this choice in the question object.
$question->answer[$j] = addslashes($choice);
$id = $choices[$j]["@"]["id"];
$correct_answer_id = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"][0]["@"]["answer_id"];
// if choice is the answer, give 100%, otherwise give 0%
if (strcmp ($id, $correct_answer_id) == 0) {
$question->fraction[$j] = 1;
$question->feedback[$j] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]));
} else {
$question->fraction[$j] = 0;
$question->feedback[$j] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]));
}
}
$questions[] = $question;
}
}
//----------------------------------------
// Process Multiple Choice Questions With Multiple Answers
//----------------------------------------
function process_ma($xml, &$questions) {
$maquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLEANSWER"];
for ($i = 0; $i < sizeof ($maquestions); $i++) {
$question = $this->defaultquestion();
$question->qtype = MULTICHOICE;
$question->defaultgrade = 1;
$question->single = 0; // More than one answers allowed
$question->image = ""; // No images with this format
$thisquestion = $maquestions[$i];
// put questiontext in question object
$question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
// put name of question in question object
$question->name = $question->questiontext;
$choices = $thisquestion["#"]["ANSWER"];
$correctanswers = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"];
for ($j = 0; $j < sizeof ($choices); $j++) {
$choice = trim($choices[$j]["#"]["TEXT"][0]["#"]);
// put this choice in the question object.
$question->answer[$j] = addslashes($choice);
$correctanswercount = sizeof($correctanswers);
$id = $choices[$j]["@"]["id"];
$iscorrect = 0;
for ($k = 0; $k < $correctanswercount; $k++) {
$correct_answer_id = trim($correctanswers[$k]["@"]["answer_id"]);
if (strcmp ($id, $correct_answer_id) == 0) {
$iscorrect = 1;
}
}
if ($iscorrect) {
$question->fraction[$j] = floor(100000/$correctanswercount)/100000; // strange behavior if we have more than 5 decimal places
$question->feedback[$j] = addslashes(trim($thisquestion["#"]["GRADABLE"][$j]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]));
} else {
$question->fraction[$j] = 0;
$question->feedback[$j] = addslashes(trim($thisquestion["#"]["GRADABLE"][$j]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]));
}
}
$questions[] = $question;
}
}
//----------------------------------------
// Process Fill in the Blank Questions
//----------------------------------------
function process_fib($xml, &$questions) {
$fibquestions = $xml["POOL"]["#"]["QUESTION_FILLINBLANK"];
for ($i = 0; $i < sizeof ($fibquestions); $i++) {
$question = $this->defaultquestion();
$question->qtype = SHORTANSWER;
$question->usecase = 0; // Ignore case
$thisquestion = $fibquestions[$i];
// put questiontext in question object
$question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
// put name of question in question object
$question->name = $question->questiontext;
$answer = trim($thisquestion["#"]["ANSWER"][0]["#"]["TEXT"][0]["#"]);
$question->answer[] = addslashes($answer);
$question->fraction[] = 1;
$question->feedback[0] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]));
$question->feedback[1] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]));
$questions[] = $question;
}
}
//----------------------------------------
// Process Matching Questions
//----------------------------------------
function process_matching($xml, &$questions) {
$matchquestions = $xml["POOL"]["#"]["QUESTION_MATCH"];
for ($i = 0; $i < sizeof ($matchquestions); $i++) {
$question = $this->defaultquestion();
$question->qtype = MATCH;
$thisquestion = $matchquestions[$i];
// put questiontext in question object
$question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
// put name of question in question object
$question->name = $question->questiontext;
$choices = $thisquestion["#"]["CHOICE"];
for ($j = 0; $j < sizeof ($choices); $j++) {
$subquestion = NULL;
$choice = $choices[$j]["#"]["TEXT"][0]["#"];
$choice_id = $choices[$j]["@"]["id"];
$question->subanswers[] = addslashes(trim($choice));
$correctanswers = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"];
for ($k = 0; $k < sizeof ($correctanswers); $k++) {
if (strcmp($choice_id, $correctanswers[$k]["@"]["choice_id"]) == 0) {
$answer_id = $correctanswers[$k]["@"]["answer_id"];
$answers = $thisquestion["#"]["ANSWER"];
for ($m = 0; $m < sizeof ($answers); $m++) {
$answer = $answers[$m];
$current_ans_id = $answer["@"]["id"];
if (strcmp ($current_ans_id, $answer_id) == 0) {
$answer = $answer["#"]["TEXT"][0]["#"];
$question->subquestions[] = addslashes(trim($answer));
break;
}
}
break;
}
}
}
$questions[] = $question;
}
}
?>

View File

@ -1,512 +0,0 @@
<?php // $Id$
////////////////////////////////////////////////////////////////////
/// Class for importing course test manager questions. //
/// //
/// //
////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
require_once($CFG->dirroot.'/lib/uploadlib.php');
class quiz_format_coursetestmanager extends quiz_default_format {
function provide_import() {
return true;
}
function importpreprocess($category) {
$this->category = $category; // Important
return true;
}
function importprocess($filename) {
global $CFG,$QUIZ_FILE_FORMAT,$strimportquestions,$form,$question_category,$category,$course,
$hostname, $mdapath, $mdbpath;
if ((PHP_OS == "Linux") and isset($hostname)) {
$hostname = trim($hostname);
// test the ODBC socket server connection
// if failure, unset hostname and set hostname_access_error
$question_categories = $this->getquestioncategories($mdbpath, $mdapath, $hostname);
if (!$question_categories) {
$hostname_access_error = $hostname . " ";
unset($hostname);
} else {
$hostname_access_error = 0;
}
}
if ((PHP_OS == "Linux") and !isset($hostname)) {
// copy the file to a semi-permanent location
if (! $basedir = make_upload_directory("$course->id")) {
error("The site administrator needs to fix the file permissions for the data directory");
}
if (!isset($hostname_access_error)) {
$bname=basename($filename);
$cleanfilename = clean_filename($bname);
if ($cleanfilename) {
$newfile = "$basedir/$cleanfilename";
if (move_uploaded_file($filename, $newfile)) {
chmod($newfile, 0666);
clam_log_upload($newfile,$course);
} else {
notify(get_string("uploadproblem", "", $filename));
}
}
$filename = $newfile;
}
print_heading_with_help($strimportquestions, "import", "quiz");
print_simple_box_start("center");
if ($hostname_access_error) { notify("couldn't connect to ODBC Socket Server on " . $hostname_access_error); }
echo "<form method=\"post\" action=\"import.php\">";
echo "<table cellpadding=\"5\">";
echo "<tr><td align=\"right\">";
echo "What is the hostname or IP address of the ODBC Socket Server:</td><td>";
echo " <input name=\"hostname\" type=\"text\" size=\"50\" value=\"".stripslashes($hostname_access_error)."\" />";
echo " <input name=\"filename\" type=\"hidden\" value=\"".$filename."\" />";
echo " <input name=\"category\" type=\"hidden\" value=\"".$category->id."\" />";
echo " <input name=\"format\" type=\"hidden\" value=\"".$form->format."\" />";
echo "</td><td>&nbsp;</td></tr>";
echo "<tr><td align=\"right\">";
echo "What is the location of the database (.mdb file) on the Socket Server:</td><td>";
echo " <input name=\"mdbpath\" type=\"text\" size=\"50\" value=\"".stripslashes($mdbpath)."\" />";
echo "</td><td>&nbsp;</td></tr>";
echo "<tr><td align=\"right\">";
echo "What is the location of the system database (System.mda file) on the Socket Server:</td><td>";
echo " <input name=\"mdapath\" type=\"text\" size=\"50\" value=\"".stripslashes($mdapath)."\" />";
echo "</td><td>&nbsp;</td></tr>";
echo "<tr><td>&nbsp;</td><td>";
echo " <input type=\"submit\" name=\"save\" value=\"Connect to Server\" />";
echo "</td></tr>";
echo "</table>";
echo "</form>";
print_simple_box_end();
print_footer($course);
exit;
}
// we get here if running windows or after connect to ODBC socket server on linux
//
// this generates the page to choose categories of questions to import
//
if (!isset($question_category)) {
if (PHP_OS == "WINNT") {
// copy the file to a semi-permanent location
if (! $basedir = make_upload_directory("$course->id")) {
error("The site administrator needs to fix the file permissions for the data directory");
}
$bname=basename($filename);
$cleanfilename = clean_filename($bname);
if ($cleanfilename) {
$newfile = "$basedir/$cleanfilename";
if (move_uploaded_file($filename, $newfile)) {
chmod($newfile, 0666);
clam_log_upload($newfile,$course);
} else {
notify(get_string("uploadproblem", "", $filename));
}
}
$filename = $newfile;
}
// end of file copy
// don't have to do this on linux, since it's alreay been done in the test above
if (PHP_OS == "WINNT") { $question_categories = $this->getquestioncategories($filename); }
// print the intermediary form
if (!$categories = quiz_get_category_menu($course->id, true)) {
error("No categories!");
}
print_heading_with_help($strimportquestions, "import", "quiz");
print_simple_box_start("center");
echo "<form method=\"post\" action=\"import.php\">";
echo "<table cellpadding=\"5\">";
echo "<tr><td align=\"right\">";
echo "Choose a category of questions to import:</td><td>";
asort($question_categories);
choose_from_menu($question_categories, "question_category","All Categories","All Categories", "", "allcategories");
echo " <input name=\"filename\" type=\"hidden\" value=\"".$filename."\" />";
echo " <input name=\"category\" type=\"hidden\" value=\"".$category->id."\" />";
echo " <input name=\"format\" type=\"hidden\" value=\"".$form->format."\" />";
if (PHP_OS == "Linux") {
echo " <input name=\"hostname\" type=\"hidden\" value=\"".stripslashes(trim($hostname))."\" />";
echo " <input name=\"mdbpath\" type=\"hidden\" value=\"".stripslashes($mdbpath)."\" />";
echo " <input name=\"mdapath\" type=\"hidden\" value=\"".stripslashes($mdapath)."\" />";
}
echo "</td><td>&nbsp;</td>";
echo "</tr><tr><td>&nbsp;</td><td>";
echo " <input type=\"submit\" name=\"save\" value=\"Import Questions\" />";
echo "</td></tr>";
echo "</table>";
echo "</form>";
print_simple_box_end();
print_footer($course);
exit;
}
//
// this is the main import section
//
notify("Importing questions");
if (PHP_OS == "Linux") {
$hostname = trim($hostname);
$records = $this->getquestions($mdbpath,$question_category,$mdapath, $hostname);
} else {
$records = $this->getquestions($filename,$question_category);
}
foreach ($records as $qrec)
{
$question = $this->defaultquestion();
if ($qrec[9] != "") {
$question->image = $qrec[9];
}
// 0 Selected
// 1 PracticeTestOK?
// 2 QuestionText
// 3 QuestionType
// 4 Option1Text
// 5 Option2Text
// 6 Option3Text
// 7 Option4Text
// 8 CorrectAnswer
// 9 Graphic
// 10 Module
// 11 ChapterNumber
// 12 PageNumber
$ref = "Answer can be found in chapter ". $qrec[11] . ", page " . $qrec[12] . ".";
switch ($qrec[3]) {
case 1:
$question->qtype = MULTICHOICE; // MULTICHOICE, SHORTANSWER, TRUEFALSE
// echo "<pre>";echo htmlspecialchars($qrec[2]); echo "</pre>";
$question->questiontext = addslashes(trim($qrec[2]));
// echo "<pre>";echo $question->questiontext; echo "</pre>";
$question->name = preg_replace("/<br />/", "", $question->questiontext);
$question->single = 1; // Only one answer is allowed -- used for multiple choicers
$fractionset = 0;
for ($i=4;$i<=7;$i++) {
if ($qrec[$i] != "") {
$question->answer[$i-3]=addslashes($qrec[$i]);
if ($qrec[8] == $i-3) { // if this is the index of CorrectAnswer
$question->fraction[$i-3] = 1;
$fractionset = 1;
} else {
$question->fraction[$i-3] = 0;
}
$question->feedback[$i-3] = (($qrec[8] == $i-3)?"Correct. ":"Incorrect. ") . $ref;
}
}
if ($fractionset == 0) { $question->fraction[1] = 1; }
break;
case 2: // TRUE FALSE
$question->qtype = TRUEFALSE;
$question->questiontext = addslashes(trim($qrec[2]));
$question->name = preg_replace("/<br />/", "", $question->questiontext);
// for TF, $question->answer should be 1 for true, 0 for false
if ($qrec[8] == "T") { $question->answer =1;} else { $question->answer = 0; }
// for TF, use $question->feedbacktrue and feedbackfalse
$question->feedbacktrue = (($qrec[8] =="T")?"Correct. ":"Incorrect. ") . $ref;
$question->feedbackfalse = (($qrec[8] =="F")?"Correct. ":"Incorrect. ") . $ref;
break;
case 3:
$question->qtype = SHORTANSWER;
$question->questiontext = addslashes(trim($qrec[2]));
// echo "<pre>";echo $question->questiontext; echo "</pre>";
$question->name = preg_replace("/<br />/", "", $question->questiontext);
$question->usecase=0; // Ignore case -- for SHORT ANSWER questions
$answers = explode("~", $qrec[8]);
$question->answer[0]=" ";
$question->fraction[0]=1;
for ($i=0;$i<count($answers);$i++) {
$question->answer[$i] = addslashes(trim($answers[$i]));
$question->feedback[$i] = $ref;
$question->fraction[$i] = 1; // 1 for 100%, 0 for none or somewhere in between
}
break;
case 4:
$question = 0;
notify("Cannot use essay questions - skipping question ". $qrec[2] . " " . $ref);
break;
default:
$question = 0;
notify("Misformatted Record. Question Skipped.");
break;
}
if ($question) { $questions[] = $question; }
}
$count = 0;
// process all the questions
if (PHP_OS == "WINNT") {
$filename = str_replace("\\\\","\\",$filename);
$filename = str_replace("/","\\",$filename);
}
foreach ($questions as $question) { // Process and store each question
$count++;
echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>";
$question->category = $this->category->id;
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
$question->version = 1; // Original version of this question
if (!$question->id = insert_record("quiz_questions", $question)) {
error("Could not insert new question!");
}
$this->questionids[] = $question->id;
// Now to save all the answers and type-specific options
$result = quiz_save_question_options($question);
if (!empty($result->error)) {
notify($result->error);
$this->deletedatabase($filename);
return false;
}
if (!empty($result->notice)) {
notify($result->notice);
$this->deletedatabase($filename);
return true;
}
}
$this->deletedatabase($filename);
return true;
}
function importpostprocess() {
return true;
}
function deletedatabase($filename) {
if (! $this->fulldelete($filename)) {
echo "<br />Error: Could not delete: $filename";
return false;
}
return true;
}
function getquestions($filename, $category, $mdapath="", $hostname="") {
if (($category == "allcategories") or ($category == "")) {
$sql = "SELECT * FROM TBQuestions";
} else {
$sql = "SELECT * FROM TBQuestions where module = '".$category."'";
}
if (PHP_OS == "WINNT") {
$ldb =& $this->connect_win($filename);
$qset = $ldb->Execute("$sql");
if ( $qset->RecordCount() > 0 ) {
$records = $qset->GetAssoc(true);
} else {
$this->err("There were no records in the database.",$dsn);
$ldb->Close();
return false;
}
$ldb->Close();
} else { // if PHP_OS == WINNT
// we have a linux installation
$result = $this->query_linux($sql,$filename, $mdapath,$hostname);
if ( count($result) > 0 ) {
// get rid of the ID field in the first column.
for($i=0;$i<count($result);$i++) {
foreach (array_keys($result[$i]) as $j) {
$records[$i][$j-1] = $result[$i][$j];
}
}
} else {
$this->err("There were no records in the database.",$dsn);
$ldb->Close();
return false;
}
// xml test and connect
} // PHP_OS TEST
return $records;
}
function getquestioncategories($filename, $mdapath="", $hostname="") {
global $CFG, $result;
$sql = "SELECT Distinct module FROM TBQuestions";
if (PHP_OS == "WINNT") {
$ldb =& $this->connect_win($filename);
$qset = $ldb->Execute("$sql");
if ( $qset->RecordCount() > 0 ) {
$records = $qset->GetArray(true);
foreach ($records as $record) {
$categories[$record[0]] = $record[0];
}
} else { // if recordcount
$this->err("There were no records in the database.",$dsn);
$ldb->Close();
return false;
}
$ldb->Close();
} else { // if PHP_OS == WINNT
// we have a linux installation
$result = $this->query_linux($sql, $filename, $mdapath, $hostname);
for($i=0;$i<count($result);$i++) {
$categories[$result[$i][0]] = $result[$i][0];
}
} // PHP_OS TEST
return $categories;
}
function query_linux($sql, $mdbpath, $mdapath, $hostname) {
global $result;
include_once("odbcsocketserver.class.php");
// set up socket server object to connect to remote host
$oTest = new ODBCSocketServer;
//Set the Hostname, port, and connection string
$oTest->sHostName = $hostname;
$oTest->nPort = 9628;
// $oTest->sConnectionString="DRIVER=Microsoft Access Driver (*.mdb);SystemDB=C:\CTM\System.mda;DBQ=C:\CTM\of2K3\ctm.mdb;UID=Assess;PWD=VBMango;";
$oTest->sConnectionString="DRIVER=Microsoft Access Driver (*.mdb);SystemDB=".
$mdapath.";DBQ=".$mdbpath.";UID=Assess;PWD=VBMango;";
// send and receive XML communication
$qResult = $oTest->ExecSQL($sql);
// set up XML parser to read the results
$xml_parser = xml_parser_create("US-ASCII");
xml_set_element_handler($xml_parser, "quiz_xmlstart", "quiz_xmlend");
xml_set_character_data_handler($xml_parser, "quiz_xmldata");
// parse the XML and get back the result set array
if (!xml_parse($xml_parser, $qResult)) {
$this->err("XML error: ".xml_error_string(xml_get_error_code($xml_parser))
." at line ".xml_get_current_line_number($xml_parser),$oTest->sConnectionString);
return false;
} else {
// echo("Successful XML parse. ");
// prepare the array for use in the pull-down
/* echo "<br />count of rows is ". count ($result);
echo "<pre>\n";
$qResult = HtmlSpecialChars($qResult);
echo $qResult;
echo "\n</pre>";
*/
xml_parser_free($xml_parser);
// $sResult = HtmlSpecialChars($qResult);
//echo("<pre>");
// echo($sResult);
// echo("</pre>");
return $result;
}
}
function connect_win($filename) {
global $CFG, $systemdb;
// first, verify the location of System.mda
if (!isset($systemdb)) {
$systemdb=$this->findfile("System.mda");
}
if (! $systemdb) {
$this->err("The system database System.mda cannot be found. Check that you've uploaded it to the course.",$dsn);
die;
}
$ldb = &ADONewConnection('access');
$dsn="DRIVER=Microsoft Access Driver (*.mdb);SystemDB=".$systemdb.";DBQ=".$filename.";UID=Assess;PWD=VBMango;";
$dbconnected = $ldb->Connect($dsn);
if (! $dbconnected) {
$this->err("Moodle could not connect to the database.",$dsn);
die;
}
return $ldb;
}
function err($message, $dsn) {
echo "<font color=\"#990000\">";
echo "<p>Error: $message</p>";
echo "<p>ODBC File DSN: $dsn<br />";
echo "</font>";
}
function fulldelete($location) {
if (is_dir($location)) {
$currdir = opendir($location);
while ($file = readdir($currdir)) {
if ($file <> ".." && $file <> ".") {
$fullfile = $location."/".$file;
if (is_dir($fullfile)) {
if (!fulldelete($fullfile)) {
return false;
}
} else {
if (!unlink($fullfile)) {
return false;
}
}
}
}
closedir($currdir);
if (! rmdir($location)) {
return false;
}
} else {
if (!unlink($location)) {
return false;
}
}
return true;
}
function findfile($filename) {
global $CFG;
$dirs = $this->getcoursedirs();
$dirs[] = $CFG->dirroot."\mod\quiz\format";
foreach ($dirs as $dir) {
$file = $dir . "\System.mda";
// look for System.mda
if (is_file($file)) return $file;
}
return false;
}
function getcoursedirs() {
global $CFG;
// for every course in the system, find the root of the data directory
$courses = get_records_sql("select distinct id,fullname from ".$CFG->prefix."course");
$dirs = array();
if ($courses) {
foreach ($courses as $course) {
$dir = $CFG->dataroot . "/" . $course->id;
if (is_dir($dir)) { $dirs[] = $dir; }
}
}
return $dirs;
}
} // END OF CLASS
//Handler for starting elements
function quiz_xmlstart($parser, $name, $attribs) {
global $result,$row, $col, $incolumn;
$name = strtolower($name);
switch ($name) {
case "row":
$col=0;break;
case "column":
$incolumn = 1;break;
case "error":
break;
case "result":
$row = 0; break;
} // switch
}
//handler for the end of elements
function quiz_xmlend($parser, $name) {
global $result, $row, $col, $incolumn;
$name = strtolower($name);
switch ($name) {
case "row":
$row++;break;
case "column":
$incolumn = 0;
$col++;
break;
case "error":
break;
case "result":
break;
} // switch
} // function
//handler for character data
function quiz_xmldata($parser, $data) {
global $result, $row, $col, $incolumn;
if ($incolumn) { $result[$row][$col] = $result[$row][$col] . $data;
}
}
?>

View File

@ -1,334 +0,0 @@
<?php // $Id$
/*
**
** Examview 4.0 XML format into Moodle 1.4.3
** Author: Dan McCuaig ( dmccuaig@wvc.edu )
**
** @TODO:
** Take care of odd unicode character mapping (ex: curly quotes)
** Image and table support
** Formatting support
** Support of rejoinders
**
** $Log$
** Revision 1.3 2006/02/13 16:17:22 thepurpleblob
** Now used defaultquestion() method. Bug #4752
**
** Revision 1.2 2005/05/31 15:19:00 thepurpleblob
** merged from STABLE
**
** Revision 1.1.2.1 2005/05/31 15:17:20 thepurpleblob
** Took out dos line endings
**
** Revision 1.1 2005/05/16 08:12:40 thepurpleblob
** Added support for Examview import.
**
**
*/
// Based on default.php, included by ../import.php
require_once("$CFG->libdir/xmlize.php");
//require_once("xmlize.php");
/*
define("SHORTANSWER", "1");
define("TRUEFALSE", "2");
define("MULTICHOICE", "3");
define("MATCH", "5");
define("DESCRIPTION", "7");
define("NUMERICAL", "8");
define("MULTIANSWER", "9");
define("CALCULATED", "10");
*/
class quiz_format_examview extends quiz_default_format {
var $qtypes = array('tf' => TRUEFALSE,
'mc' => MULTICHOICE,
'yn' => TRUEFALSE,
'co' => SHORTANSWER,
'ma' => MATCH,
'mtf' => 99,
'nr' => NUMERICAL,
'pr' => 99,
'es' => 99,
'ca' => 99,
'ot' => 99
);
var $matching_questions = array();
function provide_import() {
return true;
}
function print_matching_questions()
{
foreach($this->matching_questions as $key => $value) {
print("$key => $value->questiontext<BR>");
print("Questions:<UL>");
foreach($value->subquestions as $qkey => $qvalue) {
print("<LI>$qkey => $qvalue</LI>");
}
print("</UL>");
print("Choices:<UL>");
foreach($value->subchoices as $ckey => $cvalue) {
print("<LI>$ckey => $cvalue</LI>");
}
print("</UL>");
print("Answers:<UL>");
foreach($value->subanswers as $akey => $avalue) {
print("<LI>$akey => $avalue</LI>");
}
print("</UL>");
}
}
function parse_matching_groups($matching_groups)
{
if (empty($matching_groups)) {
return;
}
foreach($matching_groups as $match_group) {
$newgroup = NULL;
$groupname = trim($match_group['@']['name']);
$questiontext = $this->ArrayTagToString($match_group['#']['text']['0']['#']);
$newgroup->questiontext = trim($questiontext);
$newgroup->subchoices = array();
$newgroup->subquestions = array();
$newgroup->subanswers = array();
$choices = $match_group['#']['choices']['0']['#'];
foreach($choices as $key => $value) {
if (strpos(trim($key),'choice-') !== FALSE) {
$key = strtoupper(trim(str_replace('choice-', '', $key)));
$newgroup->subchoices[$key] = trim($value['0']['#']);
}
}
$this->matching_questions[$groupname] = $newgroup;
}
}
function parse_ma($qrec, $groupname)
{
$match_group = $this->matching_questions[$groupname];
$phrase = trim($qrec['text']['0']['#']);
$answer = trim($qrec['answer']['0']['#']);
$match_group->subquestions[] = $phrase;
$match_group->subanswers[] = $match_group->subchoices[$answer];
$this->matching_questions[$groupname] = $match_group;
return NULL;
}
function process_matches(&$questions)
{
if (empty($this->matching_questions)) {
return;
}
foreach($this->matching_questions as $match_group) {
$question = $this->defaultquestion();
$htmltext = $this->htmlPrepare($match_group->questiontext);
$htmltext = addslashes($htmltext);
$question->questiontext = $htmltext;
$question->name = $question->questiontext;
$question->qtype = MATCH;
// No images with this format
// print($question->questiontext.' '.$question->id."<BR>");
$question->subquestions = array();
$question->subanswers = array();
foreach($match_group->subquestions as $key => $value) {
$htmltext = $this->htmlPrepare($value);
$htmltext = addslashes($htmltext);
$question->subquestions[] = $htmltext;
$htmltext = $this->htmlPrepare($match_group->subanswers[$key]);
$htmltext = addslashes($htmltext);
$question->subanswers[] = $htmltext;
}
$questions[] = $question;
}
}
// cleans unicode characters from string
// add to the array unicode_array as necessary
function cleanUnicode($text) {
//$unicode_array = array( "&#2019;" => "'");
//return strtr($text, $unicode_array);
return str_replace('&#x2019;', "'", $text);
}
function readquestions($lines)
{
/// Parses an array of lines into an array of questions,
/// where each item is a question object as defined by
/// readquestion().
$questions = array();
$currentquestion = array();
$text = implode($lines, ' ');
$text = $this->cleanUnicode($text);
$xml = xmlize($text, 0);
$this->parse_matching_groups($xml['examview']['#']['matching-group']);
$questionNode = $xml['examview']['#']['question'];
foreach($questionNode as $currentquestion) {
if ($question = $this->readquestion($currentquestion)) {
$questions[] = $question;
}
}
// print('<hr>');
// $this->print_matching_questions();
$this->process_matches($questions);
// print('<hr>');
return $questions;
}
// end readquestions
function htmlPrepare($htmltext)
{
$text = trim($text);
$text = htmlentities($htmltext, ENT_QUOTES);
//$htmltext = nl2br($text);
return $text;
}
function ArrayTagToString($aTag)
{
if (!is_array($aTag)) {
return $aTag;
}
$out = '';
foreach($aTag as $key => $value) {
if (is_array($value)) {
$out = $out.$this->ArrayTagToString($value);
} else {
$out = $value;
}
}
return $out;
}
function readquestion($qrec)
{
$type = trim($qrec['@']['type']);
$question = $this->defaultquestion();
$question->qtype = $this->qtypes[$type];
$question->single = 1;
// Only one answer is allowed
$htmltext = $this->ArrayTagToString($qrec['#']['text'][0]['#']);
$htmltext = $this->htmlPrepare($htmltext);
$htmltext = addslashes($htmltext);
$question->questiontext = $htmltext;
$question->name = $question->questiontext;
switch ($question->qtype) {
case MULTICHOICE:
$question = $this->parse_mc($qrec['#'], $question);
break;
case MATCH:
$groupname = trim($qrec['@']['group']);
$question = $this->parse_ma($qrec['#'], $groupname);
break;
case TRUEFALSE:
$question = $this->parse_tf_yn($qrec['#'], $question);
break;
case SHORTANSWER:
$question = $this->parse_co($qrec['#'], $question);
break;
case NUMERICAL:
$question = $this->parse_nr($qrec['#'], $question);
break;
break;
default:
print("<p>Question type ".$type." import not supported for ".$question->questiontext."<p>");
$question = NULL;
}
// end switch ($question->qtype)
return $question;
}
// end readquestion
function parse_tf_yn($qrec, $question)
{
$choices = array('T' => 1, 'Y' => 1, 'F' => 0, 'N' => 0 );
$answer = trim($qrec['answer'][0]['#']);
$question->answer = $choices[$answer];
if ($question->answer == 1) {
$question->feedbacktrue = 'Correct';
$question->feedbackfalse = 'Incorrect';
} else {
$question->feedbacktrue = 'Incorrect';
$question->feedbackfalse = 'Correct';
}
return $question;
}
function parse_mc($qrec, $question)
{
$answer = 'choice-'.strtolower(trim($qrec['answer'][0]['#']));
$choices = $qrec['choices'][0]['#'];
foreach($choices as $key => $value) {
if (strpos(trim($key),'choice-') !== FALSE) {
$question->answer[$key] = $this->htmlPrepare($value[0]['#']);
if (strcmp($key, $answer) == 0) {
$question->fraction[$key] = 1;
$question->feedback[$key] = 'Correct';
} else {
$question->fraction[$key] = 0;
$question->feedback[$key] = 'Incorrect';
}
}
}
return $question;
}
function parse_co($qrec, $question)
{
$question->usecase = 0;
$answer = trim($qrec['answer'][0]['#']);
$answers = explode("\n",$answer);
foreach($answers as $key => $value) {
$value = trim($value);
if (strlen($value) > 0) {
$question->answer[$key] = addslashes($value);
$question->fraction[$key] = 1;
$question->feedback[$key] = "Correct";
}
}
return $question;
}
function parse_nr($qrec, $question)
{
$answer = trim($qrec['answer'][0]['#']);
$answers = explode("\n",$answer);
foreach($answers as $key => $value) {
$value = trim($value);
if (is_numeric($value)) {
$errormargin = 0;
$question->answer[$key] = $value;
$question->fraction[$key] = 1;
$question->feedback[$key] = "Correct";
$question->min[$key] = $question->answer[$key] - $errormargin;
$question->max[$key] = $question->answer[$key] + $errormargin;
}
}
return $question;
}
}
// end class
?>

View File

@ -1,207 +0,0 @@
// EXAMPLE QUESTIONS for the GIFT import filter
// by Paul Tsuchido Shew, January 2004.
//-----------------------------------------//
// EXAMPLES FROM DESCRIPTION
//-----------------------------------------//
Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}
Grant is {~buried =entombed ~living} in Grant's tomb.
Grant is buried in Grant's tomb.{FALSE}
Who's buried in Grant's tomb?{=no one =nobody}
When was Ulysses S. Grant born?{#1822:1}
//-----------------------------------------//
// EXAMPLES FROM DOCUMENTATION
//-----------------------------------------//
// ===Multiple Choice===
Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}
Grant is {~buried =entombed ~living} in Grant's tomb.
The American holiday of Thanksgiving is celebrated on the {
~second
~third
=fourth
} Thursday of November.
Japanese characters originally came from what country? {
~India
=China
~Korea
~Egypt}
// ===Short Answer===
Who's buried in Grant's tomb?{=no one =nobody}
Two plus two equals {=four =4}.
// ===True-False===
Grant is buried in Grant's tomb.{F}
The sun rises in the east.{T}
// ===Matching===
Matching Question. {
=subquestion1 -> subanswer1
=subquestion2 -> subanswer2
=subquestion3 -> subanswer3
}
Match the following countries with their corresponding capitals. {
=Canada -> Ottawa
=Italy -> Rome
=Japan -> Tokyo
=India -> New Delhi
}
// ===Numerical===
When was Ulysses S. Grant born? {#1822}
What is the value of pi (to 3 decimal places)? {#3.1415:0.0005}.
What is the value of pi (to 3 decimal places)? {#3.141..3.142}.
When was Ulysses S. Grant born? {#
=1822:0
=%50%1822:2}
// OPTIONS
// ===Line Comments===
// Subheading: Numerical questions below
What's 2 plus 2? {#4}
// ===Question Name===
::Kanji Origins::Japanese characters originally
came from what country? {=China}
::Thanksgiving Date::The American holiday of Thanksgiving is
celebrated on the {~second ~third =fourth} Thursday of November.
// ===Feedback===
What's the answer to this multiple-choice question?{
~wrong answer#feedback comment on the wrong answer
~another wrong answer#feedback comment on this wrong answer
=right answer#Very good!}
Who's buried in Grant's tomb?{
=no one#excellent answer!
=nobody#excellent answer!}
// ===Specify text format===
[markdown]Who's buried in **Grant's tomb**?{
=no one#excellent answer!
=nobody#excellent answer!}
// ===Percentage Answer Weights===
Grant is buried in Grant's tomb.{FALSE#No one is buried in Grant's tomb.}
Difficult question.{~wrong answer ~%50%half credit answer =full credit answer}
::Jesus' hometown::Jesus Christ was from {
~Jerusalem#This was an important city, but the wrong answer.
~%25%Bethlehem#He was born here, but not raised here.
~%50%Galilee#You need to be more specific.
=Nazareth#Yes! That's right!}.
::Jesus' hometown:: Jesus Christ was from {
=Nazareth#Yes! That's right!
=%75%Nazereth#Right, but misspelled.
=%25%Bethlehem#He was born here, but not raised here.}
// ===Multiple Answers===
What two people are entombed in Grant's tomb? {
~No one
~%50%Grant
~%50%Grant's wife
~Grant's father }
What two people are entombed in Grant's tomb? {
~%-50%No one
~%50%Grant
~%50%Grant's wife
~%-50%Grant's father }
// ===Special Characters===
Which answer equals 5? {
~ \= 2 + 2
= \= 2 + 3
~ \= 2 + 4 }
::GIFT Control Characters::
Which of the following is NOT a control character for the GIFT import format? {
~ \~ # \~ is a control character.
~ \= # \= is a control character.
~ \# # \# is a control character.
~ \{ # \{ is a control character.
~ \} # \} is a control character.
= \\ # Correct! \\ (backslash) is not a control character. BUT,
it is used to escape the control characters. So, to specify
a literal backslash, you must escape it with a backslash
(as shown in this example).
}
//-----------------------------------------//
// EXAMPLES FROM gift/format.php
//-----------------------------------------//
Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}
Grant is {~buried =entombed ~living} in Grant's tomb.
Grant is buried in Grant's tomb.{FALSE}
Who's buried in Grant's tomb?{=no one =nobody}
When was Ulysses S. Grant born?{#1822:5}
Match the following countries with their corresponding
capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo}
//-----------------------------------------//
// MORE COMPLICATED EXAMPLES
//-----------------------------------------//
::Grant's Tomb::Grant is {
~buried#No one is buried there.
=entombed#Right answer!
~living#We hope not!
} in Grant's tomb.
Difficult multiple choice question.{
~wrong answer #comment on wrong answer
~%50%half credit answer #comment on answer
=full credit answer #well done!}
::Jesus' hometown (Short answer ex.):: Jesus Christ was from {
=Nazareth#Yes! That's right!
=%75%Nazereth#Right, but misspelled.
=%25%Bethlehem#He was born here, but not raised here.
}.
//this comment will be ignored by the filter
::Numerical example::
When was Ulysses S. Grant born? {#
=1822:0 #Correct! 100% credit
=%50%1822:2 #He was born in 1822.
You get 50% credit for being close.
}

View File

@ -1,588 +0,0 @@
<?php // $Id$
//
///////////////////////////////////////////////////////////////
// The GIFT import filter was designed as an easy to use method
// for teachers writing questions as a text file. It supports most
// question types and the missing word format.
//
// Multiple Choice / Missing Word
// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}
// Grant is {~buried =entombed ~living} in Grant's tomb.
// True-False:
// Grant is buried in Grant's tomb.{FALSE}
// Short-Answer.
// Who's buried in Grant's tomb?{=no one =nobody}
// Numerical
// When was Ulysses S. Grant born?{#1822:5}
// Matching
// Match the following countries with their corresponding
// capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo}
//
// Comment lines start with a double backslash (//).
// Optional question names are enclosed in double colon(::).
// Answer feedback is indicated with hash mark (#).
// Percentage answer weights immediately follow the tilde (for
// multiple choice) or equal sign (for short answer and numerical),
// and are enclosed in percent signs (% %). See docs and examples.txt for more.
//
// This filter was written through the collaboration of numerous
// members of the Moodle community. It was originally based on
// the missingword format, which included code from Thomas Robb
// and others. Paul Tsuchido Shew wrote this filter in December 2003.
//////////////////////////////////////////////////////////////////////////
// Based on default.php, included by ../import.php
class quiz_format_gift extends quiz_default_format {
function provide_import() {
return true;
}
function provide_export() {
return true;
}
function answerweightparser(&$answer) {
$answer = substr($answer, 1); // removes initial %
$end_position = strpos($answer, "%");
$answer_weight = substr($answer, 0, $end_position); // gets weight as integer
$answer_weight = $answer_weight/100; // converts to percent
$answer = substr($answer, $end_position+1); // removes comment from answer
return $answer_weight;
}
function commentparser(&$answer) {
if (strpos($answer,"#") > 0){
$hashpos = strpos($answer,"#");
$comment = substr($answer, $hashpos+1);
$comment = addslashes(trim($this->escapedchar_post($comment)));
$answer = substr($answer, 0, $hashpos);
} else {
$comment = " ";
}
return $comment;
}
function split_truefalse_comment($comment){
// splits up comment around # marks
// returns an array of true/false feedback
$feedback = explode('#',$comment);
if (count($feedback)>=2) {
$true_feedback = $feedback[0];
$false_feedback = $feedback[1];
}
else {
$true_feedback = $feedback[0];
$false_feedback = '';
}
return array( 'true'=>$true_feedback, 'false'=>$false_feedback );
}
function escapedchar_pre($string) {
//Replaces escaped control characters with a placeholder BEFORE processing
$escapedcharacters = array("\\#", "\\=", "\\{", "\\}", "\\~", "\\n" ); //dlnsk
$placeholders = array("&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010" ); //dlnsk
$string = str_replace("\\\\", "&&092;", $string);
$string = str_replace($escapedcharacters, $placeholders, $string);
$string = str_replace("&&092;", "\\", $string);
return $string;
}
function escapedchar_post($string) {
//Replaces placeholders with corresponding character AFTER processing is done
$placeholders = array("&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); //dlnsk
$characters = array("#", "=", "{", "}", "~", "\n" ); //dlnsk
$string = str_replace($placeholders, $characters, $string);
return $string;
}
function check_answer_count( $min, $answers, $text ) {
$countanswers = count($answers);
if ($countanswers < $min) {
if ($this->displayerrors) {
$errormessage = get_string( 'importminerror', 'quiz' );
echo "<p>$text</p>\n";
echo "<p>$errormessage</p>\n";
}
return false;
}
return true;
}
function readquestion($lines) {
// Given an array of lines known to define a question in this format, this function
// converts it into a question object suitable for processing and insertion into Moodle.
$question = $this->defaultquestion();
$comment = NULL;
// define replaced by simple assignment, stop redefine notices
$gift_answerweight_regex = "^%\-*([0-9]{1,2})\.?([0-9]*)%";
// REMOVED COMMENTED LINES and IMPLODE
foreach ($lines as $key => $line) {
$line = trim($line);
if (substr($line, 0, 2) == "//") {
// echo "Commented line removed.<br />";
$lines[$key] = " ";
}
}
$text = trim(implode(" ", $lines));
if ($text == "") {
// echo "<p>Empty line.</p>";
return false;
}
// Substitute escaped control characters with placeholders
$text = $this->escapedchar_pre($text);
// QUESTION NAME parser
if (substr($text, 0, 2) == "::") {
$text = substr($text, 2);
$namefinish = strpos($text, "::");
if ($namefinish === false) {
$question->name = false;
// name will be assigned after processing question text below
} else {
$questionname = substr($text, 0, $namefinish);
$question->name = addslashes(trim($this->escapedchar_post($questionname)));
$text = trim(substr($text, $namefinish+2)); // Remove name from text
}
} else {
$question->name = false;
}
// FIND ANSWER section
$answerstart = strpos($text, "{");
if ($answerstart === false) {
if ($this->displayerrors) {
echo "<p>$text<p>Could not find a {";
}
return false;
}
$answerfinish = strpos($text, "}");
if ($answerfinish === false) {
if ($this->displayerrors) {
echo "<p>$text<p>Could not find a }";
}
return false;
}
$answerlength = $answerfinish - $answerstart;
$answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));
// Format QUESTION TEXT without answer, inserting "_____" as necessary
if (substr($text, -1) == "}") {
// no blank line if answers follow question, outside of closing punctuation
$questiontext = substr_replace($text, "", $answerstart, $answerlength+1);
} else {
// inserts blank line for missing word format
$questiontext = substr_replace($text, "_____", $answerstart, $answerlength+1);
}
// get questiontext format from questiontext
$oldquestiontext = $questiontext;
$questiontextformat = 0;
if (substr($questiontext,0,1)=='[') {
$questiontext = substr( $questiontext,1 );
$rh_brace = strpos( $questiontext, ']' );
$qtformat= substr( $questiontext, 0, $rh_brace );
$questiontext = substr( $questiontext, $rh_brace+1 );
if (!$questiontextformat = text_format_name( $qtformat )) {
$questiontext = $oldquestiontext;
}
}
$question->questiontextformat = $questiontextformat;
$question->questiontext = addslashes(trim($this->escapedchar_post($questiontext)));
// set question name if not already set
if ($question->name === false) {
$question->name = $question->questiontext;
}
// determine QUESTION TYPE
$question->qtype = NULL;
if ($answertext{0} == "#"){
$question->qtype = NUMERICAL;
} elseif (strpos($answertext, "~") !== false) {
// only Multiplechoice questions contain tilde ~
$question->qtype = MULTICHOICE;
} elseif (strpos($answertext, "=") !== false
AND strpos($answertext, "->") !== false) {
// only Matching contains both = and ->
$question->qtype = MATCH;
} else { // either TRUEFALSE or SHORTANSWER
// TRUEFALSE question check
$truefalse_check = $answertext;
if (strpos($answertext,"#") > 0){
// strip comments to check for TrueFalse question
$truefalse_check = trim(substr($answertext, 0, strpos($answertext,"#")));
}
$valid_tf_answers = array("T", "TRUE", "F", "FALSE");
if (in_array($truefalse_check, $valid_tf_answers)) {
$question->qtype = TRUEFALSE;
} else { // Must be SHORTANSWER
$question->qtype = SHORTANSWER;
}
}
if (!isset($question->qtype)) {
if ($this->displayerrors) {
echo "<p>$text<p>Question type not set.";
}
return false;
}
switch ($question->qtype) {
case MULTICHOICE:
if (strpos($answertext,"=") === false) {
$question->single = 0; // multiple answers are enabled if no single answer is 100% correct
} else {
$question->single = 1; // only one answer allowed (the default)
}
$answertext = str_replace("=", "~=", $answertext);
$answers = explode("~", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
$countanswers = count($answers);
if (!$this->check_answer_count( 2,$answers,$text )) {
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// determine answer weight
if ($answer[0] == "=") {
$answer_weight = 1;
$answer = substr($answer, 1);
} elseif (ereg($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
$answer_weight = $this->answerweightparser($answer);
} else { //default, i.e., wrong anwer
$answer_weight = 0;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this->commentparser($answer); // commentparser also removes comment from $answer
$question->answer[$key] = addslashes($this->escapedchar_post($answer));
} // end foreach answer
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
return $question;
break;
case MATCH:
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (!$this->check_answer_count( 2,$answers,$text )) {
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
if (strpos($answer, "->") <= 0) {
if ($this->displayerrors) {
echo "<p>$text<p>Error processing Matching question.<br />
Improperly formatted answer: $answer";
}
return false;
break 2;
}
$marker = strpos($answer,"->");
$question->subquestions[$key] = addslashes(trim($this->escapedchar_post(substr($answer, 0, $marker))));
$question->subanswers[$key] = addslashes(trim($this->escapedchar_post(substr($answer, $marker+2))));
} // end foreach answer
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
return $question;
break;
case TRUEFALSE:
$answer = $answertext;
$comment = $this->commentparser($answer); // commentparser also removes comment from $answer
$feedback = $this->split_truefalse_comment( $comment );
if ($answer == "T" OR $answer == "TRUE") {
$question->answer = 1;
$question->feedbackfalse = $feedback['true'];; //feedback if answer is wrong
$question->feedbacktrue = $feedback['false']; // make sure this exists to stop notifications
} else {
$question->answer = 0;
$question->feedbacktrue = $feedback['true']; //feedback if answer is wrong
$question->feedbackfalse = $feedback['false']; // make sure this exists to stop notifications
}
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
return $question;
break;
case SHORTANSWER:
// SHORTANSWER Question
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (!$this->check_answer_count( 1,$answers,$text )) {
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// Answer Weight
if (ereg($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
$answer_weight = $this->answerweightparser($answer);
} else { //default, i.e., full-credit anwer
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer
$question->answer[$key] = addslashes($this->escapedchar_post($answer));
} // end foreach
//$question->usecase = 0; // Ignore case
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
return $question;
break;
case NUMERICAL:
// Note similarities to ShortAnswer
$answertext = substr($answertext, 1); // remove leading "#"
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (count($answers) == 0) {
// invalid question
if ($this->displayerrors) {
echo "<p>$text<p>No answers found in answertext (Numerical answer)";
}
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// Answer weight
if (ereg($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
$answer_weight = $this->answerweightparser($answer);
} else { //default, i.e., full-credit anwer
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer
//Calculate Answer and Min/Max values
if (strpos($answer,"..") > 0) { // optional [min]..[max] format
$marker = strpos($answer,"..");
$max = trim(substr($answer, $marker+2));
$min = trim(substr($answer, 0, $marker));
$ans = ($max + $min)/2;
$tol = $max - $ans;
} elseif (strpos($answer,":") > 0){ // standard [answer]:[errormargin] format
$marker = strpos($answer,":");
$tol = trim(substr($answer, $marker+1));
$ans = trim(substr($answer, 0, $marker));
} else { // only one valid answer (zero errormargin)
$tol = 0;
$ans = trim($answer);
}
if (!is_numeric($ans)
OR !is_numeric($tol)) {
if ($this->displayerrors) {
$err = get_string( 'errornotnumbers' );
echo "<p>$text</p><p>$err</p>
<p>Answer: <u>$answer</u></p><p>Tolerance: <u>$tol</u></p> ";
}
return false;
break;
}
// store results
$question->answer[$key] = $ans;
$question->tolerance[$key] = $tol;
} // end foreach
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
//$question->multiplier = array(); // no numeric multipliers with GIFT
return $question;
break;
default:
if ($this->displayerrors) {
echo "<p>$text<p> No valid question type. Error in switch(question->qtype)";
}
return false;
break;
} // end switch ($question->qtype)
} // end function readquestion($lines)
function repchar( $text, $format=0 ) {
// escapes 'reserved' characters # = ~ { ) and removes new lines
// also pushes text through format routine
$reserved = array( '#', '=', '~', '{', '}', "\n","\r");
$escaped = array( '\#','\=','\~','\{','\}','\n','' ); //dlnsk
$newtext = str_replace( $reserved, $escaped, $text );
$format = 0; // turn this off for now
if ($format) {
$newtext = format_text( $format );
}
return $newtext;
}
function writequestion( $question ) {
// turns question into string
// question reflects database fields for general question and specific to type
// initial string;
$expout = "";
// add comment
$expout .= "// question: $question->id name: $question->name \n";
// get question text format
$textformat = $question->questiontextformat;
$tfname = "";
if ($textformat!=FORMAT_MOODLE) {
$tfname = text_format_name( (int)$textformat );
$tfname = "[$tfname]";
}
// output depends on question type
switch($question->qtype) {
case TRUEFALSE:
$answers = $question->options->answers;
if ($answers['true']->fraction==1) {
$answertext = 'TRUE';
$wrong_feedback = $this->repchar( $answers['false']->feedback );
$right_feedback = $this->repchar( $answers['true']->feedback );
}
else {
$answertext = 'FALSE';
$wrong_feedback = $this->repchar( $answers['true']->feedback );
$right_feedback = $this->repchar( $answers['false']->feedback );
}
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext,$textformat )."{".$this->repchar( $answertext );
if ($wrong_feedback!="") {
$expout .= "#".$wrong_feedback;
}
if ($right_feedback!="") {
$expout .= "#".$right_feedback;
}
$expout .= "}\n";
break;
case MULTICHOICE:
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext, $textformat )."{\n";
foreach($question->options->answers as $answer) {
if ($answer->fraction==1) {
$answertext = '=';
}
elseif ($answer->fraction==0) {
$answertext = '~';
}
else {
$export_weight = $answer->fraction*100;
$answertext = "~%$export_weight%";
}
$expout .= "\t".$answertext.$this->repchar( $answer->answer );
if ($answer->feedback!="") {
$expout .= "#".$this->repchar( $answer->feedback );
}
$expout .= "\n";
}
$expout .= "}\n";
break;
case SHORTANSWER:
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext, $textformat )."{\n";
foreach($question->options->answers as $answer) {
$weight = 100 * $answer->fraction;
$expout .= "\t=%".$weight."%".$this->repchar( $answer->answer )."#".$this->repchar( $answer->feedback )."\n";
}
$expout .= "}\n";
break;
case NUMERICAL:
$answer = array_pop( $question->options->answers );
$tolerance = $answer->tolerance;
$min = $answer->answer - $tolerance;
$max = $answer->answer + $tolerance;
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext, $textformat )."{\n";
$expout .= "\t#".$min."..".$max."#".$this->repchar( $answer->feedback )."\n";
$expout .= "}\n";
break;
case MATCH:
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext, $textformat )."{\n";
foreach($question->options->subquestions as $subquestion) {
$expout .= "\t=".$this->repchar( $subquestion->questiontext )." -> ".$this->repchar( $subquestion->answertext )."\n";
}
$expout .= "}\n";
break;
case DESCRIPTION:
$expout .= "// DESCRIPTION type is not supported\n";
break;
case MULTIANSWER:
$expout .= "// CLOZE type is not supported\n";
break;
default:
notify("No handler for qtype $question->qtype for GIFT export" );
}
// add empty line to delimit questions
$expout .= "\n";
return $expout;
}
}
?>

View File

@ -1,600 +0,0 @@
<?PHP // $Id$
////////////////////////////////////////////////////////////////////////////
/// Hotpotatoes 5.0 and 6.0 Format
///
/// This Moodle class provides all functions necessary to import
/// (export is not implemented ... yet)
///
////////////////////////////////////////////////////////////////////////////
// Based on default.php, included by ../import.php
class quiz_format_hotpot extends quiz_default_format {
function provide_import() {
return true;
}
function readquestions ($lines) {
/// Parses an array of lines into an array of questions,
/// where each item is a question object as defined by
/// readquestion().
// parse the xml source
$source = implode($lines, " ");
$xml = new hotpot_xml_tree($source);
// determine the quiz type
$xml->quiztype = '';
$keys = array_keys($xml->xml);
foreach ($keys as $key) {
if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {
$xml->quiztype = strtolower($matches[2]);
$xml->xml_root = "['$key']['#']";
break;
}
}
// convert xml to questions array
$questions = array();
switch ($xml->quiztype) {
case 'jcloze':
$this->process_jcloze($xml, $questions);
break;
case 'jcross':
$this->process_jcross($xml, $questions);
break;
case 'jmatch':
$this->process_jmatch($xml, $questions);
break;
case 'jmix':
$this->process_jmix($xml, $questions);
break;
case 'jbc':
case 'jquiz':
$this->process_jquiz($xml, $questions);
break;
default:
if (empty($xml->quiztype)) {
notice("Input file not recognized as a Hot Potatoes XML file");
} else {
notice("Unknown quiz type '$xml->quiztype'");
}
} // end switch
return $questions;
}
function process_jcloze(&$xml, &$questions) {
// define default grade (per cloze gap)
$defaultgrade = 1;
$gap_count = 0;
// detect old Moodles (1.4 and earlier)
global $CFG, $db;
$moodle_14 = false;
if ($columns = $db->MetaColumns("{$CFG->prefix}quiz_multianswers")) {
foreach ($columns as $column) {
if ($column->name=='answers' || $column->name=='positionkey' || $column->name=='answertype' || $column->name=='norm') {
$moodle_14 = true;
}
}
}
// xml tags for the start of the gap-fill exercise
$tags = 'data,gap-fill';
$x = 0;
while (($exercise = "[$x]['#']") && $xml->xml_value($tags, $exercise)) {
// there is usually only one exercise in a file
if (method_exists($this, 'defaultquestion')) {
$question = $this->defaultquestion();
} else {
$question = new stdClass();
$question->usecase = 0; // Ignore case
$question->image = ""; // No images with this format
}
$question->qtype = MULTIANSWER;
$question->name = $this->hotpot_get_title($xml, $x);
$question->questiontext = $this->hotpot_get_reading($xml);
// setup answer arrays
if ($moodle_14) {
$question->answers = array();
} else {
global $course; // set in mod/quiz/import.php
$question->course = $course->id;
$question->options = new stdClass();
$question->options->questions = array(); // one for each gap
}
$q = 0;
while ($text = $xml->xml_value($tags, $exercise."[$q]")) {
// add next bit of text
$question->questiontext .= addslashes($text);
// check for a gap
$question_record = $exercise."['question-record'][$q]['#']";
if ($xml->xml_value($tags, $question_record)) {
// add gap
$gap_count ++;
$positionkey = $q+1;
$question->questiontext .= '{#'.$positionkey.'}';
// initialize answer settings
if ($moodle_14) {
$question->answers[$q]->positionkey = $positionkey;
$question->answers[$q]->answertype = SHORTANSWER;
$question->answers[$q]->norm = $defaultgrade;
$question->answers[$q]->alternatives = array();
} else {
$wrapped = new stdClass();
$wrapped->qtype = SHORTANSWER;
$wrapped->usecase = 0;
$wrapped->defaultgrade = $defaultgrade;
$wrapped->questiontextformat = 0;
$wrapped->answer = array();
$wrapped->fraction = array();
$wrapped->feedback = array();
$answers = array();
}
// add answers
$a = 0;
while (($answer=$question_record."['answer'][$a]['#']") && $xml->xml_value($tags, $answer)) {
$text = addslashes($xml->xml_value($tags, $answer."['text'][0]['#']"));
$correct = $xml->xml_value($tags, $answer."['correct'][0]['#']");
$feedback = addslashes($xml->xml_value($tags, $answer."['feedback'][0]['#']"));
if ($text) {
// set score (0=0%, 1=100%)
$fraction = empty($correct) ? 0 : 1;
// store answer
if ($moodle_14) {
$question->answers[$q]->alternatives[$a] = new stdClass();
$question->answers[$q]->alternatives[$a]->answer = $text;
$question->answers[$q]->alternatives[$a]->fraction = $fraction;
$question->answers[$q]->alternatives[$a]->feedback = $feedback;
} else {
$wrapped->answer[] = $text;
$wrapped->fraction[] = $fraction;
$wrapped->feedback[] = $feedback;
$answers[] = (empty($fraction) ? '' : '=').$text.(empty($feedback) ? '' : ('#'.$feedback));
}
}
$a++;
}
// compile answers into question text, if necessary
if ($moodle_14) {
// do nothing
} else {
$wrapped->questiontext = '{'.$defaultgrade.':SHORTANSWER:'.implode('~', $answers).'}';
$question->options->questions[] = $wrapped;
}
} // end if gap
$q++;
} // end while $text
// define total grade for this exercise
$question->defaultgrade = $gap_count * $defaultgrade;
$questions[] = $question;
$x++;
} // end while $exercise
}
function process_jcross(&$xml, &$questions) {
// xml tags to the start of the crossword exercise clue items
$tags = 'data,crossword,clues,item';
$x = 0;
while (($item = "[$x]['#']") && $xml->xml_value($tags, $item)) {
$text = $xml->xml_value($tags, $item."['def'][0]['#']");
$answer = $xml->xml_value($tags, $item."['word'][0]['#']");
if ($text && $answer) {
if (method_exists($this, 'defaultquestion')) {
$question = $this->defaultquestion();
} else {
$question = new stdClass();
$question->usecase = 0; // Ignore case
$question->image = ""; // No images with this format
}
$question->qtype = SHORTANSWER;
$question->name = $this->hotpot_get_title($xml, $x, true);
$question->questiontext = addslashes($text);
$question->answer = array(addslashes($answer));
$question->fraction = array(1);
$question->feedback = array('');
$questions[] = $question;
}
$x++;
}
}
function process_jmatch(&$xml, &$questions) {
// define default grade (per matched pair)
$defaultgrade = 1;
$match_count = 0;
// xml tags to the start of the matching exercise
$tags = 'data,matching-exercise';
$x = 0;
while (($exercise = "[$x]['#']") && $xml->xml_value($tags, $exercise)) {
// there is usually only one exercise in a file
if (method_exists($this, 'defaultquestion')) {
$question = $this->defaultquestion();
} else {
$question = new stdClass();
$question->usecase = 0; // Ignore case
$question->image = ""; // No images with this format
}
$question->qtype = MATCH;
$question->name = $this->hotpot_get_title($xml, $x);
$question->questiontext = $this->hotpot_get_reading($xml);
$question->questiontext .= $this->hotpot_get_instructions($xml);
$question->subquestions = array();
$question->subanswers = array();
$p = 0;
while (($pair = $exercise."['pair'][$p]['#']") && $xml->xml_value($tags, $pair)) {
$left = $xml->xml_value($tags, $pair."['left-item'][0]['#']['text'][0]['#']");
$right = $xml->xml_value($tags, $pair."['right-item'][0]['#']['text'][0]['#']");
if ($left && $right) {
$match_count++;
$question->subquestions[$p] = addslashes($left);
$question->subanswers[$p] = addslashes($right);
}
$p++;
}
$question->defaultgrade = $match_count * $defaultgrade;
$questions[] = $question;
$x++;
}
}
function process_jmix(&$xml, &$questions) {
// define default grade (per segment)
$defaultgrade = 1;
$segment_count = 0;
// xml tags to the start of the jumbled order exercise
$tags = 'data,jumbled-order-exercise';
$x = 0;
while (($exercise = "[$x]['#']") && $xml->xml_value($tags, $exercise)) {
// there is usually only one exercise in a file
if (method_exists($this, 'defaultquestion')) {
$question = $this->defaultquestion();
} else {
$question = new stdClass();
$question->usecase = 0; // Ignore case
$question->image = ""; // No images with this format
}
$question->qtype = SHORTANSWER;
$question->name = $this->hotpot_get_title($xml, $x);
$question->answer = array();
$question->fraction = array();
$question->feedback = array();
$i = 0;
$segments = array();
while ($segment = $xml->xml_value($tags, $exercise."['main-order'][0]['#']['segment'][$i]['#']")) {
$segments[] = addslashes($segment);
$segment_count++;
$i++;
}
$answer = implode(' ', $segments);
$this->hotpot_seed_RNG();
shuffle($segments);
$question->questiontext = $this->hotpot_get_reading($xml);
$question->questiontext .= $this->hotpot_get_instructions($xml);
$question->questiontext .= ' &nbsp; <NOBR><B>[ &nbsp; '.implode(' &nbsp; ', $segments).' &nbsp; ]</B></NOBR>';
$a = 0;
while (!empty($answer)) {
$question->answer[$a] = $answer;
$question->fraction[$a] = 1;
$question->feedback[$a] = '';
$answer = addslashes($xml->xml_value($tags, $exercise."['alternate'][$a]['#']"));
$a++;
}
$question->defaultgrade = $segment_count * $defaultgrade;
$questions[] = $question;
$x++;
}
}
function process_jquiz(&$xml, &$questions) {
// define default grade (per question)
$defaultgrade = 1;
// xml tags to the start of the questions
$tags = 'data,questions';
$x = 0;
while (($exercise = "[$x]['#']") && $xml->xml_value($tags, $exercise)) {
// there is usually only one 'questions' object in a single exercise
$q = 0;
while (($question_record = $exercise."['question-record'][$q]['#']") && $xml->xml_value($tags, $question_record)) {
if (method_exists($this, 'defaultquestion')) {
$question = $this->defaultquestion();
} else {
$question = new stdClass();
$question->usecase = 0; // Ignore case
$question->image = ""; // No images with this format
}
$question->defaultgrade = $defaultgrade;
$question->name = $this->hotpot_get_title($xml, $q, true);
$text = $xml->xml_value($tags, $question_record."['question'][0]['#']");
$question->questiontext = addslashes($text);
if ($xml->xml_value($tags, $question_record."['answers']")) {
// HP6 JQuiz
$answers = $question_record."['answers'][0]['#']";
} else {
// HP5 JBC or JQuiz
$answers = $question_record;
}
if($xml->xml_value($tags, $question_record."['question-type']")) {
// HP6 JQuiz
$type = $xml->xml_value($tags, $question_record."['question-type'][0]['#']");
// 1 : multiple choice
// 2 : short-answer
// 3 : hybrid
// 4 : multiple select
} else {
// HP5
switch ($xml->quiztype) {
case 'jbc':
$must_select_all = $xml->xml_value($tags, $question_record."['must-select-all'][0]['#']");
if (empty($must_select_all)) {
$type = 1; // multichoice
} else {
$type = 4; // multiselect
}
break;
case 'jquiz':
$type = 2; // shortanswer
break;
default:
$type = 0; // unknown
}
}
$question->qtype = ($type==2 ? SHORTANSWER : MULTICHOICE);
$question->single = ($type==4 ? 0 : 1);
// workaround required to calculate scores for multiple select answers
$no_of_correct_answers = 0;
if ($type==4) {
$a = 0;
while (($answer = $answers."['answer'][$a]['#']") && $xml->xml_value($tags, $answer)) {
$correct = $xml->xml_value($tags, $answer."['correct'][0]['#']");
if (empty($correct)) {
// do nothing
} else {
$no_of_correct_answers++;
}
$a++;
}
}
$a = 0;
$question->answer = array();
$question->fraction = array();
$question->feedback = array();
while (($answer = $answers."['answer'][$a]['#']") && $xml->xml_value($tags, $answer)) {
$correct = $xml->xml_value($tags, $answer."['correct'][0]['#']");
if (empty($correct)) {
$fraction = 0;
} else if ($type==4) { // multiple select
// strange behavior if the $fraction isn't exact to 5 decimal places
$fraction = round(1/$no_of_correct_answers, 5);
} else {
if ($xml->xml_value($tags, $answer."['percent-correct']")) {
// HP6 JQuiz
$percent = $xml->xml_value($tags, $answer."['percent-correct'][0]['#']");
$fraction = $percent/100;
} else {
// HP5 JBC or JQuiz
$fraction = 1;
}
}
$question->fraction[] = $fraction;
$question->feedback[] = addslashes($xml->xml_value($tags, $answer."['feedback'][0]['#']"));
$question->answer[] = addslashes($xml->xml_value($tags, $answer."['text'][0]['#']"));
$a++;
}
$questions[] = $question;
$q++;
}
$x++;
}
}
function hotpot_seed_RNG() {
// seed the random number generator
static $HOTPOT_SEEDED_RNG = FALSE;
if (!$HOTPOT_SEEDED_RNG) {
srand((double) microtime() * 1000000);
$HOTPOT_SEEDED_RNG = TRUE;
}
}
function hotpot_get_title(&$xml, $x, $flag=false) {
$title = $xml->xml_value('data,title');
if ($x || $flag) {
$title .= ' ('.($x+1).')';
}
return addslashes($title);
}
function hotpot_get_instructions(&$xml) {
$text = $xml->xml_value('hotpot-config-file,instructions');
if (empty($text)) {
$text = "Hot Potatoes $xml->quiztype";
}
return addslashes($text);
}
function hotpot_get_reading(&$xml) {
$str = '';
$tags = 'data,reading';
if ($xml->xml_value("$tags,include-reading")) {
if ($title = $xml->xml_value("$tags,reading-title")) {
$str .= "<H3>$title</H3>";
}
if ($text = $xml->xml_value("$tags,reading-text")) {
$str .= "<P>$text</P>";
}
}
return addslashes($str);
}
} // end class
// get the standard XML parser supplied with Moodle
require_once("$CFG->libdir/xmlize.php");
class hotpot_xml_tree {
function hotpot_xml_tree($str, $xml_root='') {
if (empty($str)) {
$this->xml = array();
} else {
// encode htmlentities in JCloze
$this->encode_cdata($str, 'gap-fill');
// encode as utf8
$str = utf8_encode($str);
// xmlize (=convert xml to tree)
$this->xml = xmlize($str, 0);
}
$this->xml_root = $xml_root;
}
function xml_value($tags, $more_tags="[0]['#']") {
$tags = empty($tags) ? '' : "['".str_replace(",", "'][0]['#']['", $tags)."']";
eval('$value = &$this->xml'.$this->xml_root.$tags.$more_tags.';');
if (is_string($value)) {
$value = utf8_decode($value);
// decode angle brackets and ampersands
$value = strtr($value, array('&#x003C;'=>'<', '&#x003E;'=>'>', '&#x0026;'=>'&'));
// remove white space between <table>, <ul|OL|DL> and <OBJECT|EMBED> parts
// (so it doesn't get converted to <br />)
$htmltags = '('
. 'TABLE|/?CAPTION|/?COL|/?COLGROUP|/?TBODY|/?TFOOT|/?THEAD|/?TD|/?TH|/?TR'
. '|OL|UL|/?LI'
. '|DL|/?DT|/?DD'
. '|EMBED|OBJECT|APPLET|/?PARAM'
//. '|SELECT|/?OPTION'
//. '|FIELDSET|/?LEGEND'
//. '|FRAMESET|/?FRAME'
. ')'
;
$search = '#(<'.$htmltags.'[^>]*'.'>)\s+'.'(?='.'<'.')#is';
$value = preg_replace($search, '\\1', $value);
// replace remaining newlines with <br />
$value = str_replace("\n", '<br />', $value);
// encode unicode characters as HTML entities
// (in particular, accented charaters that have not been encoded by HP)
// multibyte unicode characters can be detected by checking the hex value of the first character
// 00 - 7F : ascii char (roman alphabet + punctuation)
// 80 - BF : byte 2, 3 or 4 of a unicode char
// C0 - DF : 1st byte of 2-byte char
// E0 - EF : 1st byte of 3-byte char
// F0 - FF : 1st byte of 4-byte char
// if the string doesn't match the above, it might be
// 80 - FF : single-byte, non-ascii char
$search = '#('.'[\xc0-\xdf][\x80-\xbf]'.'|'.'[\xe0-\xef][\x80-\xbf]{2}'.'|'.'[\xf0-\xff][\x80-\xbf]{3}'.'|'.'[\x80-\xff]'.')#se';
$value = preg_replace($search, "hotpot_utf8_to_html_entity('\\1')", $value);
}
return $value;
}
function encode_cdata(&$str, $tag) {
// conversion tables
static $HTML_ENTITIES = array(
'&apos;' => "'",
'&quot;' => '"',
'&lt;' => '<',
'&gt;' => '>',
'&amp;' => '&',
);
static $ILLEGAL_STRINGS = array(
"\r" => '',
"\n" => '&lt;br /&gt;',
']]>' => '&#93;&#93;&#62;',
);
// extract the $tag from the $str(ing), if possible
$pattern = '|(^.*<'.$tag.'[^>]*)(>.*<)(/'.$tag.'>.*$)|is';
if (preg_match($pattern, $str, $matches)) {
// encode problematic CDATA chars and strings
$matches[2] = strtr($matches[2], $ILLEGAL_STRINGS);
// if there are any ampersands in "open text"
// surround them by CDATA start and end markers
// (and convert HTML entities to plain text)
$search = '/>([^<]*&[^<]*)</e';
$replace = '"><![CDATA[".strtr("$1", $HTML_ENTITIES)."]]><"';
$matches[2] = preg_replace($search, $replace, $matches[2]);
$str = $matches[1].$matches[2].$matches[3];
}
}
}
function hotpot_utf8_to_html_entity($char) {
// http://www.zend.com/codex.php?id=835&single=1
// array used to figure what number to decrement from character order value
// according to number of characters used to map unicode to ascii by utf-8
static $HOTPOT_UTF8_DECREMENT = array(
1=>0, 2=>192, 3=>224, 4=>240
);
// the number of bits to shift each character by
static $HOTPOT_UTF8_SHIFT = array(
1=>array(0=>0),
2=>array(0=>6, 1=>0),
3=>array(0=>12, 1=>6, 2=>0),
4=>array(0=>18, 1=>12, 2=>6, 3=>0)
);
$dec = 0;
$len = strlen($char);
for ($pos=0; $pos<$len; $pos++) {
$ord = ord ($char{$pos});
$ord -= ($pos ? 128 : $HOTPOT_UTF8_DECREMENT[$len]);
$dec += ($ord << $HOTPOT_UTF8_SHIFT[$len][$pos]);
}
return '&#x'.sprintf('%04X', $dec).';';
}
// allow importing in Moodle v1.4 (and less)
// same core functions but different class name
if (!class_exists("quiz_file_format")) {
class quiz_file_format extends quiz_default_format {
function readquestions ($lines) {
$format = new quiz_format_hotpot();
return $format->readquestions($lines);
}
}
}
?>

View File

@ -1,147 +0,0 @@
<?php // $Id$
// Alton College, Hampshire, UK - Tom Flannaghan, Andrew Walker
// Imports learnwise multiple choice quizzes (single and multiple answers)
// currently ignores the deduct attribute for multiple answer questions
// deductions are currently simply found by dividing the award for the incorrect
// answer by the total number of options
// Based on format.php, included by ../../import.php
class quiz_format_learnwise extends quiz_default_format {
function provide_import() {
return true;
}
function readquestions($lines) {
$questions = array();
$currentquestion = array();
foreach($lines as $line) {
$line = trim($line);
$currentquestion[] = $line;
if ($question = $this->readquestion($currentquestion)) {
$questions[] = $question;
$currentquestion = array();
}
}
return $questions;
}
function readquestion($lines) {
$text = implode(' ', $lines);
$text = str_replace(array('\t','\n','\r','\''), array('','','','\\\''), $text);
$startpos = strpos($text, '<question type');
$endpos = strpos($text, '</question>');
if ($startpos === false || $endpos === false) {
return false;
}
preg_match("/<question type=[\"\']([^\"\']+)[\"\']>/i", $text, $matches);
$type = strtolower($matches[1]); // multichoice or multianswerchoice
$questiontext = $this->unhtmlentities($this->stringbetween($text, '<text>', '</text>'));
$questionhint = $this->unhtmlentities($this->stringbetween($text, '<hint>', '</hint>'));
$questionaward = $this->stringbetween($text, '<award>', '</award>');
$optionlist = $this->stringbetween($text, '<answer>', '</answer>');
$optionlist = explode('<option', $optionlist);
$n = 0;
$optionscorrect = array();
$optionstext = array();
if ($type == 'multichoice') {
foreach ($optionlist as $option) {
$correct = $this->stringbetween($option, ' correct="', '">');
$answer = $this->stringbetween($option, '">', '</option>');
$optionscorrect[$n] = $correct;
$optionstext[$n] = $this->unhtmlentities($answer);
++$n;
}
} else if ($type == 'multianswerchoice') {
$numcorrect = 0;
$totalaward = 0;
$optionsaward = array();
foreach ($optionlist as $option) {
preg_match("/correct=\"([^\"]*)\"/i", $option, $correctmatch);
preg_match("/award=\"([^\"]*)\"/i", $option, $awardmatch);
$correct = $correctmatch[1];
$award = $awardmatch[1];
if ($correct == 'yes') {
$totalaward += $award;
++$numcorrect;
}
$answer = $this->stringbetween($option, '">', '</option>');
$optionscorrect[$n] = $correct;
$optionstext[$n] = $this->unhtmlentities($answer);
$optionsaward[$n] = $award;
++$n;
}
} else {
echo "<p>I don't understand this question type (type = <strong>$type</strong>).</p>\n";
}
$question = $this->defaultquestion();
$question->qtype = MULTICHOICE;
$question->name = substr($questiontext, 0, 30);
if (strlen($questiontext) > 30) {
$question->name .= '...';
}
$question->questiontext = $questiontext;
$question->single = ($type == 'multichoice') ? 1 : 0;
$question->feedback[] = '';
$question->fraction = array();
$question->answer = array();
for ($n = 0; $n < count($optionstext); ++$n) {
if ($optionstext[$n]) {
if (!isset($numcorrect)) { // single answer
if ($optionscorrect[$n] == 'yes') {
$fraction = (int) $questionaward;
} else {
$fraction = 0;
}
} else { // mulitple answers
if ($optionscorrect[$n] == 'yes') {
$fraction = $optionsaward[$n] / $totalaward;
} else {
$fraction = -$optionsaward[$n] / count($optionstext);
}
}
$question->fraction[] = $fraction;
$question->answer[] = $optionstext[$n];
$question->feedback[] = ''; // no feedback in this type
}
}
return $question;
}
function stringbetween($text, $start, $end) {
$startpos = strpos($text, $start) + strlen($start);
$endpos = strpos($text, $end);
if ($startpos <= $endpos) {
return substr($text, $startpos, $endpos - $startpos);
}
}
function unhtmlentities($string) {
$transtable = get_html_translation_table(HTML_ENTITIES);
$transtable = array_flip($transtable);
return strtr($string, $transtable);
}
}
?>

View File

@ -1,146 +0,0 @@
<?xml version="1.0" ?>
<activityset setno="2">
<title>Maths</title>
<questions>
<question type="multiChoice">
<text>The ages, in years, of 10 horses in a field were 3, 3, 4, 5, 7, 7, 7, 8, 8, 8,&lt;p&gt;
Which one of the following is true?</text>
<award>1</award>
<hint>mean = (3+3+4+5+7+7+7+8+8+8)/10 = 6&lt;p&gt;
median = 7 i.e the middle number&lt;p&gt;
range = 8-3 = 5</hint>
<answer>
<option correct="yes">median &gt; mean &gt; range</option>
<option correct="no">mean = median</option>
<option correct="no">mean &gt; median &gt; range</option>
<option correct="no">median &gt; range &gt; mean</option>
</answer>
</question>
<question type="multiChoice">
<text>At a college, students are given a points score calculated from their GCSE grades.&lt;p&gt;
Someone with 4 A grades and 4 B grades gains a score of 36 points.&lt;br&gt;Someone with 2 A grades and 4 B grades gains a score of 26 points.&lt;br&gt;Someone with 3 A grades and 3 B grades would have a score of</text>
<award>1</award>
<hint>4A + 4B = 36 and 2A + 4B = 26&lt;p&gt;solving simultaneously gives A = 5 and B = 4</hint>
<answer>
<option correct="yes">27 points</option>
<option correct="no">21 points</option>
<option correct="no">30 points</option>
<option correct="no">31 points</option>
</answer>
</question>
<question type="multiChoice">
<text>A sequence consists of adding the previous three terms together to form the next term. The first three terms of the sequence are 1, 2, 3.&lt;p&gt;
What is the sixth term in the sequence?</text>
<award>1</award>
<hint>1, 2, 3, 6, 11, ...</hint>
<answer>
<option correct="yes">20</option>
<option correct="no">6</option>
<option correct="no">11</option>
<option correct="no">13</option>
</answer>
</question>
<question type="multiChoice">
<text>The value of an item is described as decreasing exponentially. Which description below best matches this statement?</text>
<award>1</award>
<hint>No hint - you either know this or you don't!</hint>
<answer>
<option correct="yes">The value falls by a smaller amount each year.</option>
<option correct="no">The value falls by the same amount each year.</option>
<option correct="no">The value falls by the same amount each year for a while, and then remains constant.</option>
<option correct="no">The value falls by a larger amount each year.</option>
</answer>
</question>
<question type="multiChoice">
<text>Consider the statement&lt;p&gt;
&quot;Schools with high numbers of pupils on free school meals do not do well in league tables.&quot;&lt;p&gt;
Which of the following statements follows logically from the one above?</text>
<award>1</award>
<hint>Factors other than the number of pupils on free school meals affect how well a school does in league tables.</hint>
<answer>
<option correct="yes">Schools which do well in league tables do not have high numbers of pupils on free school meals.</option>
<option correct="no">Schools with low numbers of pupils on free school meals do well in league tables.</option>
<option correct="no">Making all pupils pay for school meals will improve league table results.</option>
<option correct="no">Schools which do not do well in league tables have high numbers of pupils on free school meals.</option>
</answer>
</question>
<question type="multiChoice">
<text>The force between two point electric charges is inversely proportional to the square of their distance apart. What effect does doubling this distance have on the force?</text>
<award>1</award>
<hint>Inversely proportional means that increasing the distance decreases the force.&lt;br&gt;2 squared gives 4.</hint>
<answer>
<option correct="yes">The force decreases by a factor of four.</option>
<option correct="no">The force is halved.</option>
<option correct="no">The force is doubled.</option>
<option correct="no">The force increases by a factor of four.</option>
</answer>
</question>
<question type="multiChoice">
<text>Two of the five playing cards that Colin has are aces. He shuffles the five cards, then puts them face down on the table. Madge takes a card and then another. The probability that she now has &lt;b&gt;both&lt;/b&gt; of the aces is</text>
<award>1</award>
<hint>2/5 x 1/4</hint>
<answer>
<option correct="yes">1/10</option>
<option correct="no">4/25</option>
<option correct="no">1/5</option>
<option correct="no">2/5</option>
</answer>
</question>
<question type="multiChoice">
<text>A college's guidelines say that classes must have a minimum of 12 students and a maximum of 20 students. For what numbers of students studying a particular subject is it impossible to run classes without breaking the guidelines?</text>
<award>1</award>
<hint>between 12 and 20 students - one class&lt;br&gt;
between 24 and 36 students - two classes&lt;br&gt;
between 36 and 40 students - two classes</hint>
<answer>
<option correct="yes">between 20 and 24 students</option>
<option correct="no">between 12 and 20 students</option>
<option correct="no">between 24 and 36 students</option>
<option correct="no">between 36 and 40 students</option>
</answer>
</question>
<question type="multiChoice">
<text>The ages of two friends are in the ratio 3:4. In 8 years time their ages will be in the ratio 5:6. How old are they now?</text>
<award>1</award>
<hint>(12+8):(16+8) = 20:24 = 5:6</hint>
<answer>
<option correct="yes">12, 16</option>
<option correct="no">3, 4</option>
<option correct="no">6, 8</option>
<option correct="no">9, 12</option>
</answer>
</question>
<question type="multiChoice">
<text>A heavy construction vehicle travels for 40 minutes between sites, at an average speed of 16 km per second. The distance between sites is</text>
<award>1</award>
<hint>16/60 x 40</hint>
<answer>
<option correct="yes">10.7 km</option>
<option correct="no">2.5 km</option>
<option correct="no">6.4 km</option>
<option correct="no">38.4 km</option>
</answer>
</question>
<question type="multianswerchoice">
<text>Which of the following are features of a Virtual Learning Environment? (Select all that apply)</text>
<hint />
<answer>
<option correct="yes" award="1" deduct="0">course resources are available from home and from college</option>
<option correct="yes" award="1" deduct="0">forums enable collaborative work</option>
<option correct="yes" award="1" deduct="0">assessments can give instant feedback</option>
<option correct="yes" award="1" deduct="0">course progress is recorded</option>
<option correct="no" award="1" deduct="0">kettle is put on automatically for tea/coffee</option>
</answer>
</question>
<question type="multianswerchoice">
<text>Which of the following may a Virtual Learning Environment be used for? (Select all that apply)</text>
<hint />
<answer>
<option correct="yes" award="1" deduct="1">delivering a course online</option>
<option correct="yes" award="1" deduct="1">supporting face-to-face teaching</option>
<option correct="no" award="1" deduct="0">as a complete replacement for teachers</option>
</answer>
</question>
</questions>
</questions>
</activityset>

View File

@ -1,154 +0,0 @@
<?php // $Id$
/// Modified by Tom Robb 12 June 2003 to include percentage and comment insertion
/// facility.
////////////////////////////////////////////////////////////////////////////
/// MISSING WORD FORMAT
///
/// This Moodle class provides all functions necessary to import and export
/// one-correct-answer multiple choice questions in this format:
///
/// As soon as we begin to explore our body parts as infants
/// we become students of {=anatomy and physiology ~reflexology
/// ~science ~experiment}, and in a sense we remain students for life.
///
/// Each answer is separated with a tilde ~, and the correct answer is
/// prefixed with an equals sign =
///
/// Percentage weights can be included by following the tilde with the
/// desired percent. Comments can be included for each choice by following
/// the comment with a hash mark ("#") and the comment. Example:
///
/// This is {=the best answer#comment on the best answer ~75%a good
/// answer#comment on the good answer ~a wrong one#comment on the bad answer}
///
////////////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
class quiz_format_missingword extends quiz_default_format {
function provide_import() {
return true;
}
function readquestion($lines) {
/// Given an array of lines known to define a question in
/// this format, this function converts it into a question
/// object suitable for processing and insertion into Moodle.
$question = $this->defaultquestion();
///$comment added by T Robb
$comment = NULL;
$text = implode(" ", $lines);
/// Find answer section
$answerstart = strpos($text, "{");
if ($answerstart === false) {
if ($this->displayerrors) {
echo "<p>$text<p>Could not find a {";
}
return false;
}
$answerfinish = strpos($text, "}");
if ($answerfinish === false) {
if ($this->displayerrors) {
echo "<p>$text<p>Could not find a }";
}
return false;
}
$answerlength = $answerfinish - $answerstart;
$answertext = substr($text, $answerstart + 1, $answerlength - 1);
/// Save the new question text
$question->questiontext = addslashes(substr_replace($text, "_____", $answerstart, $answerlength+1));
$question->name = $question->questiontext;
/// Parse the answers
$answertext = str_replace("=", "~=", $answertext);
$answers = explode("~", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
$countanswers = count($answers);
switch ($countanswers) {
case 0: // invalid question
if ($this->displayerrors) {
echo "<p>No answers found in $answertext";
}
return false;
case 1:
$question->qtype = SHORTANSWER;
$answer = trim($answers[0]);
if ($answer[0] == "=") {
$answer = substr($answer, 1);
}
$question->answer[] = addslashes($answer);
$question->fraction[] = 1;
$question->feedback[] = "";
return $question;
default:
$question->qtype = MULTICHOICE;
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// Tom's addition starts here
$answeight = 0;
if (strspn($answer,"1234567890%") > 0){
//Make sure that the percent sign is the last in the span
if (strpos($answer,"%") == strspn($answer,"1234567890%") - 1) {
$answeight0 = substr($answer,0,strspn($answer,"1234567890%"));
$answeight = round(($answeight0/100),2);
$answer = substr($answer,(strspn($answer,"1234567890%")));
}
}
if ($answer[0] == "="){
$answeight = 1;
}
//remove the protective underscore for leading numbers in answers
if ($answer[0] == "_"){
$answer = substr($answer, 1);
}
$answer = trim($answer);
if (strpos($answer,"#") > 0){
$hashpos = strpos($answer,"#");
$comment = addslashes(substr(($answer),$hashpos+1));
$answer = substr($answer,0,$hashpos);
} else {
$comment = " ";
}
// End of Tom's addition
if ($answer[0] == "=") {
# $question->fraction[$key] = 1;
$question->fraction[$key] = $answeight;
$answer = substr($answer, 1);
} else {
# $question->fraction[$key] = 0;
$question->fraction[$key] = $answeight;
}
$question->answer[$key] = addslashes($answer);
$question->feedback[$key] = $comment;
}
return $question;
}
}
}
?>

View File

@ -1,40 +0,0 @@
<?php // $Id$
////////////////////////////////////////////////////////////////////////////
/// MULTIANSWER FORMAT
///
/// Created by Henrik Kaipe
///
////////////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
class quiz_format_multianswer extends quiz_default_format {
function provide_import() {
return true;
}
function readquestions($lines) {
/// Parses an array of lines into an array of questions.
/// For this class the method has been simplified as
/// there can never be more than one question for a
/// multianswer import
$questions= array();
$thequestion= quiz_qtype_multianswer_extract_question
(addslashes(implode('',$lines)));
$thequestion->qtype = MULTIANSWER;
$thequestion->course = $this->course;
if (!empty($thequestion)) {
$thequestion->name = addslashes($lines[0]);
$questions[] = $thequestion;
}
return $questions;
}
}
?>

View File

@ -1,146 +0,0 @@
<?php // $id$
// note: modified from the original filter/mediaplugin/filter.php
// given a href for a movie or sound file, it returns an appropriate tag
function custom_mediaplugin_filter($text, $width = null, $height = null) {
global $CFG;
if (is_null($width) || $width == 0) {
$usedefaults = true;
$width = 400;
$height = 300;
} else {
$usedefaults = false;
}
if (empty($CFG->filter_mediaplugin_ignore_mp3)) {
$search = '/<a(.*?)href=\"([^<]+)\.mp3\"([^>]*)>(.*?)<\/a>/i';
$replace = '\\0&nbsp;<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
$replace .= ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" ';
$replace .= ' width="35" height="18" id="mp3player" align="">';
$replace .= " <param name=movie value=\"$CFG->wwwroot/filter/mediaplugin/mp3player.swf?src=\\2.mp3\">";
$replace .= ' <param name=quality value=high>';
$replace .= ' <param name=bgcolor value="#333333">';
$replace .= " <embed src=\"$CFG->wwwroot/filter/mediaplugin/mp3player.swf?src=\\2.mp3\" ";
$replace .= " quality=high bgcolor=\"#333333\" width=\"35\" height=\"18\" name=\"mp3player\" ";
$replace .= ' type="application/x-shockwave-flash" ';
$replace .= ' pluginspage="http://www.macromedia.com/go/getflashplayer">';
$replace .= '</embed>';
$replace .= '</object>&nbsp;';
$text = preg_replace($search, $replace, $text);
}
if (empty($CFG->filter_mediaplugin_ignore_swf)) {
$search = '/<a(.*?)href=\"([^<]+)\.swf\"([^>]*)>(.*?)<\/a>/i';
$replace = '\\0<object '.
'type="application/x-shockwave-flash" ' .
'data="\\2.swf" ' .
'width="' . $width . '" ' .
'height="' . $height . '"> ' .
'<param name="movie" value="\\2.swf" /> ' .
'<param name="wmode" value="transparent" />' .
'</object>';
/* $replace = '\\0<p class="mediaplugin"><object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
$replace .= ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" ';
$replace .= ' width="' . $width . '" height="' . $height . '" id="mp3player" align="">';
$replace .= " <param name=movie value=\"\\2.swf\">";
$replace .= ' <param name=quality value=high>';
$replace .= " <embed src=\"\\2.swf\" ";
$replace .= " quality=high width=\"$width\" height=\"$height\" name=\"flashfilter\" ";
$replace .= ' type="application/x-shockwave-flash" ';
$replace .= ' pluginspage="http://www.macromedia.com/go/getflashplayer">';
$replace .= '</embed>';
$replace .= '</object></p>';*/
$text = preg_replace($search, $replace, $text);
}
if (empty($CFG->filter_mediaplugin_ignore_mov)) {
$search = '/<a(.*?)href=\"([^<]+)\.mov\"([^>]*)>(.*?)<\/a>/i';
$replace = '\\0<p class="mediaplugin"><object classid="CLSID:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"';
$replace .= ' codebase="http://www.apple.com/qtactivex/qtplugin.cab" ';
$replace .= ' height="' . $height . '" width="' . $width . '"';
$replace .= ' id="quicktime" align="" type="application/x-oleobject">';
$replace .= "<param name=\"src\" value=\"\\2.mov\" />";
$replace .= '<param name="autoplay" value=false />';
$replace .= '<param name="loop" value=true />';
$replace .= '<param name="controller" value=true />';
$replace .= '<param name="scale" value="aspect" />';
$replace .= "\n<embed src=\"\\2.mov\" name=\"quicktime\" type=\"video/quicktime\" ";
$replace .= ' height="' . $height . '" width="' . $width . '" scale="aspect" ';
$replace .= ' autoplay="false" controller="true" loop="true" ';
$replace .= ' pluginspage="http://quicktime.apple.com/">';
$replace .= '</embed>';
$replace .= '</object>&nbsp;';
$text = preg_replace($search, $replace, $text);
}
if (empty($CFG->filter_mediaplugin_ignore_wmv)) {
$search = '/<a(.*?)href=\"([^<]+)\.wmv\"([^>]*)>(.*?)<\/a>/i';
$replace = '\\0<p class="mediaplugin"><object classid="CLSID:22D6f312-B0F6-11D0-94AB-0080C74C7E95"';
$replace .= ' codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701" ';
$replace .= ' standby="Loading Microsoft? Windows? Media Player components..." ';
$replace .= ' id="msplayer" align="" type="application/x-oleobject">';
$replace .= "<param name=\"Filename\" value=\"\\2.wmv\">";
$replace .= '<param name="ShowControls" value=true />';
$replace .= '<param name="AutoRewind" value=true />';
$replace .= '<param name="AutoStart" value=false />';
$replace .= '<param name="Autosize" value=true />';
$replace .= '<param name="EnableContextMenu" value=true />';
$replace .= '<param name="TransparentAtStart" value=false />';
$replace .= '<param name="AnimationAtStart" value=false />';
$replace .= '<param name="ShowGotoBar" value=false />';
$replace .= '<param name="EnableFullScreenControls" value=true />';
$replace .= "\n<embed src=\"\\2.wmv\" name=\"msplayer\" type=\"video/x-ms\" ";
$replace .= ' ShowControls="1" AutoRewind="1" AutoStart="0" Autosize="0" EnableContextMenu="1"';
$replace .= ' TransparentAtStart="0" AnimationAtStart="0" ShowGotoBar="0" EnableFullScreenControls="1"';
$replace .= ' pluginspage="http://www.microsoft.com/Windows/Downloads/Contents/Products/MediaPlayer/">';
$replace .= '</embed>';
$replace .= '</object>&nbsp;';
$text = preg_replace($search, $replace, $text);
}
if ($usedefaults) {
$width = 240;
$height = 180;
}
if (empty($CFG->filter_mediaplugin_ignore_mpg)) {
$search = '/<a(.*?)href=\"([^<]+)\.(mpe?g)\"([^>]*)>(.*?)<\/a>/i';
$replace = '\\0<p class="mediaplugin"><object width="' . $width . '" height="' . $height . '">';
$replace .= '<param name="src" value="\\2.\\3">';
$replace .= '<param name="controller" value="true">';
$replace .= '<param name="autoplay" value="false">';
$replace .= '<embed src="\\2.\\3" width="' . $width . '" height="' . $height . '" controller="true" autoplay="false"> </embed>';
$replace .= '</object></p>';
$text = preg_replace($search, $replace, $text);
}
if (empty($CFG->filter_mediaplugin_ignore_avi)) {
$search = '/<a(.*?)href=\"([^<]+)\.avi\"([^>]*)>(.*?)<\/a>/i';
$replace = '\\0<p class="mediaplugin"><object width="' . $width . '" height="' . $height . '">';
$replace .= '<param name="src" value="\\2.avi">';
$replace .= '<param name="controller" value="true">';
$replace .= '<param name="autoplay" value="false">';
$replace .= '<embed src="\\2.avi" width="' . $width . '" height="' . $height . '" controller="true" autoplay="false"> </embed>';
$replace .= '</object>&nbsp;';
$text = preg_replace($search, $replace, $text);
}
return $text;
}
?>

View File

@ -1,927 +0,0 @@
<?php
require_once("$CFG->dirroot/mod/quiz/format/qti2/qt_common.php");
////////////////////////////////////////////////////////////////////////////
/// IMS QTI 2.0 FORMAT
///
/// HISTORY: created 28.01.2005 brian@mediagonal.ch
////////////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
define('CLOZE_TRAILING_TEXT_ID', 9999999);
class quiz_format_qti2 extends quiz_default_format {
var $lang;
function provide_export() {
return true;
}
function indent_xhtml($source, $indenter = ' ') {
// xml tidier-upper
// (c) Ari Koivula http://ventionline.com
// Remove all pre-existing formatting.
// Remove all newlines.
$source = str_replace("\n", '', $source);
$source = str_replace("\r", '', $source);
// Remove all tabs.
$source = str_replace("\t", '', $source);
// Remove all space after ">" and before "<".
$source = ereg_replace(">( )*", ">", $source);
$source = ereg_replace("( )*<", "<", $source);
// Iterate through the source.
$level = 0;
$source_len = strlen($source);
$pt = 0;
while ($pt < $source_len) {
if ($source{$pt} === '<') {
// We have entered a tag.
// Remember the point where the tag starts.
$started_at = $pt;
$tag_level = 1;
// If the second letter of the tag is "/", assume its an ending tag.
if ($source{$pt+1} === '/') {
$tag_level = -1;
}
// If the second letter of the tag is "!", assume its an "invisible" tag.
if ($source{$pt+1} === '!') {
$tag_level = 0;
}
// Iterate throught the source until the end of tag.
while ($source{$pt} !== '>') {
$pt++;
}
// If the second last letter is "/", assume its a self ending tag.
if ($source{$pt-1} === '/') {
$tag_level = 0;
}
$tag_lenght = $pt+1-$started_at;
// Decide the level of indention for this tag.
// If this was an ending tag, decrease indent level for this tag..
if ($tag_level === -1) {
$level--;
}
// Place the tag in an array with proper indention.
$array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght);
// If this was a starting tag, increase the indent level after this tag.
if ($tag_level === 1) {
$level++;
}
// if it was a self closing tag, dont do shit.
}
// Were out of the tag.
// If next letter exists...
if (($pt+1) < $source_len) {
// ... and its not an "<".
if ($source{$pt+1} !== '<') {
$started_at = $pt+1;
// Iterate through the source until the start of new tag or until we reach the end of file.
while ($source{$pt} !== '<' && $pt < $source_len) {
$pt++;
}
// If we found a "<" (we didnt find the end of file)
if ($source{$pt} === '<') {
$tag_lenght = $pt-$started_at;
// Place the stuff in an array with proper indention.
$array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght);
}
// If the next tag is "<", just advance pointer and let the tag indenter take care of it.
} else {
$pt++;
}
// If the next letter doesnt exist... Were done... well, almost..
} else {
break;
}
}
// Replace old source with the new one we just collected into our array.
$source = implode($array, "\n");
return $source;
}
function importpreprocess($category) {
global $CFG;
error("Sorry, importing this format is not yet implemented!",
"$CFG->wwwroot/mod/quiz/import.php?category=$category->id");
}
function exportpreprocess($category, $course, $lang = null) {
global $CFG;
require_once("{$CFG->libdir}/smarty/Smarty.class.php");
// assign the language for the export: by parameter, SESSION, USER, or the default of 'en'
if (is_null($lang)) {
global $SESSION, $USER;
$lang = empty($SESSION->lang) ? (empty($USER->lang) ? 'en' : $USER->lang) : $SESSION->lang;
}
$this->lang = $lang;
return parent::exportpreprocess($category, $course);
}
function export_file_extension() {
// override default type so extension is .xml
return ".zip";
}
function get_qtype( $type_id ) {
// translates question type code number into actual name
switch( $type_id ) {
case TRUEFALSE:
$name = 'truefalse';
break;
case MULTICHOICE:
$name = 'multichoice';
break;
case SHORTANSWER:
$name = 'shortanswer';
break;
case NUMERICAL:
$name = 'numerical';
break;
case MATCH:
$name = 'matching';
break;
case DESCRIPTION:
$name = 'description';
break;
case MULTIANSWER:
$name = 'multianswer';
break;
default:
$name = 'Unknown';
}
return $name;
}
function writetext( $raw ) {
// generates <text></text> tags, processing raw text therein
// for now, don't allow any additional tags in text
// otherwise xml rules would probably get broken
$raw = strip_tags( $raw );
return "<text>$raw</text>\n";
}
/**
* flattens $object['media'], copies $object['media'] to $path, and sets $object['mediamimetype']
*
* @param array &$object containing a field 'media'
* @param string $path the full path name to where the media files need to be copied
* @param int $courseid
* @return: mixed - true on success or in case of an empty media field, an error string if the file copy fails
*/
function copy_and_flatten(&$object, $path, $courseid) {
global $CFG;
if (!empty($object['media'])) {
$location = $object['media'];
$object['media'] = $this->flatten_image_name($location);
if (!@copy("{$CFG->dataroot}/$courseid/$location", "$path/{$object['media']}")) {
return "Failed to copy {$CFG->dataroot}/$courseid/$location to $path/{$object['media']}";
}
if (empty($object['mediamimetype'])) {
$object['mediamimetype'] = mimeinfo('type', $object['media']);
}
}
return true;
}
/**
* copies all files needed by the questions to the given $path, and flattens the file names
*
* @param array $questions the question objects
* @param string $path the full path name to where the media files need to be copied
* @param int $courseid
* @return mixed true on success, an array of error messages otherwise
*/
function handle_questions_media(&$questions, $path, $courseid) {
global $CFG;
$errors = array();
foreach ($questions as $key=>$question) {
// todo: handle in-line media (specified in the question text)
if (!empty($question->image)) {
$location = $questions[$key]->image;
$questions[$key]->mediaurl = $this->flatten_image_name($location);
if (!@copy("{$CFG->dataroot}/$courseid/$location", "$path/{$questions[$key]->mediaurl}")) {
$errors[] = "Failed to copy {$CFG->dataroot}/$courseid/$location to $path/{$questions[$key]->mediaurl}";
}
if (empty($question->mediamimetype)) {
$questions[$key]->mediamimetype = mimeinfo('type', $question->image);
}
}
}
return empty($errors) ? true : $errors;
}
/**
* exports the questions in a question category to the given location
*
* The parent class method was overridden because the IMS export consists of multiple files
*
* @param string $filename the directory name which will hold the exported files
* @return boolean - or errors out
*/
function exportprocess($filename) {
global $CFG;
// create a directory for the exports (if not already existing)
$dirname = get_string("exportfilename","quiz");
$courseid = $this->course->id;
$path = $CFG->dataroot.'/'.$courseid.'/'.$dirname;
if (!is_dir($path)) {
if (!mkdir($path, $CFG->directorypermissions)) {
error("Cannot create path: $path");
}
}
// create directory for this particular IMS export, if not already existing
$path = $path."/".$filename;
if (!is_dir($path)) {
if (!mkdir($path, $CFG->directorypermissions)) {
error("Cannot create path: $path");
}
}
// get the questions (from database) in this category
// $questions = get_records("quiz_questions","category",$this->category->id);
$questions = get_questions_category( $this->category );
notify("Exporting ".count($questions)." questions.");
$count = 0;
// create the imsmanifest file
$smarty =& $this->init_smarty();
$this->add_qti_info($questions);
// copy files used by the main questions to the export directory
$result = $this->handle_questions_media($questions, $path, $courseid);
if ($result !== true) {
notify(implode("<br />", $result));
}
$manifestquestions = $this->objects_to_array($questions);
$manifestid = str_replace(array(':', '/'), array('-','_'), "question_category_{$this->category->id}---{$CFG->wwwroot}");
$smarty->assign('externalfiles', 1);
$smarty->assign('manifestidentifier', $manifestid);
$smarty->assign('quiztitle', "question_category_{$this->category->id}");
$smarty->assign('quizinfo', "All questions in category {$this->category->id}");
$smarty->assign('questions', $manifestquestions);
$smarty->assign('lang', $this->lang);
$smarty->error_reporting = 99;
$expout = $smarty->fetch('imsmanifest.tpl');
$filepath = $path.'/imsmanifest.xml';
if (!$fh=fopen($filepath,"w")) {
error("Cannot open for writing: $filepath");
}
if (!fwrite($fh, $expout)) {
error("Cannot write exported questions to $filepath");
}
fclose($fh);
// iterate through questions
foreach($questions as $question) {
// results are first written into string (and then to a file)
$count++;
echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>";
$expout = $this->writequestion( $question , null, true, $path) . "\n";
$expout = $this->presave_process( $expout );
$filepath = $path.'/'.$this->get_assesment_item_id($question) . ".xml";
if (!$fh=fopen($filepath,"w")) {
error("Cannot open for writing: $filepath");
}
if (!fwrite($fh, $expout)) {
error("Cannot write exported questions to $filepath");
}
fclose($fh);
}
// zip files into single export file
zip_files( array($path), "$path.zip" );
// remove the temporary directory
delDirContents( $path );
return true;
}
/**
* exports a quiz (as opposed to exporting a category of questions)
*
* The parent class method was overridden because the IMS export consists of multiple files
*
* @param object $quiz
* @param array $questions - an array of question objects
* @param object $result - if set, contains result of calling quiz_grade_responses()
* @param string $redirect - a URL to redirect to in case of failure
* @param string $submiturl - the URL for the qti player to send the results to (e.g. attempt.php)
* @todo use $result in the ouput
*/
function export_quiz($course, $quiz, $questions, $result, $redirect, $submiturl = null) {
$this->xml_entitize($course);
$this->xml_entitize($quiz);
$this->xml_entitize($questions);
$this->xml_entitize($result);
$this->xml_entitize($submiturl);
if (! $this->exportpreprocess(0, $course)) { // Do anything before that we need to
error("Error occurred during pre-processing!", $redirect);
}
if (! $this->exportprocess_quiz($quiz, $questions, $result, $submiturl, $course)) { // Process the export data
error("Error occurred during processing!", $redirect);
}
if (! $this->exportpostprocess()) { // In case anything needs to be done after
error("Error occurred during post-processing!", $redirect);
}
}
/**
* This function is called to export a quiz (as opposed to exporting a category of questions)
*
* @uses $USER
* @param object $quiz
* @param array $questions - an array of question objects
* @param object $result - if set, contains result of calling quiz_grade_responses()
* @todo use $result in the ouput
*/
function exportprocess_quiz($quiz, $questions, $result, $submiturl, $course) {
global $USER;
global $CFG;
$gradingmethod = array (1 => 'GRADEHIGHEST',
2 => 'GRADEAVERAGE',
3 => 'ATTEMPTFIRST' ,
4 => 'ATTEMPTLAST');
$questions = $this->quiz_export_prepare_questions($questions, $quiz->id, $course->id, $quiz->shuffleanswers);
$smarty =& $this->init_smarty();
$smarty->assign('questions', $questions);
// quiz level smarty variables
$manifestid = str_replace(array(':', '/'), array('-','_'), "quiz{$quiz->id}-{$CFG->wwwroot}");
$smarty->assign('manifestidentifier', $manifestid);
$smarty->assign('submiturl', $submiturl);
$smarty->assign('userid', $USER->id);
$smarty->assign('username', htmlspecialchars($USER->username, ENT_COMPAT, 'UTF-8'));
$smarty->assign('quiz_level_export', 1);
$smarty->assign('quiztitle', format_string($quiz->name,true)); //assigned specifically so as not to cause problems with category-level export
$smarty->assign('quiztimeopen', date('Y-m-d\TH:i:s', $quiz->timeopen)); // ditto
$smarty->assign('quiztimeclose', date('Y-m-d\TH:i:s', $quiz->timeclose)); // ditto
$smarty->assign('grademethod', $gradingmethod[$quiz->grademethod]);
$smarty->assign('quiz', $quiz);
$smarty->assign('course', $course);
$smarty->assign('lang', $this->lang);
$expout = $smarty->fetch('imsmanifest.tpl');
echo utf8_encode($expout);
return true;
}
/**
* Prepares questions for quiz export
*
* The questions are changed as follows:
* - the question answers atached to the questions
* - image set to an http reference instead of a file path
* - qti specific info added
* - exporttext added, which contains an xml-formatted qti assesmentItem
*
* @param array $questions - an array of question objects
* @param int $quizid
* @return an array of question arrays
*/
function quiz_export_prepare_questions($questions, $quizid, $courseid, $shuffleanswers = null) {
global $CFG;
// add the answers to the questions and format the image property
foreach ($questions as $key=>$question) {
$questions[$key] = get_question_data($question);
$questions[$key]->courseid = $courseid;
$questions[$key]->quizid = $quizid;
if ($question->image) {
if (empty($question->mediamimetype)) {
$questions[$key]->mediamimetype = mimeinfo('type',$question->image);
}
$localfile = (substr(strtolower($question->image), 0, 7) == 'http://') ? false : true;
if ($localfile) {
// create the http url that the player will need to access the file
if ($CFG->slasharguments) { // Use this method if possible for better caching
$questions[$key]->mediaurl = "$CFG->wwwroot/file.php/$question->image";
} else {
$questions[$key]->mediaurl = "$CFG->wwwroot/file.php?file=$question->image";
}
} else {
$questions[$key]->mediaurl = $question->image;
}
}
}
$this->add_qti_info($questions);
$questions = $this->questions_with_export_info($questions, $shuffleanswers);
$questions = $this->objects_to_array($questions);
return $questions;
}
/**
* calls htmlspecialchars for each string field, to convert, for example, & to &amp;
*
* collections are processed recursively
*
* @param array $collection - an array or object or string
*/
function xml_entitize(&$collection) {
if (is_array($collection)) {
foreach ($collection as $key=>$var) {
if (is_string($var)) {
$collection[$key]= htmlspecialchars($var, ENT_COMPAT, 'UTF-8');
} else if (is_array($var) || is_object($var)) {
$this->xml_entitize($collection[$key]);
}
}
} else if (is_object($collection)) {
$vars = get_object_vars($collection);
foreach ($vars as $key=>$var) {
if (is_string($var)) {
$collection->$key = htmlspecialchars($var, ENT_COMPAT, 'UTF-8');
} else if (is_array($var) || is_object($var)) {
$this->xml_entitize($collection->$key);
}
}
} else if (is_string($collection)) {
$collection = htmlspecialchars($collection, ENT_COMPAT, 'UTF-8');
}
}
/**
* adds exporttext property to the questions
*
* Adds the qti export text to the questions
*
* @param array $questions - an array of question objects
* @return an array of question objects
*/
function questions_with_export_info($questions, $shuffleanswers = null) {
$exportquestions = array();
foreach($questions as $key=>$question) {
$expout = $this->writequestion( $question , $shuffleanswers) . "\n";
$expout = $this->presave_process( $expout );
$key = $this->get_assesment_item_id($question);
$exportquestions[$key] = $question;
$exportquestions[$key]->exporttext = $expout;
}
return $exportquestions;
}
/**
* Creates the export text for a question
*
* @todo handle in-line media (specified in the question/subquestion/answer text) for course-level exports
* @param object $question
* @param boolean $shuffleanswers whether or not to shuffle the answers
* @param boolean $courselevel whether or not this is a course-level export
* @param string $path provide the path to copy question media files to, if $courselevel == true
* @return string containing export text
*/
function writequestion($question, $shuffleanswers = null, $courselevel = false, $path = '') {
// turns question into string
// question reflects database fields for general question and specific to type
global $CFG;
$expout = '';
//need to unencode the html entities in the questiontext field.
// the whole question object was earlier run throught htmlspecialchars in xml_entitize().
$question->questiontext = html_entity_decode($question->questiontext, ENT_COMPAT);
$hasimage = empty($question->image) ? 0 : 1;
$hassize = empty($question->mediax) ? 0 : 1;
$allowedtags = '<a><br><b><h1><h2><h3><h4><i><img><li><ol><strong><table><tr><td><th><u><ul><object>'; // all other tags will be stripped from question text
$smarty =& $this->init_smarty();
$assesmentitemid = $this->get_assesment_item_id($question);
$question_type = $this->get_qtype( $question->qtype );
$questionid = "question{$question->id}$question_type";
$smarty->assign('question_has_image', $hasimage);
$smarty->assign('hassize', $hassize);
$smarty->assign('questionid', $questionid);
$smarty->assign('assessmentitemidentifier', $assesmentitemid);
$smarty->assign('assessmentitemtitle', $question->name);
$smarty->assign('courselevelexport', $courselevel);
if ($question->qtype == MULTIANSWER) {
$question->questiontext = strip_tags($question->questiontext, $allowedtags . '<intro>');
$smarty->assign('questionText', $this->get_cloze_intro($question->questiontext));
} else {
$smarty->assign('questionText', strip_tags($question->questiontext, $allowedtags));
}
$smarty->assign('question', $question);
// the following two are left for compatibility; the templates should be changed, though, to make object tags for the questions
//$smarty->assign('questionimage', $question->image);
//$smarty->assign('questionimagealt', "image: $question->image");
// output depends on question type
switch($question->qtype) {
case TRUEFALSE:
$qanswers = $question->options->answers;
$answers[0] = (array)$qanswers['true'];
$answers[0]['answer'] = get_string("true", "quiz");
$answers[1] = (array)$qanswers['false'];
$answers[1]['answer'] = get_string("false", "quiz");
if (!empty($shuffleanswers)) {
$answers = $this->shuffle_things($answers);
}
if (isset($question->response)) {
$correctresponseid = $question->response[$questionid];
if ($answers[0]['id'] == $correctresponseid) {
$correctresponse = $answers[0];
} else {
$correctresponse = $answers[1];
}
}
else {
$correctresponse = '';
}
$smarty->assign('correctresponse', $correctresponse);
$smarty->assign('answers', $answers);
$expout = $smarty->fetch('choice.tpl');
break;
case MULTICHOICE:
$answers = $this->objects_to_array($question->options->answers);
if (!empty($shuffleanswers)) {
$answers = $this->shuffle_things($answers);
}
$correctresponses = $this->get_correct_answers($answers);
$correctcount = count($correctresponses);
$smarty->assign('responsedeclarationcardinality', $correctcount > 1 ? 'multiple' : 'single');
$smarty->assign('correctresponses', $correctresponses);
$smarty->assign('answers', $answers);
$smarty->assign('maxChoices', $question->options->single ? '1' : count($answers));
$expout = $smarty->fetch('choiceMultiple.tpl');
break;
case SHORTANSWER:
$answers = $this->objects_to_array($question->options->answers);
if (!empty($shuffleanswers)) {
$answers = $this->shuffle_things($answers);
}
$correctresponses = $this->get_correct_answers($answers);
$correctcount = count($correctresponses);
$smarty->assign('responsedeclarationcardinality', $correctcount > 1 ? 'multiple' : 'single');
$smarty->assign('correctresponses', $correctresponses);
$smarty->assign('answers', $answers);
$expout = $smarty->fetch('textEntry.tpl');
break;
case NUMERICAL:
$qanswer = array_pop( $question->options->answers );
$smarty->assign('lowerbound', $qanswer->answer - $qanswer->tolerance);
$smarty->assign('upperbound', $qanswer->answer + $qanswer->tolerance);
$smarty->assign('answer', $qanswer->answer);
$expout = $smarty->fetch('numerical.tpl');
break;
case MATCH:
$this->xml_entitize($question->options->subquestions);
$subquestions = $this->objects_to_array($question->options->subquestions);
if (!empty($shuffleanswers)) {
$subquestions = $this->shuffle_things($subquestions);
}
$setcount = count($subquestions);
$smarty->assign('setcount', $setcount);
$smarty->assign('matchsets', $subquestions);
$expout = $smarty->fetch('match.tpl');
break;
case DESCRIPTION:
$expout = $smarty->fetch('extendedText.tpl');
break;
// loss of get_answers() from quiz_embedded_close_qtype class during
// Gustav's refactor breaks MULTIANSWER badly - one for another day!!
/*
case MULTIANSWER:
$answers = $this->get_cloze_answers_array($question);
$questions = $this->get_cloze_questions($question, $answers, $allowedtags);
$smarty->assign('cloze_trailing_text_id', CLOZE_TRAILING_TEXT_ID);
$smarty->assign('answers', $answers);
$smarty->assign('questions', $questions);
$expout = $smarty->fetch('composite.tpl');
break; */
default:
$smarty->assign('questionText', "This question type (Unknown: type $question_type) has not yet been implemented");
$expout = $smarty->fetch('notimplemented.tpl');
}
// run through xml tidy function
//$tidy_expout = $this->indent_xhtml( $expout, ' ' ) . "\n\n";
//return $tidy_expout;
return $expout;
}
/**
* Gets an id to use for a qti assesment item
*
* @param object $question
* @return string containing a qti assesment item id
*/
function get_assesment_item_id($question) {
return "question{$question->id}";
}
/**
* gets the answers whose grade fraction > 0
*
* @param array $answers
* @return array (0-indexed) containing the answers whose grade fraction > 0
*/
function get_correct_answers($answers)
{
$correctanswers = array();
foreach ($answers as $answer) {
if ($answer['fraction'] > 0) {
$correctanswers[] = $answer;
}
}
return $correctanswers;
}
/**
* gets a new Smarty object, with the template and compile directories set
*
* @return object a smarty object
*/
function & init_smarty() {
global $CFG;
// create smarty compile dir in dataroot
$path = $CFG->dataroot."/smarty_c";
if (!is_dir($path)) {
if (!mkdir($path, $CFG->directorypermissions)) {
error("Cannot create path: $path");
}
}
$smarty = new Smarty;
$smarty->template_dir = "{$CFG->dirroot}/mod/quiz/format/qti2/templates";
$smarty->compile_dir = "$path";
return $smarty;
}
/**
* converts an array of objects to an array of arrays (not recursively)
*
* @param array $objectarray
* @return array - an array of answer arrays
*/
function objects_to_array($objectarray)
{
$arrayarray = array();
foreach ($objectarray as $object) {
$arrayarray[] = (array)$object;
}
return $arrayarray;
}
/**
* gets a question's cloze answer objects as arrays containing only arrays and basic data types
*
* @param object $question
* @return array - an array of answer arrays
*/
function get_cloze_answers_array($question) {
$answers = $this->get_answers($question);
$this->xml_entitize($answers);
foreach ($answers as $answerkey => $answer) {
$answers[$answerkey]->subanswers = $this->objects_to_array($answer->subanswers);
}
return $this->objects_to_array($answers);
}
/**
* gets an array with text and question arrays for the given cloze question
*
* To make smarty processing easier, the returned text and question sub-arrays have an equal number of elements.
* If it is necessary to add a dummy element to the question sub-array, the question will be given an id of CLOZE_TRAILING_TEXT_ID.
*
* @param object $question
* @param array $answers - an array of arrays containing the question's answers
* @param string $allowabletags - tags not to strip out of the question text (e.g. '<i><br>')
* @return array with text and question arrays for the given cloze question
*/
function get_cloze_questions($question, $answers, $allowabletags) {
$questiontext = strip_tags($question->questiontext, $allowabletags);
if (preg_match_all('/(.*){#([0-9]+)}/U', $questiontext, $matches)) {
// matches[1] contains the text inbetween the question blanks
// matches[2] contains the id of the question blanks (db: quiz_multianswers.positionkey)
// find any trailing text after the last {#XX} and add it to the array
if (preg_match('/.*{#[0-9]+}(.*)$/', $questiontext, $tail)) {
$matches[1][] = $tail[1];
$tailadded = true;
}
$questions['text'] = $matches[1];
$questions['question'] = array();
foreach ($matches[2] as $key => $questionid) {
foreach ($answers as $answer) {
if ($answer['positionkey'] == $questionid) {
$questions['question'][$key] = $answer;
break;
}
}
}
if ($tailadded) {
// to have a matching number of question and text array entries:
$questions['question'][] = array('id'=>CLOZE_TRAILING_TEXT_ID, 'answertype'=>SHORTANSWER);
}
} else {
$questions['text'][0] = $question->questiontext;
$questions['question'][0] = array('id'=>CLOZE_TRAILING_TEXT_ID, 'answertype'=>SHORTANSWER);
}
return $questions;
}
/**
* strips out the <intro>...</intro> section, if any, and returns the text
*
* changes the text object passed to it.
*
* @param string $&text
* @return string the intro text, if there was an intro tag. '' otherwise.
*/
function get_cloze_intro(&$text) {
if (preg_match('/(.*)?\<intro>(.+)?\<\/intro>(.*)/s', $text, $matches)) {
$text = $matches[1] . $matches[3];
return $matches[2];
}
else {
return '';
}
}
/**
* adds qti metadata properties to the questions
*
* The passed array of questions is altered by this function
*
* @param &questions an array of question objects
*/
function add_qti_info(&$questions)
{
foreach ($questions as $key=>$question) {
$questions[$key]->qtiinteractiontype = $this->get_qti_interaction_type($question->qtype);
$questions[$key]->qtiscoreable = $this->get_qti_scoreable($question);
$questions[$key]->qtisolutionavailable = $this->get_qti_solution_available($question);
}
}
/**
* returns whether or not a given question is scoreable
*
* @param object $question
* @return boolean
*/
function get_qti_scoreable($question) {
switch ($question->qtype) {
case DESCRIPTION:
return 'false';
default:
return 'true';
}
}
/**
* returns whether or not a solution is available for a given question
*
* The results are based on whether or not Moodle stores answers for the given question type
*
* @param object $question
* @return boolean
*/
function get_qti_solution_available($question) {
switch($question->qtype) {
case TRUEFALSE:
return 'true';
case MULTICHOICE:
return 'true';
case SHORTANSWER:
return 'true';
case NUMERICAL:
return 'true';
case MATCH:
return 'true';
case DESCRIPTION:
return 'false';
case MULTIANSWER:
return 'true';
default:
return 'true';
}
}
/**
* maps a moodle question type to a qti 2.0 question type
*
* @param int type_id - the moodle question type
* @return string qti 2.0 question type
*/
function get_qti_interaction_type($type_id) {
switch( $type_id ) {
case TRUEFALSE:
$name = 'choiceInteraction';
break;
case MULTICHOICE:
$name = 'choiceInteraction';
break;
case SHORTANSWER:
$name = 'textInteraction';
break;
case NUMERICAL:
$name = 'textInteraction';
break;
case MATCH:
$name = 'matchInteraction';
break;
case DESCRIPTION:
$name = 'extendedTextInteraction';
break;
case MULTIANSWER:
$name = 'textInteraction';
break;
default:
$name = 'textInteraction';
}
return $name;
}
/**
* returns the given array, shuffled
*
*
* @param array $things
* @return array
*/
function shuffle_things($things) {
$things = swapshuffle_assoc($things);
$oldthings = $things;
$things = array();
foreach ($oldthings as $key=>$value) {
$things[] = $value; // This loses the index key, but doesn't matter
}
return $things;
}
/**
* returns a flattened image name - with all /, \ and : replaced with other characters
*
* used to convert a file or url to a qti-permissable identifier
*
* @param string name
* @return string
*/
function flatten_image_name($name) {
return str_replace(array('/', '\\', ':'), array ('_','-','.'), $name);
}
function file_full_path($file, $courseid) {
global $CFG;
if (substr(strtolower($file), 0, 7) == 'http://') {
$url = $file;
} else if ($CFG->slasharguments) { // Use this method if possible for better caching
$url = "{$CFG->wwwroot}/file.php/$courseid/{$file}";
} else {
$url = "{$CFG->wwwroot}/file.php?file=/$courseid/{$file}";
}
return $url;
}
}
?>

View File

@ -1,147 +0,0 @@
<?php
//***********************************
// qt_common.php
//***********************************
// This contains code common to mediagonal-modified questions
//
/**
* gets a list of all the media files for the given course
*
* @param int courseid
* @return array containing filenames
* @calledfrom questiontypes/<typename>/editquestion.php
*/
function get_course_media_files($courseid)
{
// this code lifted from mod/quiz/question.php and modified
global $CFG;
$images = null;
make_upload_directory("$course->id"); // Just in case
$coursefiles = get_directory_list("$CFG->dataroot/$courseid", $CFG->moddata);
foreach ($coursefiles as $filename) {
if (is_media_by_extension($filename)) {
$images["$filename"] = $filename;
}
}
return $images;
}
/**
* determines whether or not a file is an image, based on the file extension
*
* @param string $file the filename
* @return boolean
*/
function is_image_by_extentsion($file) {
$extensionsregex = '/\.(gif|jpg|jpeg|jpe|png|tif|tiff|bmp|xbm|rgb|svf)$/';
if (preg_match($extensionsregex, $file)) {
return true;
}
return false;
}
/**
* determines whether or not a file is a media file, based on the file extension
*
* @param string $file the filename
* @return boolean
*/
function is_media_by_extension($file) {
$extensionsregex = '/\.(gif|jpg|jpeg|jpe|png|tif|tiff|bmp|xbm|rgb|svf|swf|mov|mpg|mpeg|wmf|avi|mpe|flv|mp3|ra|ram)$/';
if (preg_match($extensionsregex, $file)) {
return true;
}
return false;
}
/**
* determines whether or not a file is a multimedia file, based on the file extension
*
* @param string $file the filename
* @return boolean
*/
function is_multimedia_by_extension($file) {
$extensionsregex = '/\.(swf|mov|mpg|mpeg|wmf|avi|mpe|flv)$/';
if (preg_match($extensionsregex, $file)) {
return true;
}
return false;
}
/**
* determines whether or not a file is a multimedia file of a type php can get the dimension for, based on the file extension
*
* @param string $file the filename
* @return boolean
*/
function is_sizable_multimedia($file) {
$extensionsregex = '/\.(swf)$/';
if (preg_match($extensionsregex, $file)) {
return true;
}
return false;
}
/**
* creates a media tag to use for choice media
*
* @param string $file the filename
* @param string $courseid the course id
* @param string $alt to specify the alt tag
* @return string either an image tag, or html for an embedded object
*/
function get_media_tag($file, $courseid = 0, $alt = 'media file', $width = 0, $height = 0) {
global $CFG;
// if it's a moodle library file, it will be served through file.php
if (substr(strtolower($file), 0, 7) == 'http://') {
$media = $file;
} else if ($CFG->slasharguments) { // Use this method if possible for better caching
$media = "{$CFG->wwwroot}/file.php/$courseid/$file";
} else {
$media = "{$CFG->wwwroot}/file.php?file=/$courseid/$file";
}
$ismultimedia = false;
if (!$isimage = is_image_by_extension($file)) {
$ismultimedia = is_multimedia_by_extension($file);
}
// if there is no known width and height, try to get one
if ($width == 0) {
if ($isimage || is_sizable_multimedia($file)) {
}
}
// create either an image link or a generic link.
// if the moodle multimedia filter is turned on, it'll catch multimedia content in the generic link
if (is_image_by_extension($file)) {
return "<img src=\"$media\" alt=\"$alt\" width=\"$width\" height=\"$height\" />";
}
else {
require_once("$CFG->dirroot/mod/quiz/format/qti/custommediafilter.php");
return custom_mediaplugin_filter('<a href="' . $media . '"></a>', $courseid, $width, $height);
}
}
/**
* determines the x and y size of the given file
*
* @param string $file the filename
* @return array looks like array('x'=>171, 'y'=>323), or array('x'=>0, 'y'=>0) if size can't be determined
*/
function get_file_dimensions($file) {
$imginfo = @getimagesize($file);
if ($imginfo !== FALSE) {
return array('x'=>$imginfo[0], 'y'=>$imginfo[1]);
} else {
return array('x'=> 0, 'y'=> 0);
}
}
?>

View File

@ -1,70 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="single" baseType="identifier">
<correctResponse>
<value>{$correctresponse.id}</value>
</correctResponse>
<mapping defaultValue="0">
<mapEntry mapKey="{$correctresponse.id}" mappedValue="{$correctresponse.fraction}"/>
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
<defaultValue>
<value>0</value>
</defaultValue>
</outcomeDeclaration>
<itemBody>
<p>{$questionText}</p>
<div class="intreactive.choiceSimple">
<choiceInteraction responseIdentifier="{$questionid}" shuffle="false" maxChoices="1">
{section name=answer loop=$answers}
<simpleChoice identifier="{$answers[answer].id}">{$answers[answer].answer}
{if $answers[answer].feedback != ''}
{if $answers[answer].answer != $correctresponse.answer}
<feedbackInline identifier="{$answers[answer].id}" outcomeIdentifier="FEEDBACK" showHide="hide">{$answers[answer].feedback}</feedbackInline>
{/if}
{/if}
</simpleChoice>
{/section}
</choiceInteraction>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</div>
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
<responseCondition>
<responseIf>
<match>
<variable identifier="{$questionid}"/>
<correct identifier="{$questionid}"/>
</match>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="float">1</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="float">0</baseValue>
</setOutcomeValue>
</responseElse>
</responseCondition>
<setOutcomeValue identifier="FEEDBACK">
<variable identifier="{$questionid}"/>
</setOutcomeValue>
</responseProcessing>
{section name=answer loop=$answers}
{if $answers[answer].feedback != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$answers[answer].id}" showHide="hide">{$answers[answer].feedback}</modalFeedback>
{/if}
{/section}
</assessmentItem>

View File

@ -1,64 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="{$responsedeclarationcardinality}" baseType="identifier">
<correctResponse>
{section name=answer loop=$correctresponses}
<value>{$correctresponses[answer].id}</value>
{/section}
</correctResponse>
<mapping lowerBound="0" upperBound="1" defaultValue="-1">
{section name=answer loop=$answers}
{if $answers[answer].fraction != 0}
<mapEntry mapKey="{$answers[answer].id}" mappedValue="{$answers[answer].fraction}" />
{/if}
{/section}
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float" />
<itemBody>
<div class="assesmentItemBody">
<p>{$questionText}</p>
</div>
<div class="interactive.choiceMultiple">
<choiceInteraction responseIdentifier="{$questionid}" shuffle="false" maxChoices="{$maxChoices}">
{section name=answer loop=$answers}
<simpleChoice identifier="{$answers[answer].id}" fixed="false">{$answers[answer].answer}</simpleChoice>
{/section}
</choiceInteraction>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
<responseCondition>
<responseIf>
<isNull>
<variable identifier="{$questionid}"/>
</isNull>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="float">0</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE">
<mapResponse identifier="{$questionid}"/>
</setOutcomeValue>
</responseElse>
</responseCondition>
<setOutcomeValue identifier="FEEDBACK">
<variable identifier="{$questionid}"/>
</setOutcomeValue>
</responseProcessing>
{section name=answer loop=$answers}
{if $answers[answer].feedback != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$answers[answer].id}" showHide="show">{$answers[answer].feedback}</modalFeedback>
{/if}
{/section}
</assessmentItem>

View File

@ -1,101 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
{section name=aid loop=$answers}
{if $answers[aid].answertype == 3}
<responseDeclaration identifier="{$questionid}{$answers[aid].id}" cardinality="single" baseType="identifier">
<correctResponse>
{section name=subanswer loop=$answers[aid].subanswers}
{if $answers[aid].subanswers[subanswer].fraction > 0}
<value>{$answers[aid].subanswers[subanswer].id}</value>
{/if}
{/section}
</correctResponse>
<mapping defaultValue="0">
{section name=subanswer loop=$answers[aid].subanswers}
{if $answers[aid].subanswers[subanswer].fraction != 0}
<mapEntry mapKey="{$answers[aid].subanswers[subanswer].id}" mappedValue="{$answers[aid].subanswers[subanswer].fraction}"/>
{/if}
{/section}
</mapping>
</responseDeclaration>
{elseif $answers[aid].answertype == 1}
<responseDeclaration identifier="{$questionid}{$answers[aid].id}" cardinality="single" baseType="string">
<correctResponse>
{section name=subanswer loop=$answers[aid].subanswers}
{if $answers[aid].subanswers[subanswer].fraction > 0}
<value>{$answers[aid].subanswers[subanswer].answer}</value>
{/if}
{/section}
</correctResponse>
<mapping lowerBound="0" upperBound="1" defaultValue="0">
{section name=subanswer loop=$answers[aid].subanswers}
{if $answers[aid].subanswers[subanswer].fraction != 0}
<mapEntry mapKey="{$answers[aid].subanswers[subanswer].answer}" mappedValue="{$answers[aid].subanswers[subanswer].fraction}" />
{/if}
{/section}
</mapping>
</responseDeclaration>
{/if}
{/section}
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
<itemBody>
{if $questionText != ''}
<div class="assesmentItemBody">
<p>{$questionText}</p>
</div>
{/if}
<div class="interactive.cloze"><p>
{section name=qid loop=$questions.question}
{$questions.text[qid]}
{if $questions.question[qid].id != $cloze_trailing_text_id}
{if $questions.question[qid].answertype == 3}
<inlineChoiceInteraction responseIdentifier="{$questionid}{$questions.question[qid].id}" shuffle="false">
{section name=aid loop=$questions.question[qid].subanswers}
<inlineChoice identifier="{$questions.question[qid].subanswers[aid].id}">{$questions.question[qid].subanswers[aid].answer}</inlineChoice>
{/section}
</inlineChoiceInteraction>
{elseif $questions.question[qid].answertype == 1}
<textEntryInteraction responseIdentifier="{$questionid}{$questions.question[qid].id}" expectedLength="15"/>
{/if}
{/if}
{/section}</p></div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
{section name=answer loop=$answers}{if $answers[answer].answertype == 1 || $answers[answer].answertype == 3}
<responseCondition>
<responseIf>
<isNull>
<variable identifier="{$questionid}{$answers[answer].id}"/>
</isNull>
<setOutcomeValue identifier="SCORE{$questionid}{$answers[answer].id}">
<baseValue baseType="float">0</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE{$questionid}{$answers[answer].id}">
<mapResponse identifier="{$questionid}{$answers[answer].id}"/>
</setOutcomeValue>
</responseElse>
</responseCondition>
<setOutcomeValue identifier="FEEDBACK">
<variable identifier="{$questionid}{$answers[answer].id}"/>
</setOutcomeValue>
{/if}{/section}
</responseProcessing>
{section name=answer loop=$answers}{if $answers[answer].answertype == 1 || $answers[answer].answertype == 3}
{section name=subanswer loop=$answers[answer].subanswers}
{if $answers[answer].subanswers[subanswer].feedback != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$answers[answer].subanswers[subanswer].id}" showHide="show">{$answers[answer].subanswers[subanswer].feedback}</modalFeedback>
{/if}{/section}
{/if}
{/section}
</assessmentItem>

View File

@ -1,17 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="single" baseType="string"/>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="integer"/>
<itemBody>
<p>{$questionText}</p>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
</assessmentItem>

View File

@ -1,27 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="single" baseType="string"/>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="integer"/>
<itemBody>
<div class="assesmentItemBody">
<p>{$questionText}</p>
</div>
<div class="interactive.extendedText">
<extendedTextInteraction responseIdentifier="{$questionid}" expectedLength="600">
</extendedTextInteraction>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
{if $question->feedback != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$questionid}" showHide="hide">{$question->feedback}</modalFeedback>
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$questionid}" showHide="show">{$question->feedback}</modalFeedback>
{/if}
</assessmentItem>

View File

@ -1,33 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="multiple" baseType="directedPair">
<correctResponse>
{section name=item loop=$gapitems}
<value>{$gapitems[item].id} {$gapitems[item].id}</value>
{/section}
</correctResponse>
<mapping defaultValue="1">
{section name=item loop=$gapitems}
<mapEntry mapKey="{$gapitems[item].id} {$gapitems[item].id}" mappedValue="1" />
{/section}
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
<itemBody>
<div class="assesmentItemBody"><p>{$questionText}</p></div>
<div class="interactive.graphicGapMatch">
<graphicGapMatchInteraction responseIdentifier="{$questionid}">
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}"/>
{section name=item loop=$gapitems}
<gapImg identifier="{$gapitems[item].id}" matchMax="1">
<object type="{$gapitems[item].mediamimetype}" data="{$gapitems[item].media}" width="{$gapitems[item].snaptowidth}" height="{$gapitems[item].snaptoheight}" label="{$gapitems[item].questiontext}"/>
</gapImg>
{/section}
{section name=item loop=$gapitems}
<associableHotspot identifier="{$gapitems[item].id}" matchMax="{$hotspotmaxmatch}" shape="rect" coords="{$gapitems[item].targetx},{$gapitems[item].targety},{$gapitems[item].targetrx},{$gapitems[item].targetby}"/>
{/section}
</graphicGapMatchInteraction>
</div>
</itemBody>
<responseProcessing template="http://www.imsglobal.org/xml/imsqti_item_v2p0/rpMapResponse" templateLocation="../RPTemplates/rpMapResponse.xml"/>
</assessmentItem>

View File

@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" identifier="{$manifestidentifier}" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd">
<metadata>
<schema>ADL SCORM</schema>
<schemaversion>1.2</schemaversion>
<lom xmlns="http://www.imsglobal.org/xsd/imsmd_v1p2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd">
<general>
<title><langstring xml:lang="{$lang}">{$quiztitle}</langstring></title>
<description><langstring xml:lang="{$lang}">{$quizinfo}</langstring></description>
<keyword><langstring xml:lang="{$lang}">{$quizkeywords}</langstring></keyword>
</general>
</lom>
{if $quiz_level_export == 1}
<imsqti:var id="submiturl">{$submiturl}</imsqti:var>
<imsqti:var id="userid">{$userid}</imsqti:var>
<imsqti:var id="username">{$username}</imsqti:var>
<imsqti:var id="id">{$quiz->id}</imsqti:var>
<imsqti:var id="course">{$quiz->course}</imsqti:var>
<imsqti:var id="timeopen">{$quiztimeopen}</imsqti:var>
<imsqti:var id="timeclose">{$quiztimeclose}</imsqti:var>
<imsqti:var id="timelimit">{$quiz->timelimit}</imsqti:var>
<imsqti:var id="shufflequestions">{$quiz->shufflequestions}</imsqti:var>
<imsqti:var id="shuffleanswers">{$quiz->shuffleanswers}</imsqti:var>
<imsqti:var id="attempts">{$quiz->attempts}</imsqti:var>
<imsqti:var id="attemptbuildsonlast">{$quiz->attemptonlast}</imsqti:var>
<imsqti:var id="grademethod">{$grademethod}</imsqti:var>
<imsqti:var id="feedback">{$quiz->feedback}</imsqti:var>
<imsqti:var id="feedbackcorrectanswers">{$quiz->correctanswers}</imsqti:var>
<imsqti:var id="maxgrade">{$quiz->grade}</imsqti:var>
<imsqti:var id="rawpointspossible">{$quiz->sumgrades}</imsqti:var>
<imsqti:var id="password">{$quiz->password}</imsqti:var>
<imsqti:var id="subnet">{$quiz->subnet}</imsqti:var>
<imsqti:var id="coursefullname">{$course->fullname}</imsqti:var>
<imsqti:var id="courseshortname">{$course->shortname}</imsqti:var>
{/if}
</metadata>
<organizations/>
<resources>
{section name=question loop=$questions}
<resource identifier="category{$questions[question].category}-question{$questions[question].id}" type="imsqti_item_xmlv2p0" {if $externalfiles == 1}href="./category{$questions[question].category}-question{$questions[question].id}.xml"{/if}>
<metadata>
<schema>IMS QTI Item</schema>
<schemaversion>2.0</schemaversion>
<imsmd:lom>
<imsmd:general>
<imsmd:identifier>category{$questions[question].category}-question{$questions[question].id}</imsmd:identifier>
<imsmd:title>
<imsmd:langstring xml:lang="{$lang}">{$questions[question].name}</imsmd:langstring>
</imsmd:title>
<imsmd:description>
<imsmd:langstring xml:lang="en">Question {$questions[question].id} from category {$questions[question].category}</imsmd:langstring>
</imsmd:description>
</imsmd:general>
<imsmd:lifecycle>
<imsmd:version>
<imsmd:langstring xml:lang="en">1.0</imsmd:langstring>
</imsmd:version>
<imsmd:status>
<imsmd:source>
<imsmd:langstring xml:lang="en">LOMv1.0</imsmd:langstring>
</imsmd:source>
<imsmd:value>
<imsmd:langstring xml:lang="en">Draft</imsmd:langstring>
</imsmd:value>
</imsmd:status>
</imsmd:lifecycle>
</imsmd:lom>
<imsqti:qtiMetadata>
<imsqti:timeDependent>false</imsqti:timeDependent>
<imsqti:interactionType>{$questions[question].qtiinteractiontype}</imsqti:interactionType>
<imsqti:canComputerScore>{$questions[question].qtiscoreable}</imsqti:canComputerScore>
<imsqti:feedbackType>nonadaptive</imsqti:feedbackType>
<imsqti:solutionAvailable>{$questions[question].qtisolutionavailable}</imsqti:solutionAvailable>
</imsqti:qtiMetadata>
</metadata>
{if $questions[question].image != ''}
<file href="{$questions[question].mediaurl}" />
{/if}
{$questions[question].exporttext}
</resource>
{/section}
</resources>
</manifest>

View File

@ -1,62 +0,0 @@
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="multiple" baseType="directedPair">
<correctResponse>
{section name=set loop=$matchsets}
<value>q{$matchsets[set].id} a{$matchsets[set].id}</value>
{/section}
</correctResponse>
<mapping defaultValue="0">
{section name=set loop=$matchsets}
<mapEntry mapKey="q{$matchsets[set].id} a{$matchsets[set].id}" mappedValue="1"/>
{/section}
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
<itemBody>
<p>{$questionText}</p>
<div class="interactive.match">
<matchInteraction responseIdentifier="{$questionid}" shuffle="false" maxAssociations="{$setcount}">
<simpleMatchSet>
{section name=set loop=$matchsets}
<simpleAssociableChoice identifier="q{$matchsets[set].id}" matchMax="1">{$matchsets[set].questiontext}</simpleAssociableChoice>
{/section}
</simpleMatchSet>
<simpleMatchSet>
{section name=set loop=$matchsets}
<simpleAssociableChoice identifier="a{$matchsets[set].id}" matchMax="{$setcount}">{$matchsets[set].answertext}</simpleAssociableChoice>
{/section}
</simpleMatchSet>
</matchInteraction>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
<responseCondition>
<responseIf>
<isNull>
<variable identifier="{$questionid}"/>
</isNull>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="integer">0</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE">
<mapResponse identifier="{$questionid}"/>
</setOutcomeValue>
</responseElse>
</responseCondition>
</responseProcessing>
</assessmentItem>

View File

@ -1,88 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="{$responsedeclarationcardinality}" baseType="identifier">
<correctResponse>
{section name=answer loop=$correctresponses}
<value>{$correctresponses[answer].id}</value>
{/section}
</correctResponse>
<mapping lowerBound="0" upperBound="1" defaultValue="{$defaultvalue}">
{section name=answer loop=$answers}
{if $answers[answer].fraction != 0}
<mapEntry mapKey="{$answers[answer].id}" mappedValue="{$answers[answer].fraction}" />
{/if}
{/section}
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float" />
<itemBody>
<div class="assesmentItemBody">
<p>{$questionText}</p>
</div>
<div class="interactive.choiceMultiple">
<choiceInteraction responseIdentifier="{$questionid}" shuffle="false" maxChoices="{$maxChoices}">
{section name=answer loop=$answers}
<simpleChoice identifier="{$answers[answer].id}" fixed="false"><p>{$answers[answer].choice}
{if $answers[answer].media != ''}
<object type="{$answers[answer].mediamimetype}" data="{$answers[answer].media}" width="{$answers[answer].mediax}" height="{$answers[answer].mediay}" />
{/if}</p>
{if $answers[answer].feedback != ''}
<feedbackInline identifier="{$answers[answer].id}" outcomeIdentifier="FEEDBACK" showHide="show">{$answers[answer].feedback}</feedbackInline>
{/if}
{if $answers[answer].altfeedback != ''}
<feedbackInline identifier="{$answers[answer].id}" outcomeIdentifier="ALTFEEDBACK" showHide="hide">{$answers[answer].altfeedback}</feedbackInline>
{/if}
</simpleChoice>
{/section}
</choiceInteraction>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
<responseCondition>
<responseIf>
<isNull>
<variable identifier="{$questionid}"/>
</isNull>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="float">0</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE">
<mapResponse identifier="{$questionid}"/>
</setOutcomeValue>
</responseElse>
</responseCondition>
<responseCondition>
<responseIf>
<gte>
<variable identifier="SCORE"/>
<baseValue baseType="float">{$question->feedbackfraction}</baseValue>
</gte>
<setOutcomeValue identifier="FEEDBACK">
<variable identifier="feedbackok"/>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="FEEDBACK">
<variable identifier="feedbackmissed"/>
</setOutcomeValue>
</responseElse>
</responseCondition>
</responseProcessing>
{if $question->feedbackok != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="feedbackok" showHide="show">{$question->feedbackok}</modalFeedback>
{/if}
{if $question->feedbackmissed != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="feedbackmissed" showHide="hide">{$question->feedbackmissed}</modalFeedback>
{/if}
</assessmentItem>

View File

@ -1,20 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="single" baseType="string"/>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="integer"/>
<itemBody>
<p>{$questionText}</p>
<div class="interactive.textEntry">
<textEntryInteraction responseIdentifier="{$questionid}" expectedLength="200"></textEntryInteraction>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
</assessmentItem>

View File

@ -1,65 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="single" baseType="float">
<correctResponse>
<value>{$answer->answer}</value>
</correctResponse>
<mapping defaultValue="0">
<mapEntry mapKey="{$answer->answer}" mappedValue="{$answer->fraction}" />
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
<itemBody>
<p>{$questionText}</p>
<div class="interactive.textEntry">
<textEntryInteraction responseIdentifier="{$questionid}" expectedLength="10"/>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
<responseCondition>
<responseIf>
<and>
<not>
<isNull>
<variable identifier="{$questionid}" />
</isNull>
</not>
<gte>
<baseValue baseType="float">{$lowerbound}</baseValue>
<variable identifier="{$questionid}" />
</gte>
<lte>
<baseValue baseType="float">{$upperbound}</baseValue>
<variable identifier="{$questionid}" />
</lte>
</and>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="integer">1</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="integer">0</baseValue>
</setOutcomeValue>
</responseElse>
</responseCondition>
<setOutcomeValue identifier="FEEDBACK">
<variable identifier="{$questionid}"/>
</setOutcomeValue>
</responseProcessing>
{if $answer->feedback != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$answer->id}" showHide="show">{$answer->feedback}</modalFeedback>
{/if}
{if $answer->altfeedback != ''}
<modalFeedback outcomeIdentifier="FEEDBACK" identifier="{$answer->id}" showHide="hide">{$answer->altfeedback}</modalFeedback>
{/if}
</assessmentItem>

View File

@ -1,50 +0,0 @@
{if $courselevelexport}<?xml version="1.0" encoding="UTF-8"?>{/if}
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ./imsqti_item_v2p0.xsd" identifier="{$assessmentitemidentifier}" title="{$assessmentitemtitle}" adaptive="false" timeDependent="false">
<responseDeclaration identifier="{$questionid}" cardinality="{$responsedeclarationcardinality}" baseType="string">
<correctResponse>
{section name=answer loop=$correctresponses}
<value>{$correctresponses[answer].answer}</value>
{/section}
</correctResponse>
<mapping lowerBound="0" upperBound="1" defaultValue="0">
{section name=answer loop=$answers}
{if $answers[answer].fraction != 0}
<mapEntry mapKey="{$answers[answer].answer}" mappedValue="{$answers[answer].fraction}" />
{/if}
{/section}
</mapping>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
<itemBody>
<p>{$questionText}</p>
<div class="interactive.textEntry">
<textEntryInteraction responseIdentifier="{$questionid}" expectedLength="15"/>
</div>
{if $question_has_image == 1}
<div class="media">
{if $hassize == 1}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" width="{$question->mediax}" height="{$question->mediay}" />
{else}
<object type="{$question->mediamimetype}" data="{$question->mediaurl}" />
{/if}
</div>
{/if}
</itemBody>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_item_v2p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_item_v2p0 ../imsqti_item_v2p0.xsd">
<responseCondition>
<responseIf>
<isNull>
<variable identifier="{$questionid}"/>
</isNull>
<setOutcomeValue identifier="SCORE">
<baseValue baseType="integer">0</baseValue>
</setOutcomeValue>
</responseIf>
<responseElse>
<setOutcomeValue identifier="SCORE">
<mapResponse identifier="{$questionid}"/>
</setOutcomeValue>
</responseElse>
</responseCondition>
</responseProcessing>
</assessmentItem>

View File

@ -1,7 +0,0 @@
- Ajouter un espace après un symbole <= s'il préfixe une parenthèse ouvrante.
- Gérer 'and', 'or', ... comme &&, ||
- Ne pas mettre de '$' sur les @param si aucun nom de variable n'esp spécifié.
- Ajouter OneTrueBrace pour les if, while, for, else ... (Gustavo Carreno <gcarreno@netvisao.pt>, Richard Bateman <richard@randyb.byu.edu>, Yaroslav Shvetsov <yaro@totaldesign.ru>, Chris Small <chris@owta.net>)
- Remove blank lines (Sergio Marchesini <smarques@tin.it>)
- Forcer une ligne vide après une déclaration de fonction (Richard Bateman <richard@randyb.byu.edu>)

View File

@ -1,668 +0,0 @@
<?php // $Id$
///////////////////////////////////////////////////////////////////////////
// //
// WebCT FORMAT //
// //
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Part of Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.com //
// //
// Copyright (C) 2004 ASP Consulting http://www.asp-consulting.net //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
// Based on format.php, included by ../../import.php
function unhtmlentities($string){
$search = array ("'<script[?>]*?>.*?</script>'si", // remove javascript
"'<[\/\!]*?[^<?>]*?>'si", // remove HTML tags
"'([\r\n])[\s]+'", // remove spaces
"'&(quot|#34);'i", // remove HTML entites
"'&(amp|#38);'i",
"'&(lt|#60);'i",
"'&(gt|#62);'i",
"'&(nbsp|#160);'i",
"'&(iexcl|#161);'i",
"'&(cent|#162);'i",
"'&(pound|#163);'i",
"'&(copy|#169);'i",
"'&#(\d+);'e"); // Evaluate like PHP
$replace = array ("",
"",
"\\1",
"\"",
"&",
"<",
"?>",
" ",
chr(161),
chr(162),
chr(163),
chr(169),
"chr(\\1)");
return preg_replace ($search, $replace, $string);
}
class quiz_format_webct_modified_calculated_qtype extends quiz_calculated_qtype {
// We need to make a modification of this qtype so that
// it will be able to save webct style calculated questions
// The difference between webct and Moodle is that webct
// pass the individual data items as part of the question
// while Moodle core treat them separately
function save_question_options($question, $options = false) {
if (false !== $options) {
// function is called from save_question...
return parent::save_question_options($question, $options);
}
// function is called from format.php...
$datasetdatas = $question->datasets;
// Set dataset
$form->dataset = array();
foreach ($datasetdatas as $datasetname => $notimportant) {
// Literal - question local - name
$form->dataset[] = "1-0-$datasetname";
}
$subtypeoptions->answers = $question->answers;
$subtypeoptions->units = $question->units;
unset($question->datasets);
unset($question->answers);
unset($question->units);
$this->save_question($question, $form, 'not used', $subtypeoptions);
// Save dataset options and items...
// Get datasetdefinitions
global $CFG;
$datasetdefs = get_records_sql(
"SELECT a.*
FROM {$CFG->prefix}quiz_dataset_definitions a,
{$CFG->prefix}quiz_question_datasets b
WHERE a.id = b.datasetdefinition
AND b.question = '$question->id' ");
foreach ($datasetdefs as $datasetdef) {
$datasetdata = $datasetdatas[$datasetdef->name];
// Set items and retrieve ->itemcout
$item->definition = $datasetdef->id;
for ($item->number=1 ; isset($datasetdata->items["$item->number"]) ; ++$item->number) {
$item->value = $datasetdata->items["$item->number"];
if (!insert_record('quiz_dataset_items', $item)) {
error("Unable to insert dataset item $item->number with $item->value for $datasetdef->name");
}
}
$datasetdef->itemcount = $item->number - 1;
// Retrieve ->options
if (is_numeric($datasetdata->min) && is_numeric($datasetdata->max)
&& $datasetdata->min <= $datasetdata->max) {
if (is_numeric($datasetdata->dec)) {
$dec = max(0, ceil($datasetdata->dec));
} else {
$dec = 1; // A try
}
$datasetdef->options = "uniform:$datasetdata->min:$datasetdata->max:$dec";
} else {
$datasetdef->options = '';
}
// Save definition
if ($datasetdef->itemcount || $datasetdef->options) {
if (!update_record('quiz_dataset_definitions', $datasetdef)) {
error("Unable to update dataset definition $datasetdef->name on question $question->id");
}
}
}
// Done
return true;
}
}
$QTYPES[CALCULATED] = new quiz_format_webct_modified_calculated_qtype();
function quiz_format_webct_convert_formula($formula) {
// Remove empty space, as it would cause problems otherwise:
$formula = str_replace(' ', '', $formula);
// Remove paranthesis after e,E and *10**:
while (ereg('[0-9.](e|E|\\*10\\*\\*)\\([+-]?[0-9]+\\)', $formula, $regs)) {
$formula = str_replace(
$regs[0], ereg_replace('[)(]', '', $regs[0]), $formula);
}
// Replace *10** with e where possible
while (ereg(
'(^[+-]?|[^eE][+-]|[^0-9eE+-])[0-9.]+\\*10\\*\\*[+-]?[0-9]+([^0-9.eE]|$)',
$formula, $regs)) {
$formula = str_replace(
$regs[0], str_replace('*10**', 'e', $regs[0]), $formula);
}
// Replace other 10** with 1e where possible
while (ereg('(^|[^0-9.eE])10\\*\\*[+-]?[0-9]+([^0-9.eE]|$)', $formula, $regs)) {
$formula = str_replace(
$regs[0], str_replace('10**', '1e', $regs[0]), $formula);
}
// Replace all other base**exp with the PHP equivalent function pow(base,exp)
// (Pretty tricky to exchange an operator with a function)
while (2 == count($splits = explode('**', $formula, 2))) {
// Find $base
if (ereg('^(.*[^0-9.eE])?(([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][+-]?[0-9]+)?|\\{[^}]*\\})$',
$splits[0], $regs)) {
// The simple cases
$base = $regs[2];
$splits[0] = $regs[1];
} else if (ereg('\\)$', $splits[0])) {
// Find the start of this parenthesis
$deep = 1;
for ($i = 1 ; $deep ; ++$i) {
if (!ereg('^(.*[^[:alnum:]_])?([[:alnum:]_]*([)(])([^)(]*[)(]){'.$i.'})$',
$splits[0], $regs)) {
error("Parenthesis before ** is not properly started in $splits[0]**");
}
if ('(' == $regs[3]) {
--$deep;
} else if (')' == $regs[3]) {
++$deep;
} else {
error("Impossible character $regs[3] detected as parenthesis character");
}
}
$base = $regs[2];
$splits[0] = $regs[1];
} else {
error("Bad base before **: $splits[0]**");
}
// Find $exp (similar to above but a little easier)
if (ereg('^([+-]?(\\{[^}]\\}|([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][+-]?[0-9]+)?))(.*)',
$splits[1], $regs)) {
// The simple case
$exp = $regs[1];
$splits[1] = $regs[6];
} else if (ereg('^[+-]?[[:alnum:]_]*\\(', $splits[1])) {
// Find the end of the parenthesis
$deep = 1;
for ($i = 1 ; $deep ; ++$i) {
if (!ereg('^([+-]?[[:alnum:]_]*([)(][^)(]*){'.$i.'}([)(]))(.*)',
$splits[1], $regs)) {
error("Parenthesis after ** is not properly closed in **$splits[1]");
}
if (')' == $regs[3]) {
--$deep;
} else if ('(' == $regs[3]) {
++$deep;
} else {
error("Impossible character $regs[3] detected as parenthesis character");
}
}
$exp = $regs[1];
$splits[1] = $regs[4];
}
// Replace it!
$formula = "$splits[0]pow($base,$exp)$splits[1]";
}
// Nothing more is known to need to be converted
return $formula;
}
class quiz_format_webct extends quiz_default_format {
function provide_import() {
return true;
}
function readquestions ($lines) {
$qtypecalculated = new quiz_format_webct_modified_calculated_qtype();
$webctnumberregex =
'[+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)((e|E|\\*10\\*\\*)([+-]?[0-9]+|\\([+-]?[0-9]+\\)))?';
$questions = array();
$errors = array();
$warnings = array();
$webct_options = array();
$ignore_rest_of_question = FALSE;
$nLineCounter = 0;
$nQuestionStartLine = 0;
$bIsHTMLText = FALSE;
$lines[] = ":EOF:"; // for an easiest processing of the last line
$question = $this->defaultquestion();
foreach ($lines as $line) {
$nLineCounter++;
// Processing multiples lines strings
if (isset($questiontext) and is_string($questiontext)) {
if (ereg("^:",$line)) {
$question->questiontext = addslashes(trim($questiontext));
unset($questiontext);
}
else {
$questiontext .= str_replace('\:', ':', $line);
continue;
}
}
if (isset($answertext) and is_string($answertext)) {
if (ereg("^:",$line)) {
$answertext = addslashes(trim($answertext));
$question->answer[$currentchoice] = $answertext;
$question->subanswers[$currentchoice] = $answertext;
unset($answertext);
}
else {
$answertext .= str_replace('\:', ':', $line);
continue;
}
}
if (isset($responsetext) and is_string($responsetext)) {
if (ereg("^:",$line)) {
$question->subquestions[$currentchoice] = addslashes(trim($responsetext));
unset($responsetext);
}
else {
$responsetext .= str_replace('\:', ':', $line);
continue;
}
}
if (isset($feedbacktext) and is_string($feedbacktext)) {
if (ereg("^:",$line)) {
$question->feedback[$currentchoice] = addslashes(trim($feedbacktext));
unset($feedbacktext);
}
else {
$feedbacktext .= str_replace('\:', ':', $line);
continue;
}
}
$line = trim($line);
if (eregi("^:(TYPE|EOF):",$line)) {
// New Question or End of File
if (isset($question)) { // if previous question exists, complete, check and save it
// Setup default value of missing fields
if (!isset($question->name)) {
$question->name = $question->questiontext;
}
if (strlen($question->name) > 255) {
$question->name = substr($question->name,0,250)."...";
$warnings[] = get_string("questionnametoolong", "quiz", $nQuestionStartLine);
}
if (!isset($question->defaultgrade)) {
$question->defaultgrade = 1;
}
if (!isset($question->image)) {
$question->image = "";
}
// Perform sanity checks
$QuestionOK = TRUE;
if (strlen($question->questiontext) == 0) {
$warnings[] = get_string("missingquestion", "quiz", $nQuestionStartLine);
$QuestionOK = FALSE;
}
if (sizeof($question->answer) < 1) { // a question must have at least 1 answer
$errors[] = get_string("missinganswer", "quiz", $nQuestionStartLine);
$QuestionOK = FALSE;
}
else {
// Create empty feedback array
$question->feedback = array();
foreach ($question->answer as $key => $dataanswer) {
$question->feedback[$key] = '';
}
$maxfraction = -1;
$totalfraction = 0;
foreach($question->fraction as $fraction) {
if ($fraction > 0) {
$totalfraction += $fraction;
}
if ($fraction > $maxfraction) {
$maxfraction = $fraction;
}
}
switch ($question->qtype) {
case SHORTANSWER:
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
$errors[] = "'$question->name': ".get_string("wronggrade", "quiz", $nLineCounter).get_string("fractionsnomax", "quiz", $maxfraction);
$QuestionOK = FALSE;
}
break;
case MULTICHOICE:
if ($question->single) {
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
$errors[] = "'$question->name': ".get_string("wronggrade", "quiz", $nLineCounter).get_string("fractionsnomax", "quiz", $maxfraction);
$QuestionOK = FALSE;
}
} else {
$totalfraction = round($totalfraction,2);
if ($totalfraction != 1) {
echo "<p>$totalfraction</p>";
$totalfraction = $totalfraction * 100;
$errors[] = "'$question->name': ".get_string("wronggrade", "quiz", $nLineCounter).get_string("fractionsaddwrong", "quiz", $totalfraction);
$QuestionOK = FALSE;
}
}
break;
case CALCULATED:
foreach ($question->answers as $answer) {
if ($formulaerror = quiz_qtype_calculated_find_formula_errors($answer->answer)) {
$warnings[] = "'$question->name': ". $formulaerror;
$QuestionOK = FALSE;
}
}
break;
default:
// No problemo
}
}
if ($QuestionOK) {
// $question->feedback = array();
$questions[] = $question; // store it
unset($question); // and prepare a new one
$question = $this->defaultquestion();
}
}
$nQuestionStartLine = $nLineCounter;
}
// Processing Question Header
if (eregi("^:TYPE:MC:1(.*)",$line,$webct_options)) {
// Multiple Choice Question with only one good answer
$question->qtype = MULTICHOICE;
$question->single = 1; // Only one answer is allowed
$ignore_rest_of_question = FALSE;
continue;
}
if (eregi("^:TYPE:MC:N(.*)",$line,$webct_options)) {
// Multiple Choice Question with several good answers
$question->qtype = MULTICHOICE;
$question->single = 0; // Many answers allowed
$ignore_rest_of_question = FALSE;
continue;
}
if (eregi("^:TYPE:S",$line)) {
// Short Answer Question
$question->qtype = SHORTANSWER;
$question->usecase = 0; // Ignore case
$ignore_rest_of_question = FALSE;
continue;
}
if (eregi("^:TYPE:C",$line)) {
// Calculated Question
$question->qtype = CALCULATED;
$question->answers = array(); // No problem as they go as :FORMULA: from webct
$question->units = array();
$question->datasets = array();
// To make us pass the end-of-question sanity checks
$question->answer = array('dummy');
$question->fraction = array('1.0');
$currentchoice = -1;
$ignore_rest_of_question = FALSE;
continue;
}
if (eregi("^:TYPE:M",$line)) {
// Match Question
$question->qtype = MATCH;
$ignore_rest_of_question = FALSE; // match question processing is not debugged
continue;
}
if (eregi("^:TYPE:P",$line)) {
// Paragraph Question
$warnings[] = get_string("paragraphquestion", "quiz", $nLineCounter);
$ignore_rest_of_question = TRUE; // Question Type not handled by Moodle
continue;
}
if (eregi("^:TYPE:",$line)) {
// Unknow Question
$warnings[] = get_string("unknowntype", "quiz", $nLineCounter);
$ignore_rest_of_question = TRUE; // Question Type not handled by Moodle
continue;
}
if ($ignore_rest_of_question) {
continue;
}
if (eregi("^:TITLE:(.*)",$line,$webct_options)) {
$name = trim($webct_options[1]);
if (strlen($name) > 255) {
$name = substr($name,0,250)."...";
$warnings[] = get_string("questionnametoolong", "quiz", $nLineCounter);
}
$question->name = addslashes($name);
continue;
}
if (eregi("^:IMAGE:(.*)",$line,$webct_options)) {
$filename = trim($webct_options[1]);
if (eregi("^http://",$filename)) {
$question->image = $filename;
}
continue;
}
// Need to put the parsing of calculated items here to avoid ambitiuosness:
// if question isn't defined yet there is nothing to do here (avoid notices)
if (!isset($question)) {
continue;
}
if (CALCULATED == $question->qtype && ereg(
"^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?($webctnumberregex)", $line, $webct_options)) {
$datasetname = ereg_replace('^::', '', $webct_options[1]);
$datasetvalue = quiz_format_webct_convert_formula($webct_options[4]);
switch ($webct_options[2]) {
case 'MIN':
$question->datasets[$datasetname]->min = $datasetvalue;
break;
case 'MAX':
$question->datasets[$datasetname]->max = $datasetvalue;
break;
case 'DEC':
$datasetvalue = floor($datasetvalue); // int only!
$question->datasets[$datasetname]->dec = max(0, $datasetvalue);
break;
default:
// The VAL case:
$question->datasets[$datasetname]->items[$webct_options[3]] = $datasetvalue;
break;
}
continue;
}
$bIsHTMLText = eregi(":H$",$line); // True if next lines are coded in HTML
if (eregi("^:QUESTION",$line)) {
$questiontext=""; // Start gathering next lines
continue;
}
if (eregi("^:ANSWER([0-9]+):([^:]+):([0-9\.\-]+):(.*)",$line,$webct_options)) { /// SHORTANSWER
$currentchoice=$webct_options[1];
$answertext=$webct_options[2]; // Start gathering next lines
$question->fraction[$currentchoice]=($webct_options[3]/100);
continue;
}
if (eregi("^:ANSWER([0-9]+):([0-9\.\-]+)",$line,$webct_options)) {
$answertext=""; // Start gathering next lines
$currentchoice=$webct_options[1];
$question->fraction[$currentchoice]=($webct_options[2]/100);
continue;
}
if (eregi('^:FORMULA:(.*)', $line, $webct_options)) {
// Answer for a CALCULATED question
++$currentchoice;
$question->answers[$currentchoice]->answer =
quiz_format_webct_convert_formula($webct_options[1]);
// Default settings:
$question->answers[$currentchoice]->fraction = 1.0;
$question->answers[$currentchoice]->tolerance = 0.0;
$question->answers[$currentchoice]->tolerancetype = 2; // nominal (units in webct)
$question->answers[$currentchoice]->feedback = '';
$question->answers[$currentchoice]->correctanswerlength = 4;
$datasetnames = $qtypecalculated->find_dataset_names($webct_options[1]);
foreach ($datasetnames as $datasetname) {
$question->datasets[$datasetname]->items = array();
}
continue;
}
if (eregi("^:L([0-9]+)",$line,$webct_options)) {
$answertext=""; // Start gathering next lines
$currentchoice=$webct_options[1];
$question->fraction[$currentchoice]=1;
continue;
}
if (eregi("^:R([0-9]+)",$line,$webct_options)) {
$responsetext=""; // Start gathering next lines
$currentchoice=$webct_options[1];
continue;
}
if (eregi("^:REASON([0-9]+):?",$line,$webct_options)) {
$feedbacktext=""; // Start gathering next lines
$currentchoice=$webct_options[1];
continue;
}
if (CALCULATED == $question->qtype && eregi('^:ANS-DEC:([1-9][0-9]*)', $line, $webct_options)) {
// We can but hope that this always appear before the ANSTYPE property
$question->answers[$currentchoice]->correctanswerlength = $webct_options[1];
continue;
}
if (CALCULATED == $question->qtype && eregi("^:TOL:($webctnumberregex)", $line, $webct_options)) {
// We can but hope that this always appear before the TOL property
$question->answers[$currentchoice]->tolerance =
quiz_format_webct_convert_formula($webct_options[1]);
continue;
}
if (CALCULATED == $question->qtype && eregi('^:TOLTYPE:percent', $line)) {
// Percentage case is handled as relative in Moodle:
$question->answers[$currentchoice]->tolerance /= 100;
$question->answers[$currentchoice]->tolerancetype = 1; // Relative
continue;
}
if (eregi('^:UNITS:(.+)', $line, $webct_options)
and $webctunits = trim($webct_options[1])) {
// This is a guess - I really do not know how different webct units are separated...
$webctunits = explode(':', $webctunits);
$unitrec->multiplier = 1.0; // Webct does not seem to support this
foreach ($webctunits as $webctunit) {
$unitrec->unit = trim($webctunit);
$question->units[] = $unitrec;
}
continue;
}
if (!empty($question->units) && eregi('^:UNITREQ:(.*)', $line, $webct_options)
&& !$webct_options[1]) {
// There are units but units are not required so add the no unit alternative
// We can but hope that the UNITS property always appear before this property
$unitrec->unit = '';
$unitrec->multiplier = 1.0;
$question->units[] = $unitrec;
continue;
}
if (!empty($question->units) && eregi('^:UNITCASE:', $line)) {
// This could be important but I was not able to figure out how
// it works so I ignore it for now
continue;
}
if (CALCULATED == $question->qtype && eregi('^:ANSTYPE:dec', $line)) {
// Houston - we have a problem
// Moodle does not support this - we try something defensively by
// setting the correct answer length to 5, it shoud be enough for
// most cases
$question->answers[$currentchoice]->correctanswerlength = 5;
continue;
}
}
if (sizeof($errors) > 0) {
echo "<p>".get_string("errorsdetected", "quiz", sizeof($errors))."</p><ul>";
foreach($errors as $error) {
echo "<li>$error</li>";
}
echo "</ul>";
unset($questions); // no questions imported
}
if (sizeof($warnings) > 0) {
echo "<p>".get_string("warningsdetected", "quiz", sizeof($warnings))."</p><ul>";
foreach($warnings as $warning) {
echo "<li>$warning</li>";
}
echo "</ul>";
}
echo "<pre>"; print_r( $questions ); die;
return $questions;
}
}
?>

View File

@ -1,145 +0,0 @@
<?php
// Based on default.php, included by ../import.php
class quiz_format_xhtml extends quiz_default_format {
function provide_export() {
return true;
}
function repchar( $text ) {
// escapes 'reserved' characters # = ~ { ) and removes new lines
$reserved = array( '#','=','~','{','}',"\n","\r" );
$escaped = array( '\#','\=','\~','\{','\}',' ','' );
return str_replace( $reserved, $escaped, $text );
}
function writequestion( $question ) {
// turns question into string
// question reflects database fields for general question and specific to type
// initial string;
$expout = "";
$id = $question->id;
// add comment and div tags
$expout .= "<!-- question: $id name: $question->name -->\n";
$expout .= "<div class=\"question\">\n";
// add header
$expout .= "<h3>$question->name</h3>\n";
// format and add question text
$questiontext = $question->questiontext;
$format = $question->questiontextformat;
$formatted_text = format_text( $questiontext, $format );
$expout .= "<p class=\"questiontext\">$formatted_text</p>\n";
// selection depends on question type
switch($question->qtype) {
case TRUEFALSE:
$st_true = get_string( 'true','quiz' );
$st_false = get_string( 'false','quiz' );
$expout .= "<ul class=\"truefalse\">\n";
$expout .= " <li><input name=\"quest_$id\" type=\"radio\" value=\"$st_true\" />$st_true</li>\n";
$expout .= " <li><input name=\"quest_$id\" type=\"radio\" value=\"$st_false\" />$st_false</li>\n";
$expout .= "</ul>\n";
break;
case MULTICHOICE:
$expout .= "<ul class=\"multichoice\">\n";
foreach($question->options->answers as $answer) {
$ans_text = $this->repchar( $answer->answer );
if ($question->options->single) {
$expout .= " <li><input name=\"quest_$id\" type=\"radio\" value=\"$ans_text\" />$ans_text</li>\n";
}
else {
$expout .= " <li><input name=\"quest_$id\" type=\"checkbox\" value=\"$ans_text\" />$ans_text</li>\n";
}
}
$expout .= "</ul>\n";
break;
case SHORTANSWER:
$expout .= "<ul class=\"shortanswer\">\n";
$expout .= " <li><input name=\"quest_$id\" type=\"text\" /></li>\n";
$expout .= "</ul>\n";
break;
case NUMERICAL:
$expout .= "<ul class=\"numerical\">\n";
$expout .= " <li><input name=\"quest_$id\" type=\"text\" /></li>\n";
$expout .= "</ul>\n";
break;
case MATCH:
$expout .= "<ul class=\"match\">\n";
// build answer list
$ans_list = array();
foreach($question->options->subquestions as $subquestion) {
$ans_list[] = $this->repchar( $subquestion->answertext );
}
shuffle( $ans_list ); // random display order
// build drop down for answers
$dropdown = "<select name=\"quest_$id\">\n";
foreach($ans_list as $ans) {
$dropdown .= "<option value=\"$ans\">$ans</option>\n";
}
$dropdown .= "</select>\n";
// finally display
foreach($question->options->subquestions as $subquestion) {
$quest_text = $this->repchar( $subquestion->questiontext );
$expout .= " <li>$quest_text</li>\n";
$expout .= $dropdown;
}
$expout .= "</ul>\n";
break;
case DESCRIPTION:
break;
case MULTIANSWER:
$expout .= "<!-- CLOZE type is not supported -->\n";
break;
default:
notify("No handler for qtype $question->qtype for GIFT export" );
}
// close off div
$expout .= "</div>\n\n\n";
return $expout;
}
function presave_process( $content ) {
// override method to allow us to add xhtml headers and footers
global $CFG;
// get css bit
$css_lines = file( "$CFG->dirroot/mod/quiz/format/xhtml/xhtml.css" );
$css = implode( ' ',$css_lines );
$xp = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n";
$xp .= " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
$xp .= "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
$xp .= "<head>\n";
$xp .= "<title>Moodle Quiz XHTML Export</title>\n";
$xp .= $css;
$xp .= "</head>\n";
$xp .= "<body>\n";
$xp .= "<form action=\"...REPLACE ME...\" method=\"post\">\n\n";
$xp .= $content;
$xp .= "<p class=\"submit\">\n";
$xp .= " <input type=\"submit\" />\n";
$xp .= "</p>\n";
$xp .= "</form>\n";
$xp .= "</body>\n";
$xp .= "</html>\n";
return $xp;
}
function export_file_extension() {
return ".html";
}
}
?>

View File

@ -1,24 +0,0 @@
<style>
body {
font-family: Verdana, Helvetica, Sans-Serif;
background-color: #fff;
color: #000;
}
.question {
border: 1px solid #ddd;
margin: 5px;
padding: 3px;
}
.question h3 {
font-weight: normal;
font-size: 125%;
}
.question ul {
list-style-type: none;
}
</style>

View File

@ -1,593 +0,0 @@
<?php // $Id$
//
///////////////////////////////////////////////////////////////
// XML import/export
//
//////////////////////////////////////////////////////////////////////////
// Based on default.php, included by ../import.php
require_once( "$CFG->libdir/xmlize.php" );
class quiz_format_xml extends quiz_default_format {
function provide_import() {
return true;
}
function provide_export() {
return true;
}
// IMPORT FUNCTIONS START HERE
function trans_format( $name ) {
// translate text format string to its internal code
$name = trim($name);
if ($name=='moodle_auto_format') {
$id = 0;
}
elseif ($name=='html') {
$id = 1;
}
elseif ($name=='plain_text') {
$id = 2;
}
elseif ($name=='wiki_like') {
$id = 3;
}
elseif ($name=='markdown') {
$id = 4;
}
else {
$id = 0; // or maybe warning required
}
return $id;
}
function trans_single( $name ) {
// translate single string to its internal format
$name = trim($name);
if ($name=="true") {
$id = 1;
}
elseif ($name=="false") {
$id = 0;
}
else {
$id = 0; // or maybe warning required
}
return $id;
}
function import_text( $text ) {
// handle xml 'text' element
$data = $text[0]['#'];
$data = html_entity_decode( $data );
return addslashes(trim( $data ));
}
function import_headers( $question ) {
// read bits that are common to all questions
// this routine initialises the question object
$name = $this->import_text( $question['#']['name'][0]['#']['text'] );
$qtext = $this->import_text( $question['#']['questiontext'][0]['#']['text'] );
$qformat = $question['#']['questiontext'][0]['@']['format'];
$image = $question['#']['image'][0]['#'];
$penalty = $question['#']['penalty'][0]['#'];
$qo = null;
$qo->name = $name;
$qo->questiontext = $qtext;
$qo->questiontextformat = $this->trans_format( $qformat );
$qo->image = ((!empty($image)) ? $image : '');
$qo->penalty = $penalty;
return $qo;
}
function import_answer( $answer ) {
// import answer part of question
$fraction = $answer['@']['fraction'];
$text = $this->import_text( $answer['#']['text']);
$feedback = $this->import_text( $answer['#']['feedback'][0]['#']['text'] );
$ans = null;
$ans->answer = $text;
$ans->fraction = $fraction / 100;
$ans->feedback = $feedback;
return $ans;
}
function import_multichoice( $question ) {
// import multichoice type questions
// get common parts
$qo = $this->import_headers( $question );
// 'header' parts particular to multichoice
$qo->qtype = MULTICHOICE;
$single = $question['#']['single'][0]['#'];
$qo->single = $this->trans_single( $single );
// run through the answers
$answers = $question['#']['answer'];
$a_count = 0;
foreach ($answers as $answer) {
$ans = $this->import_answer( $answer );
$qo->answer[$a_count] = $ans->answer;
$qo->fraction[$a_count] = $ans->fraction;
$qo->feedback[$a_count] = $ans->feedback;
++$a_count;
}
return $qo;
}
function import_truefalse( $question ) {
// import true/false type question
// get common parts
$qo = $this->import_headers( $question );
// 'header' parts particular to true/false
$qo->qtype = TRUEFALSE;
// get answer info
$answers = $question['#']['answer'];
$fraction0 = $answers[0]['@']['fraction'];
$feedback0 = $this->import_text($answers[0]['#']['feedback'][0]['#']['text']);
$fraction1 = $answers[1]['@']['fraction'];
$feedback1 = $this->import_text($answers[1]['#']['feedback'][0]['#']['text']);
// sort out which is true and build object accordingly
if ($fraction0==100) { // then 0 index is true
$qo->answer = 1;
$qo->feedbacktrue=$feedback0;
$qo->feedbackfalse=$feedback1;
}
else {
$qo->answer = 0;
$qo->feedbacktrue = $feedback1;
$qo->feedbackfalse = $feedback0;
}
return $qo;
}
function import_shortanswer( $question ) {
// import short answer question
// get common parts
$qo = $this->import_headers( $question );
// header parts particular to shortanswer
$qo->qtype = SHORTANSWER;
// get usecase
$qo->usecase = $question['#']['usecase'][0]['#'];
// run through the answers
$answers = $question['#']['answer'];
$a_count = 0;
foreach ($answers as $answer) {
$ans = $this->import_answer( $answer );
$qo->answer[$a_count] = $ans->answer;
$qo->fraction[$a_count] = $ans->fraction;
$qo->feedback[$a_count] = $ans->feedback;
++$a_count;
}
return $qo;
}
function import_numerical( $question ) {
// import numerical question
// get common parts
$qo = $this->import_headers( $question );
// header parts particular to numerical
$qo->qtype = NUMERICAL;
// get answers array
$answers = $question['#']['answer'];
$qo->answer = array();
$qo->feedback = array();
$qo->fraction = array();
$qo->tolerance = array();
foreach ($answers as $answer) {
$qo->answer[] = $answer['#'][0];
$qo->feedback[] = $answer['#']['feedback'][0]['#']['text'][0]['#'];
$qo->fraction[] = $answer['#']['fraction'][0]['#'];
$qo->tolerance[] = $answer['#']['tolerance'][0]['#'];
}
// get units array
$units = $question['#']['units'][0]['#']['unit'];
$qo->unit = array();
$qo->multiplier = array();
foreach ($units as $unit) {
$qo->multiplier[] = $unit['#']['multiplier'][0]['#'];
$qo->unit[] = $unit['#']['unit_name'][0]['#'];
}
return $qo;
}
function readquestions($lines) {
// parse the array of lines into an array of questions
// this *could* burn memory - but it won't happen that much
// so fingers crossed!
// we just need it as one big string
$text = implode($lines, " ");
unset( $lines );
// this converts xml to big nasty data structure
// the 0 means keep white space as it is (important for markdown format)
// print_r it if you want to see what it looks like!
$xml = xmlize( $text, 0 );
// set up array to hold all our questions
$questions = array();
// iterate through questions
foreach ($xml['quiz']['#']['question'] as $question) {
$question_type = $question['@']['type'];
$questiontype = get_string( 'questiontype','quiz',$question_type );
echo "<p>$questiontype</p>";
if ($question_type=='multichoice') {
$qo = $this->import_multichoice( $question );
}
elseif ($question_type=='truefalse') {
$qo = $this->import_truefalse( $question );
}
elseif ($question_type=='shortanswer') {
$qo = $this->import_shortanswer( $question );
}
elseif ($question_type=='numerical') {
$qo = $this->import_numerical( $question );
}
else {
$notsupported = get_string( 'xmlnotsupported','quiz',$question_type );
echo "<p>$notsupported</p>";
$qo = null;
}
// stick the result in the $questions array
if ($qo) {
$questions[] = $qo;
}
}
return $questions;
}
// EXPORT FUNCTIONS START HERE
function indent_xhtml($source, $indenter = ' ') {
// xml tidier-upper
// (c) Ari Koivula http://ventionline.com
// Remove all pre-existing formatting.
// Remove all newlines.
$source = str_replace("\n", '', $source);
$source = str_replace("\r", '', $source);
// Remove all tabs.
$source = str_replace("\t", '', $source);
// Remove all space after ">" and before "<".
$source = ereg_replace(">( )*", ">", $source);
$source = ereg_replace("( )*<", "<", $source);
// Iterate through the source.
$level = 0;
$source_len = strlen($source);
$pt = 0;
while ($pt < $source_len) {
if ($source{$pt} === '<') {
// We have entered a tag.
// Remember the point where the tag starts.
$started_at = $pt;
$tag_level = 1;
// If the second letter of the tag is "/", assume its an ending tag.
if ($source{$pt+1} === '/') {
$tag_level = -1;
}
// If the second letter of the tag is "!", assume its an "invisible" tag.
if ($source{$pt+1} === '!') {
$tag_level = 0;
}
// Iterate throught the source until the end of tag.
while ($source{$pt} !== '>') {
$pt++;
}
// If the second last letter is "/", assume its a self ending tag.
if ($source{$pt-1} === '/') {
$tag_level = 0;
}
$tag_lenght = $pt+1-$started_at;
// Decide the level of indention for this tag.
// If this was an ending tag, decrease indent level for this tag..
if ($tag_level === -1) {
$level--;
}
// Place the tag in an array with proper indention.
$array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght);
// If this was a starting tag, increase the indent level after this tag.
if ($tag_level === 1) {
$level++;
}
// if it was a self closing tag, dont do shit.
}
// Were out of the tag.
// If next letter exists...
if (($pt+1) < $source_len) {
// ... and its not an "<".
if ($source{$pt+1} !== '<') {
$started_at = $pt+1;
// Iterate through the source until the start of new tag or until we reach the end of file.
while ($source{$pt} !== '<' && $pt < $source_len) {
$pt++;
}
// If we found a "<" (we didnt find the end of file)
if ($source{$pt} === '<') {
$tag_lenght = $pt-$started_at;
// Place the stuff in an array with proper indention.
$array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght);
}
// If the next tag is "<", just advance pointer and let the tag indenter take care of it.
} else {
$pt++;
}
// If the next letter doesnt exist... Were done... well, almost..
} else {
break;
}
}
// Replace old source with the new one we just collected into our array.
$source = implode($array, "\n");
return $source;
}
function export_file_extension() {
// override default type so extension is .xml
return ".xml";
}
function get_qtype( $type_id ) {
// translates question type code number into actual name
switch( $type_id ) {
case TRUEFALSE:
$name = 'truefalse';
break;
case MULTICHOICE:
$name = 'multichoice';
break;
case SHORTANSWER:
$name = 'shortanswer';
break;
case NUMERICAL:
$name = 'numerical';
break;
case MATCH:
$name = 'matching';
break;
case DESCRIPTION:
$name = 'description';
break;
case MULTIANSWER:
$name = 'cloze';
break;
default:
$name = 'unknown';
}
return $name;
}
function get_format( $id ) {
// translates question text format id number into something sensible
switch( $id ) {
case 0:
$name = "moodle_auto_format";
break;
case 1:
$name = "html";
break;
case 2:
$name = "plain_text";
break;
case 3:
$name = "wiki_like";
break;
case 4:
$name = "markdown";
break;
default:
$name = "unknown";
}
return $name;
}
function get_single( $id ) {
// translate single value into something sensible
switch( $id ) {
case 0:
$name = "false";
break;
case 1:
$name = "true";
break;
default:
$name = "unknown";
}
return $name;
}
function writetext( $raw, $ilev=0, $short=true) {
// generates <text></text> tags, processing raw text therein
// $ilev is the current indent level
// $short=true sticks it on one line
$indent = str_repeat( " ",$ilev );
// encode the text to 'disguise' HTML content
$raw = htmlspecialchars( $raw );
if ($short) {
$xml = "$indent<text>$raw</text>\n";
}
else {
$xml = "$indent<text>\n$raw\n$indent</text>\n";
}
return $xml;
}
function presave_process( $content ) {
// override method to allow us to add xml headers and footers
$content = "<?xml version=\"1.0\"?>\n" .
"<quiz>\n" .
$content . "\n" .
"</quiz>";
return $content;
}
function writequestion( $question ) {
// turns question into string
// question reflects database fields for general question and specific to type
// initial string;
$expout = "";
// add comment
$expout .= "\n\n<!-- question: $question->id -->\n";
// add opening tag
$question_type = $this->get_qtype( $question->qtype );
$name_text = $this->writetext( $question->name );
$qtformat = $this->get_format($question->questiontextformat);
$question_text = $this->writetext( $question->questiontext );
$expout .= " <question type=\"$question_type\">\n";
$expout .= " <name>$name_text</name>\n";
$expout .= " <questiontext format=\"$qtformat\">\n";
$expout .= $question_text;
$expout .= " </questiontext>\n";
$expout .= " <image>".$question->image."</image>\n";
$expout .= " <penalty>{$question->penalty}</penalty>\n";
$expout .= " <hidden>{$question->hidden}</hidden>\n";
// output depends on question type
switch($question->qtype) {
case TRUEFALSE:
$answer = $question->options->answers;
$true_percent = round( $answer['true']->fraction * 100 );
$false_percent = round( $answer['false']->fraction * 100 );
// true answer
$expout .= " <answer fraction=\"$true_percent\">\n";
$expout .= $this->writetext("true",3)."\n";
$expout .= " <feedback>\n";
$expout .= $this->writetext( $answer['true']->feedback,4,false );
$expout .= " </feedback>\n";
$expout .= " </answer>\n";
// false answer
$expout .= " <answer fraction=\"$false_percent\">\n";
$expout .= $this->writetext("false")."\n";
$expout .= " <feedback>\n";
$expout .= $this->writetext( $answer['false']->feedback,4,false );
$expout .= " </feedback>\n";
$expout .= " </answer>\n";
break;
case MULTICHOICE:
$expout .= " <single>".$this->get_single($question->options->single)."</single>\n";
foreach($question->options->answers as $answer) {
$percent = $answer->fraction * 100;
$expout .= " <answer fraction=\"$percent\">\n";
$expout .= $this->writetext( $answer->answer,4,false );
$expout .= " <feedback>\n";
$expout .= $this->writetext( $answer->feedback,4,false );
$expout .= " </feedback>\n";
$expout .= " </answer>\n";
}
break;
case SHORTANSWER:
$expout .= " <usecase>{$question->options->usecase}</usecase>\n ";
foreach($question->options->answers as $answer) {
$percent = 100 * $answer->fraction;
$expout .= " <answer fraction=\"$percent\">\n";
$expout .= $this->writetext( $answer->answer,3,false );
$expout .= " <feedback>\n";
$expout .= $this->writetext( $answer->feedback,4,false );
$expout .= " </feedback>\n";
$expout .= " </answer>\n";
}
break;
case NUMERICAL:
foreach ($question->options->answers as $answer) {
$tolerance = $answer->tolerance;
$expout .= "<answer>\n";
$expout .= " {$answer->answer}\n";
$expout .= " <tolerance>$tolerance</tolerance>\n";
$expout .= " <feedback>".$this->writetext( $answer->feedback )."</feedback>\n";
$expout .= " <fraction>{$answer->fraction}</fraction>\n";
$expout .= "</answer>\n";
}
$units = $question->options->units;
if (count($units)) {
$expout .= "<units>\n";
foreach ($units as $unit) {
$expout .= " <unit>\n";
$expout .= " <multiplier>{$unit->multiplier}</multiplier>\n";
$expout .= " <unit_name>{$unit->unit}</unit_name>\n";
$expout .= " </unit>\n";
}
$expout .= "</units>\n";
}
break;
case MATCH:
foreach($question->options->subquestions as $subquestion) {
$expout .= "<subquestion>\n";
$expout .= $this->writetext( $subquestion->questiontext );
$expout .= "<answer>".$this->writetext( $subquestion->answertext )."</answer>\n";
$expout .= "</subquestion>\n";
}
break;
case DESCRIPTION:
// nothing more to do for this type
break;
case MULTIANSWER:
$expout .= "<!-- CLOZE type is not supported -->\n";
break;
default:
$expout .= "<!-- Question type is unknown or not supported (Type=$question->qtype) -->\n";
}
// close the question tag
$expout .= "</question>\n";
// run through xml tidy function
// $tidy_expout = $this->indent_xhtml( $expout, ' ' ) . "\n\n";
return $expout;
}
}
?>