question export MDL-25088 this is the first part. I think question export is now working for all types.

Import to follow soon.
This commit is contained in:
Tim Hunt 2010-11-12 12:06:48 +00:00
parent e7ff48d284
commit 13bb604ed5
3 changed files with 358 additions and 292 deletions

View File

@ -10,22 +10,22 @@
*/
class qformat_default {
var $displayerrors = true;
var $category = NULL;
var $questions = array();
var $course = NULL;
var $filename = '';
var $realfilename = '';
var $matchgrades = 'error';
var $catfromfile = 0;
var $contextfromfile = 0;
var $cattofile = 0;
var $contexttofile = 0;
var $questionids = array();
var $importerrors = 0;
var $stoponerror = true;
var $translator = null;
var $canaccessbackupdata = true;
public $displayerrors = true;
public $category = NULL;
public $questions = array();
public $course = NULL;
public $filename = '';
public $realfilename = '';
public $matchgrades = 'error';
public $catfromfile = 0;
public $contextfromfile = 0;
public $cattofile = 0;
public $contexttofile = 0;
public $questionids = array();
public $importerrors = 0;
public $stoponerror = true;
public $translator = null;
public $canaccessbackupdata = true;
protected $importcontext = null;
@ -61,8 +61,8 @@ class qformat_default {
* set the category
* @param object category the category object
*/
function setCategory( $category ) {
if (count($this->questions)){
function setCategory($category) {
if (count($this->questions)) {
debugging('You shouldn\'t call setCategory after setQuestions');
}
$this->category = $category;
@ -74,8 +74,8 @@ class qformat_default {
* Only used for question export.
* @param array of question objects
*/
function setQuestions( $questions ) {
if ($this->category !== null){
function setQuestions($questions) {
if ($this->category !== null) {
debugging('You shouldn\'t call setQuestions after setCategory');
}
$this->questions = $questions;
@ -85,9 +85,10 @@ class qformat_default {
* set the course class variable
* @param course object Moodle course variable
*/
function setCourse( $course ) {
function setCourse($course) {
$this->course = $course;
}
/**
* set an array of contexts.
* @param array $contexts Moodle course variable
@ -101,7 +102,7 @@ class qformat_default {
* set the filename
* @param string filename name of file to import/export
*/
function setFilename( $filename ) {
function setFilename($filename) {
$this->filename = $filename;
}
@ -110,7 +111,7 @@ class qformat_default {
* (this is what the user typed, regardless of wha happened next)
* @param string realfilename name of file as typed by user
*/
function setRealfilename( $realfilename ) {
function setRealfilename($realfilename) {
$this->realfilename = $realfilename;
}
@ -118,7 +119,7 @@ class qformat_default {
* set matchgrades
* @param string matchgrades error or nearest for grades
*/
function setMatchgrades( $matchgrades ) {
function setMatchgrades($matchgrades) {
$this->matchgrades = $matchgrades;
}
@ -126,7 +127,7 @@ class qformat_default {
* set catfromfile
* @param bool catfromfile allow categories embedded in import file
*/
function setCatfromfile( $catfromfile ) {
function setCatfromfile($catfromfile) {
$this->catfromfile = $catfromfile;
}
@ -142,9 +143,10 @@ class qformat_default {
* set cattofile
* @param bool cattofile exports categories within export file
*/
function setCattofile( $cattofile ) {
function setCattofile($cattofile) {
$this->cattofile = $cattofile;
}
/**
* set contexttofile
* @param bool cattofile exports categories within export file
@ -157,7 +159,7 @@ class qformat_default {
* set stoponerror
* @param bool stoponerror stops database write if any errors reported
*/
function setStoponerror( $stoponerror ) {
function setStoponerror($stoponerror) {
$this->stoponerror = $stoponerror;
}
@ -176,7 +178,7 @@ class qformat_default {
/**
* Handle parsing error
*/
function error( $message, $text='', $questionname='' ) {
function error($message, $text='', $questionname='') {
$importerrorquestion = get_string('importerrorquestion','quiz');
echo "<div class=\"importerror\">\n";
@ -200,7 +202,7 @@ class qformat_default {
* @param qtypehint hint about a question type from format
* @return object question object suitable for save_options() or false if cannot handle
*/
function try_importing_using_qtypes( $data, $question=null, $extra=null, $qtypehint='') {
function try_importing_using_qtypes($data, $question=null, $extra=null, $qtypehint='') {
global $QTYPES;
// work out what format we are using
@ -221,8 +223,8 @@ class qformat_default {
// loop through installed questiontypes checking for
// function to handle this question
foreach ($QTYPES as $qtype) {
if (method_exists( $qtype, $methodname)) {
if ($question = $qtype->$methodname( $data, $question, $this, $extra )) {
if (method_exists($qtype, $methodname)) {
if ($question = $qtype->$methodname($data, $question, $this, $extra)) {
return $question;
}
}
@ -254,24 +256,24 @@ class qformat_default {
set_time_limit(0);
// STAGE 1: Parse the file
echo $OUTPUT->notification( get_string('parsingquestions','quiz') );
echo $OUTPUT->notification(get_string('parsingquestions','quiz'));
if (! $lines = $this->readdata($this->filename)) {
echo $OUTPUT->notification( get_string('cannotread','quiz') );
echo $OUTPUT->notification(get_string('cannotread','quiz'));
return false;
}
if (!$questions = $this->readquestions($lines)) { // Extract all the questions
echo $OUTPUT->notification( get_string('noquestionsinfile','quiz') );
if (!$questions = $this->readquestions($lines, $context)) { // Extract all the questions
echo $OUTPUT->notification(get_string('noquestionsinfile','quiz'));
return false;
}
// STAGE 2: Write data to database
echo $OUTPUT->notification( get_string('importingquestions','quiz',$this->count_questions($questions)) );
echo $OUTPUT->notification(get_string('importingquestions','quiz',$this->count_questions($questions)));
// check for errors before we continue
if ($this->stoponerror and ($this->importerrors>0)) {
echo $OUTPUT->notification( get_string('importparseerror','quiz') );
echo $OUTPUT->notification(get_string('importparseerror','quiz'));
return true;
}
@ -380,6 +382,7 @@ class qformat_default {
}
return true;
}
/**
* Count all non-category questions in the questions array.
*
@ -436,7 +439,7 @@ class qformat_default {
// Now create any categories that need to be created.
foreach ($catnames as $catname) {
if ($category = $DB->get_record( 'question_categories', array('name' => $catname, 'contextid' => $context->id, 'parent' => $parent))) {
if ($category = $DB->get_record('question_categories', array('name' => $catname, 'contextid' => $context->id, 'parent' => $parent))) {
$parent = $category->id;
} else {
require_capability('moodle/question:managecategory', $context);
@ -516,7 +519,6 @@ class qformat_default {
return $questions;
}
/**
* return an "empty" question
* Somewhere to specify question parameters that are not handled
@ -565,7 +567,7 @@ class qformat_default {
*/
function readquestion($lines) {
$formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
$formatnotimplemented = get_string('formatnotimplemented','quiz');
echo "<p>$formatnotimplemented</p>";
return NULL;
@ -592,17 +594,17 @@ class qformat_default {
* @param extra mixed any addition format specific data needed
* @return string the data to append to export or false if error (or unhandled)
*/
function try_exporting_using_qtypes( $name, $question, $extra=null ) {
function try_exporting_using_qtypes($name, $question, $extra=null) {
global $QTYPES;
// work out the name of format in use
$formatname = substr( get_class( $this ), strlen( 'qformat_' ));
$formatname = substr(get_class($this), strlen('qformat_'));
$methodname = "export_to_$formatname";
if (array_key_exists( $name, $QTYPES )) {
if (array_key_exists($name, $QTYPES)) {
$qtype = $QTYPES[ $name ];
if (method_exists( $qtype, $methodname )) {
if ($data = $qtype->$methodname( $question, $this, $extra )) {
if (method_exists($qtype, $methodname)) {
if ($data = $qtype->$methodname($question, $this, $extra)) {
return $data;
}
}
@ -625,7 +627,7 @@ class qformat_default {
* @param string output text
* @param string processed output text
*/
function presave_process( $content ) {
function presave_process($content) {
return $content;
}
@ -640,12 +642,12 @@ class qformat_default {
// get the questions (from database) in this category
// only get q's with no parents (no cloze subquestions specifically)
if ($this->category) {
$questions = get_questions_category( $this->category, true );
$questions = get_questions_category($this->category, true);
} else {
$questions = $this->questions;
}
//echo $OUTPUT->notification( get_string('exportingquestions','quiz') );
//echo $OUTPUT->notification(get_string('exportingquestions','quiz'));
$count = 0;
// results are first written into string (and then to a file)
@ -726,7 +728,7 @@ class qformat_default {
// did we actually process anything
if ($count==0) {
print_error( 'noquestions','quiz',$continuepath );
print_error('noquestions','quiz',$continuepath);
}
// final pre-process on exported data
@ -751,9 +753,9 @@ class qformat_default {
do {
$pathsections[] = $category->name;
$id = $category->parent;
} while ($category = $DB->get_record( 'question_categories', array('id' => $id )));
} while ($category = $DB->get_record('question_categories', array('id' => $id)));
if ($includecontext){
if ($includecontext) {
$pathsections[] = '$' . $contextstring . '$';
}
@ -826,7 +828,7 @@ class qformat_default {
*/
function writequestion($question) {
// if not overidden, then this is an error.
$formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
$formatnotimplemented = get_string('formatnotimplemented','quiz');
echo "<p>$formatnotimplemented</p>";
return NULL;
}
@ -847,8 +849,8 @@ class qformat_default {
}
/**
* where question specifies a moodle (text) format this
* performs the conversion.
* Convert the question text to plain text, so it can safely be displayed
* during import to let the user see roughly what is going on.
*/
function format_question_text($question) {
global $DB;
@ -861,7 +863,7 @@ class qformat_default {
$format = $question->questiontextformat;
}
$text = $question->questiontext;
return format_text(html_to_text($text), $format, $formatoptions);
return format_text(html_to_text($text, 0, false), $format, $formatoptions);
}
/**

View File

@ -54,9 +54,8 @@ class qformat_gift extends qformat_default {
return $answer_weight;
}
function commentparser(&$answer) {
if (strpos($answer,"#") > 0){
if (strpos($answer,"#") > 0) {
$hashpos = strpos($answer,"#");
$comment = substr($answer, $hashpos+1);
$comment = trim($this->escapedchar_post($comment));
@ -67,7 +66,7 @@ class qformat_gift extends qformat_default {
return $comment;
}
function split_truefalse_comment($comment){
function split_truefalse_comment($comment) {
// splits up comment around # marks
// returns an array of true/false feedback
$bits = explode('#',$comment);
@ -83,8 +82,8 @@ class qformat_gift extends qformat_default {
function escapedchar_pre($string) {
//Replaces escaped control characters with a placeholder BEFORE processing
$escapedcharacters = array("\\:", "\\#", "\\=", "\\{", "\\}", "\\~", "\\n" ); //dlnsk
$placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010" ); //dlnsk
$escapedcharacters = array("\\:", "\\#", "\\=", "\\{", "\\}", "\\~", "\\n" ); //dlnsk
$placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); //dlnsk
$string = str_replace("\\\\", "&&092;", $string);
$string = str_replace($escapedcharacters, $placeholders, $string);
@ -95,16 +94,16 @@ class qformat_gift extends qformat_default {
function escapedchar_post($string) {
//Replaces placeholders with corresponding character AFTER processing is done
$placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); //dlnsk
$characters = array(":", "#", "=", "{", "}", "~", "\n" ); //dlnsk
$characters = array(":", "#", "=", "{", "}", "~", "\n" ); //dlnsk
$string = str_replace($placeholders, $characters, $string);
return $string;
}
function check_answer_count( $min, $answers, $text ) {
function check_answer_count($min, $answers, $text) {
$countanswers = count($answers);
if ($countanswers < $min) {
$importminerror = get_string( 'importminerror', 'quiz' );
$this->error( $importminerror, $text );
$importminerror = get_string('importminerror', 'quiz');
$this->error($importminerror, $text);
return false;
}
@ -119,19 +118,19 @@ class qformat_gift extends qformat_default {
$question = $this->defaultquestion();
$comment = NULL;
// define replaced by simple assignment, stop redefine notices
$gift_answerweight_regex = "/^%\-*([0-9]{1,2})\.?([0-9]*)%/";
$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) == "//") {
$lines[$key] = " ";
if (substr($line, 0, 2) == '//') {
$lines[$key] = ' ';
}
}
$text = trim(implode(" ", $lines));
$text = trim(implode(' ', $lines));
if ($text == "") {
if ($text == '') {
return false;
}
@ -139,9 +138,9 @@ class qformat_gift extends qformat_default {
$text = $this->escapedchar_pre($text);
// Look for category modifier
if (preg_match( '~^\$CATEGORY:~', $text)) {
if (preg_match('~^\$CATEGORY:~', $text)) {
// $newcategory = $matches[1];
$newcategory = trim(substr( $text, 10 ));
$newcategory = trim(substr($text, 10));
// build fake question to contain category
$question->qtype = 'category';
@ -150,10 +149,10 @@ class qformat_gift extends qformat_default {
}
// QUESTION NAME parser
if (substr($text, 0, 2) == "::") {
if (substr($text, 0, 2) == '::') {
$text = substr($text, 2);
$namefinish = strpos($text, "::");
$namefinish = strpos($text, '::');
if ($namefinish === false) {
$question->name = false;
// name will be assigned after processing question text below
@ -169,20 +168,18 @@ class qformat_gift extends qformat_default {
// FIND ANSWER section
// no answer means its a description
$answerstart = strpos($text, "{");
$answerfinish = strpos($text, "}");
$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 );
} else if (!(($answerstart !== false) and ($answerfinish !== false))) {
$this->error(get_string('braceerror', 'quiz'), $text);
return false;
}
else {
} else {
$answerlength = $answerfinish - $answerstart;
$answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));
}
@ -190,8 +187,7 @@ class qformat_gift extends qformat_default {
// Format QUESTION TEXT without answer, inserting "_____" as necessary
if ($description) {
$questiontext = $text;
}
elseif (substr($text, -1) == "}") {
} else if (substr($text, -1) == "}") {
// no blank line if answers follow question, outside of closing punctuation
$questiontext = substr_replace($text, "", $answerstart, $answerlength+1);
} else {
@ -199,16 +195,15 @@ class qformat_gift extends qformat_default {
$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;
// Get questiontext format from questiontext
$questiontextformat = FORMAT_MOODLE;
if (strpos($questiontext, '[') === 0) {
$formatend = strpos($questiontext, ']');
$questiontextformat = $this->format_name_to_const(substr($questiontext, 1, $formatend));
if ($questiontextformat == -1) {
$questiontextformat = FORMAT_MOODLE;
} else {
$questiontext = substr($questiontext, $formatend + 1);
}
}
$question->questiontextformat = $questiontextformat;
@ -217,11 +212,11 @@ class qformat_gift extends qformat_default {
// set question name if not already set
if ($question->name === false) {
$question->name = $question->questiontext;
}
}
// ensure name is not longer than 250 characters
$question->name = shorten_text( $question->name, 200 );
$question->name = strip_tags(substr( $question->name, 0, 250 ));
$question->name = shorten_text($question->name, 200);
$question->name = strip_tags(substr($question->name, 0, 250));
// determine QUESTION TYPE
$question->qtype = NULL;
@ -229,25 +224,25 @@ class qformat_gift extends qformat_default {
// give plugins first try
// plugins must promise not to intercept standard qtypes
// MDL-12346, this could be called from lesson mod which has its own base class =(
if (method_exists($this, 'try_importing_using_qtypes') && ($try_question = $this->try_importing_using_qtypes( $lines, $question, $answertext ))) {
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 == '') {
} else if ($answertext == '') {
$question->qtype = ESSAY;
}
elseif ($answertext{0} == "#"){
} else if ($answertext{0} == '#') {
$question->qtype = NUMERICAL;
} elseif (strpos($answertext, "~") !== false) {
} else if (strpos($answertext, '~') !== false) {
// only Multiplechoice questions contain tilde ~
$question->qtype = MULTICHOICE;
} elseif (strpos($answertext, "=") !== false
&& strpos($answertext, "->") !== false) {
} else if (strpos($answertext, '=') !== false
&& strpos($answertext, '->') !== false) {
// only Matching contains both = and ->
$question->qtype = MATCH;
@ -255,23 +250,23 @@ class qformat_gift extends qformat_default {
// TRUEFALSE question check
$truefalse_check = $answertext;
if (strpos($answertext,"#") > 0){
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");
$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;
$question->qtype = SHORTANSWER;
}
}
if (!isset($question->qtype)) {
$giftqtypenotset = get_string('giftqtypenotset','quiz');
$this->error( $giftqtypenotset, $text );
$giftqtypenotset = get_string('giftqtypenotset', 'quiz');
$this->error($giftqtypenotset, $text);
return false;
}
@ -282,16 +277,27 @@ class qformat_gift extends qformat_default {
return $question;
break;
case ESSAY:
$question->feedback = '';
$question->fraction = 0;
$question->feedback['text'] = '';
$question->feedback['format'] = $questiontextformat;
$question->feedback['files'] = array();
return $question;
break;
case MULTICHOICE:
if (strpos($answertext,"=") === false) {
$question->single = 0; // multiple answers are enabled if no single answer is 100% correct
$question->single = 0; // multiple answers are enabled if no single answer is 100% correct
} else {
$question->single = 1; // only one answer allowed (the default)
$question->single = 1; // only one answer allowed (the default)
}
$question->correctfeedback['text'] = '';
$question->correctfeedback['format'] = $questiontextformat;
$question->correctfeedback['files'] = array();
$question->partiallycorrectfeedback['text'] = '';
$question->partiallycorrectfeedback['format'] = $questiontextformat;
$question->partiallycorrectfeedback['files'] = array();
$question->incorrectfeedback['text'] = '';
$question->incorrectfeedback['format'] = $questiontextformat;
$question->incorrectfeedback['files'] = array();
$answertext = str_replace("=", "~=", $answertext);
$answers = explode("~", $answertext);
@ -304,7 +310,7 @@ class qformat_gift extends qformat_default {
$countanswers = count($answers);
if (!$this->check_answer_count( 2,$answers,$text )) {
if (!$this->check_answer_count(2, $answers, $text)) {
return false;
break;
}
@ -313,22 +319,24 @@ class qformat_gift extends qformat_default {
$answer = trim($answer);
// determine answer weight
if ($answer[0] == "=") {
if ($answer[0] == '=') {
$answer_weight = 1;
$answer = substr($answer, 1);
} elseif (preg_match($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
} else if (preg_match($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->answer[$key]['text'] =
$this->escapedchar_post($answer);
$question->answer[$key]['format'] = $questiontextformat;
$question->answer[$key]['files'] = array();
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this->commentparser($answer); // commentparser also removes comment from $answer
$question->answer[$key] = $this->escapedchar_post($answer);
$question->correctfeedback = '';
$question->partiallycorrectfeedback = '';
$question->incorrectfeedback = '';
$question->feedback[$key]['text'] = $this->commentparser($answer); // commentparser also removes comment from $answer
$question->feedback[$key]['format'] = $questiontextformat;
$question->feedback[$key]['files'] = array();
} // end foreach answer
//$question->defaultgrade = 1;
@ -337,7 +345,7 @@ class qformat_gift extends qformat_default {
break;
case MATCH:
$answers = explode("=", $answertext);
$answers = explode('=', $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
@ -345,7 +353,7 @@ class qformat_gift extends qformat_default {
array_shift($answers);
}
if (!$this->check_answer_count( 2,$answers,$text )) {
if (!$this->check_answer_count(2,$answers,$text)) {
return false;
break;
}
@ -354,15 +362,18 @@ class qformat_gift extends qformat_default {
$answer = trim($answer);
if (strpos($answer, "->") === false) {
$giftmatchingformat = get_string('giftmatchingformat','quiz');
$this->error($giftmatchingformat, $answer );
$this->error($giftmatchingformat, $answer);
return false;
break 2;
}
$marker = strpos($answer,"->");
$question->subquestions[$key] = trim($this->escapedchar_post(substr($answer, 0, $marker)));
$question->subanswers[$key] = trim($this->escapedchar_post(substr($answer, $marker+2)));
$marker = strpos($answer, '->');
$question->subquestions[$key]['text'] =
trim($this->escapedchar_post(substr($answer, 0, $marker)));
$question->subquestions[$key]['format'] = $questiontextformat;
$question->subquestions[$key]['files'] = array();
$question->subanswers[$key] =
trim($this->escapedchar_post(substr($answer, $marker + 2)));
} // end foreach answer
return $question;
@ -375,13 +386,17 @@ class qformat_gift extends qformat_default {
if ($answer == "T" OR $answer == "TRUE") {
$question->answer = 1;
$question->feedbacktrue = $feedback['right'];
$question->feedbackfalse = $feedback['wrong'];
$question->feedbacktrue['text'] = $feedback['right'];
$question->feedbackfalse['text'] = $feedback['wrong'];
} else {
$question->answer = 0;
$question->feedbackfalse = $feedback['right'];
$question->feedbacktrue = $feedback['wrong'];
$question->feedbackfalse['text'] = $feedback['right'];
$question->feedbacktrue['text'] = $feedback['wrong'];
}
$question->feedbackfalse['format'] = $questiontextformat;
$question->feedbacktrue['format'] = $questiontextformat;
$question->feedbackfalse['files'] = array();
$question->feedbacktrue['files'] = array();
$question->penalty = 1;
$question->correctanswer = $question->answer;
@ -399,7 +414,7 @@ class qformat_gift extends qformat_default {
array_shift($answers);
}
if (!$this->check_answer_count( 1,$answers,$text )) {
if (!$this->check_answer_count(1,$answers,$text)) {
return false;
break;
}
@ -413,9 +428,11 @@ class qformat_gift extends qformat_default {
} 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] = $this->escapedchar_post($answer);
$question->fraction[$key] = $answer_weight;
$question->feedback[$key]['text'] = $this->commentparser($answer); //commentparser also removes comment from $answer
$question->feedback[$key]['format'] = $questiontextformat;
$question->feedback[$key]['files'] = array();
} // end foreach
//$question->usecase = 0; // Ignore case
@ -447,7 +464,7 @@ class qformat_gift extends qformat_default {
if (count($answers) == 0) {
// invalid question
$giftnonumericalanswers = get_string('giftnonumericalanswers','quiz');
$this->error( $giftnonumericalanswers, $text );
$this->error($giftnonumericalanswers, $text);
return false;
break;
}
@ -462,7 +479,9 @@ class qformat_gift extends qformat_default {
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer
$question->feedback[$key]['text'] = $this->commentparser($answer); //commentparser also removes comment from $answer
$question->feedback[$key]['format'] = $questiontextformat;
$question->feedback[$key]['files'] = array();
//Calculate Answer and Min/Max values
if (strpos($answer,"..") > 0) { // optional [min]..[max] format
@ -471,8 +490,8 @@ class qformat_gift extends qformat_default {
$min = trim(substr($answer, 0, $marker));
$ans = ($max + $min)/2;
$tol = $max - $ans;
} elseif (strpos($answer,":") > 0){ // standard [answer]:[errormargin] format
$marker = strpos($answer,":");
} else if (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)
@ -481,8 +500,8 @@ class qformat_gift extends qformat_default {
}
if (!(is_numeric($ans) || $ans = '*') || !is_numeric($tol)) {
$errornotnumbers = get_string( 'errornotnumbers' );
$this->error( $errornotnumbers, $text );
$errornotnumbers = get_string('errornotnumbers');
$this->error($errornotnumbers, $text);
return false;
break;
}
@ -495,8 +514,10 @@ class qformat_gift extends qformat_default {
if ($wrongfeedback) {
$key += 1;
$question->fraction[$key] = 0;
$question->feedback[$key] = $this->commentparser($wrongfeedback);
$question->answer[$key] = '';
$question->feedback[$key]['text'] = $this->commentparser($wrongfeedback);
$question->feedback[$key]['format'] = $questiontextformat;
$question->feedback[$key]['files'] = array();
$question->answer[$key] = '*';
$question->tolerance[$key] = '';
}
@ -504,156 +525,203 @@ class qformat_gift extends qformat_default {
break;
default:
$giftnovalidquestion = get_string('giftnovalidquestion','quiz');
$this->error( $giftnovalidquestion, $text );
$this->error(get_string('giftnovalidquestion', 'quiz'), $text);
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 ) {
global $QTYPES, $OUTPUT;
// turns question into string
// question reflects database fields for general question and specific to type
function repchar($text, $notused = 0) {
// Escapes 'reserved' characters # = ~ {) :
// Removes new lines
$reserved = array( '#', '=', '~', '{', '}', ':', "\n", "\r");
$escaped = array('\#','\=','\~','\{','\}','\:', '\n', '' );
// 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]";
$newtext = str_replace($reserved, $escaped, $text);
return $newtext;
}
// output depends on question type
switch($question->qtype) {
case 'category':
// not a real question, used to insert category switch
$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;
/**
* @param integer $format one of the FORMAT_ constants.
* @return string the corresponding name.
*/
function format_const_to_name($format) {
if ($format == FORMAT_MOODLE) {
return 'moodle';
} else if ($format == FORMAT_HTML) {
return 'html';
} else if ($format == FORMAT_PLAIN) {
return 'plain';
} else if ($format == FORMAT_MARKDOWN) {
return 'markdown';
} 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.":".(float)$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:
// check for plugins
if ($out = $this->try_exporting_using_qtypes( $question->qtype, $question )) {
$expout .= $out;
}
else {
$expout .= "Question type $question->qtype is not supported\n";
echo $OUTPUT->notification( get_string('nohandler', 'qformat_gift', $QTYPES[$question->qtype]->local_name()) );
return 'moodle';
}
}
// add empty line to delimit questions
$expout .= "\n";
return $expout;
}
/**
* @param integer $format one of the FORMAT_ constants.
* @return string the corresponding name.
*/
function format_name_to_const($format) {
if ($format == 'moodle') {
return FORMAT_MOODLE;
} else if ($format == 'html') {
return FORMAT_PLAIN;
} else if ($format == 'plain') {
return FORMAT_PLAIN;
} else if ($format == 'markdown') {
return FORMAT_MARKDOWN;
} else {
return -1;
}
}
public function write_name($name) {
return '::' . $this->repchar($name) . '::';
}
public function write_questiontext($text, $format) {
$output = '';
if ($format != FORMAT_MOODLE) {
$output .= '[' . $this->format_const_to_name($format) . ']';
}
$output .= $this->repchar($text, $format);
return $output;
}
function writequestion($question) {
global $QTYPES, $OUTPUT;
// Start with a comment
$expout = "// question: $question->id name: $question->name \n";
// output depends on question type
switch($question->qtype) {
case 'category':
// not a real question, used to insert category switch
$expout .= "\$CATEGORY: $question->category\n";
break;
case DESCRIPTION:
$expout .= $this->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
break;
case ESSAY:
$expout .= $this->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
$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->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
$expout .= '{' . $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->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
$expout .= "{\n";
foreach($question->options->answers as $answer) {
if ($answer->fraction == 1) {
$answertext = '=';
} else if ($answer->fraction == 0) {
$answertext = '~';
} else {
$weight = $answer->fraction * 100;
$answertext = '~%' . $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->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
$expout .= "{\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->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
$expout .= "{#\n";
foreach ($question->options->answers as $answer) {
if ($answer->answer != '' && $answer->answer != '*') {
$weight = 100 * $answer->fraction;
$expout .= "\t=%" . $weight . '%' . $answer->answer . ':' .
(float)$answer->tolerance . '#' . $this->repchar($answer->feedback) . "\n";
} else {
$expout .= "\t~#" . $this->repchar($answer->feedback) . "\n";
}
}
$expout .= "}\n";
break;
case MATCH:
$expout .= $this->write_name($question->name);
$expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
$expout .= "{\n";
foreach($question->options->subquestions as $subquestion) {
$expout .= "\t=" . $this->repchar($subquestion->questiontext) .
' -> ' . $this->repchar($subquestion->answertext) . "\n";
}
$expout .= "}\n";
break;
default:
// Check for plugins
if ($out = $this->try_exporting_using_qtypes($question->qtype, $question)) {
$expout .= $out;
} else {
$expout .= "Question type $question->qtype is not supported\n";
echo $OUTPUT->notification(get_string('nohandler', 'qformat_gift',
$QTYPES[$question->qtype]->local_name()));
}
}
// Add empty line to delimit questions
$expout .= "\n";
return $expout;
}
}

View File

@ -10,15 +10,11 @@ class qformat_xhtml extends qformat_default {
return true;
}
function repchar( $text ) {
// escapes 'reserved' characters # = ~ { ) and removes new lines
$reserved = array( '#','=','~','{','}',"\n","\r" );
$escaped = array( '\#','\=','\~','\{','\}',' ','' );
function repchar($text) {
return $text;
}
return str_replace( $reserved, $escaped, $text );
}
function writequestion( $question ) {
function writequestion($question) {
global $OUTPUT;
// turns question into string
// question reflects database fields for general question and specific to type
@ -42,7 +38,7 @@ function writequestion( $question ) {
// format and add question text
$questiontext = $question->questiontext;
$format = $question->questiontextformat;
$formatted_text = format_text( $questiontext, $format );
$formatted_text = format_text($questiontext, $format);
$expout .= "<p class=\"questiontext\">$formatted_text</p>\n";
// selection depends on question type
@ -60,10 +56,10 @@ function writequestion( $question ) {
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";
$expout .= " <li><input name=\"quest_$id\" type=\"radio\" value=\"" . s($ans_text) . "\" />$ans_text</li>\n";
}
else {
$expout .= " <li><input name=\"quest_$id\" type=\"checkbox\" value=\"$ans_text\" />$ans_text</li>\n";
$expout .= " <li><input name=\"quest_$id\" type=\"checkbox\" value=\"" . s($ans_text) . "\" />$ans_text</li>\n";
}
}
$expout .= "</ul>\n";
@ -91,7 +87,7 @@ function writequestion( $question ) {
// build drop down for answers
$dropdown = "<select name=\"quest_$id\">\n";
foreach($ans_list as $ans) {
$dropdown .= "<option value=\"$ans\">$ans</option>\n";
$dropdown .= "<option value=\"" . s($ans) . "\">" . s($ans) . "</option>\n";
}
$dropdown .= "</select>\n";