View source
<?php
class qformat_gift extends qformat_default {
function provide_import() {
return true;
}
function provide_export() {
return true;
}
function answerweightparser(&$answer) {
$answer = substr($answer, 1);
$end_position = strpos($answer, "%");
$answer_weight = substr($answer, 0, $end_position);
$answer_weight = $answer_weight / 100;
$answer = substr($answer, $end_position + 1);
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) {
$bits = explode('#', $comment);
$feedback = array(
'wrong' => $bits[0],
);
if (count($bits) >= 2) {
$feedback['right'] = $bits[1];
}
else {
$feedback['right'] = '';
}
return $feedback;
}
function escapedchar_pre($string) {
$escapedcharacters = array(
"\\:",
"\\#",
"\\=",
"\\{",
"\\}",
"\\~",
"\\n",
);
$placeholders = array(
"&&058;",
"&&035;",
"&&061;",
"&&123;",
"&&125;",
"&&126;",
"&&010",
);
$string = str_replace("\\\\", "&&092;", $string);
$string = str_replace($escapedcharacters, $placeholders, $string);
$string = str_replace("&&092;", "\\", $string);
return $string;
}
function escapedchar_post($string) {
$placeholders = array(
"&&058;",
"&&035;",
"&&061;",
"&&123;",
"&&125;",
"&&126;",
"&&010",
);
$characters = array(
":",
"#",
"=",
"{",
"}",
"~",
"\n",
);
$string = str_replace($placeholders, $characters, $string);
return $string;
}
function check_answer_count($min, $answers, $text) {
$countanswers = count($answers);
if ($countanswers < $min) {
$importminerror = get_string('importminerror', 'quiz');
$this
->error($importminerror, $text);
return false;
}
return true;
}
function readquestion($lines) {
$question = $this
->defaultquestion();
$comment = NULL;
$gift_answerweight_regex = "^%\\-*([0-9]{1,2})\\.?([0-9]*)%";
foreach ($lines as $key => $line) {
$line = trim($line);
if (substr($line, 0, 2) == "//") {
$lines[$key] = " ";
}
}
$text = trim(implode(" ", $lines));
if ($text == "") {
return false;
}
$text = $this
->escapedchar_pre($text);
if (ereg('^\\$CATEGORY:', $text)) {
$newcategory = trim(substr($text, 10));
$question->qtype = 'category';
$question->category = $newcategory;
return $question;
}
if (substr($text, 0, 2) == "::") {
$text = substr($text, 2);
$namefinish = strpos($text, "::");
if ($namefinish === false) {
$question->name = false;
}
else {
$questionname = substr($text, 0, $namefinish);
$question->name = addslashes(trim($this
->escapedchar_post($questionname)));
$text = trim(substr($text, $namefinish + 2));
}
}
else {
$question->name = false;
}
$answerstart = strpos($text, "{");
$answerfinish = strpos($text, "}");
$description = false;
if ($answerstart === false and $answerfinish === false) {
$description = true;
$answertext = '';
$answerlength = 0;
}
elseif (!($answerstart !== false and $answerfinish !== false)) {
$this
->error(get_string('braceerror', 'quiz'), $text);
return false;
}
else {
$answerlength = $answerfinish - $answerstart;
$answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));
}
if ($description) {
$questiontext = $text;
}
elseif (substr($text, -1) == "}") {
$questiontext = substr_replace($text, "", $answerstart, $answerlength + 1);
}
else {
$questiontext = substr_replace($text, "_____", $answerstart, $answerlength + 1);
}
$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)));
if ($question->name === false) {
$question->name = $question->questiontext;
}
$question->name = substr($question->name, 0, 200);
$question->name = strip_tags(substr($question->name, 0, 250));
$question->qtype = NULL;
if (method_exists($this, 'try_importing_using_qtypes') && ($try_question = $this
->try_importing_using_qtypes($lines, $question, $answertext))) {
return $try_question;
}
if ($description) {
$question->qtype = DESCRIPTION;
}
elseif ($answertext == '') {
$question->qtype = ESSAY;
}
elseif ($answertext[0] == "#") {
$question->qtype = NUMERICAL;
}
elseif (strpos($answertext, "~") !== false) {
$question->qtype = MULTICHOICE;
}
elseif (strpos($answertext, "=") !== false && strpos($answertext, "->") !== false) {
$question->qtype = MATCH;
}
else {
$truefalse_check = $answertext;
if (strpos($answertext, "#") > 0) {
$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 {
$question->qtype = SHORTANSWER;
}
}
if (!isset($question->qtype)) {
$giftqtypenotset = get_string('giftqtypenotset', 'quiz');
$this
->error($giftqtypenotset, $text);
return false;
}
switch ($question->qtype) {
case DESCRIPTION:
$question->defaultgrade = 0;
$question->length = 0;
return $question;
break;
case ESSAY:
$question->feedback = '';
$question->fraction = 0;
return $question;
break;
case MULTICHOICE:
if (strpos($answertext, "=") === false) {
$question->single = 0;
}
else {
$question->single = 1;
}
$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);
if ($answer[0] == "=") {
$answer_weight = 1;
$answer = substr($answer, 1);
}
elseif (ereg($gift_answerweight_regex, $answer)) {
$answer_weight = $this
->answerweightparser($answer);
}
else {
$answer_weight = 0;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this
->commentparser($answer);
$question->answer[$key] = addslashes($this
->escapedchar_post($answer));
$question->correctfeedback = '';
$question->partiallycorrectfeedback = '';
$question->incorrectfeedback = '';
}
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, "->") === false) {
$giftmatchingformat = get_string('giftmatchingformat', 'quiz');
$this
->error($giftmatchingformat, $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))));
}
return $question;
break;
case TRUEFALSE:
$answer = $answertext;
$comment = $this
->commentparser($answer);
$feedback = $this
->split_truefalse_comment($comment);
if ($answer == "T" or $answer == "TRUE") {
$question->answer = 1;
$question->feedbacktrue = $feedback['right'];
$question->feedbackfalse = $feedback['wrong'];
}
else {
$question->answer = 0;
$question->feedbackfalse = $feedback['right'];
$question->feedbacktrue = $feedback['wrong'];
}
$question->penalty = 1;
$question->correctanswer = $question->answer;
return $question;
break;
case SHORTANSWER:
$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);
if (ereg($gift_answerweight_regex, $answer)) {
$answer_weight = $this
->answerweightparser($answer);
}
else {
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this
->commentparser($answer);
$question->answer[$key] = addslashes($this
->escapedchar_post($answer));
}
return $question;
break;
case NUMERICAL:
$answertext = substr($answertext, 1);
if (($pos = strpos($answertext, '~')) !== false) {
$wrongfeedback = substr($answertext, $pos);
$answertext = substr($answertext, 0, $pos);
}
else {
$wrongfeedback = '';
}
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (count($answers) == 0) {
$giftnonumericalanswers = get_string('giftnonumericalanswers', 'quiz');
$this
->error($giftnonumericalanswers, $text);
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
if (ereg($gift_answerweight_regex, $answer)) {
$answer_weight = $this
->answerweightparser($answer);
}
else {
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this
->commentparser($answer);
if (strpos($answer, "..") > 0) {
$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) {
$marker = strpos($answer, ":");
$tol = trim(substr($answer, $marker + 1));
$ans = trim(substr($answer, 0, $marker));
}
else {
$tol = 0;
$ans = trim($answer);
}
if (!(is_numeric($ans) || ($ans = '*')) || !is_numeric($tol)) {
$errornotnumbers = get_string('errornotnumbers');
$this
->error($errornotnumbers, $text);
return false;
break;
}
$question->answer[$key] = $ans;
$question->tolerance[$key] = $tol;
}
if ($wrongfeedback) {
$key += 1;
$question->fraction[$key] = 0;
$question->feedback[$key] = $this
->commentparser($wrongfeedback);
$question->answer[$key] = '';
$question->tolerance[$key] = '';
}
return $question;
break;
default:
$giftnovalidquestion = get_string('giftnovalidquestion', 'quiz');
$this
->error($giftnovalidquestion, $text);
return false;
break;
}
}
function repchar($text, $format = 0) {
$reserved = array(
'#',
'=',
'~',
'{',
'}',
':',
"\n",
"\r",
);
$escaped = array(
'\\#',
'\\=',
'\\~',
'\\{',
'\\}',
'\\:',
'\\n',
'',
);
$newtext = str_replace($reserved, $escaped, $text);
$format = 0;
if ($format) {
$newtext = format_text($format);
}
return $newtext;
}
function writequestion($question) {
global $QTYPES;
$expout = "";
$expout .= "// question: {$question->id} name: {$question->name} \n";
$textformat = $question->questiontextformat;
$tfname = "";
if ($textformat != FORMAT_MOODLE) {
$tfname = text_format_name((int) $textformat);
$tfname = "[{$tfname}]";
}
switch ($question->qtype) {
case 'category':
$expout .= "\$CATEGORY: {$question->category}\n";
break;
case DESCRIPTION:
$expout .= '::' . $this
->repchar($question->name) . '::';
$expout .= $tfname;
$expout .= $this
->repchar($question->questiontext, $textformat);
break;
case ESSAY:
$expout .= '::' . $this
->repchar($question->name) . '::';
$expout .= $tfname;
$expout .= $this
->repchar($question->questiontext, $textformat);
$expout .= "{}\n";
break;
case TRUEFALSE:
$trueanswer = $question->options->answers[$question->options->trueanswer];
$falseanswer = $question->options->answers[$question->options->falseanswer];
if ($trueanswer->fraction == 1) {
$answertext = 'TRUE';
$right_feedback = $trueanswer->feedback;
$wrong_feedback = $falseanswer->feedback;
}
else {
$answertext = 'FALSE';
$right_feedback = $falseanswer->feedback;
$wrong_feedback = $trueanswer->feedback;
}
$wrong_feedback = $this
->repchar($wrong_feedback);
$right_feedback = $this
->repchar($right_feedback);
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{" . $this
->repchar($answertext);
if ($wrong_feedback) {
$expout .= "#" . $wrong_feedback;
}
else {
if ($right_feedback) {
$expout .= "#";
}
}
if ($right_feedback) {
$expout .= "#" . $right_feedback;
}
$expout .= "}\n";
break;
case MULTICHOICE:
$expout .= "::" . $this
->repchar($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 .= "::" . $this
->repchar($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:
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{#\n";
foreach ($question->options->answers as $answer) {
if ($answer->answer != '') {
$percentage = '';
if ($answer->fraction < 1) {
$pval = $answer->fraction * 100;
$percentage = "%{$pval}%";
}
$expout .= "\t={$percentage}" . $answer->answer . ":" . (double) $answer->tolerance . "#" . $this
->repchar($answer->feedback) . "\n";
}
else {
$expout .= "\t~#" . $this
->repchar($answer->feedback) . "\n";
}
}
$expout .= "}\n";
break;
case MATCH:
$expout .= "::" . $this
->repchar($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;
default:
if ($out = $this
->try_exporting_using_qtypes($question->qtype, $question)) {
$expout .= $out;
}
else {
$expout .= "// {$question->qtype} is not supported by the GIFT format\n";
$menuname = $QTYPES[$question->qtype]
->menu_name();
notify(get_string('nohandler', 'qformat_gift', $menuname));
}
}
$expout .= "\n";
return $expout;
}
}