diff --git a/question/format/xml/format.php b/question/format/xml/format.php index e4bf3edd52d..7c6dbda2436 100644 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -151,8 +151,8 @@ class qformat_xml extends qformat_default { global $CFG; // get some error strings - $error_noname = get_string('xmlimportnoname','qformat_xml'); - $error_noquestion = get_string('xmlimportnoquestion','qformat_xml'); + $error_noname = get_string('xmlimportnoname', 'qformat_xml'); + $error_noquestion = get_string('xmlimportnoquestion', 'qformat_xml'); // this routine initialises the question object $qo = $this->defaultquestion(); @@ -185,13 +185,15 @@ class qformat_xml extends qformat_default { $qo->generalfeedback = $this->getpath($question, array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true); $qo->generalfeedbackfiles = array(); - $qo->generalfeedbackformat = $this->trans_format( - $this->getpath($question, array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format')); + $qo->generalfeedbackformat = $this->trans_format($this->getpath($question, + array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format')); $qo->generalfeedbackfiles = $this->import_files($this->getpath($question, array('#', 'generalfeedback', 0, '#', 'file'), array(), false)); - $qo->defaultmark = $this->getpath($question, array('#', 'defaultgrade', 0, '#'), $qo->defaultmark); - $qo->penalty = $this->getpath($question, array('#', 'penalty', 0, '#'), $qo->penalty); + $qo->defaultmark = $this->getpath($question, + array('#', 'defaultgrade', 0, '#'), $qo->defaultmark); + $qo->penalty = $this->getpath($question, + array('#', 'penalty', 0, '#'), $qo->penalty); // Fix problematic rounding from old files: if (abs($qo->penalty - 0.3333333) < 0.005) { @@ -224,7 +226,8 @@ class qformat_xml extends qformat_default { $answerfiles = $this->import_files($this->getpath($answer, array('#', 'file'), array())); - $feedbacktext = $this->getpath($answer, array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true); + $feedbacktext = $this->getpath($answer, + array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true); $feedbackformat = $this->trans_format($this->getpath($answer, array('#', 'feedback', 0, '@', 'format'), 'moodle_auto_format')); $feedbackfiles = $this->import_files($this->getpath($answer, @@ -253,7 +256,8 @@ class qformat_xml extends qformat_default { * @param bool $withshownumpartscorrect include the shownumcorrect field. */ public function import_combined_feedback($qo, $questionxml, $withshownumpartscorrect = false) { - foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $field) { + $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'); + foreach ($fields as $field) { $text = array(); $text['text'] = $this->getpath($questionxml, array('#', $field, 0, '#', 'text', 0, '#'), '', true); @@ -288,7 +292,7 @@ class qformat_xml extends qformat_default { $hint = new stdClass(); $hint->hint = array('format' => FORMAT_HTML, 'files' => array()); $hint->hint['text'] = $this->getpath($hintxml, - array('#', 'hintcontent', 0, '#', 'text' ,0, '#'), '', true); + array('#', 'hintcontent', 0, '#', 'text', 0, '#'), '', true); $hint->shownumcorrect = $this->getpath($hintxml, array('#', 'statenumberofcorrectresponses', 0, '#'), 0); $hint->clearwrong = $this->getpath($hintxml, @@ -314,7 +318,7 @@ class qformat_xml extends qformat_default { $hint->hint = $hinttext; $hint->shownumcorrect = array_key_exists('shownumcorrect', $hintxml['#']); $hint->clearwrong = array_key_exists('clearwrong', $hintxml['#']); - $hint->options = $this->getpath($hintxml, array('#', 'options', 0 , '#'), '', true); + $hint->options = $this->getpath($hintxml, array('#', 'options', 0, '#'), '', true); return $hint; } @@ -375,11 +379,14 @@ class qformat_xml extends qformat_default { $qo->qtype = MULTICHOICE; $single = $this->getpath($question, array('#', 'single', 0, '#'), 'true'); $qo->single = $this->trans_single($single); - $shuffleanswers = $this->getpath($question, array('#', 'shuffleanswers', 0, '#'), 'false'); - $qo->answernumbering = $this->getpath($question, array('#', 'answernumbering', 0, '#'), 'abc'); + $shuffleanswers = $this->getpath($question, + array('#', 'shuffleanswers', 0, '#'), 'false'); + $qo->answernumbering = $this->getpath($question, + array('#', 'answernumbering', 0, '#'), 'abc'); $qo->shuffleanswers = $this->trans_single($shuffleanswers); - // There was a time on the 1.8 branch when it could output an empty answernumbering tag, so fix up any found. + // There was a time on the 1.8 branch when it could output an empty + // answernumbering tag, so fix up any found. if (empty($qo->answernumbering)) { $qo->answernumbering = 'abc'; } @@ -406,7 +413,7 @@ class qformat_xml extends qformat_default { * @param array question question array from xml tree * @return object question object */ - public function import_multianswer( $questions ) { + public function import_multianswer($questions) { $questiontext = array(); $questiontext['text'] = $this->import_text($questions['#']['questiontext'][0]['#']['text']); $questiontext['format'] = '1'; @@ -416,19 +423,20 @@ class qformat_xml extends qformat_default { // 'header' parts particular to multianswer $qo->qtype = MULTIANSWER; $qo->course = $this->course; - $qo->generalfeedback = '' ; + $qo->generalfeedback = ''; // restore files in generalfeedback - $qo->generalfeedback = $this->getpath($questions, array('#','generalfeedback',0,'#','text',0,'#'), $qo->generalfeedback, true); - $qo->generalfeedbackformat = $this->trans_format( - $this->getpath($questions, array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format')); + $qo->generalfeedback = $this->getpath($questions, + array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true); + $qo->generalfeedbackformat = $this->trans_format($this->getpath($questions, + array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format')); $qo->generalfeedbackfiles = $this->import_files($this->getpath($questions, array('#', 'generalfeedback', 0, '#', 'file'), array(), false)); if (!empty($questions)) { $qo->name = $this->import_text($questions['#']['name'][0]['#']['text']); } - $qo->questiontext = $qo->questiontext['text'] ; - $qo->questiontextformat = '' ; + $qo->questiontext = $qo->questiontext['text']; + $qo->questiontextformat = ''; $this->import_hints($qo, $question, true); @@ -455,10 +463,14 @@ class qformat_xml extends qformat_default { $first = true; $warning = false; foreach ($question['#']['answer'] as $answer) { - $answertext = $this->getpath($answer, array('#', 'text', 0, '#'), '', true); - $feedback = $this->getpath($answer, array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true); - $feedbackformat = $this->getpath($answer, array('#', 'feedback',0, '@', 'format'), 'moodle_auto_format'); - $feedbackfiles = $this->getpath($answer, array('#', 'feedback', 0, '#', 'file'), array()); + $answertext = $this->getpath($answer, + array('#', 'text', 0, '#'), '', true); + $feedback = $this->getpath($answer, + array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true); + $feedbackformat = $this->getpath($answer, + array('#', 'feedback', 0, '@', 'format'), 'moodle_auto_format'); + $feedbackfiles = $this->getpath($answer, + array('#', 'feedback', 0, '#', 'file'), array()); $files = array(); foreach ($feedbackfiles as $file) { $data = new stdClass(); @@ -583,7 +595,8 @@ class qformat_xml extends qformat_default { // fraction as a tag is deprecated $fraction = $this->getpath($answer, array('@', 'fraction'), 0) / 100; - $qo->fraction[] = $this->getpath($answer, array('#', 'fraction', 0, '#'), $fraction); // deprecated + $qo->fraction[] = $this->getpath($answer, + array('#', 'fraction', 0, '#'), $fraction); // deprecated } // Get the units array @@ -592,14 +605,14 @@ class qformat_xml extends qformat_default { if (!empty($units)) { $qo->multiplier = array(); foreach ($units as $unit) { - $qo->multiplier[] = $this->getpath( $unit, array('#','multiplier',0,'#'), 1 ); - $qo->unit[] = $this->getpath( $unit, array('#','unit_name',0,'#'), '', true ); + $qo->multiplier[] = $this->getpath($unit, array('#', 'multiplier', 0, '#'), 1); + $qo->unit[] = $this->getpath($unit, array('#', 'unit_name', 0, '#'), '', true); } } - $qo->unitgradingtype = $this->getpath( $question, array('#','unitgradingtype',0,'#'), 0 ); - $qo->unitpenalty = $this->getpath( $question, array('#','unitpenalty',0,'#'), 0 ); - $qo->showunits = $this->getpath( $question, array('#','showunits',0,'#'), 0 ); - $qo->unitsleft = $this->getpath( $question, array('#','unitsleft',0,'#'), 0 ); + $qo->unitgradingtype = $this->getpath($question, array('#', 'unitgradingtype', 0, '#'), 0); + $qo->unitpenalty = $this->getpath($question, array('#', 'unitpenalty', 0, '#'), 0); + $qo->showunits = $this->getpath($question, array('#', 'showunits', 0, '#'), 0); + $qo->unitsleft = $this->getpath($question, array('#', 'unitsleft', 0, '#'), 0); $qo->instructions['text'] = ''; $qo->instructions['format'] = FORMAT_HTML; $instructions = $this->getpath($question, array('#', 'instructions'), array()); @@ -638,14 +651,15 @@ class qformat_xml extends qformat_default { foreach ($question['#']['subquestion'] as $subqxml) { $subquestion = array(); $subquestion['text'] = $this->getpath($subqxml, array('#', 'text', 0, '#'), '', true); - $subquestion['format'] = $this->trans_format( - $this->getpath($subqxml, array('@', 'format'), 'moodle_auto_format')); + $subquestion['format'] = $this->trans_format($this->getpath($subqxml, + array('@', 'format'), 'moodle_auto_format')); $subquestion['files'] = $this->import_files($this->getpath($subqxml, array('#', 'file'), array())); $qo->subquestions[] = $subquestion; $answers = $this->getpath($subqxml, array('#', 'answer'), array()); - $qo->subanswers[] = $this->getpath($subqxml, array('#','answer',0,'#','text',0,'#'), '', true); + $qo->subanswers[] = $this->getpath($subqxml, + array('#', 'answer', 0, '#', 'text', 0, '#'), '', true); } $this->import_combined_feedback($qo, $question, true); @@ -666,64 +680,71 @@ class qformat_xml extends qformat_default { // header parts particular to essay $qo->qtype = ESSAY; - $answers = $this->getpath($question, array('#', 'answer'), null); - if ($answers) { - $answer = array_pop($answers); - $answer = $this->import_answer($answer); - // get feedback - $qo->feedback = $answer->feedback; - } else { - $qo->feedback = array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()); - } - - // get fraction - tag is deprecated - $qo->fraction = $this->getpath($question, array('@', 'fraction'), 0) / 100; - $qo->fraction = $this->getpath($question, array('#', 'fraction', 0, '#'), $qo->fraction); + $qo->responseformat = $this->getpath($question, + array('#', 'responseformat', 0, '#'), 'editor'); + $qo->responsefieldlines = $this->getpath($question, + array('#', 'responsefieldlines', 0, '#'), 15); + $qo->attachments = $this->getpath($question, + array('#', 'attachments', 0, '#'), 0); + $qo->graderinfo['text'] = $this->getpath($question, + array('#', 'graderinfo', 0, '#', 'text', 0, '#'), '', true); + $qo->graderinfo['format'] = $this->trans_format($this->getpath($question, + array('#', 'graderinfo', 0, '@', 'format'), 'moodle_auto_format')); return $qo; } - public function import_calculated($question, $qtype) { - // import calculated question + /** + * Import a calculated question + * @param object $question the imported XML data. + */ + public function import_calculated($question) { // get common parts $qo = $this->import_headers($question); // header parts particular to calculated - $qo->qtype = CALCULATED ;//CALCULATED; - $qo->synchronize = $this->getpath( $question, array( '#','synchronize',0,'#' ), 0 ); - $single = $this->getpath( $question, array('#','single',0,'#'), 'true' ); - $qo->single = $this->trans_single( $single ); - $shuffleanswers = $this->getpath( $question, array('#','shuffleanswers',0,'#'), 'false' ); - $qo->answernumbering = $this->getpath( $question, array('#','answernumbering',0,'#'), 'abc' ); + $qo->qtype = CALCULATED; + $qo->synchronize = $this->getpath($question, array('#', 'synchronize', 0, '#'), 0); + $single = $this->getpath($question, array('#', 'single', 0, '#'), 'true'); + $qo->single = $this->trans_single($single); + $shuffleanswers = $this->getpath($question, array('#', 'shuffleanswers', 0, '#'), 'false'); + $qo->answernumbering = $this->getpath($question, + array('#', 'answernumbering', 0, '#'), 'abc'); $qo->shuffleanswers = $this->trans_single($shuffleanswers); $qo->correctfeedback = array(); - $qo->correctfeedback['text'] = $this->getpath($question, array('#','correctfeedback',0,'#','text',0,'#'), '', true ); + $qo->correctfeedback['text'] = $this->getpath( + $question, array('#', 'correctfeedback', 0, '#', 'text', 0, '#'), '', true); $qo->correctfeedback['format'] = $this->trans_format($this->getpath( - $question, array('#', 'correctfeedback', 0, '@', 'formath'), 'moodle_auto_format')); + $question, array('#', 'correctfeedback', 0, '@', 'format'), 'moodle_auto_format')); $qo->correctfeedback['files'] = $this->import_files($this->getpath( $question, array('#', 'correctfeedback', '0', '#', 'file'), array())); $qo->partiallycorrectfeedback = array(); - $qo->partiallycorrectfeedback['text'] = $this->getpath( $question, array('#','partiallycorrectfeedback',0,'#','text',0,'#'), '', true ); + $qo->partiallycorrectfeedback['text'] = $this->getpath($question, + array('#', 'partiallycorrectfeedback', 0, '#', 'text', 0, '#'), '', true); $qo->partiallycorrectfeedback['format'] = $this->trans_format( - $this->getpath($question, array('#','partiallycorrectfeedback', 0, '@','format'), 'moodle_auto_format')); + $this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '@', 'format'), + 'moodle_auto_format')); $qo->partiallycorrectfeedback['files'] = $this->import_files($this->getpath( $question, array('#', 'partiallycorrectfeedback', '0', '#', 'file'), array())); $qo->incorrectfeedback = array(); - $qo->incorrectfeedback['text'] = $this->getpath( $question, array('#','incorrectfeedback',0,'#','text',0,'#'), '', true ); - $qo->incorrectfeedback['format'] = $this->trans_format($this->getpath( - $question, array('#','incorrectfeedback', 0, '@','format'), 'moodle_auto_format')); - $qo->incorrectfeedback['files'] = $this->import_files($this->getpath( - $question, array('#', 'incorrectfeedback', '0', '#', 'file'), array())); + $qo->incorrectfeedback['text'] = $this->getpath($question, + array('#', 'incorrectfeedback', 0, '#', 'text', 0, '#'), '', true); + $qo->incorrectfeedback['format'] = $this->trans_format($this->getpath($question, + array('#', 'incorrectfeedback', 0, '@', 'format'), 'moodle_auto_format')); + $qo->incorrectfeedback['files'] = $this->import_files($this->getpath($question, + array('#', 'incorrectfeedback', '0', '#', 'file'), array())); - $qo->unitgradingtype = $this->getpath($question, array('#','unitgradingtype',0,'#'), 0 ); - $qo->unitpenalty = $this->getpath($question, array('#','unitpenalty',0,'#'), 0 ); - $qo->showunits = $this->getpath($question, array('#','showunits',0,'#'), 0 ); - $qo->unitsleft = $this->getpath($question, array('#','unitsleft',0,'#'), 0 ); - $qo->instructions = $this->getpath( $question, array('#','instructions',0,'#','text',0,'#'), '', true ); + $qo->unitgradingtype = $this->getpath($question, + array('#', 'unitgradingtype', 0, '#'), 0); + $qo->unitpenalty = $this->getpath($question, array('#', 'unitpenalty', 0, '#'), 0); + $qo->showunits = $this->getpath($question, array('#', 'showunits', 0, '#'), 0); + $qo->unitsleft = $this->getpath($question, array('#', 'unitsleft', 0, '#'), 0); + $qo->instructions = $this->getpath($question, + array('#', 'instructions', 0, '#', 'text', 0, '#'), '', true); if (!empty($instructions)) { $qo->instructions = array(); $qo->instructions['text'] = $this->getpath($instructions, @@ -780,33 +801,47 @@ class qformat_xml extends qformat_default { array('0', '#', 'text', '0', '#'), '', true); $qo->instructions['format'] = $this->trans_format($this->getpath($instructions, array('0', '@', 'format'), 'moodle_auto_format')); - $qo->instructions['files'] = $this->import_files($this->getpath(instructions, + $qo->instructions['files'] = $this->import_files($this->getpath($instructions, array('0', '#', 'file'), array())); } $datasets = $question['#']['dataset_definitions'][0]['#']['dataset_definition']; $qo->dataset = array(); - $qo->datasetindex= 0 ; + $qo->datasetindex= 0; foreach ($datasets as $dataset) { $qo->datasetindex++; $qo->dataset[$qo->datasetindex] = new stdClass(); - $qo->dataset[$qo->datasetindex]->status = $this->import_text( $dataset['#']['status'][0]['#']['text']); - $qo->dataset[$qo->datasetindex]->name = $this->import_text( $dataset['#']['name'][0]['#']['text']); - $qo->dataset[$qo->datasetindex]->type = $dataset['#']['type'][0]['#']; - $qo->dataset[$qo->datasetindex]->distribution = $this->import_text( $dataset['#']['distribution'][0]['#']['text']); - $qo->dataset[$qo->datasetindex]->max = $this->import_text( $dataset['#']['maximum'][0]['#']['text']); - $qo->dataset[$qo->datasetindex]->min = $this->import_text( $dataset['#']['minimum'][0]['#']['text']); - $qo->dataset[$qo->datasetindex]->length = $this->import_text( $dataset['#']['decimals'][0]['#']['text']); - $qo->dataset[$qo->datasetindex]->distribution = $this->import_text( $dataset['#']['distribution'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->status = + $this->import_text($dataset['#']['status'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->name = + $this->import_text($dataset['#']['name'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->type = + $dataset['#']['type'][0]['#']; + $qo->dataset[$qo->datasetindex]->distribution = + $this->import_text($dataset['#']['distribution'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->max = + $this->import_text($dataset['#']['maximum'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->min = + $this->import_text($dataset['#']['minimum'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->length = + $this->import_text($dataset['#']['decimals'][0]['#']['text']); + $qo->dataset[$qo->datasetindex]->distribution = + $this->import_text($dataset['#']['distribution'][0]['#']['text']); $qo->dataset[$qo->datasetindex]->itemcount = $dataset['#']['itemcount'][0]['#']; $qo->dataset[$qo->datasetindex]->datasetitem = array(); $qo->dataset[$qo->datasetindex]->itemindex = 0; - $qo->dataset[$qo->datasetindex]->number_of_items=$dataset['#']['number_of_items'][0]['#']; + $qo->dataset[$qo->datasetindex]->number_of_items = + $dataset['#']['number_of_items'][0]['#']; $datasetitems = $dataset['#']['dataset_items'][0]['#']['dataset_item']; foreach ($datasetitems as $datasetitem) { $qo->dataset[$qo->datasetindex]->itemindex++; - $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex] = new stdClass(); - $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex]->itemnumber = $datasetitem['#']['number'][0]['#']; //[0]['#']['number'][0]['#'] ; // [0]['numberitems'] ;//['#']['number'][0]['#'];// $datasetitems['#']['number'][0]['#']; - $qo->dataset[$qo->datasetindex]->datasetitem[$qo->dataset[$qo->datasetindex]->itemindex]->value = $datasetitem['#']['value'][0]['#'] ;//$datasetitem['#']['value'][0]['#']; + $qo->dataset[$qo->datasetindex]->datasetitem[ + $qo->dataset[$qo->datasetindex]->itemindex] = new stdClass(); + $qo->dataset[$qo->datasetindex]->datasetitem[ + $qo->dataset[$qo->datasetindex]->itemindex]->itemnumber = + $datasetitem['#']['number'][0]['#']; + $qo->dataset[$qo->datasetindex]->datasetitem[ + $qo->dataset[$qo->datasetindex]->itemindex]->value = + $datasetitem['#']['value'][0]['#']; } } @@ -845,7 +880,7 @@ class qformat_xml extends qformat_default { // the 0 means keep white space as it is (important for markdown format) try { $xml = xmlize($text, 0, 'UTF-8', true); - } catch (xml_format_exception $e){ + } catch (xml_format_exception $e) { $this->error($e->getMessage(), ''); return false; } @@ -1053,23 +1088,17 @@ class qformat_xml extends qformat_default { // for all question types except Close $name_text = $this->writetext($question->name, 3); - $question_text = $this->writetext($question->questiontext, 3); - $question_text_files = $this->writefiles($question->questiontextfiles); - - $generalfeedback = $this->writetext($question->generalfeedback, 3); - $generalfeedback_files = $this->writefiles($question->generalfeedbackfiles); - $expout .= " \n"; $expout .= " \n"; $expout .= $name_text; $expout .= " \n"; $expout .= " format($question->questiontextformat)}>\n"; - $expout .= $question_text; - $expout .= $question_text_files; + $expout .= $this->writetext($question->questiontext, 3); + $expout .= $this->writefiles($question->questiontextfiles); $expout .= " \n"; $expout .= " format($question->generalfeedbackformat)}>\n"; - $expout .= $generalfeedback; - $expout .= $generalfeedback_files; + $expout .= $this->writetext($question->generalfeedback, 3); + $expout .= $this->writefiles($question->generalfeedbackfiles); $expout .= " \n"; $expout .= " {$question->defaultmark}\n"; $expout .= " {$question->penalty}\n"; @@ -1077,187 +1106,62 @@ class qformat_xml extends qformat_default { } else { // for Cloze type only - $name_text = $this->writetext( $question->name ); - $question_text = $this->writetext( $question->questiontext ); - $generalfeedback = $this->writetext( $question->generalfeedback ); - $expout .= " \n"; - $expout .= " $name_text\n"; + $name_text = $this->writetext($question->name); + $question_text = $this->writetext($question->questiontext); + $generalfeedback = $this->writetext($question->generalfeedback); + $expout .= " \n"; + $expout .= " \n"; + $expout .= $name_text; + $expout .= " \n"; $expout .= " \n"; - $expout .= $question_text; + $expout .= $this->writetext($question->questiontext, 3); + $expout .= $this->writefiles($question->questiontextfiles); $expout .= " \n"; $expout .= " \n"; - $expout .= $generalfeedback; + $expout .= $this->writetext($question->generalfeedback, 3); + $expout .= $this->writefiles($question->generalfeedbackfiles); $expout .= " \n"; } // output depends on question type switch($question->qtype) { - case 'category': - // not a qtype really - dummy used for category switching - break; + case 'category': + // not a qtype really - dummy used for category switching + break; - case 'truefalse': - $trueanswer = $question->options->answers[$question->options->trueanswer]; - $trueanswer->answer = 'true'; - $expout .= $this->write_answer($trueanswer); + case 'truefalse': + $trueanswer = $question->options->answers[$question->options->trueanswer]; + $trueanswer->answer = 'true'; + $expout .= $this->write_answer($trueanswer); - $falseanswer = $question->options->answers[$question->options->falseanswer]; - $falseanswer->answer = 'false'; - $expout .= $this->write_answer($falseanswer); - break; + $falseanswer = $question->options->answers[$question->options->falseanswer]; + $falseanswer->answer = 'false'; + $expout .= $this->write_answer($falseanswer); + break; - case 'multichoice': - $expout .= " " . $this->get_single($question->options->single) . "\n"; - $expout .= " " . $this->get_single($question->options->shuffleanswers) . "\n"; - $expout .= " {$question->options->answernumbering}\n"; - $expout .= $this->write_combined_feedback($question->options); - $expout .= $this->write_answers($question->options->answers); - break; + case 'multichoice': + $expout .= " " . $this->get_single($question->options->single) . + "\n"; + $expout .= " " . + $this->get_single($question->options->shuffleanswers) . + "\n"; + $expout .= " " . $question->options->answernumbering . + "\n"; + $expout .= $this->write_combined_feedback($question->options); + $expout .= $this->write_answers($question->options->answers); + break; - case 'shortanswer': - $expout .= " {$question->options->usecase}\n"; - $expout .= $this->write_answers($question->options->answers); - break; + case 'shortanswer': + $expout .= " {$question->options->usecase}\n"; + $expout .= $this->write_answers($question->options->answers); + break; - case 'numerical': - foreach ($question->options->answers as $answer) { - $expout .= $this->write_answer($answer, - " $answer->tolerance\n"); - } - - $units = $question->options->units; - if (count($units)) { - $expout .= "\n"; - foreach ($units as $unit) { - $expout .= " \n"; - $expout .= " {$unit->multiplier}\n"; - $expout .= " {$unit->unit}\n"; - $expout .= " \n"; + case 'numerical': + foreach ($question->options->answers as $answer) { + $expout .= $this->write_answer($answer, + " $answer->tolerance\n"); } - $expout .= "\n"; - } - if (isset($question->options->unitgradingtype)) { - $expout .= " {$question->options->unitgradingtype}\n"; - } - if (isset($question->options->unitpenalty)) { - $expout .= " {$question->options->unitpenalty}\n"; - } - if (isset($question->options->showunits)) { - $expout .= " {$question->options->showunits}\n"; - } - if (isset($question->options->unitsleft)) { - $expout .= " {$question->options->unitsleft}\n"; - } - if (!empty($question->options->instructionsformat)) { - $files = $fs->get_area_files($contextid, 'qtype_numerical', 'instruction', $question->id); - $expout .= " format($question->options->instructionsformat)}>\n"; - $expout .= $this->writetext($question->options->instructions, 3); - $expout .= $this->writefiles($files); - $expout .= " \n"; - } - break; - case 'match': - $expout .= " " . $this->get_single($question->options->shuffleanswers) . "\n"; - $expout .= $this->write_combined_feedback($question->options); - foreach ($question->options->subquestions as $subquestion) { - $files = $fs->get_area_files($contextid, 'qtype_match', 'subquestion', $subquestion->id); - $expout .= " format($subquestion->questiontextformat)}>\n"; - $expout .= $this->writetext($subquestion->questiontext, 3); - $expout .= $this->writefiles($files); - $expout .= " \n"; - $expout .= $this->writetext($subquestion->answertext, 4); - $expout .= " \n"; - $expout .= " \n"; - } - break; - - case 'description': - // Nothing else to do. - break; - - case 'multianswer': - $acount = 1; - foreach ($question->options->questions as $question) { - $thispattern = "{#".$acount."}"; - $thisreplace = $question->questiontext; - $expout = preg_replace("~$thispattern~", $thisreplace, $expout ); - $acount++; - } - break; - - case 'essay': - // Nothing else to do. - break; - - case 'calculated': - case 'calculatedsimple': - case 'calculatedmulti': - $expout .= " {$question->options->synchronize}\n"; - $expout .= " {$question->options->single}\n"; - $expout .= " {$question->options->answernumbering}\n"; - $expout .= " ".$this->writetext($question->options->shuffleanswers, 3)."\n"; - - $component = 'qtype_' . $question->qtype; - $files = $fs->get_area_files($contextid, $component, 'correctfeedback', $question->id); - $expout .= " \n"; - $expout .= $this->writetext($question->options->correctfeedback, 3); - $expout .= $this->writefiles($files); - $expout .= " \n"; - - $files = $fs->get_area_files($contextid, $component, 'partiallycorrectfeedback', $question->id); - $expout .= " \n"; - $expout .= $this->writetext($question->options->partiallycorrectfeedback, 3); - $expout .= $this->writefiles($files); - $expout .= " \n"; - - $files = $fs->get_area_files($contextid, $component, 'incorrectfeedback', $question->id); - $expout .= " \n"; - $expout .= $this->writetext($question->options->incorrectfeedback, 3); - $expout .= $this->writefiles($files); - $expout .= " \n"; - - foreach ($question->options->answers as $answer) { - $tolerance = $answer->tolerance; - $tolerancetype = $answer->tolerancetype; - $correctanswerlength= $answer->correctanswerlength ; - $percent = 100 * $answer->fraction; - $expout .= "\n"; - // "" tags are an added feature, old files won't have them - $expout .= " {$answer->answer}\n"; - $expout .= " $tolerance\n"; - $expout .= " $tolerancetype\n"; - $expout .= " $correctanswerformat\n"; - $expout .= " $correctanswerlength\n"; - $feedbackformat = $this->get_format($answer->feedbackformat); - $expout .= " format($answer->correctanswerformat)}>\n"; - $expout .= $this->writetext($answer->feedback); - $expout .= $this->writefiles($answer->feedbackfiles); - $expout .= " \n"; - $expout .= "\n"; - } - if (isset($question->options->unitgradingtype)) { - $expout .= " {$question->options->unitgradingtype}\n"; - } - if (isset($question->options->unitpenalty)) { - $expout .= " {$question->options->unitpenalty}\n"; - } - if (isset($question->options->showunits)) { - $expout .= " {$question->options->showunits}\n"; - } - if (isset($question->options->unitsleft)) { - $expout .= " {$question->options->unitsleft}\n"; - } - - if (isset($question->options->instructionsformat)) { - $files = $fs->get_area_files($contextid, $component, 'instruction', $question->id); - $expout .= " format($question->options->instructionsformat)}>\n"; - $expout .= $this->writetext($question->options->instructions, 3); - $expout .= $this->writefiles($files); - $expout .= " \n"; - } - - if (isset($question->options->units)) { $units = $question->options->units; if (count($units)) { $expout .= "\n"; @@ -1269,51 +1173,215 @@ class qformat_xml extends qformat_default { } $expout .= "\n"; } - } - - // The tag $question->export_process has been set so we get all the - // data items in the database from the function - // qtype_calculated::get_question_options calculatedsimple defaults - // to calculated - if( isset($question->options->datasets)&&count($question->options->datasets)){// there should be - $expout .= "\n"; - foreach ($question->options->datasets as $def) { - $expout .= "\n"; - $expout .= " ".$this->writetext($def->status)."\n"; - $expout .= " ".$this->writetext($def->name)."\n"; - if ( $question->qtype == CALCULATED){ - $expout .= " calculated\n"; - }else { - $expout .= " calculatedsimple\n"; - } - $expout .= " ".$this->writetext($def->distribution)."\n"; - $expout .= " ".$this->writetext($def->minimum)."\n"; - $expout .= " ".$this->writetext($def->maximum)."\n"; - $expout .= " ".$this->writetext($def->decimals)."\n"; - $expout .= " $def->itemcount\n"; - if ($def->itemcount > 0) { - $expout .= " \n"; - foreach ($def->items as $item) { - $expout .= " \n"; - $expout .= " ".$item->itemnumber."\n"; - $expout .= " ".$item->value."\n"; - $expout .= " \n"; - } - $expout .= " \n"; - $expout .= " ".$def-> number_of_items."\n"; - } - $expout .= "\n"; + if (isset($question->options->unitgradingtype)) { + $expout .= " " . $question->options->unitgradingtype . + "\n"; } - $expout .= "\n"; - } - break; + if (isset($question->options->unitpenalty)) { + $expout .= " {$question->options->unitpenalty}\n"; + } + if (isset($question->options->showunits)) { + $expout .= " {$question->options->showunits}\n"; + } + if (isset($question->options->unitsleft)) { + $expout .= " {$question->options->unitsleft}\n"; + } + if (!empty($question->options->instructionsformat)) { + $files = $fs->get_area_files($contextid, 'qtype_numerical', + 'instruction', $question->id); + $expout .= " format($question->options->instructionsformat) . ">\n"; + $expout .= $this->writetext($question->options->instructions, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + } + break; - default: - // try support by optional plugin - if (!$data = $this->try_exporting_using_qtypes($question->qtype, $question)) { - notify(get_string('unsupportedexport', 'qformat_xml', $question->qtype)); - } - $expout .= $data; + case 'match': + $expout .= " " . + $this->get_single($question->options->shuffleanswers) . + "\n"; + $expout .= $this->write_combined_feedback($question->options); + foreach ($question->options->subquestions as $subquestion) { + $files = $fs->get_area_files($contextid, 'qtype_match', + 'subquestion', $subquestion->id); + $expout .= " format($subquestion->questiontextformat) . ">\n"; + $expout .= $this->writetext($subquestion->questiontext, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + $expout .= $this->writetext($subquestion->answertext, 4); + $expout .= " \n"; + $expout .= " \n"; + } + break; + + case 'description': + // Nothing else to do. + break; + + case 'multianswer': + $acount = 1; + foreach ($question->options->questions as $question) { + $thispattern = "{#".$acount."}"; + $thisreplace = $question->questiontext; + $expout = preg_replace("~$thispattern~", $thisreplace, $expout); + $acount++; + } + break; + + case 'essay': + $expout .= " " . $question->options->responseformat . + "\n"; + $expout .= " " . $question->options->responsefieldlines . + "\n"; + $expout .= " " . $question->options->attachments . + "\n"; + $expout .= " format($question->options->graderinfoformat) . ">\n"; + $expout .= $this->writetext($question->options->graderinfo, 3); + $expout .= " \n"; + break; + + case 'calculated': + case 'calculatedsimple': + case 'calculatedmulti': + $expout .= " {$question->options->synchronize}\n"; + $expout .= " {$question->options->single}\n"; + $expout .= " " . $question->options->answernumbering . + "\n"; + $expout .= " " . + $this->writetext($question->options->shuffleanswers, 3) . + "\n"; + + $component = 'qtype_' . $question->qtype; + $files = $fs->get_area_files($contextid, $component, + 'correctfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->correctfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + $files = $fs->get_area_files($contextid, $component, + 'partiallycorrectfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->partiallycorrectfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + $files = $fs->get_area_files($contextid, $component, + 'incorrectfeedback', $question->id); + $expout .= " \n"; + $expout .= $this->writetext($question->options->incorrectfeedback, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + + foreach ($question->options->answers as $answer) { + $percent = 100 * $answer->fraction; + $expout .= "\n"; + // "" tags are an added feature, old files won't have them + $expout .= " {$answer->answer}\n"; + $expout .= " {$answer->tolerance}\n"; + $expout .= " {$answer->tolerancetype}\n"; + $expout .= " " . + $answer->correctanswerformat . "\n"; + $expout .= " " . + $answer->correctanswerlength . "\n"; + $expout .= " format($answer->feedbackformat)}>\n"; + $files = $fs->get_area_files($contextid, $component, + 'instruction', $question->id); + $expout .= $this->writetext($answer->feedback); + $expout .= $this->writefiles($answer->feedbackfiles); + $expout .= " \n"; + $expout .= "\n"; + } + if (isset($question->options->unitgradingtype)) { + $expout .= " " . + $question->options->unitgradingtype . "\n"; + } + if (isset($question->options->unitpenalty)) { + $expout .= " " . + $question->options->unitpenalty . "\n"; + } + if (isset($question->options->showunits)) { + $expout .= " {$question->options->showunits}\n"; + } + if (isset($question->options->unitsleft)) { + $expout .= " {$question->options->unitsleft}\n"; + } + + if (isset($question->options->instructionsformat)) { + $files = $fs->get_area_files($contextid, $component, + 'instruction', $question->id); + $expout .= " format($question->options->instructionsformat) . ">\n"; + $expout .= $this->writetext($question->options->instructions, 3); + $expout .= $this->writefiles($files); + $expout .= " \n"; + } + + if (isset($question->options->units)) { + $units = $question->options->units; + if (count($units)) { + $expout .= "\n"; + foreach ($units as $unit) { + $expout .= " \n"; + $expout .= " {$unit->multiplier}\n"; + $expout .= " {$unit->unit}\n"; + $expout .= " \n"; + } + $expout .= "\n"; + } + } + + // The tag $question->export_process has been set so we get all the + // data items in the database from the function + // qtype_calculated::get_question_options calculatedsimple defaults + // to calculated + if (isset($question->options->datasets) && count($question->options->datasets)) { + $expout .= "\n"; + foreach ($question->options->datasets as $def) { + $expout .= "\n"; + $expout .= " ".$this->writetext($def->status)."\n"; + $expout .= " ".$this->writetext($def->name)."\n"; + if ($question->qtype == CALCULATED) { + $expout .= " calculated\n"; + } else { + $expout .= " calculatedsimple\n"; + } + $expout .= " " . $this->writetext($def->distribution) . + "\n"; + $expout .= " " . $this->writetext($def->minimum) . + "\n"; + $expout .= " " . $this->writetext($def->maximum) . + "\n"; + $expout .= " " . $this->writetext($def->decimals) . + "\n"; + $expout .= " $def->itemcount\n"; + if ($def->itemcount > 0) { + $expout .= " \n"; + foreach ($def->items as $item) { + $expout .= " \n"; + $expout .= " ".$item->itemnumber."\n"; + $expout .= " ".$item->value."\n"; + $expout .= " \n"; + } + $expout .= " \n"; + $expout .= " " . $def->number_of_items . + "\n"; + } + $expout .= "\n"; + } + $expout .= "\n"; + } + break; + + default: + // try support by optional plugin + if (!$data = $this->try_exporting_using_qtypes($question->qtype, $question)) { + notify(get_string('unsupportedexport', 'qformat_xml', $question->qtype)); + } + $expout .= $data; } // Output any hints. diff --git a/question/format/xml/simpletest/testxmlformat.php b/question/format/xml/simpletest/testxmlformat.php index 214099e7f6c..9ab295fac37 100644 --- a/question/format/xml/simpletest/testxmlformat.php +++ b/question/format/xml/simpletest/testxmlformat.php @@ -83,7 +83,8 @@ class qformat_xml_test extends UnitTestCase { $exporter = new qformat_xml(); $xml = $exporter->writequestion($q); - $this->assertPattern('|\s*\s*This is the first hint\.\s*\s*|', $xml); + $this->assertPattern('|\s*\s*' . + 'This is the first hint\.\s*\s*|', $xml); $this->assertNoPattern('||', $xml); $this->assertNoPattern('||', $xml); $this->assertNoPattern('||', $xml); @@ -113,8 +114,10 @@ class qformat_xml_test extends UnitTestCase { $exporter = new qformat_xml(); $xml = $exporter->writequestion($q); - $this->assertPattern('|\s*\s*This is the first hint\.\s*|', $xml); - $this->assertPattern('|\s*\s*This is the second hint\.\s*|', $xml); + $this->assertPattern( + '|\s*\s*This is the first hint\.\s*|', $xml); + $this->assertPattern( + '|\s*\s*This is the second hint\.\s*|', $xml); list($ignored, $hint1, $hint2) = explode('assertNoPattern('||', $hint1); $this->assertPattern('||', $hint1); @@ -144,8 +147,10 @@ END; $importer->import_hints($qo, $questionxml['question']); $this->assertEqual(array( - array('text' => 'This is the first hint', 'format' => FORMAT_MOODLE, 'files' => array()), - array('text' => 'This is the second hint', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'This is the first hint', + 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'This is the second hint', + 'format' => FORMAT_MOODLE, 'files' => array()), ), $qo->hint); $this->assertFalse(isset($qo->hintclearwrong)); $this->assertFalse(isset($qo->hintshownumcorrect)); @@ -172,8 +177,10 @@ END; $importer->import_hints($qo, $questionxml['question'], true, true); $this->assertEqual(array( - array('text' => 'This is the first hint', 'format' => FORMAT_MOODLE, 'files' => array()), - array('text' => 'This is the second hint', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'This is the first hint', + 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'This is the second hint', + 'format' => FORMAT_MOODLE, 'files' => array()), ), $qo->hint); $this->assertEqual(array(1, 0), $qo->hintclearwrong); $this->assertEqual(array(0, 1), $qo->hintshownumcorrect); @@ -265,7 +272,7 @@ END; $this->assert_same_xml($expectedxml, $xml); } - public function test_import_essay() { + public function test_import_essay_20() { $xml = ' An essay @@ -294,6 +301,55 @@ END; $expectedq->defaultmark = 1; $expectedq->length = 1; $expectedq->penalty = 0; + $expectedq->responseformat = 'editor'; + $expectedq->responsefieldlines = 15; + $expectedq->attachments = 0; + $expectedq->graderinfo['text'] = ''; + $expectedq->graderinfo['format'] = FORMAT_MOODLE; + + $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); + } + + public function test_import_essay_21() { + $xml = ' + + An essay + + + Write something. + + + I hope you wrote something interesting. + + 1 + 0 + 0 + monospaced + 42 + -1 + + Grade generously!

]]>
+
+
'; + $xmldata = xmlize($xml); + + $importer = new qformat_xml(); + $q = $importer->import_essay($xmldata['question']); + + $expectedq = new stdClass(); + $expectedq->qtype = 'essay'; + $expectedq->name = 'An essay'; + $expectedq->questiontext = 'Write something.'; + $expectedq->questiontextformat = FORMAT_MOODLE; + $expectedq->generalfeedback = 'I hope you wrote something interesting.'; + $expectedq->defaultmark = 1; + $expectedq->length = 1; + $expectedq->penalty = 0; + $expectedq->responseformat = 'monospaced'; + $expectedq->responsefieldlines = 42; + $expectedq->attachments = -1; + $expectedq->graderinfo['text'] = '

