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