mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 14:27:22 +01:00
Removed import/export files. Moved to new (question) location
This commit is contained in:
parent
7b4ac82a37
commit
d8c2c55bf9
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
?>
|
@ -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> </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> </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> </td></tr>";
|
||||
echo "<tr><td> </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> </td>";
|
||||
echo "</tr><tr><td> </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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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( "ߣ" => "'");
|
||||
//return strtr($text, $unicode_array);
|
||||
return str_replace('’', "'", $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
|
||||
|
||||
?>
|
@ -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.
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
@ -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 .= ' <NOBR><B>[ '.implode(' ', $segments).' ]</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('<'=>'<', '>'=>'>', '&'=>'&'));
|
||||
|
||||
// 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(
|
||||
''' => "'",
|
||||
'"' => '"',
|
||||
'<' => '<',
|
||||
'>' => '>',
|
||||
'&' => '&',
|
||||
);
|
||||
static $ILLEGAL_STRINGS = array(
|
||||
"\r" => '',
|
||||
"\n" => '<br />',
|
||||
']]>' => ']]>',
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -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,<p>
|
||||
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<p>
|
||||
median = 7 i.e the middle number<p>
|
||||
range = 8-3 = 5</hint>
|
||||
<answer>
|
||||
<option correct="yes">median > mean > range</option>
|
||||
<option correct="no">mean = median</option>
|
||||
<option correct="no">mean > median > range</option>
|
||||
<option correct="no">median > range > mean</option>
|
||||
</answer>
|
||||
</question>
|
||||
<question type="multiChoice">
|
||||
<text>At a college, students are given a points score calculated from their GCSE grades.<p>
|
||||
Someone with 4 A grades and 4 B grades gains a score of 36 points.<br>Someone with 2 A grades and 4 B grades gains a score of 26 points.<br>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<p>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.<p>
|
||||
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<p>
|
||||
"Schools with high numbers of pupils on free school meals do not do well in league tables."<p>
|
||||
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.<br>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 <b>both</b> 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<br>
|
||||
between 24 and 36 students - two classes<br>
|
||||
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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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 <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> ';
|
||||
|
||||
$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> ';
|
||||
|
||||
$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> ';
|
||||
|
||||
$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> ';
|
||||
|
||||
$text = preg_replace($search, $replace, $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
?>
|
@ -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 &
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Loading…
x
Reference in New Issue
Block a user