From 13bb604ed5f04535ba992594f31fa7437f67beb4 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 12 Nov 2010 12:06:48 +0000 Subject: [PATCH] question export MDL-25088 this is the first part. I think question export is now working for all types. Import to follow soon. --- question/format.php | 112 +++---- question/format/gift/format.php | 518 +++++++++++++++++-------------- question/format/xhtml/format.php | 20 +- 3 files changed, 358 insertions(+), 292 deletions(-) diff --git a/question/format.php b/question/format.php index a67e9aed022..ee72b6159f4 100644 --- a/question/format.php +++ b/question/format.php @@ -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 "
\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 "

$formatnotimplemented

"; 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 "

$formatnotimplemented

"; 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); } /** diff --git a/question/format/gift/format.php b/question/format/gift/format.php index 9ad9d32d2e2..d8d037ddcd9 100755 --- a/question/format/gift/format.php +++ b/question/format/gift/format.php @@ -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; + } } diff --git a/question/format/xhtml/format.php b/question/format/xhtml/format.php index bb73ff5cdb8..cfa47b9ed4f 100755 --- a/question/format/xhtml/format.php +++ b/question/format/xhtml/format.php @@ -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 .= "

$formatted_text

\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 .= "
  • $ans_text
  • \n"; + $expout .= "
  • $ans_text
  • \n"; } else { - $expout .= "
  • $ans_text
  • \n"; + $expout .= "
  • $ans_text
  • \n"; } } $expout .= "\n"; @@ -91,7 +87,7 @@ function writequestion( $question ) { // build drop down for answers $dropdown = "\n";