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