diff --git a/lang/en/help/quiz/import.html b/lang/en/help/quiz/import.html
index 0ce15388ec0..d99a847beb2 100644
--- a/lang/en/help/quiz/import.html
+++ b/lang/en/help/quiz/import.html
@@ -1,11 +1,11 @@
Importing new questions
This function allows you to import questions from
- external text files.
+ external text files, uploaded through a form.
-
Several common file formats are supported:
+
A number of file formats are supported:
-
Missing word format
+Missing Word
This format only supports multiple choice questions.
Each answer is separated with a tilde (~), and the correct answer is
@@ -20,24 +20,25 @@ we become students of {=anatomy and physiology ~reflexology
-IMS QTI format
+AON
-The standard format for Question and Test Interoperability, from IMS.
-
Not yet implemented
-
-
More info: helpbutton("formatqti", "", "quiz") ?>
+This is the same as Missing Word Format, except that after importing
+ the questions all Short-Answer questions are converted four at a time
+ into Matching Questions.
+It's named after an organisation that sponsored the development of many
+ quiz features
-WebCT format
+Blackboard
-The format of files exported from WebCT.
-
Not yet implemented
+
This module can import questions saved in Blackboard's export
+format. It relies on XML functions being compiled into your PHP.
-More info: helpbutton("formatwebct", "", "quiz") ?>
+More info: helpbutton("formatblackboard", "", "quiz") ?>
-Custom format
+Custom
If you have your own format that you need to import, you can
implement it yourself by editing mod/quiz/format/custom.php
@@ -47,3 +48,7 @@ we become students of {=anatomy and physiology ~reflexology
More info: helpbutton("formatcustom", "", "quiz") ?>
+
+
+More formats are yet to come, including WebCT, IMS QTI and whatever else
+ Moodle users can contribute!
diff --git a/lang/en/quiz.php b/lang/en/quiz.php
index 806d9e8019f..614ad3dd927 100644
--- a/lang/en/quiz.php
+++ b/lang/en/quiz.php
@@ -24,6 +24,7 @@ $string['attemptsallowed'] = "Attempts allowed";
$string['attemptsunlimited'] = "Unlimited attempts";
$string['backtoquiz'] = "Back to quiz editing";
$string['bestgrade'] = "Best grade";
+$string['blackboard'] = "Blackboard";
$string['casesensitive'] = "Case sensitivity";
$string['caseyes'] = "Yes, case must match";
$string['caseno'] = "No, case is unimportant";
diff --git a/mod/quiz/format/aon.php b/mod/quiz/format/aon.php
index e07fcadd971..b6aa66018ce 100644
--- a/mod/quiz/format/aon.php
+++ b/mod/quiz/format/aon.php
@@ -110,17 +110,17 @@ class quiz_file_format extends quiz_default_format {
}
}
- function postprocess($category, $questionids) {
+ function postprocess() {
/// 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 $db, $CFG;
+ global $CFG;
- print_heading(count($questionids)." ".get_string("questions", "quiz"));
+ print_heading(count($this->questionids)." ".get_string("questions", "quiz"));
- $questionids = implode(',', $questionids);
+ $questionids = implode(',', $this->questionids);
if (!$shortanswers = get_records_select("quiz_questions",
"id IN ($questionids) AND qtype = ".SHORTANSWER,
@@ -134,14 +134,14 @@ class quiz_file_format extends quiz_default_format {
$shortanswerids[] = $key;
}
- $strmatch = "$category->name - ".get_string("match", "quiz");
+ $strmatch = get_string("match", "quiz")." (".$this->category->name.")";
$shortanswerids = swapshuffle($shortanswerids);
$count = $shortanswercount = count($shortanswerids);
$i = 1;
$matchcount = 0;
- $question->category = $category->id;
+ $question->category = $this->category->id;
$question->qtype = MATCH;
$question->questiontext = get_string("randomsamatchintro", "quiz");
$question->image = "";
@@ -208,10 +208,10 @@ class quiz_file_format extends quiz_default_format {
print_heading($info);
- $options['category'] = $category->id;
- echo "";
+ $options['category'] = $this->category->id;
+ echo "";
print_single_button("multiple.php", $options, get_string("randomcreate", "quiz"));
- echo "";
+ echo "";
return true;
}
diff --git a/mod/quiz/format/blackboard.php b/mod/quiz/format/blackboard.php
new file mode 100644
index 00000000000..66019c57ac5
--- /dev/null
+++ b/mod/quiz/format/blackboard.php
@@ -0,0 +1,249 @@
+libdir/xmlize.php");
+
+class quiz_file_format extends quiz_default_format {
+
+/********************************
+ Need to re-compile php with zip support before testing this
+
+ 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);
+
+ $questions = array();
+
+ process_tf($xml, $questions);
+ process_mc($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 = NULL;
+
+ $question->qtype = TRUEFALSE;
+ $question->defaultgrade = 1;
+ $question->single = 1; // Only one answer is allowed
+ $question->image = ""; // No images with this format
+
+ $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"];
+
+ $question->feedbacktrue = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]));
+ $question->feedbackfalse = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]));
+
+ if (strcmp($id, $correct_answer) == 0) // true is correct
+ $question->answer = 1;
+ else // false is correct
+ $question->answer = 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 = NULL;
+
+ $question->qtype = MULTICHOICE;
+ $question->defaultgrade = 1;
+ $question->single = 1; // Only one answer is allowed
+ $question->image = ""; // No images with this format
+
+ $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 Fill in the Blank Questions
+//----------------------------------------
+function process_fib($xml, &$questions) {
+
+ $fibquestions = $xml["POOL"]["#"]["QUESTION_FILLINBLANK"];
+ for ($i = 0; $i < sizeof ($fibquestions); $i++) {
+
+ $question = NULL;
+
+ $question->qtype = SHORTANSWER;
+ $question->defaultgrade = 1;
+ $question->usecase = 0; // Ignore case
+ $question->image = ""; // No images with this format
+
+ $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 = NULL;
+
+ $question->qtype = MATCH;
+ $question->defaultgrade = 1;
+ $question->image = ""; // No images with this format
+
+ $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;
+
+ }
+}
+?>
diff --git a/mod/quiz/format/custom-template.php b/mod/quiz/format/custom-template.php
deleted file mode 100644
index dacb12784fe..00000000000
--- a/mod/quiz/format/custom-template.php
+++ /dev/null
@@ -1,63 +0,0 @@
-readquestion($currentquestion)) {
- $questions[] = $question;
- }
- $currentquestion = array();
- }
- } else {
- $currentquestion[] = $line;
- }
- }
-
- return $questions;
- }
-
-
-
- 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 = NULL;
-
- return $question;
- }
-}
-
-?>
diff --git a/mod/quiz/format/custom.php b/mod/quiz/format/custom.php
index dacb12784fe..a8994465f85 100644
--- a/mod/quiz/format/custom.php
+++ b/mod/quiz/format/custom.php
@@ -6,11 +6,8 @@
/// This format provides a starting point for creating your own
/// import format.
///
-/// If your questions are separated by blank lines, then you will
-/// only need to modify the readquestion() function to parse the
-/// lines into each question. See default.php for the other
-/// functions that you may need to override if you have different
-/// needs.
+/// See default.php for the functions that you may need to override
+/// if you have different needs.
///
/// See missingword.php for an example of how it's done, and see
/// the top of ../lib.php for some constants you might need.
@@ -21,43 +18,8 @@ require("default.php");
class quiz_file_format extends quiz_default_format {
- 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 are defined as anything
- /// between blank lines.
-
- $questions = array();
- $currentquestion = array();
-
- foreach ($lines as $line) {
- $line = trim($line);
- if (empty($line)) {
- if (!empty($currentquestion)) {
- if ($question = $this->readquestion($currentquestion)) {
- $questions[] = $question;
- }
- $currentquestion = array();
- }
- } else {
- $currentquestion[] = $line;
- }
- }
-
- return $questions;
- }
-
- 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 = NULL;
-
- return $question;
- }
}
?>
diff --git a/mod/quiz/format/default.php b/mod/quiz/format/default.php
index 91056ff697b..357e345f657 100644
--- a/mod/quiz/format/default.php
+++ b/mod/quiz/format/default.php
@@ -10,15 +10,67 @@
class quiz_default_format {
var $displayerrors = true;
+ var $category = NULL;
+ var $questionids = array();
/// Importing functions
function preprocess($category) {
/// Does any pre-processing that may be desired
+ $this->category = $category; // Important
+
return true;
}
+ function process($filename) {
+ /// Processes a given file. There's probably little need to change this
+
+ if (! $lines = $this->readdata($filename)) {
+ notify("File could not be read, or was empty");
+ return false;
+ }
+
+ if (! $questions = $this->readquestions($lines)) { // Extract all the questions
+ notify("There are no questions in this file!");
+ return false;
+ }
+
+ notify("Importing ".count($questions)." questions");
+
+ $count = 0;
+
+ foreach ($questions as $question) { // Process and store each question
+ $count++;
+
+ echo "
$count. ".stripslashes($question->questiontext)."
";
+
+ $question->category = $this->category->id;
+
+ 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);
+ return false;
+ }
+
+ if (!empty($result->notice)) {
+ notify($result->notice);
+ return true;
+ }
+ }
+ return true;
+ }
+
+
function readdata($filename) {
/// Returns complete file with an array, one item per line
@@ -61,13 +113,13 @@ class quiz_default_format {
/// this format, this function converts it into a question
/// object suitable for processing and insertion into Moodle.
- echo "You need to override the readquestion() function";
+ echo "
This quiz format has not yet been completed!
";
return NULL;
}
- function postprocess($questionids) {
+ function postprocess() {
/// Does any post-processing that may be desired
/// Argument is a simple array of question ids that
/// have just been added.
diff --git a/mod/quiz/format/missingword.php b/mod/quiz/format/missingword.php
index fad018598f4..a7a7868460c 100644
--- a/mod/quiz/format/missingword.php
+++ b/mod/quiz/format/missingword.php
@@ -105,14 +105,6 @@ class quiz_file_format extends quiz_default_format {
}
}
- function postprocess($questionids) {
- /// Does any post-processing that may be desired
- /// Argument is a simple array of question ids that
- /// have just been added.
-
- return true;
- }
-
}
?>
diff --git a/mod/quiz/format/qti.php b/mod/quiz/format/qti.php
index ca996eef523..a0692975954 100644
--- a/mod/quiz/format/qti.php
+++ b/mod/quiz/format/qti.php
@@ -2,40 +2,13 @@
////////////////////////////////////////////////////////////////////////////
/// IMS QTI FORMAT
-///
-/// This Moodle class provides all functions necessary to import and export
-/// QTI-formatted XML question files.
-///
////////////////////////////////////////////////////////////////////////////
require("default.php");
class quiz_file_format extends quiz_default_format {
- 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();
- /// FIXME
-
- return $questions;
- }
-
-
- 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 = NULL;
-
- /// FIXME
-
- return $question;
- }
}
diff --git a/mod/quiz/format/webct.php b/mod/quiz/format/webct.php
index 3d7bfea60d9..c19999e1fe4 100644
--- a/mod/quiz/format/webct.php
+++ b/mod/quiz/format/webct.php
@@ -2,40 +2,13 @@
////////////////////////////////////////////////////////////////////////////
/// WEBCT FORMAT
-///
-/// This Moodle class provides all functions necessary to import and export
-/// WebCT-formatted question files.
-///
////////////////////////////////////////////////////////////////////////////
require("default.php");
class quiz_file_format extends quiz_default_format {
- 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();
- /// FIXME
-
- return $questions;
- }
-
-
- 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 = NULL;
-
- /// FIXME
-
- return $question;
- }
}
diff --git a/mod/quiz/import.php b/mod/quiz/import.php
index 02406da4690..fad020fba3b 100644
--- a/mod/quiz/import.php
+++ b/mod/quiz/import.php
@@ -51,51 +51,16 @@
$format = new quiz_file_format();
- if (! $format->preprocess($category)) {
- error("Error occurred during pre-processing.");
+ if (! $format->preprocess($category)) { // Do anything before that we need to
+ error("Error occurred during pre-processing!");
}
- if (! $lines = $format->readdata($newfile['tmp_name'])) {
- error("File could not be read, or was empty");
+ if (! $format->process($newfile['tmp_name'])) { // Process the uploaded file
+ error("Error occurred during processing!");
}
- if (! $questions = $format->readquestions($lines)) {
- error("There are no questions in this file!");
- }
-
- notify("Importing ".count($questions)." questions");
-
- $count = 0;
- $questionids = array();
-
- foreach ($questions as $question) {
- $count++;
-
- echo "
$count. ".stripslashes($question->questiontext)."
";
-
- $question->category = $category->id;
-
- if (!$question->id = insert_record("quiz_questions", $question)) {
- error("Could not insert new question!");
- }
-
- $questionids[] = $question->id;
-
- // Now to save all the answers and type-specific options
-
- $result = quiz_save_question_options($question);
-
- if (!empty($result->error)) {
- error($result->error);
- }
-
- if (!empty($result->notice)) {
- notice($result->notice);
- }
- }
-
- if (! $format->postprocess($category, $questionids)) {
- error("Error occurred during post-processing.");
+ if (! $format->postprocess()) { // In case anything needs to be done after
+ error("Error occurred during post-processing!");
}
echo "
";
@@ -114,32 +79,36 @@
print_heading_with_help($strimportquestions, "import", "quiz");
print_simple_box_start("center", "", "$THEME->cellheading");
- echo "";
print_simple_box_end();
print_footer($course);
-
?>
diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php
index a436b2ba389..69b9fb67764 100644
--- a/mod/quiz/lib.php
+++ b/mod/quiz/lib.php
@@ -28,9 +28,10 @@ $QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz")
RANDOMSAMATCH => get_string("randomsamatch", "quiz") );
$QUIZ_FILE_FORMAT = array ( "custom" => get_string("custom", "quiz"),
- "webct" => get_string("webct", "quiz"),
- "qti" => get_string("qti", "quiz"),
- "missingword" => get_string("missingword", "quiz") );
+ "missingword" => get_string("missingword", "quiz"),
+ "blackboard" => get_string("blackboard", "quiz"),
+ "aon" => "AON"
+ );
define("QUIZ_PICTURE_DEFAULT_HEIGHT", "200");