Grade generously!

'; + $expectedq->graderinfo['format'] = FORMAT_HTML; $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); } @@ -312,6 +368,13 @@ END; $qdata->length = 1; $qdata->penalty = 0; $qdata->hidden = 0; + $qdata->options->id = 456; + $qdata->options->questionid = 123; + $qdata->options->responseformat = 'monospaced'; + $qdata->options->responsefieldlines = 42; + $qdata->options->attachments = -1; + $qdata->options->graderinfo = '

Grade generously!

'; + $qdata->options->graderinfoformat = FORMAT_HTML; $exporter = new qformat_xml(); $xml = $exporter->writequestion($qdata); @@ -330,6 +393,12 @@ END; 1 0 0 + monospaced + 42 + -1 + + Grade generously!

]]>
+
'; @@ -404,10 +473,13 @@ END; $expectedq->name = 'Matching question'; $expectedq->questiontext = 'Match the upper and lower case letters.'; $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = array('text' => 'Well done.', 'format' => FORMAT_MOODLE, 'files' => array()); - $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->correctfeedback = array('text' => 'Well done.', + 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.', + 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->shownumcorrect = false; - $expectedq->incorrectfeedback = array('text' => 'Completely wrong!', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->incorrectfeedback = array('text' => 'Completely wrong!', + 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.'; $expectedq->generalfeedbackformat = FORMAT_MOODLE; $expectedq->defaultmark = 1; @@ -621,11 +693,20 @@ END; $expectedq->name = 'Multiple choice question'; $expectedq->questiontext = 'Which are the even numbers?'; $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = array('text' => '

Your answer is correct.

', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->correctfeedback = array( + 'text' => '

Your answer is correct.

', + 'format' => FORMAT_MOODLE, + 'files' => array()); $expectedq->shownumcorrect = false; - $expectedq->partiallycorrectfeedback = array('text' => '

Your answer is partially correct.

', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->partiallycorrectfeedback = array( + 'text' => '

Your answer is partially correct.

', + 'format' => FORMAT_MOODLE, + 'files' => array()); $expectedq->shownumcorrect = true; - $expectedq->incorrectfeedback = array('text' => '

Your answer is incorrect.

', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->incorrectfeedback = array( + 'text' => '

Your answer is incorrect.

', + 'format' => FORMAT_MOODLE, + 'files' => array()); $expectedq->generalfeedback = 'The even numbers are 2 and 4.'; $expectedq->defaultmark = 2; $expectedq->length = 1; @@ -815,9 +896,12 @@ END; $expectedq->answer = array('42', '13', '*'); $expectedq->fraction = array(1, 0, 0); $expectedq->feedback = array( - array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()), - array('text' => 'What were you thinking?!', 'format' => FORMAT_MOODLE, 'files' => array()), - array('text' => 'Completely wrong.', 'format' => FORMAT_MOODLE, 'files' => array())); + array('text' => 'Well done!', + 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'What were you thinking?!', + 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'Completely wrong.', + 'format' => FORMAT_MOODLE, 'files' => array())); $expectedq->tolerance = array(0.001, 1, 0); $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); @@ -841,9 +925,12 @@ END; $qdata->hidden = 0; $qdata->options->answers = array( - 13 => new qtype_numerical_answer(13, '42', 1, 'Well done!', FORMAT_HTML, 0.001), - 14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!', FORMAT_HTML, 1), - 15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.', FORMAT_HTML, ''), + 13 => new qtype_numerical_answer(13, '42', 1, 'Well done!', + FORMAT_HTML, 0.001), + 14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!', + FORMAT_HTML, 1), + 15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.', + FORMAT_HTML, ''), ); $qdata->options->units = array(); @@ -1062,8 +1149,10 @@ END; $expectedq->length = 1; $expectedq->penalty = 1; - $expectedq->feedbacktrue = array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()); - $expectedq->feedbackfalse = array('text' => 'Doh!', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->feedbacktrue = array('text' => 'Well done!', + 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->feedbackfalse = array('text' => 'Doh!', + 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->correctanswer = true; $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);