MDL-20636 Fix new stdClass -> stdClass(), and trailing whitespace.
[moodle.git] / question / format / gift / format.php
CommitLineData
aeb15530 1<?php
f1abd39f 2//
84769fd8 3///////////////////////////////////////////////////////////////
aeb15530 4// The GIFT import filter was designed as an easy to use method
84769fd8 5// for teachers writing questions as a text file. It supports most
6// question types and the missing word format.
7//
8// Multiple Choice / Missing Word
9// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}
10// Grant is {~buried =entombed ~living} in Grant's tomb.
11// True-False:
12// Grant is buried in Grant's tomb.{FALSE}
13// Short-Answer.
14// Who's buried in Grant's tomb?{=no one =nobody}
15// Numerical
16// When was Ulysses S. Grant born?{#1822:5}
17// Matching
18// Match the following countries with their corresponding
19// capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo}
20//
aeb15530
PS
21// Comment lines start with a double backslash (//).
22// Optional question names are enclosed in double colon(::).
84769fd8 23// Answer feedback is indicated with hash mark (#).
24// Percentage answer weights immediately follow the tilde (for
25// multiple choice) or equal sign (for short answer and numerical),
26// and are enclosed in percent signs (% %). See docs and examples.txt for more.
aeb15530
PS
27//
28// This filter was written through the collaboration of numerous
29// members of the Moodle community. It was originally based on
84769fd8 30// the missingword format, which included code from Thomas Robb
31// and others. Paul Tsuchido Shew wrote this filter in December 2003.
32//////////////////////////////////////////////////////////////////////////
33// Based on default.php, included by ../import.php
41a89a07 34/**
35 * @package questionbank
36 * @subpackage importexport
37 */
f5565b69 38class qformat_gift extends qformat_default {
84769fd8 39
40 function provide_import() {
09db6da2 41 return true;
84769fd8 42 }
43
44 function provide_export() {
09db6da2 45 return true;
84769fd8 46 }
47
b80d424c
TH
48 function export_file_extension() {
49 return '.txt';
50 }
51
84769fd8 52 function answerweightparser(&$answer) {
53 $answer = substr($answer, 1); // removes initial %
54 $end_position = strpos($answer, "%");
55 $answer_weight = substr($answer, 0, $end_position); // gets weight as integer
56 $answer_weight = $answer_weight/100; // converts to percent
57 $answer = substr($answer, $end_position+1); // removes comment from answer
58 return $answer_weight;
59 }
60
5b744492
TH
61 function commentparser($answer, $defaultformat) {
62 $bits = explode('#', $answer, 2);
63 $ans = $this->parse_text_with_format(trim($bits[0]), $defaultformat);
64 if (count($bits) > 1) {
65 $feedback = $this->parse_text_with_format(trim($bits[1]), $defaultformat);
84769fd8 66 } else {
5b744492 67 $feedback = array('text' => '', 'format' => $defaultformat, 'files' => array());
84769fd8 68 }
5b744492 69 return array($ans, $feedback);
84769fd8 70 }
71
5b744492
TH
72 function split_truefalse_comment($answer, $defaultformat) {
73 $bits = explode('#', $answer, 3);
74 $ans = $this->parse_text_with_format(trim($bits[0]), $defaultformat);
75 if (count($bits) > 1) {
76 $wrongfeedback = $this->parse_text_with_format(trim($bits[1]), $defaultformat);
09db6da2 77 } else {
5b744492 78 $wrongfeedback = array('text' => '', 'format' => $defaultformat, 'files' => array());
09db6da2 79 }
5b744492
TH
80 if (count($bits) > 2) {
81 $rightfeedback = $this->parse_text_with_format(trim($bits[2]), $defaultformat);
82 } else {
83 $rightfeedback = array('text' => '', 'format' => $defaultformat, 'files' => array());
84 }
85 return array($ans, $wrongfeedback, $rightfeedback);
84769fd8 86 }
aeb15530 87
84769fd8 88 function escapedchar_pre($string) {
89 //Replaces escaped control characters with a placeholder BEFORE processing
aeb15530 90
13bb604e
TH
91 $escapedcharacters = array("\\:", "\\#", "\\=", "\\{", "\\}", "\\~", "\\n" ); //dlnsk
92 $placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); //dlnsk
84769fd8 93
94 $string = str_replace("\\\\", "&&092;", $string);
95 $string = str_replace($escapedcharacters, $placeholders, $string);
96 $string = str_replace("&&092;", "\\", $string);
97 return $string;
98 }
99
100 function escapedchar_post($string) {
101 //Replaces placeholders with corresponding character AFTER processing is done
dfdce7fb 102 $placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); //dlnsk
13bb604e 103 $characters = array(":", "#", "=", "{", "}", "~", "\n" ); //dlnsk
84769fd8 104 $string = str_replace($placeholders, $characters, $string);
105 return $string;
106 }
107
13bb604e 108 function check_answer_count($min, $answers, $text) {
09db6da2 109 $countanswers = count($answers);
110 if ($countanswers < $min) {
13bb604e
TH
111 $importminerror = get_string('importminerror', 'quiz');
112 $this->error($importminerror, $text);
09db6da2 113 return false;
114 }
115
116 return true;
84769fd8 117 }
118
5b744492
TH
119 protected function parse_text_with_format($text, $defaultformat = FORMAT_MOODLE) {
120 $result = array(
121 'text' => $text,
122 'format' => $defaultformat,
123 'files' => array(),
124 );
125 if (strpos($text, '[') === 0) {
126 $formatend = strpos($text, ']');
127 $result['format'] = $this->format_name_to_const(substr($text, 1, $formatend - 1));
128 if ($result['format'] == -1) {
129 $result['format'] = $defaultformat;
130 } else {
131 $result['text'] = substr($text, $formatend + 1);
132 }
133 }
134 $result['text'] = trim($this->escapedchar_post($result['text']));
135 return $result;
136 }
84769fd8 137
138 function readquestion($lines) {
139 // Given an array of lines known to define a question in this format, this function
140 // converts it into a question object suitable for processing and insertion into Moodle.
141
142 $question = $this->defaultquestion();
143 $comment = NULL;
144 // define replaced by simple assignment, stop redefine notices
13bb604e 145 $gift_answerweight_regex = '/^%\-*([0-9]{1,2})\.?([0-9]*)%/';
84769fd8 146
147 // REMOVED COMMENTED LINES and IMPLODE
148 foreach ($lines as $key => $line) {
09db6da2 149 $line = trim($line);
13bb604e
TH
150 if (substr($line, 0, 2) == '//') {
151 $lines[$key] = ' ';
09db6da2 152 }
84769fd8 153 }
154
13bb604e 155 $text = trim(implode(' ', $lines));
84769fd8 156
13bb604e 157 if ($text == '') {
84769fd8 158 return false;
159 }
160
161 // Substitute escaped control characters with placeholders
162 $text = $this->escapedchar_pre($text);
163
b39c7aad 164 // Look for category modifier
13bb604e 165 if (preg_match('~^\$CATEGORY:~', $text)) {
5363b555 166 // $newcategory = $matches[1];
13bb604e 167 $newcategory = trim(substr($text, 10));
b39c7aad 168
169 // build fake question to contain category
170 $question->qtype = 'category';
171 $question->category = $newcategory;
172 return $question;
173 }
aeb15530 174
84769fd8 175 // QUESTION NAME parser
13bb604e 176 if (substr($text, 0, 2) == '::') {
84769fd8 177 $text = substr($text, 2);
178
13bb604e 179 $namefinish = strpos($text, '::');
84769fd8 180 if ($namefinish === false) {
181 $question->name = false;
182 // name will be assigned after processing question text below
09db6da2 183 } else {
84769fd8 184 $questionname = substr($text, 0, $namefinish);
294ce987 185 $question->name = trim($this->escapedchar_post($questionname));
84769fd8 186 $text = trim(substr($text, $namefinish+2)); // Remove name from text
187 }
188 } else {
189 $question->name = false;
190 }
191
192
193 // FIND ANSWER section
e8825e72 194 // no answer means its a description
13bb604e
TH
195 $answerstart = strpos($text, '{');
196 $answerfinish = strpos($text, '}');
e8825e72 197
198 $description = false;
199 if (($answerstart === false) and ($answerfinish === false)) {
200 $description = true;
201 $answertext = '';
202 $answerlength = 0;
13bb604e
TH
203 } else if (!(($answerstart !== false) and ($answerfinish !== false))) {
204 $this->error(get_string('braceerror', 'quiz'), $text);
84769fd8 205 return false;
13bb604e 206 } else {
e8825e72 207 $answerlength = $answerfinish - $answerstart;
208 $answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));
209 }
84769fd8 210
211 // Format QUESTION TEXT without answer, inserting "_____" as necessary
e8825e72 212 if ($description) {
213 $questiontext = $text;
13bb604e 214 } else if (substr($text, -1) == "}") {
84769fd8 215 // no blank line if answers follow question, outside of closing punctuation
216 $questiontext = substr_replace($text, "", $answerstart, $answerlength+1);
217 } else {
218 // inserts blank line for missing word format
219 $questiontext = substr_replace($text, "_____", $answerstart, $answerlength+1);
220 }
221
13bb604e 222 // Get questiontext format from questiontext
5b744492
TH
223 $text = $this->parse_text_with_format($questiontext);
224 $question->questiontextformat = $text['format'];
225 $question->generalfeedbackformat = $text['format'];
226 $question->questiontext = $text['text'];
84769fd8 227
228 // set question name if not already set
229 if ($question->name === false) {
230 $question->name = $question->questiontext;
13bb604e 231 }
84769fd8 232
8daaaafc 233 // ensure name is not longer than 250 characters
13bb604e
TH
234 $question->name = shorten_text($question->name, 200);
235 $question->name = strip_tags(substr($question->name, 0, 250));
84769fd8 236
09db6da2 237 // determine QUESTION TYPE
84769fd8 238 $question->qtype = NULL;
239
4433fff5 240 // give plugins first try
241 // plugins must promise not to intercept standard qtypes
3ea69b8d 242 // MDL-12346, this could be called from lesson mod which has its own base class =(
13bb604e 243 if (method_exists($this, 'try_importing_using_qtypes') && ($try_question = $this->try_importing_using_qtypes($lines, $question, $answertext))) {
d510ec64 244 return $try_question;
4433fff5 245 }
246
e8825e72 247 if ($description) {
248 $question->qtype = DESCRIPTION;
13bb604e
TH
249
250 } else if ($answertext == '') {
e8825e72 251 $question->qtype = ESSAY;
13bb604e
TH
252
253 } else if ($answertext{0} == '#') {
84769fd8 254 $question->qtype = NUMERICAL;
255
13bb604e 256 } else if (strpos($answertext, '~') !== false) {
84769fd8 257 // only Multiplechoice questions contain tilde ~
258 $question->qtype = MULTICHOICE;
aeb15530 259
13bb604e
TH
260 } else if (strpos($answertext, '=') !== false
261 && strpos($answertext, '->') !== false) {
09db6da2 262 // only Matching contains both = and ->
84769fd8 263 $question->qtype = MATCH;
264
265 } else { // either TRUEFALSE or SHORTANSWER
aeb15530 266
84769fd8 267 // TRUEFALSE question check
268 $truefalse_check = $answertext;
13bb604e 269 if (strpos($answertext, '#') > 0) {
84769fd8 270 // strip comments to check for TrueFalse question
271 $truefalse_check = trim(substr($answertext, 0, strpos($answertext,"#")));
272 }
273
13bb604e 274 $valid_tf_answers = array('T', 'TRUE', 'F', 'FALSE');
84769fd8 275 if (in_array($truefalse_check, $valid_tf_answers)) {
276 $question->qtype = TRUEFALSE;
277
278 } else { // Must be SHORTANSWER
13bb604e 279 $question->qtype = SHORTANSWER;
84769fd8 280 }
281 }
282
283 if (!isset($question->qtype)) {
13bb604e
TH
284 $giftqtypenotset = get_string('giftqtypenotset', 'quiz');
285 $this->error($giftqtypenotset, $text);
84769fd8 286 return false;
287 }
288
289 switch ($question->qtype) {
e8825e72 290 case DESCRIPTION:
49e2bba7 291 $question->defaultmark = 0;
3f5633df 292 $question->length = 0;
e8825e72 293 return $question;
294 break;
295 case ESSAY:
e8825e72 296 $question->fraction = 0;
13bb604e 297 $question->feedback['text'] = '';
5b744492 298 $question->feedback['format'] = $question->questiontextformat;
13bb604e 299 $question->feedback['files'] = array();
e8825e72 300 return $question;
301 break;
84769fd8 302 case MULTICHOICE:
303 if (strpos($answertext,"=") === false) {
13bb604e 304 $question->single = 0; // multiple answers are enabled if no single answer is 100% correct
84769fd8 305 } else {
13bb604e 306 $question->single = 1; // only one answer allowed (the default)
84769fd8 307 }
13bb604e 308 $question->correctfeedback['text'] = '';
5b744492 309 $question->correctfeedback['format'] = $question->questiontextformat;
13bb604e
TH
310 $question->correctfeedback['files'] = array();
311 $question->partiallycorrectfeedback['text'] = '';
5b744492 312 $question->partiallycorrectfeedback['format'] = $question->questiontextformat;
13bb604e
TH
313 $question->partiallycorrectfeedback['files'] = array();
314 $question->incorrectfeedback['text'] = '';
5b744492 315 $question->incorrectfeedback['format'] = $question->questiontextformat;
13bb604e 316 $question->incorrectfeedback['files'] = array();
84769fd8 317
318 $answertext = str_replace("=", "~=", $answertext);
319 $answers = explode("~", $answertext);
320 if (isset($answers[0])) {
321 $answers[0] = trim($answers[0]);
322 }
323 if (empty($answers[0])) {
324 array_shift($answers);
325 }
aeb15530 326
84769fd8 327 $countanswers = count($answers);
aeb15530 328
13bb604e 329 if (!$this->check_answer_count(2, $answers, $text)) {
2befe778 330 return false;
331 break;
84769fd8 332 }
333
334 foreach ($answers as $key => $answer) {
335 $answer = trim($answer);
336
337 // determine answer weight
13bb604e 338 if ($answer[0] == '=') {
84769fd8 339 $answer_weight = 1;
340 $answer = substr($answer, 1);
aeb15530 341
13bb604e 342 } else if (preg_match($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
84769fd8 343 $answer_weight = $this->answerweightparser($answer);
aeb15530 344
84769fd8 345 } else { //default, i.e., wrong anwer
346 $answer_weight = 0;
347 }
5b744492
TH
348 list($question->answer[$key], $question->feedback[$key]) =
349 $this->commentparser($answer, $question->questiontextformat);
84769fd8 350 $question->fraction[$key] = $answer_weight;
84769fd8 351 } // end foreach answer
aeb15530 352
49e2bba7 353 //$question->defaultmark = 1;
84769fd8 354 //$question->image = ""; // No images with this format
355 return $question;
356 break;
357
358 case MATCH:
13bb604e 359 $answers = explode('=', $answertext);
84769fd8 360 if (isset($answers[0])) {
361 $answers[0] = trim($answers[0]);
362 }
363 if (empty($answers[0])) {
364 array_shift($answers);
365 }
aeb15530 366
13bb604e 367 if (!$this->check_answer_count(2,$answers,$text)) {
2befe778 368 return false;
369 break;
84769fd8 370 }
aeb15530 371
84769fd8 372 foreach ($answers as $key => $answer) {
373 $answer = trim($answer);
87ee4968 374 if (strpos($answer, "->") === false) {
532344b2 375 $giftmatchingformat = get_string('giftmatchingformat','quiz');
13bb604e 376 $this->error($giftmatchingformat, $answer);
84769fd8 377 return false;
378 break 2;
379 }
380
13bb604e 381 $marker = strpos($answer, '->');
5b744492
TH
382 $question->subquestions[$key] = $this->parse_text_with_format(
383 substr($answer, 0, $marker), $question->questiontextformat);
384 $question->subanswers[$key] = trim($this->escapedchar_post(
385 substr($answer, $marker + 2)));
386 }
aeb15530 387
84769fd8 388 return $question;
389 break;
aeb15530 390
84769fd8 391 case TRUEFALSE:
5b744492
TH
392 list($answer, $wrongfeedback, $rightfeedback) =
393 $this->split_truefalse_comment($answertext, $question->questiontextformat);
84769fd8 394
2c00fa16 395 if ($answer['text'] == "T" OR $answer['text'] == "TRUE") {
5b744492
TH
396 $question->correctanswer = 1;
397 $question->feedbacktrue = $rightfeedback;
398 $question->feedbackfalse = $wrongfeedback;
84769fd8 399 } else {
5b744492
TH
400 $question->correctanswer = 0;
401 $question->feedbacktrue = $wrongfeedback;
402 $question->feedbackfalse = $rightfeedback;
84769fd8 403 }
404
0d4c63d7 405 $question->penalty = 1;
7939a4a0 406
84769fd8 407 return $question;
408 break;
aeb15530 409
84769fd8 410 case SHORTANSWER:
411 // SHORTANSWER Question
412 $answers = explode("=", $answertext);
413 if (isset($answers[0])) {
414 $answers[0] = trim($answers[0]);
415 }
416 if (empty($answers[0])) {
417 array_shift($answers);
418 }
aeb15530 419
5b744492 420 if (!$this->check_answer_count(1, $answers, $text)) {
2befe778 421 return false;
422 break;
84769fd8 423 }
424
425 foreach ($answers as $key => $answer) {
426 $answer = trim($answer);
427
5b744492 428 // Answer weight
6dbcacee 429 if (preg_match($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
84769fd8 430 $answer_weight = $this->answerweightparser($answer);
431 } else { //default, i.e., full-credit anwer
432 $answer_weight = 1;
433 }
5b744492
TH
434
435 list($answer, $question->feedback[$key]) = $this->commentparser(
436 $answer, $question->questiontextformat);
437
438 $question->answer[$key] = $answer['text'];
13bb604e 439 $question->fraction[$key] = $answer_weight;
5b744492 440 }
84769fd8 441
84769fd8 442 return $question;
443 break;
444
445 case NUMERICAL:
446 // Note similarities to ShortAnswer
447 $answertext = substr($answertext, 1); // remove leading "#"
448
1fe641f7 449 // If there is feedback for a wrong answer, store it for now.
450 if (($pos = strpos($answertext, '~')) !== false) {
451 $wrongfeedback = substr($answertext, $pos);
452 $answertext = substr($answertext, 0, $pos);
453 } else {
454 $wrongfeedback = '';
455 }
456
84769fd8 457 $answers = explode("=", $answertext);
458 if (isset($answers[0])) {
459 $answers[0] = trim($answers[0]);
460 }
461 if (empty($answers[0])) {
462 array_shift($answers);
463 }
aeb15530 464
84769fd8 465 if (count($answers) == 0) {
466 // invalid question
532344b2 467 $giftnonumericalanswers = get_string('giftnonumericalanswers','quiz');
13bb604e 468 $this->error($giftnonumericalanswers, $text);
84769fd8 469 return false;
470 break;
471 }
472
473 foreach ($answers as $key => $answer) {
474 $answer = trim($answer);
475
476 // Answer weight
6dbcacee 477 if (preg_match($gift_answerweight_regex, $answer)) { // check for properly formatted answer weight
84769fd8 478 $answer_weight = $this->answerweightparser($answer);
479 } else { //default, i.e., full-credit anwer
480 $answer_weight = 1;
481 }
5b744492
TH
482
483 list($answer, $question->feedback[$key]) = $this->commentparser(
484 $answer, $question->questiontextformat);
84769fd8 485 $question->fraction[$key] = $answer_weight;
5b744492 486 $answer = $answer['text'];
84769fd8 487
488 //Calculate Answer and Min/Max values
489 if (strpos($answer,"..") > 0) { // optional [min]..[max] format
490 $marker = strpos($answer,"..");
491 $max = trim(substr($answer, $marker+2));
492 $min = trim(substr($answer, 0, $marker));
493 $ans = ($max + $min)/2;
494 $tol = $max - $ans;
13bb604e
TH
495 } else if (strpos($answer, ':') > 0) { // standard [answer]:[errormargin] format
496 $marker = strpos($answer, ':');
84769fd8 497 $tol = trim(substr($answer, $marker+1));
498 $ans = trim(substr($answer, 0, $marker));
499 } else { // only one valid answer (zero errormargin)
500 $tol = 0;
501 $ans = trim($answer);
502 }
aeb15530 503
09db6da2 504 if (!(is_numeric($ans) || $ans = '*') || !is_numeric($tol)) {
13bb604e
TH
505 $errornotnumbers = get_string('errornotnumbers');
506 $this->error($errornotnumbers, $text);
84769fd8 507 return false;
508 break;
509 }
aeb15530 510
84769fd8 511 // store results
512 $question->answer[$key] = $ans;
513 $question->tolerance[$key] = $tol;
5b744492 514 }
84769fd8 515
1fe641f7 516 if ($wrongfeedback) {
517 $key += 1;
518 $question->fraction[$key] = 0;
5b744492
TH
519 list($notused, $question->feedback[$key]) = $this->commentparser(
520 $wrongfeedback, $question->questiontextformat);
13bb604e 521 $question->answer[$key] = '*';
1fe641f7 522 $question->tolerance[$key] = '';
523 }
524
84769fd8 525 return $question;
526 break;
527
528 default:
13bb604e 529 $this->error(get_string('giftnovalidquestion', 'quiz'), $text);
5b744492 530 return fale;
aeb15530
PS
531 break;
532
13bb604e 533 }
84769fd8 534
84769fd8 535 }
536
13bb604e
TH
537 function repchar($text, $notused = 0) {
538 // Escapes 'reserved' characters # = ~ {) :
539 // Removes new lines
540 $reserved = array( '#', '=', '~', '{', '}', ':', "\n", "\r");
541 $escaped = array('\#','\=','\~','\{','\}','\:', '\n', '' );
84769fd8 542
13bb604e
TH
543 $newtext = str_replace($reserved, $escaped, $text);
544 return $newtext;
84769fd8 545 }
546
13bb604e
TH
547 /**
548 * @param integer $format one of the FORMAT_ constants.
549 * @return string the corresponding name.
550 */
551 function format_const_to_name($format) {
552 if ($format == FORMAT_MOODLE) {
553 return 'moodle';
554 } else if ($format == FORMAT_HTML) {
555 return 'html';
556 } else if ($format == FORMAT_PLAIN) {
557 return 'plain';
558 } else if ($format == FORMAT_MARKDOWN) {
559 return 'markdown';
09db6da2 560 } else {
13bb604e 561 return 'moodle';
84769fd8 562 }
13bb604e 563 }
36e2232e 564
13bb604e
TH
565 /**
566 * @param integer $format one of the FORMAT_ constants.
567 * @return string the corresponding name.
568 */
569 function format_name_to_const($format) {
570 if ($format == 'moodle') {
571 return FORMAT_MOODLE;
572 } else if ($format == 'html') {
5b744492 573 return FORMAT_HTML;
13bb604e
TH
574 } else if ($format == 'plain') {
575 return FORMAT_PLAIN;
576 } else if ($format == 'markdown') {
577 return FORMAT_MARKDOWN;
578 } else {
579 return -1;
84769fd8 580 }
13bb604e
TH
581 }
582
583 public function write_name($name) {
584 return '::' . $this->repchar($name) . '::';
585 }
586
5b744492 587 public function write_questiontext($text, $format, $defaultformat = FORMAT_MOODLE) {
13bb604e 588 $output = '';
5b744492 589 if ($text != '' && $format != $defaultformat) {
13bb604e 590 $output .= '[' . $this->format_const_to_name($format) . ']';
84769fd8 591 }
13bb604e
TH
592 $output .= $this->repchar($text, $format);
593 return $output;
594 }
595
596 function writequestion($question) {
597 global $QTYPES, $OUTPUT;
598
599 // Start with a comment
5b744492 600 $expout = "// question: $question->id name: $question->name\n";
13bb604e
TH
601
602 // output depends on question type
603 switch($question->qtype) {
604
605 case 'category':
606 // not a real question, used to insert category switch
607 $expout .= "\$CATEGORY: $question->category\n";
608 break;
609
610 case DESCRIPTION:
611 $expout .= $this->write_name($question->name);
612 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
613 break;
614
615 case ESSAY:
616 $expout .= $this->write_name($question->name);
617 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
618 $expout .= "{}\n";
619 break;
620
621 case TRUEFALSE:
622 $trueanswer = $question->options->answers[$question->options->trueanswer];
623 $falseanswer = $question->options->answers[$question->options->falseanswer];
624 if ($trueanswer->fraction == 1) {
625 $answertext = 'TRUE';
5b744492
TH
626 $rightfeedback = $this->write_questiontext($trueanswer->feedback,
627 $trueanswer->feedbackformat, $question->questiontextformat);
628 $wrongfeedback = $this->write_questiontext($falseanswer->feedback,
629 $falseanswer->feedbackformat, $question->questiontextformat);
13bb604e
TH
630 } else {
631 $answertext = 'FALSE';
5b744492
TH
632 $rightfeedback = $this->write_questiontext($falseanswer->feedback,
633 $falseanswer->feedbackformat, $question->questiontextformat);
634 $wrongfeedback = $this->write_questiontext($trueanswer->feedback,
635 $trueanswer->feedbackformat, $question->questiontextformat);
13bb604e
TH
636 }
637
13bb604e
TH
638 $expout .= $this->write_name($question->name);
639 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
640 $expout .= '{' . $this->repchar($answertext);
5b744492
TH
641 if ($wrongfeedback) {
642 $expout .= '#' . $wrongfeedback;
643 } else if ($rightfeedback) {
13bb604e 644 $expout .= '#';
84769fd8 645 }
5b744492
TH
646 if ($rightfeedback) {
647 $expout .= '#' . $rightfeedback;
84769fd8 648 }
13bb604e
TH
649 $expout .= "}\n";
650 break;
651
652 case MULTICHOICE:
653 $expout .= $this->write_name($question->name);
654 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
655 $expout .= "{\n";
656 foreach($question->options->answers as $answer) {
657 if ($answer->fraction == 1) {
658 $answertext = '=';
659 } else if ($answer->fraction == 0) {
660 $answertext = '~';
661 } else {
662 $weight = $answer->fraction * 100;
663 $answertext = '~%' . $weight . '%';
664 }
5b744492
TH
665 $expout .= "\t" . $answertext . $this->write_questiontext($answer->answer,
666 $answer->answerformat, $question->questiontextformat);
13bb604e 667 if ($answer->feedback != '') {
5b744492
TH
668 $expout .= '#' . $this->write_questiontext($answer->feedback,
669 $answer->feedbackformat, $question->questiontextformat);
13bb604e
TH
670 }
671 $expout .= "\n";
84769fd8 672 }
13bb604e
TH
673 $expout .= "}\n";
674 break;
675
676 case SHORTANSWER:
677 $expout .= $this->write_name($question->name);
678 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
679 $expout .= "{\n";
680 foreach($question->options->answers as $answer) {
681 $weight = 100 * $answer->fraction;
682 $expout .= "\t=%" . $weight . '%' . $this->repchar($answer->answer) .
5b744492
TH
683 '#' . $this->write_questiontext($answer->feedback,
684 $answer->feedbackformat, $question->questiontextformat) . "\n";
84769fd8 685 }
13bb604e
TH
686 $expout .= "}\n";
687 break;
688
689 case NUMERICAL:
690 $expout .= $this->write_name($question->name);
691 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
692 $expout .= "{#\n";
693 foreach ($question->options->answers as $answer) {
694 if ($answer->answer != '' && $answer->answer != '*') {
695 $weight = 100 * $answer->fraction;
696 $expout .= "\t=%" . $weight . '%' . $answer->answer . ':' .
5b744492
TH
697 (float)$answer->tolerance . '#' . $this->write_questiontext($answer->feedback,
698 $answer->feedbackformat, $question->questiontextformat) . "\n";
13bb604e 699 } else {
5b744492
TH
700 $expout .= "\t~#" . $this->write_questiontext($answer->feedback,
701 $answer->feedbackformat, $question->questiontextformat) . "\n";
13e88db4 702 }
13bb604e
TH
703 }
704 $expout .= "}\n";
705 break;
706
707 case MATCH:
708 $expout .= $this->write_name($question->name);
709 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
710 $expout .= "{\n";
711 foreach($question->options->subquestions as $subquestion) {
5b744492 712 $expout .= "\t=" . $this->repchar($this->write_questiontext($subquestion->questiontext, $subquestion->questiontextformat, $question->questiontextformat)) .
13bb604e
TH
713 ' -> ' . $this->repchar($subquestion->answertext) . "\n";
714 }
715 $expout .= "}\n";
716 break;
717
718 default:
719 // Check for plugins
720 if ($out = $this->try_exporting_using_qtypes($question->qtype, $question)) {
721 $expout .= $out;
1fe641f7 722 } else {
13bb604e
TH
723 $expout .= "Question type $question->qtype is not supported\n";
724 echo $OUTPUT->notification(get_string('nohandler', 'qformat_gift',
725 $QTYPES[$question->qtype]->local_name()));
1fe641f7 726 }
727 }
13bb604e
TH
728
729 // Add empty line to delimit questions
730 $expout .= "\n";
731 return $expout;
84769fd8 732 }
84769fd8 733}