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 | |
23 | function trans_format( $name ) { |
24 | // translate text format string to its internal code |
25 | |
26 | $name = trim($name); |
27 | |
28 | if ($name=='moodle_auto_format') { |
29 | $id = 0; |
30 | } |
31 | elseif ($name=='html') { |
32 | $id = 1; |
33 | } |
34 | elseif ($name=='plain_text') { |
35 | $id = 2; |
36 | } |
37 | elseif ($name=='wiki_like') { |
38 | $id = 3; |
39 | } |
40 | elseif ($name=='markdown') { |
41 | $id = 4; |
42 | } |
43 | else { |
44 | $id = 0; // or maybe warning required |
45 | } |
46 | return $id; |
47 | } |
48 | |
49 | function trans_single( $name ) { |
50 | // translate single string to its internal format |
51 | |
52 | $name = trim($name); |
53 | |
54 | if ($name=="true") { |
55 | $id = 1; |
56 | } |
57 | elseif ($name=="false") { |
58 | $id = 0; |
59 | } |
60 | else { |
61 | $id = 0; // or maybe warning required |
62 | } |
63 | return $id; |
64 | } |
65 | |
66 | function import_text( $text ) { |
67 | // handle xml 'text' element |
68 | $data = $text[0]['#']; |
69 | $data = html_entity_decode( $data ); |
70 | return addslashes(trim( $data )); |
71 | } |
72 | |
73 | function import_headers( $question ) { |
74 | // read bits that are common to all questions |
75 | |
76 | // this routine initialises the question object |
77 | $name = $this->import_text( $question['#']['name'][0]['#']['text'] ); |
78 | $qtext = $this->import_text( $question['#']['questiontext'][0]['#']['text'] ); |
79 | $qformat = $question['#']['questiontext'][0]['@']['format']; |
80 | $image = $question['#']['image'][0]['#']; |
d08e16b2 |
81 | if (!empty($question['#']['image_base64'][0]['#'])) { |
82 | $image_base64 = stripslashes( trim( $question['#']['image_base64'][0]['#'] ) ); |
83 | $image = $this->importimagefile( $image, $image_base64 ); |
84 | } |
1b8a7434 |
85 | if (array_key_exists('commentarytext', $question['#'])) { |
86 | $commentarytext = $this->import_text( $question['#']['commentarytext'][0]['#']['text'] ); |
87 | } else { |
88 | $commentarytext = ''; |
89 | } |
84769fd8 |
90 | $penalty = $question['#']['penalty'][0]['#']; |
91 | |
51bcdf28 |
92 | $qo = $this->defaultquestion(); |
84769fd8 |
93 | $qo->name = $name; |
94 | $qo->questiontext = $qtext; |
95 | $qo->questiontextformat = $this->trans_format( $qformat ); |
96 | $qo->image = ((!empty($image)) ? $image : ''); |
1b8a7434 |
97 | $qo->commentarytext = $commentarytext; |
84769fd8 |
98 | $qo->penalty = $penalty; |
99 | |
100 | return $qo; |
101 | } |
102 | |
103 | |
104 | function import_answer( $answer ) { |
105 | // import answer part of question |
106 | |
107 | $fraction = $answer['@']['fraction']; |
108 | $text = $this->import_text( $answer['#']['text']); |
109 | $feedback = $this->import_text( $answer['#']['feedback'][0]['#']['text'] ); |
110 | |
111 | $ans = null; |
112 | $ans->answer = $text; |
113 | $ans->fraction = $fraction / 100; |
114 | $ans->feedback = $feedback; |
115 | |
116 | return $ans; |
117 | } |
118 | |
119 | function import_multichoice( $question ) { |
120 | // import multichoice type questions |
121 | |
122 | // get common parts |
123 | $qo = $this->import_headers( $question ); |
124 | |
125 | // 'header' parts particular to multichoice |
126 | $qo->qtype = MULTICHOICE; |
127 | $single = $question['#']['single'][0]['#']; |
128 | $qo->single = $this->trans_single( $single ); |
129 | |
130 | // run through the answers |
131 | $answers = $question['#']['answer']; |
132 | $a_count = 0; |
133 | foreach ($answers as $answer) { |
134 | $ans = $this->import_answer( $answer ); |
135 | $qo->answer[$a_count] = $ans->answer; |
136 | $qo->fraction[$a_count] = $ans->fraction; |
137 | $qo->feedback[$a_count] = $ans->feedback; |
138 | ++$a_count; |
139 | } |
140 | |
141 | return $qo; |
142 | } |
143 | |
7b8bc256 |
144 | function import_multianswer( $questions ) { |
145 | // import multianswer (cloze) type questions |
146 | |
147 | $qo = qtype_multianswer_extract_question($this->import_text( |
148 | $questions['#']['questiontext'][0]['#']['text'] )); |
149 | |
150 | // 'header' parts particular to multianswer |
151 | $qo->qtype = MULTIANSWER; |
152 | $qo->course = $this->course; |
153 | |
71ffbac2 |
154 | if (!empty($questions)) { |
7b8bc256 |
155 | $qo->name = $this->import_text( $questions['#']['name'][0]['#']['text'] ); |
156 | } |
157 | |
158 | return $qo; |
159 | } |
160 | |
84769fd8 |
161 | function import_truefalse( $question ) { |
162 | // import true/false type question |
163 | |
164 | // get common parts |
165 | $qo = $this->import_headers( $question ); |
166 | |
167 | // 'header' parts particular to true/false |
168 | $qo->qtype = TRUEFALSE; |
169 | |
170 | // get answer info |
171 | $answers = $question['#']['answer']; |
172 | $fraction0 = $answers[0]['@']['fraction']; |
173 | $feedback0 = $this->import_text($answers[0]['#']['feedback'][0]['#']['text']); |
174 | $fraction1 = $answers[1]['@']['fraction']; |
175 | $feedback1 = $this->import_text($answers[1]['#']['feedback'][0]['#']['text']); |
176 | |
177 | // sort out which is true and build object accordingly |
178 | if ($fraction0==100) { // then 0 index is true |
179 | $qo->answer = 1; |
180 | $qo->feedbacktrue=$feedback0; |
181 | $qo->feedbackfalse=$feedback1; |
182 | } |
183 | else { |
184 | $qo->answer = 0; |
185 | $qo->feedbacktrue = $feedback1; |
186 | $qo->feedbackfalse = $feedback0; |
187 | } |
188 | |
189 | return $qo; |
190 | } |
191 | |
192 | function import_shortanswer( $question ) { |
193 | // import short answer question |
194 | |
195 | // get common parts |
196 | $qo = $this->import_headers( $question ); |
197 | |
198 | // header parts particular to shortanswer |
199 | $qo->qtype = SHORTANSWER; |
200 | |
201 | // get usecase |
202 | $qo->usecase = $question['#']['usecase'][0]['#']; |
203 | |
204 | // run through the answers |
205 | $answers = $question['#']['answer']; |
206 | $a_count = 0; |
207 | foreach ($answers as $answer) { |
208 | $ans = $this->import_answer( $answer ); |
209 | $qo->answer[$a_count] = $ans->answer; |
210 | $qo->fraction[$a_count] = $ans->fraction; |
211 | $qo->feedback[$a_count] = $ans->feedback; |
212 | ++$a_count; |
213 | } |
214 | |
215 | return $qo; |
216 | } |
7b8bc256 |
217 | |
218 | function import_regexp( $question ) { |
219 | // import short answer question |
220 | |
221 | // get common parts |
222 | $qo = $this->import_headers( $question ); |
223 | |
224 | // header parts particular to shortanswer |
225 | $qo->qtype = regexp; |
226 | |
227 | // get usecase |
228 | $qo->usecase = $question['#']['usecase'][0]['#']; |
229 | |
230 | // run through the answers |
231 | $answers = $question['#']['answer']; |
232 | $a_count = 0; |
233 | foreach ($answers as $answer) { |
234 | $ans = $this->import_answer( $answer ); |
235 | $qo->answer[$a_count] = $ans->answer; |
236 | $qo->fraction[$a_count] = $ans->fraction; |
237 | $qo->feedback[$a_count] = $ans->feedback; |
238 | ++$a_count; |
239 | } |
84769fd8 |
240 | |
7b8bc256 |
241 | return $qo; |
242 | } |
243 | |
244 | function import_description( $question ) { |
245 | // get common parts |
246 | $qo = $this->import_headers( $question ); |
247 | // header parts particular to shortanswer |
248 | $qo->qtype = DESCRIPTION; |
249 | return $qo; |
250 | } |
84769fd8 |
251 | function import_numerical( $question ) { |
252 | // import numerical question |
253 | |
254 | // get common parts |
255 | $qo = $this->import_headers( $question ); |
256 | |
257 | // header parts particular to numerical |
258 | $qo->qtype = NUMERICAL; |
259 | |
260 | // get answers array |
261 | $answers = $question['#']['answer']; |
262 | $qo->answer = array(); |
263 | $qo->feedback = array(); |
264 | $qo->fraction = array(); |
265 | $qo->tolerance = array(); |
266 | foreach ($answers as $answer) { |
267 | $qo->answer[] = $answer['#'][0]; |
a0d187bf |
268 | $qo->feedback[] = $this->import_text( $answer['#']['feedback'][0]['#']['text'] ); |
84769fd8 |
269 | $qo->fraction[] = $answer['#']['fraction'][0]['#']; |
270 | $qo->tolerance[] = $answer['#']['tolerance'][0]['#']; |
271 | } |
272 | |
273 | // get units array |
84769fd8 |
274 | $qo->unit = array(); |
a0d187bf |
275 | if (isset($question['#']['units'][0]['#']['unit'])) { |
276 | $units = $question['#']['units'][0]['#']['unit']; |
277 | $qo->multiplier = array(); |
278 | foreach ($units as $unit) { |
279 | $qo->multiplier[] = $unit['#']['multiplier'][0]['#']; |
280 | $qo->unit[] = $unit['#']['unit_name'][0]['#']; |
281 | } |
84769fd8 |
282 | } |
84769fd8 |
283 | return $qo; |
284 | } |
285 | |
51bcdf28 |
286 | function import_matching( $question ) { |
287 | // import matching question |
288 | |
289 | // get common parts |
290 | $qo = $this->import_headers( $question ); |
291 | |
292 | // header parts particular to matching |
293 | $qo->qtype = MATCH; |
ee800653 |
294 | if (!empty($question['#']['shuffleanswers'])) { |
295 | $qo->shuffleanswers = $question['#']['shuffleanswers'][0]['#']; |
296 | } else { |
297 | $qo->shuffleanswers = false; |
298 | } |
51bcdf28 |
299 | |
300 | // get subquestions |
301 | $subquestions = $question['#']['subquestion']; |
302 | $qo->subquestions = array(); |
303 | $qo->subanswers = array(); |
304 | |
305 | // run through subquestions |
306 | foreach ($subquestions as $subquestion) { |
a0d187bf |
307 | $qtext = $this->import_text( $subquestion['#']['text'] ); |
308 | $atext = $this->import_text( $subquestion['#']['answer'][0]['#']['text'] ); |
51bcdf28 |
309 | $qo->subquestions[] = $qtext; |
310 | $qo->subanswers[] = $atext; |
311 | } |
51bcdf28 |
312 | return $qo; |
313 | } |
314 | |
84769fd8 |
315 | function readquestions($lines) { |
316 | // parse the array of lines into an array of questions |
317 | // this *could* burn memory - but it won't happen that much |
318 | // so fingers crossed! |
319 | |
320 | // we just need it as one big string |
321 | $text = implode($lines, " "); |
322 | unset( $lines ); |
323 | |
324 | // this converts xml to big nasty data structure |
325 | // the 0 means keep white space as it is (important for markdown format) |
326 | // print_r it if you want to see what it looks like! |
327 | $xml = xmlize( $text, 0 ); |
328 | |
329 | // set up array to hold all our questions |
330 | $questions = array(); |
331 | |
332 | // iterate through questions |
333 | foreach ($xml['quiz']['#']['question'] as $question) { |
334 | $question_type = $question['@']['type']; |
335 | $questiontype = get_string( 'questiontype','quiz',$question_type ); |
c515fcf7 |
336 | // echo "<p>$questiontype</p>"; |
84769fd8 |
337 | |
338 | if ($question_type=='multichoice') { |
339 | $qo = $this->import_multichoice( $question ); |
340 | } |
341 | elseif ($question_type=='truefalse') { |
342 | $qo = $this->import_truefalse( $question ); |
343 | } |
344 | elseif ($question_type=='shortanswer') { |
345 | $qo = $this->import_shortanswer( $question ); |
346 | } |
7b8bc256 |
347 | //elseif ($question_type=='regexp') { |
348 | // $qo = $this->import_regexp( $question ); |
349 | //} |
84769fd8 |
350 | elseif ($question_type=='numerical') { |
351 | $qo = $this->import_numerical( $question ); |
352 | } |
7b8bc256 |
353 | elseif ($question_type=='description') { |
354 | $qo = $this->import_description( $question ); |
355 | } |
51bcdf28 |
356 | elseif ($question_type=='matching') { |
357 | $qo = $this->import_matching( $question ); |
358 | } |
7b8bc256 |
359 | elseif ($question_type=='cloze') { |
360 | $qo = $this->import_multianswer( $question ); |
361 | } |
84769fd8 |
362 | else { |
ee800653 |
363 | $notsupported = get_string( 'xmltypeunsupported','quiz',$question_type ); |
84769fd8 |
364 | echo "<p>$notsupported</p>"; |
365 | $qo = null; |
366 | } |
367 | |
368 | // stick the result in the $questions array |
369 | if ($qo) { |
370 | $questions[] = $qo; |
371 | } |
372 | } |
373 | |
374 | return $questions; |
375 | } |
376 | |
377 | // EXPORT FUNCTIONS START HERE |
378 | |
379 | function indent_xhtml($source, $indenter = ' ') { |
380 | // xml tidier-upper |
381 | // (c) Ari Koivula http://ventionline.com |
382 | |
383 | // Remove all pre-existing formatting. |
384 | // Remove all newlines. |
385 | $source = str_replace("\n", '', $source); |
386 | $source = str_replace("\r", '', $source); |
387 | // Remove all tabs. |
388 | $source = str_replace("\t", '', $source); |
389 | // Remove all space after ">" and before "<". |
390 | $source = ereg_replace(">( )*", ">", $source); |
391 | $source = ereg_replace("( )*<", "<", $source); |
392 | |
393 | // Iterate through the source. |
394 | $level = 0; |
395 | $source_len = strlen($source); |
396 | $pt = 0; |
397 | while ($pt < $source_len) { |
398 | if ($source{$pt} === '<') { |
399 | // We have entered a tag. |
400 | // Remember the point where the tag starts. |
401 | $started_at = $pt; |
402 | $tag_level = 1; |
403 | // If the second letter of the tag is "/", assume its an ending tag. |
404 | if ($source{$pt+1} === '/') { |
405 | $tag_level = -1; |
406 | } |
407 | // If the second letter of the tag is "!", assume its an "invisible" tag. |
408 | if ($source{$pt+1} === '!') { |
409 | $tag_level = 0; |
410 | } |
411 | // Iterate throught the source until the end of tag. |
412 | while ($source{$pt} !== '>') { |
413 | $pt++; |
414 | } |
415 | // If the second last letter is "/", assume its a self ending tag. |
416 | if ($source{$pt-1} === '/') { |
417 | $tag_level = 0; |
418 | } |
419 | $tag_lenght = $pt+1-$started_at; |
420 | |
421 | // Decide the level of indention for this tag. |
422 | // If this was an ending tag, decrease indent level for this tag.. |
423 | if ($tag_level === -1) { |
424 | $level--; |
425 | } |
426 | // Place the tag in an array with proper indention. |
427 | $array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght); |
428 | // If this was a starting tag, increase the indent level after this tag. |
429 | if ($tag_level === 1) { |
430 | $level++; |
431 | } |
432 | // if it was a self closing tag, dont do shit. |
433 | } |
434 | // Were out of the tag. |
435 | // If next letter exists... |
436 | if (($pt+1) < $source_len) { |
437 | // ... and its not an "<". |
438 | if ($source{$pt+1} !== '<') { |
439 | $started_at = $pt+1; |
440 | // Iterate through the source until the start of new tag or until we reach the end of file. |
441 | while ($source{$pt} !== '<' && $pt < $source_len) { |
442 | $pt++; |
443 | } |
444 | // If we found a "<" (we didnt find the end of file) |
445 | if ($source{$pt} === '<') { |
446 | $tag_lenght = $pt-$started_at; |
447 | // Place the stuff in an array with proper indention. |
448 | $array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght); |
449 | } |
450 | // If the next tag is "<", just advance pointer and let the tag indenter take care of it. |
451 | } else { |
452 | $pt++; |
453 | } |
454 | // If the next letter doesnt exist... Were done... well, almost.. |
455 | } else { |
456 | break; |
457 | } |
458 | } |
459 | // Replace old source with the new one we just collected into our array. |
460 | $source = implode($array, "\n"); |
461 | return $source; |
462 | } |
463 | |
464 | |
465 | function export_file_extension() { |
466 | // override default type so extension is .xml |
467 | |
468 | return ".xml"; |
469 | } |
470 | |
471 | function get_qtype( $type_id ) { |
472 | // translates question type code number into actual name |
473 | |
474 | switch( $type_id ) { |
475 | case TRUEFALSE: |
476 | $name = 'truefalse'; |
477 | break; |
478 | case MULTICHOICE: |
479 | $name = 'multichoice'; |
480 | break; |
481 | case SHORTANSWER: |
482 | $name = 'shortanswer'; |
483 | break; |
7b8bc256 |
484 | //case regexp: |
485 | // $name = 'regexp'; |
486 | // break; |
84769fd8 |
487 | case NUMERICAL: |
488 | $name = 'numerical'; |
489 | break; |
490 | case MATCH: |
491 | $name = 'matching'; |
492 | break; |
493 | case DESCRIPTION: |
494 | $name = 'description'; |
495 | break; |
496 | case MULTIANSWER: |
497 | $name = 'cloze'; |
498 | break; |
499 | default: |
500 | $name = 'unknown'; |
501 | } |
502 | return $name; |
503 | } |
504 | |
505 | function get_format( $id ) { |
506 | // translates question text format id number into something sensible |
507 | |
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 | |
530 | function get_single( $id ) { |
531 | // translate single value into something sensible |
532 | |
533 | switch( $id ) { |
534 | case 0: |
535 | $name = "false"; |
536 | break; |
537 | case 1: |
538 | $name = "true"; |
539 | break; |
540 | default: |
541 | $name = "unknown"; |
542 | } |
543 | return $name; |
544 | } |
545 | |
546 | function writetext( $raw, $ilev=0, $short=true) { |
547 | // generates <text></text> tags, processing raw text therein |
548 | // $ilev is the current indent level |
549 | // $short=true sticks it on one line |
550 | $indent = str_repeat( " ",$ilev ); |
551 | |
552 | // encode the text to 'disguise' HTML content |
553 | $raw = htmlspecialchars( $raw ); |
554 | |
555 | if ($short) { |
556 | $xml = "$indent<text>$raw</text>\n"; |
557 | } |
558 | else { |
559 | $xml = "$indent<text>\n$raw\n$indent</text>\n"; |
560 | } |
561 | |
562 | return $xml; |
563 | } |
564 | |
565 | function presave_process( $content ) { |
566 | // override method to allow us to add xml headers and footers |
567 | |
568 | $content = "<?xml version=\"1.0\"?>\n" . |
569 | "<quiz>\n" . |
570 | $content . "\n" . |
571 | "</quiz>"; |
572 | |
573 | return $content; |
574 | } |
575 | |
d08e16b2 |
576 | function writeimage( $imagepath ) { |
577 | // includes image in base64 |
578 | global $CFG; |
579 | |
580 | if (empty($imagepath)) { |
581 | return ''; |
582 | } |
583 | |
584 | $courseid = $this->course->id; |
585 | if (!$binary = file_get_contents( "{$CFG->dataroot}/$courseid/$imagepath" )) { |
586 | return ''; |
587 | } |
588 | |
589 | $content = " <image_base64>\n".addslashes(base64_encode( $binary ))."\n". |
590 | "\n </image_base64>\n"; |
591 | return $content; |
592 | } |
593 | |
84769fd8 |
594 | function writequestion( $question ) { |
595 | // turns question into string |
596 | // question reflects database fields for general question and specific to type |
597 | |
598 | // initial string; |
599 | $expout = ""; |
600 | |
601 | // add comment |
602 | $expout .= "\n\n<!-- question: $question->id -->\n"; |
603 | |
604 | // add opening tag |
7b8bc256 |
605 | // generates specific header for Cloze type question |
606 | if ($question->qtype != MULTIANSWER) { |
607 | // for all question types except Close |
608 | $question_type = $this->get_qtype( $question->qtype ); |
609 | $name_text = $this->writetext( $question->name ); |
610 | $qtformat = $this->get_format($question->questiontextformat); |
611 | $question_text = $this->writetext( $question->questiontext ); |
1b8a7434 |
612 | $commentary_text = $this->writetext( $question->commentarytext ); |
7b8bc256 |
613 | $expout .= " <question type=\"$question_type\">\n"; |
614 | $expout .= " <name>$name_text</name>\n"; |
615 | $expout .= " <questiontext format=\"$qtformat\">\n"; |
616 | $expout .= $question_text; |
617 | $expout .= " </questiontext>\n"; |
618 | $expout .= " <image>{$question->image}</image>\n"; |
619 | $expout .= $this->writeimage($question->image); |
1b8a7434 |
620 | $expout .= " <commentarytext>\n"; |
621 | $expout .= $commentary_text; |
622 | $expout .= " </commentarytext>\n"; |
7b8bc256 |
623 | $expout .= " <penalty>{$question->penalty}</penalty>\n"; |
624 | $expout .= " <hidden>{$question->hidden}</hidden>\n"; |
625 | } |
626 | else { |
627 | // for Cloze type only |
628 | $question_type = $this->get_qtype( $question->qtype ); |
629 | $name_text = $this->writetext( $question->name ); |
630 | $question_text = $this->writetext( $question->questiontext ); |
631 | $expout .= " <question type=\"$question_type\">\n"; |
632 | $expout .= " <name>$name_text</name>\n"; |
633 | $expout .= " <questiontext>\n"; |
634 | $expout .= $question_text; |
635 | $expout .= " </questiontext>\n"; |
636 | } |
637 | |
d08e16b2 |
638 | if (!empty($question->options->shuffleanswers)) { |
639 | $expout .= " <shuffleanswers>{$question->options->shuffleanswers}</shuffleanswers>\n"; |
640 | } |
641 | else { |
642 | $expout .= " <shuffleanswers>0</shuffleanswers>\n"; |
643 | } |
84769fd8 |
644 | |
645 | // output depends on question type |
646 | switch($question->qtype) { |
647 | case TRUEFALSE: |
36e2232e |
648 | foreach ($question->options->answers as $answer) { |
649 | $fraction_pc = round( $answer->fraction * 100 ); |
650 | $expout .= " <answer fraction=\"$fraction_pc\">\n"; |
651 | $expout .= $this->writetext(strtolower($answer->answer),3)."\n"; |
652 | $expout .= " <feedback>\n"; |
653 | $expout .= $this->writetext( $answer->feedback,4,false ); |
654 | $expout .= " </feedback>\n"; |
655 | $expout .= " </answer>\n"; |
656 | } |
84769fd8 |
657 | break; |
658 | case MULTICHOICE: |
659 | $expout .= " <single>".$this->get_single($question->options->single)."</single>\n"; |
660 | foreach($question->options->answers as $answer) { |
661 | $percent = $answer->fraction * 100; |
662 | $expout .= " <answer fraction=\"$percent\">\n"; |
663 | $expout .= $this->writetext( $answer->answer,4,false ); |
664 | $expout .= " <feedback>\n"; |
665 | $expout .= $this->writetext( $answer->feedback,4,false ); |
666 | $expout .= " </feedback>\n"; |
667 | $expout .= " </answer>\n"; |
668 | } |
669 | break; |
670 | case SHORTANSWER: |
671 | $expout .= " <usecase>{$question->options->usecase}</usecase>\n "; |
672 | foreach($question->options->answers as $answer) { |
673 | $percent = 100 * $answer->fraction; |
674 | $expout .= " <answer fraction=\"$percent\">\n"; |
675 | $expout .= $this->writetext( $answer->answer,3,false ); |
676 | $expout .= " <feedback>\n"; |
677 | $expout .= $this->writetext( $answer->feedback,4,false ); |
678 | $expout .= " </feedback>\n"; |
679 | $expout .= " </answer>\n"; |
680 | } |
681 | break; |
7b8bc256 |
682 | //case regexp: |
683 | //$expout .= " <usecase>{$question->options->usecase}</usecase>\n "; |
684 | // foreach($question->options->answers as $answer) { |
685 | // $percent = 100 * $answer->fraction; |
686 | // $expout .= " <answer fraction=\"$percent\">\n"; |
687 | // $expout .= $this->writetext( $answer->answer,3,false ); |
688 | // $expout .= " <feedback>\n"; |
689 | // $expout .= $this->writetext( $answer->feedback,4,false ); |
690 | // $expout .= " </feedback>\n"; |
691 | // $expout .= " </answer>\n"; |
692 | // } |
693 | // break; |
84769fd8 |
694 | case NUMERICAL: |
695 | foreach ($question->options->answers as $answer) { |
696 | $tolerance = $answer->tolerance; |
697 | $expout .= "<answer>\n"; |
698 | $expout .= " {$answer->answer}\n"; |
699 | $expout .= " <tolerance>$tolerance</tolerance>\n"; |
700 | $expout .= " <feedback>".$this->writetext( $answer->feedback )."</feedback>\n"; |
701 | $expout .= " <fraction>{$answer->fraction}</fraction>\n"; |
702 | $expout .= "</answer>\n"; |
703 | } |
704 | |
705 | $units = $question->options->units; |
706 | if (count($units)) { |
707 | $expout .= "<units>\n"; |
708 | foreach ($units as $unit) { |
709 | $expout .= " <unit>\n"; |
710 | $expout .= " <multiplier>{$unit->multiplier}</multiplier>\n"; |
711 | $expout .= " <unit_name>{$unit->unit}</unit_name>\n"; |
712 | $expout .= " </unit>\n"; |
713 | } |
714 | $expout .= "</units>\n"; |
715 | } |
716 | break; |
717 | case MATCH: |
718 | foreach($question->options->subquestions as $subquestion) { |
719 | $expout .= "<subquestion>\n"; |
720 | $expout .= $this->writetext( $subquestion->questiontext ); |
721 | $expout .= "<answer>".$this->writetext( $subquestion->answertext )."</answer>\n"; |
722 | $expout .= "</subquestion>\n"; |
723 | } |
724 | break; |
725 | case DESCRIPTION: |
7b8bc256 |
726 | // nothing more to do for theis type |
84769fd8 |
727 | break; |
728 | case MULTIANSWER: |
7b8bc256 |
729 | $a_count=1; |
730 | foreach($question->options->questions as $question) { |
731 | $thispattern = addslashes("{#".$a_count."}"); |
732 | $thisreplace = $question->questiontext; |
733 | $expout=ereg_replace($thispattern, $thisreplace, $expout ); |
734 | $a_count++; |
735 | } |
736 | break; |
84769fd8 |
737 | default: |
738 | $expout .= "<!-- Question type is unknown or not supported (Type=$question->qtype) -->\n"; |
739 | } |
740 | |
741 | // close the question tag |
742 | $expout .= "</question>\n"; |
743 | |
744 | // run through xml tidy function |
745 | // $tidy_expout = $this->indent_xhtml( $expout, ' ' ) . "\n\n"; |
746 | |
747 | return $expout; |
748 | } |
749 | } |
750 | |
751 | ?> |