Added copyright notice (they paid moodle.com for this development)
[moodle.git] / question / format / xml / format.php
CommitLineData
84769fd8 1<?php // $Id$
2//
3///////////////////////////////////////////////////////////////
4// XML import/export
5//
6//////////////////////////////////////////////////////////////////////////
7// Based on default.php, included by ../import.php
8
84769fd8 9require_once( "$CFG->libdir/xmlize.php" );
10
f5565b69 11class qformat_xml extends qformat_default {
84769fd8 12
13 function provide_import() {
14 return true;
15 }
16
17 function provide_export() {
18 return true;
19 }
20
21 // IMPORT FUNCTIONS START HERE
22
6e557c08 23 /**
c81415c7 24 * Translate human readable format name
25 * into internal Moodle code number
6e557c08 26 * @param string name format name from xml file
27 * @return int Moodle format code
c81415c7 28 */
84769fd8 29 function trans_format( $name ) {
84769fd8 30 $name = trim($name);
31
32 if ($name=='moodle_auto_format') {
33 $id = 0;
34 }
35 elseif ($name=='html') {
36 $id = 1;
37 }
38 elseif ($name=='plain_text') {
39 $id = 2;
40 }
41 elseif ($name=='wiki_like') {
42 $id = 3;
43 }
44 elseif ($name=='markdown') {
45 $id = 4;
46 }
47 else {
48 $id = 0; // or maybe warning required
49 }
50 return $id;
51 }
52
6e557c08 53 /**
c81415c7 54 * Translate human readable single answer option
55 * to internal code number
6e557c08 56 * @param string name true/false
57 * @return int internal code number
c81415c7 58 */
84769fd8 59 function trans_single( $name ) {
84769fd8 60 $name = trim($name);
61
62 if ($name=="true") {
63 $id = 1;
64 }
65 elseif ($name=="false") {
66 $id = 0;
67 }
68 else {
69 $id = 0; // or maybe warning required
70 }
71 return $id;
72 }
73
6e557c08 74 /**
c81415c7 75 * process text string from xml file
6e557c08 76 * @param array $text bit of xml tree after ['text']
77 * @return string processed text
c81415c7 78 */
84769fd8 79 function import_text( $text ) {
84769fd8 80 $data = $text[0]['#'];
81 $data = html_entity_decode( $data );
82 return addslashes(trim( $data ));
83 }
84
307f045f 85 /**
86 * Process text from an element in the XML that may or not be there.
87 * @param string $subelement the name of the element which is either present or missing.
88 * @param array $question a bit of xml tree, this method looks for $question['#'][$subelement][0]['#']['text'].
89 * @return string If $subelement is present, return the content of the text tag inside it.
90 * Otherwise returns an empty string.
91 */
92 function import_optional_text($subelement, $question) {
93 if (array_key_exists($subelement, $question['#'])) {
94 return $this->import_text($question['#'][$subelement][0]['#']['text']);
95 } else {
96 return '';
97 }
98 }
99
6e557c08 100 /**
c81415c7 101 * import parts of question common to all types
6e557c08 102 * @param array question question array from xml tree
103 * @return object question object
c81415c7 104 */
84769fd8 105 function import_headers( $question ) {
84769fd8 106 // this routine initialises the question object
107 $name = $this->import_text( $question['#']['name'][0]['#']['text'] );
108 $qtext = $this->import_text( $question['#']['questiontext'][0]['#']['text'] );
109 $qformat = $question['#']['questiontext'][0]['@']['format'];
110 $image = $question['#']['image'][0]['#'];
d08e16b2 111 if (!empty($question['#']['image_base64'][0]['#'])) {
112 $image_base64 = stripslashes( trim( $question['#']['image_base64'][0]['#'] ) );
113 $image = $this->importimagefile( $image, $image_base64 );
114 }
a4514d91 115 if (array_key_exists('generalfeedback', $question['#'])) {
116 $generalfeedback = $this->import_text( $question['#']['generalfeedback'][0]['#']['text'] );
1b8a7434 117 } else {
a4514d91 118 $generalfeedback = '';
1b8a7434 119 }
c81415c7 120 if (!empty($question['#']['defaultgrade'][0]['#'])) {
121 $qo->defaultgrade = $question['#']['defaultgrade'][0]['#'];
122 }
84769fd8 123 $penalty = $question['#']['penalty'][0]['#'];
124
51bcdf28 125 $qo = $this->defaultquestion();
84769fd8 126 $qo->name = $name;
127 $qo->questiontext = $qtext;
128 $qo->questiontextformat = $this->trans_format( $qformat );
129 $qo->image = ((!empty($image)) ? $image : '');
a4514d91 130 $qo->generalfeedback = $generalfeedback;
84769fd8 131 $qo->penalty = $penalty;
132
133 return $qo;
134 }
135
6e557c08 136 /**
c81415c7 137 * import the common parts of a single answer
6e557c08 138 * @param array answer xml tree for single answer
139 * @return object answer object
c81415c7 140 */
84769fd8 141 function import_answer( $answer ) {
84769fd8 142 $fraction = $answer['@']['fraction'];
143 $text = $this->import_text( $answer['#']['text']);
144 $feedback = $this->import_text( $answer['#']['feedback'][0]['#']['text'] );
145
146 $ans = null;
147 $ans->answer = $text;
148 $ans->fraction = $fraction / 100;
149 $ans->feedback = $feedback;
150
151 return $ans;
152 }
153
6e557c08 154 /**
c81415c7 155 * import multiple choice question
6e557c08 156 * @param array question question array from xml tree
157 * @return object question object
c81415c7 158 */
84769fd8 159 function import_multichoice( $question ) {
84769fd8 160 // get common parts
161 $qo = $this->import_headers( $question );
162
163 // 'header' parts particular to multichoice
164 $qo->qtype = MULTICHOICE;
165 $single = $question['#']['single'][0]['#'];
166 $qo->single = $this->trans_single( $single );
307f045f 167 if (array_key_exists('shuffleanswers', $question['#'])) {
168 $shuffleanswers = $question['#']['shuffleanswers'][0]['#'];
169 } else {
170 $shuffleanswers = 'false';
171 }
172 $qo->$shuffleanswers = $this->trans_single($shuffleanswers);
173 $qo->correctfeedback = $this->import_optional_text('correctfeedback', $question);
174 $qo->partiallycorrectfeedback = $this->import_optional_text('partiallycorrectfeedback', $question);
175 $qo->incorrectfeedback = $this->import_optional_text('incorrectfeedback', $question);
176
84769fd8 177 // run through the answers
178 $answers = $question['#']['answer'];
179 $a_count = 0;
180 foreach ($answers as $answer) {
181 $ans = $this->import_answer( $answer );
182 $qo->answer[$a_count] = $ans->answer;
183 $qo->fraction[$a_count] = $ans->fraction;
184 $qo->feedback[$a_count] = $ans->feedback;
185 ++$a_count;
186 }
187
188 return $qo;
189 }
190
6e557c08 191 /**
c81415c7 192 * import cloze type question
6e557c08 193 * @param array question question array from xml tree
194 * @return object question object
c81415c7 195 */
7b8bc256 196 function import_multianswer( $questions ) {
7b8bc256 197 $qo = qtype_multianswer_extract_question($this->import_text(
198 $questions['#']['questiontext'][0]['#']['text'] ));
199
200 // 'header' parts particular to multianswer
201 $qo->qtype = MULTIANSWER;
202 $qo->course = $this->course;
203
71ffbac2 204 if (!empty($questions)) {
7b8bc256 205 $qo->name = $this->import_text( $questions['#']['name'][0]['#']['text'] );
206 }
207
208 return $qo;
209 }
210
6e557c08 211 /**
c81415c7 212 * import true/false type question
6e557c08 213 * @param array question question array from xml tree
214 * @return object question object
c81415c7 215 */
84769fd8 216 function import_truefalse( $question ) {
84769fd8 217 // get common parts
218 $qo = $this->import_headers( $question );
219
220 // 'header' parts particular to true/false
221 $qo->qtype = TRUEFALSE;
222
223 // get answer info
224 $answers = $question['#']['answer'];
225 $fraction0 = $answers[0]['@']['fraction'];
226 $feedback0 = $this->import_text($answers[0]['#']['feedback'][0]['#']['text']);
227 $fraction1 = $answers[1]['@']['fraction'];
228 $feedback1 = $this->import_text($answers[1]['#']['feedback'][0]['#']['text']);
229
230 // sort out which is true and build object accordingly
231 if ($fraction0==100) { // then 0 index is true
232 $qo->answer = 1;
233 $qo->feedbacktrue=$feedback0;
234 $qo->feedbackfalse=$feedback1;
235 }
236 else {
237 $qo->answer = 0;
238 $qo->feedbacktrue = $feedback1;
239 $qo->feedbackfalse = $feedback0;
240 }
241
242 return $qo;
243 }
244
6e557c08 245 /**
c81415c7 246 * import short answer type question
6e557c08 247 * @param array question question array from xml tree
248 * @return object question object
c81415c7 249 */
84769fd8 250 function import_shortanswer( $question ) {
84769fd8 251 // get common parts
252 $qo = $this->import_headers( $question );
253
254 // header parts particular to shortanswer
255 $qo->qtype = SHORTANSWER;
256
257 // get usecase
258 $qo->usecase = $question['#']['usecase'][0]['#'];
259
260 // run through the answers
261 $answers = $question['#']['answer'];
262 $a_count = 0;
263 foreach ($answers as $answer) {
264 $ans = $this->import_answer( $answer );
265 $qo->answer[$a_count] = $ans->answer;
266 $qo->fraction[$a_count] = $ans->fraction;
267 $qo->feedback[$a_count] = $ans->feedback;
268 ++$a_count;
269 }
270
271 return $qo;
272 }
7b8bc256 273
6e557c08 274 /**
c81415c7 275 * import regexp type question
6e557c08 276 * @param array question question array from xml tree
277 * @return object question object
c81415c7 278 */
7b8bc256 279 function import_regexp( $question ) {
7b8bc256 280 // get common parts
281 $qo = $this->import_headers( $question );
282
283 // header parts particular to shortanswer
284 $qo->qtype = regexp;
285
286 // get usecase
287 $qo->usecase = $question['#']['usecase'][0]['#'];
288
289 // run through the answers
290 $answers = $question['#']['answer'];
291 $a_count = 0;
292 foreach ($answers as $answer) {
293 $ans = $this->import_answer( $answer );
294 $qo->answer[$a_count] = $ans->answer;
295 $qo->fraction[$a_count] = $ans->fraction;
296 $qo->feedback[$a_count] = $ans->feedback;
297 ++$a_count;
298 }
84769fd8 299
7b8bc256 300 return $qo;
301 }
302
6e557c08 303 /**
c81415c7 304 * import description type question
6e557c08 305 * @param array question question array from xml tree
306 * @return object question object
c81415c7 307 */
7b8bc256 308 function import_description( $question ) {
309 // get common parts
310 $qo = $this->import_headers( $question );
311 // header parts particular to shortanswer
312 $qo->qtype = DESCRIPTION;
313 return $qo;
314 }
84769fd8 315
6e557c08 316 /**
c81415c7 317 * import numerical type question
6e557c08 318 * @param array question question array from xml tree
319 * @return object question object
c81415c7 320 */
321 function import_numerical( $question ) {
84769fd8 322 // get common parts
323 $qo = $this->import_headers( $question );
324
325 // header parts particular to numerical
326 $qo->qtype = NUMERICAL;
327
328 // get answers array
329 $answers = $question['#']['answer'];
330 $qo->answer = array();
331 $qo->feedback = array();
332 $qo->fraction = array();
333 $qo->tolerance = array();
334 foreach ($answers as $answer) {
335 $qo->answer[] = $answer['#'][0];
a0d187bf 336 $qo->feedback[] = $this->import_text( $answer['#']['feedback'][0]['#']['text'] );
84769fd8 337 $qo->fraction[] = $answer['#']['fraction'][0]['#'];
338 $qo->tolerance[] = $answer['#']['tolerance'][0]['#'];
339 }
340
341 // get units array
84769fd8 342 $qo->unit = array();
a0d187bf 343 if (isset($question['#']['units'][0]['#']['unit'])) {
344 $units = $question['#']['units'][0]['#']['unit'];
345 $qo->multiplier = array();
346 foreach ($units as $unit) {
347 $qo->multiplier[] = $unit['#']['multiplier'][0]['#'];
348 $qo->unit[] = $unit['#']['unit_name'][0]['#'];
349 }
84769fd8 350 }
84769fd8 351 return $qo;
352 }
353
6e557c08 354 /**
c81415c7 355 * import matching type question
6e557c08 356 * @param array question question array from xml tree
357 * @return object question object
c81415c7 358 */
51bcdf28 359 function import_matching( $question ) {
51bcdf28 360 // get common parts
361 $qo = $this->import_headers( $question );
362
363 // header parts particular to matching
364 $qo->qtype = MATCH;
ee800653 365 if (!empty($question['#']['shuffleanswers'])) {
366 $qo->shuffleanswers = $question['#']['shuffleanswers'][0]['#'];
367 } else {
368 $qo->shuffleanswers = false;
369 }
51bcdf28 370
371 // get subquestions
372 $subquestions = $question['#']['subquestion'];
373 $qo->subquestions = array();
374 $qo->subanswers = array();
375
376 // run through subquestions
377 foreach ($subquestions as $subquestion) {
a0d187bf 378 $qtext = $this->import_text( $subquestion['#']['text'] );
379 $atext = $this->import_text( $subquestion['#']['answer'][0]['#']['text'] );
51bcdf28 380 $qo->subquestions[] = $qtext;
381 $qo->subanswers[] = $atext;
382 }
51bcdf28 383 return $qo;
384 }
385
6e557c08 386 /**
c81415c7 387 * import essay type question
6e557c08 388 * @param array question question array from xml tree
389 * @return object question object
c81415c7 390 */
391 function import_essay( $question ) {
392 // get common parts
393 $qo = $this->import_headers( $question );
394
395 // header parts particular to essay
396 $qo->qtype = ESSAY;
397
398 // get feedback
399 $qo->feedback = $this->import_text( $question['#']['answer'][0]['#']['feedback'][0]['#']['text'] );
400
401 // get fraction
402 $qo->fraction = $question['#']['answer'][0]['#']['fraction'][0]['#'];
403
404 return $qo;
405 }
84769fd8 406
c81415c7 407 /**
408 * parse the array of lines into an array of questions
409 * this *could* burn memory - but it won't happen that much
410 * so fingers crossed!
6e557c08 411 * @param array lines array of lines from the input file
412 * @return array (of objects) question objects
c81415c7 413 */
414 function readquestions($lines) {
84769fd8 415 // we just need it as one big string
416 $text = implode($lines, " ");
417 unset( $lines );
418
419 // this converts xml to big nasty data structure
420 // the 0 means keep white space as it is (important for markdown format)
421 // print_r it if you want to see what it looks like!
422 $xml = xmlize( $text, 0 );
423
424 // set up array to hold all our questions
425 $questions = array();
426
427 // iterate through questions
428 foreach ($xml['quiz']['#']['question'] as $question) {
429 $question_type = $question['@']['type'];
430 $questiontype = get_string( 'questiontype','quiz',$question_type );
84769fd8 431
432 if ($question_type=='multichoice') {
433 $qo = $this->import_multichoice( $question );
434 }
435 elseif ($question_type=='truefalse') {
436 $qo = $this->import_truefalse( $question );
437 }
438 elseif ($question_type=='shortanswer') {
439 $qo = $this->import_shortanswer( $question );
440 }
7b8bc256 441 //elseif ($question_type=='regexp') {
442 // $qo = $this->import_regexp( $question );
443 //}
84769fd8 444 elseif ($question_type=='numerical') {
445 $qo = $this->import_numerical( $question );
446 }
7b8bc256 447 elseif ($question_type=='description') {
448 $qo = $this->import_description( $question );
449 }
51bcdf28 450 elseif ($question_type=='matching') {
451 $qo = $this->import_matching( $question );
452 }
7b8bc256 453 elseif ($question_type=='cloze') {
c81415c7 454 $qo = $this->import_multianswer( $question );
455 }
456 elseif ($question_type=='essay') {
457 $qo = $this->import_essay( $question );
7b8bc256 458 }
84769fd8 459 else {
ee800653 460 $notsupported = get_string( 'xmltypeunsupported','quiz',$question_type );
84769fd8 461 echo "<p>$notsupported</p>";
462 $qo = null;
463 }
464
465 // stick the result in the $questions array
466 if ($qo) {
467 $questions[] = $qo;
468 }
469 }
470
471 return $questions;
472 }
473
474 // EXPORT FUNCTIONS START HERE
475
84769fd8 476 function export_file_extension() {
477 // override default type so extension is .xml
478
479 return ".xml";
480 }
481
c81415c7 482
483 /**
484 * Turn the internal question code into a human readable form
485 * (The code used to be numeric, but this remains as some of
486 * the names don't match the new internal format)
6e557c08 487 * @param mixed type_id Internal code
488 * @return string question type string
c81415c7 489 */
84769fd8 490 function get_qtype( $type_id ) {
84769fd8 491 switch( $type_id ) {
492 case TRUEFALSE:
493 $name = 'truefalse';
494 break;
495 case MULTICHOICE:
496 $name = 'multichoice';
497 break;
498 case SHORTANSWER:
499 $name = 'shortanswer';
500 break;
7b8bc256 501 //case regexp:
502 // $name = 'regexp';
503 // break;
84769fd8 504 case NUMERICAL:
505 $name = 'numerical';
506 break;
507 case MATCH:
508 $name = 'matching';
509 break;
510 case DESCRIPTION:
511 $name = 'description';
512 break;
513 case MULTIANSWER:
514 $name = 'cloze';
515 break;
c81415c7 516 case ESSAY:
517 $name = 'essay';
518 break;
84769fd8 519 default:
520 $name = 'unknown';
521 }
522 return $name;
523 }
524
6e557c08 525 /**
c81415c7 526 * Convert internal Moodle text format code into
527 * human readable form
6e557c08 528 * @param int id internal code
529 * @return string format text
c81415c7 530 */
84769fd8 531 function get_format( $id ) {
84769fd8 532 switch( $id ) {
533 case 0:
534 $name = "moodle_auto_format";
535 break;
536 case 1:
537 $name = "html";
538 break;
539 case 2:
540 $name = "plain_text";
541 break;
542 case 3:
543 $name = "wiki_like";
544 break;
545 case 4:
546 $name = "markdown";
547 break;
548 default:
549 $name = "unknown";
550 }
551 return $name;
552 }
553
6e557c08 554 /**
c81415c7 555 * Convert internal single question code into
556 * human readable form
6e557c08 557 * @param int id single question code
558 * @return string single question string
c81415c7 559 */
84769fd8 560 function get_single( $id ) {
84769fd8 561 switch( $id ) {
562 case 0:
563 $name = "false";
564 break;
565 case 1:
566 $name = "true";
567 break;
568 default:
569 $name = "unknown";
570 }
571 return $name;
572 }
573
6e557c08 574 /**
c81415c7 575 * generates <text></text> tags, processing raw text therein
6e557c08 576 * @param int ilev the current indent level
577 * @param boolean short stick it on one line
578 * @return string formatted text
c81415c7 579 */
84769fd8 580 function writetext( $raw, $ilev=0, $short=true) {
84769fd8 581 $indent = str_repeat( " ",$ilev );
582
583 // encode the text to 'disguise' HTML content
584 $raw = htmlspecialchars( $raw );
585
586 if ($short) {
587 $xml = "$indent<text>$raw</text>\n";
588 }
589 else {
590 $xml = "$indent<text>\n$raw\n$indent</text>\n";
591 }
592
593 return $xml;
594 }
595
596 function presave_process( $content ) {
597 // override method to allow us to add xml headers and footers
598
599 $content = "<?xml version=\"1.0\"?>\n" .
600 "<quiz>\n" .
601 $content . "\n" .
602 "</quiz>";
603
604 return $content;
605 }
606
6e557c08 607 /**
c81415c7 608 * Include an image encoded in base 64
6e557c08 609 * @param string imagepath The location of the image file
610 * @return string xml code segment
c81415c7 611 */
d08e16b2 612 function writeimage( $imagepath ) {
d08e16b2 613 global $CFG;
614
615 if (empty($imagepath)) {
616 return '';
617 }
618
619 $courseid = $this->course->id;
620 if (!$binary = file_get_contents( "{$CFG->dataroot}/$courseid/$imagepath" )) {
621 return '';
622 }
623
624 $content = " <image_base64>\n".addslashes(base64_encode( $binary ))."\n".
625 "\n </image_base64>\n";
626 return $content;
627 }
628
6e557c08 629 /**
c81415c7 630 * Turns question into an xml segment
6e557c08 631 * @param array question question array
632 * @return string xml segment
c81415c7 633 */
84769fd8 634 function writequestion( $question ) {
84769fd8 635 // initial string;
636 $expout = "";
637
638 // add comment
639 $expout .= "\n\n<!-- question: $question->id -->\n";
640
641 // add opening tag
7b8bc256 642 // generates specific header for Cloze type question
643 if ($question->qtype != MULTIANSWER) {
644 // for all question types except Close
645 $question_type = $this->get_qtype( $question->qtype );
646 $name_text = $this->writetext( $question->name );
647 $qtformat = $this->get_format($question->questiontextformat);
648 $question_text = $this->writetext( $question->questiontext );
a4514d91 649 $generalfeedback = $this->writetext( $question->generalfeedback );
7b8bc256 650 $expout .= " <question type=\"$question_type\">\n";
651 $expout .= " <name>$name_text</name>\n";
652 $expout .= " <questiontext format=\"$qtformat\">\n";
653 $expout .= $question_text;
654 $expout .= " </questiontext>\n";
655 $expout .= " <image>{$question->image}</image>\n";
656 $expout .= $this->writeimage($question->image);
a4514d91 657 $expout .= " <generalfeedback>\n";
658 $expout .= $generalfeedback;
659 $expout .= " </generalfeedback>\n";
c81415c7 660 $expout .= " <defaultgrade>{$question->defaultgrade}</defaultgrade>\n";
7b8bc256 661 $expout .= " <penalty>{$question->penalty}</penalty>\n";
662 $expout .= " <hidden>{$question->hidden}</hidden>\n";
663 }
664 else {
665 // for Cloze type only
666 $question_type = $this->get_qtype( $question->qtype );
667 $name_text = $this->writetext( $question->name );
668 $question_text = $this->writetext( $question->questiontext );
669 $expout .= " <question type=\"$question_type\">\n";
670 $expout .= " <name>$name_text</name>\n";
671 $expout .= " <questiontext>\n";
672 $expout .= $question_text;
673 $expout .= " </questiontext>\n";
674 }
675
d08e16b2 676 if (!empty($question->options->shuffleanswers)) {
677 $expout .= " <shuffleanswers>{$question->options->shuffleanswers}</shuffleanswers>\n";
678 }
679 else {
680 $expout .= " <shuffleanswers>0</shuffleanswers>\n";
681 }
84769fd8 682
683 // output depends on question type
684 switch($question->qtype) {
685 case TRUEFALSE:
36e2232e 686 foreach ($question->options->answers as $answer) {
687 $fraction_pc = round( $answer->fraction * 100 );
688 $expout .= " <answer fraction=\"$fraction_pc\">\n";
689 $expout .= $this->writetext(strtolower($answer->answer),3)."\n";
690 $expout .= " <feedback>\n";
691 $expout .= $this->writetext( $answer->feedback,4,false );
692 $expout .= " </feedback>\n";
693 $expout .= " </answer>\n";
694 }
84769fd8 695 break;
696 case MULTICHOICE:
697 $expout .= " <single>".$this->get_single($question->options->single)."</single>\n";
307f045f 698 $expout .= " <shuffleanswers>".$this->get_single($question->options->shuffleanswers)."</shuffleanswers>\n";
699 $expout .= " <correctfeedback>".$this->writetext($question->options->correctfeedback, 3)."</correctfeedback>\n";
700 $expout .= " <partiallycorrectfeedback>".$this->writetext($question->options->partiallycorrectfeedback, 3)."</partiallycorrectfeedback>\n";
701 $expout .= " <incorrectfeedback>".$this->writetext($question->options->incorrectfeedback, 3)."</incorrectfeedback>\n";
84769fd8 702 foreach($question->options->answers as $answer) {
703 $percent = $answer->fraction * 100;
704 $expout .= " <answer fraction=\"$percent\">\n";
705 $expout .= $this->writetext( $answer->answer,4,false );
706 $expout .= " <feedback>\n";
6e557c08 707 $expout .= $this->writetext( $answer->feedback,5,false );
84769fd8 708 $expout .= " </feedback>\n";
709 $expout .= " </answer>\n";
710 }
711 break;
712 case SHORTANSWER:
713 $expout .= " <usecase>{$question->options->usecase}</usecase>\n ";
714 foreach($question->options->answers as $answer) {
715 $percent = 100 * $answer->fraction;
716 $expout .= " <answer fraction=\"$percent\">\n";
717 $expout .= $this->writetext( $answer->answer,3,false );
718 $expout .= " <feedback>\n";
719 $expout .= $this->writetext( $answer->feedback,4,false );
720 $expout .= " </feedback>\n";
721 $expout .= " </answer>\n";
722 }
723 break;
7b8bc256 724 //case regexp:
725 //$expout .= " <usecase>{$question->options->usecase}</usecase>\n ";
726 // foreach($question->options->answers as $answer) {
727 // $percent = 100 * $answer->fraction;
728 // $expout .= " <answer fraction=\"$percent\">\n";
729 // $expout .= $this->writetext( $answer->answer,3,false );
730 // $expout .= " <feedback>\n";
731 // $expout .= $this->writetext( $answer->feedback,4,false );
732 // $expout .= " </feedback>\n";
733 // $expout .= " </answer>\n";
734 // }
735 // break;
84769fd8 736 case NUMERICAL:
737 foreach ($question->options->answers as $answer) {
738 $tolerance = $answer->tolerance;
739 $expout .= "<answer>\n";
740 $expout .= " {$answer->answer}\n";
741 $expout .= " <tolerance>$tolerance</tolerance>\n";
742 $expout .= " <feedback>".$this->writetext( $answer->feedback )."</feedback>\n";
743 $expout .= " <fraction>{$answer->fraction}</fraction>\n";
744 $expout .= "</answer>\n";
745 }
746
747 $units = $question->options->units;
748 if (count($units)) {
749 $expout .= "<units>\n";
750 foreach ($units as $unit) {
751 $expout .= " <unit>\n";
752 $expout .= " <multiplier>{$unit->multiplier}</multiplier>\n";
753 $expout .= " <unit_name>{$unit->unit}</unit_name>\n";
754 $expout .= " </unit>\n";
755 }
756 $expout .= "</units>\n";
757 }
758 break;
759 case MATCH:
760 foreach($question->options->subquestions as $subquestion) {
761 $expout .= "<subquestion>\n";
762 $expout .= $this->writetext( $subquestion->questiontext );
763 $expout .= "<answer>".$this->writetext( $subquestion->answertext )."</answer>\n";
764 $expout .= "</subquestion>\n";
765 }
766 break;
767 case DESCRIPTION:
c81415c7 768 // nothing more to do for this type
84769fd8 769 break;
770 case MULTIANSWER:
7b8bc256 771 $a_count=1;
772 foreach($question->options->questions as $question) {
773 $thispattern = addslashes("{#".$a_count."}");
774 $thisreplace = $question->questiontext;
775 $expout=ereg_replace($thispattern, $thisreplace, $expout );
776 $a_count++;
777 }
778 break;
c81415c7 779 case ESSAY:
780 foreach ($question->options->answers as $answer) {
781 $expout .= "<answer>\n";
782 $expout .= " <feedback>".$this->writetext( $answer->feedback )."</feedback>\n";
783 $expout .= " <fraction>{$answer->fraction}</fraction>\n";
784 $expout .= "</answer>\n";
785 }
786
787 break;
84769fd8 788 default:
789 $expout .= "<!-- Question type is unknown or not supported (Type=$question->qtype) -->\n";
790 }
791
792 // close the question tag
793 $expout .= "</question>\n";
794
84769fd8 795 return $expout;
796 }
797}
798
799?>