Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / question / format / blackboard_6 / format.php
blob0cb6cbdeb8eeebd6e9d6811ed24cc415ff167aaa
1 <?php
3 ////////////////////////////////////////////////////////////////////////////
4 /// Blackboard 6.x Format
5 ///
6 /// This Moodle class provides all functions necessary to import and export
7 ///
8 ///
9 ////////////////////////////////////////////////////////////////////////////
11 // Based on default.php, included by ../import.php
12 /**
13 * @package questionbank
14 * @subpackage importexport
16 require_once ("$CFG->libdir/xmlize.php");
18 class qformat_blackboard_6 extends qformat_default {
19 function provide_import() {
20 return true;
24 //Function to check and create the needed dir to unzip file to
25 function check_and_create_import_dir($unique_code) {
27 global $CFG;
29 $status = $this->check_dir_exists($CFG->dataroot."/temp",true);
30 if ($status) {
31 $status = $this->check_dir_exists($CFG->dataroot."/temp/bbquiz_import",true);
33 if ($status) {
34 $status = $this->check_dir_exists($CFG->dataroot."/temp/bbquiz_import/".$unique_code,true);
37 return $status;
40 function clean_temp_dir($dir='') {
41 // for now we will just say everything happened okay note
42 // that a mess may be piling up in $CFG->dataroot/temp/bbquiz_import
43 return true;
45 if ($dir == '') {
46 $dir = $this->temp_dir;
48 $slash = "/";
50 // Create arrays to store files and directories
51 $dir_files = array();
52 $dir_subdirs = array();
54 // Make sure we can delete it
55 chmod($dir, 0777);
57 if ((($handle = opendir($dir))) == FALSE) {
58 // The directory could not be opened
59 return false;
62 // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
63 while($entry = readdir($handle)) {
64 if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != ".") {
65 $dir_subdirs[] = $dir. $slash .$entry;
67 else if ($entry != ".." && $entry != ".") {
68 $dir_files[] = $dir. $slash .$entry;
72 // Delete all files in the curent directory return false and halt if a file cannot be removed
73 for($i=0; $i<count($dir_files); $i++) {
74 chmod($dir_files[$i], 0777);
75 if (((unlink($dir_files[$i]))) == FALSE) {
76 return false;
80 // Empty sub directories and then remove the directory
81 for($i=0; $i<count($dir_subdirs); $i++) {
82 chmod($dir_subdirs[$i], 0777);
83 if ($this->clean_temp_dir($dir_subdirs[$i]) == FALSE) {
84 return false;
86 else {
87 if (rmdir($dir_subdirs[$i]) == FALSE) {
88 return false;
93 // Close directory
94 closedir($handle);
95 if (rmdir($this->temp_dir) == FALSE) {
96 return false;
98 // Success, every thing is gone return true
99 return true;
102 //Function to check if a directory exists and, optionally, create it
103 function check_dir_exists($dir,$create=false) {
105 global $CFG;
107 $status = true;
108 if(!is_dir($dir)) {
109 if (!$create) {
110 $status = false;
111 } else {
112 umask(0000);
113 $status = mkdir ($dir,$CFG->directorypermissions);
116 return $status;
119 function importpostprocess() {
120 /// Does any post-processing that may be desired
121 /// Argument is a simple array of question ids that
122 /// have just been added.
124 // need to clean up temporary directory
125 return $this->clean_temp_dir();
128 function copy_file_to_course($filename) {
129 global $CFG, $COURSE;
130 $filename = str_replace('\\','/',$filename);
131 $fullpath = $this->temp_dir.'/res00001/'.$filename;
132 $basename = basename($filename);
134 $copy_to = $CFG->dataroot.'/'.$COURSE->id.'/bb_import';
136 if ($this->check_dir_exists($copy_to,true)) {
137 if(is_readable($fullpath)) {
138 $copy_to.= '/'.$basename;
139 if (!copy($fullpath, $copy_to)) {
140 return false;
142 else {
143 return $copy_to;
147 else {
148 return false;
152 function readdata($filename) {
153 /// Returns complete file with an array, one item per line
154 global $CFG;
156 $unique_code = time();
157 $temp_dir = $CFG->dataroot."/temp/bbquiz_import/".$unique_code;
158 $this->temp_dir = $temp_dir;
159 if ($this->check_and_create_import_dir($unique_code)) {
160 if(is_readable($filename)) {
161 if (!copy($filename, "$temp_dir/bboard.zip")) {
162 error("Could not copy backup file");
164 if(unzip_file("$temp_dir/bboard.zip", '', false)) {
165 // assuming that the information is in res0001.dat
166 // after looking at 6 examples this was always the case
167 $q_file = "$temp_dir/res00001.dat";
168 if (is_file($q_file)) {
169 if (is_readable($q_file)) {
170 $filearray = file($q_file);
171 /// Check for Macintosh OS line returns (ie file on one line), and fix
172 if (ereg("\r", $filearray[0]) AND !ereg("\n", $filearray[0])) {
173 return explode("\r", $filearray[0]);
174 } else {
175 return $filearray;
177 return false;
180 else {
181 error("Could not find question data file in zip");
184 else {
185 print "filename: $filename<br />tempdir: $temp_dir <br />";
186 error("Could not unzip file.");
189 else {
190 error ("Could not read uploaded file");
193 else {
194 error("Could not create temporary directory");
198 function save_question_options($question) {
199 return true;
204 function readquestions ($lines) {
205 /// Parses an array of lines into an array of questions,
206 /// where each item is a question object as defined by
207 /// readquestion().
209 $text = implode($lines, " ");
210 $xml = xmlize($text, 0);
212 $raw_questions = $xml['questestinterop']['#']['assessment'][0]['#']['section'][0]['#']['item'];
213 $questions = array();
215 foreach($raw_questions as $quest) {
216 $question = $this->create_raw_question($quest);
218 switch($question->qtype) {
219 case "Matching":
220 $this->process_matching($question, $questions);
221 break;
222 case "Multiple Choice":
223 $this->process_mc($question, $questions);
224 break;
225 case "Essay":
226 $this->process_essay($question, $questions);
227 break;
228 case "Multiple Answer":
229 $this->process_ma($question, $questions);
230 break;
231 case "True/False":
232 $this->process_tf($question, $questions);
233 break;
234 case 'Fill in the Blank':
235 $this->process_fblank($question, $questions);
236 break;
237 case 'Short Response':
238 $this->process_essay($question, $questions);
239 break;
240 default:
241 print "Unknown or unhandled question type: \"$question->qtype\"<br />";
242 break;
246 return $questions;
250 // creates a cleaner object to deal with for processing into moodle
251 // the object created is NOT a moodle question object
252 function create_raw_question($quest) {
254 $question = new StdClass;
255 $question->qtype = $quest['#']['itemmetadata'][0]['#']['bbmd_questiontype'][0]['#'];
256 $question->id = $quest['#']['itemmetadata'][0]['#']['bbmd_asi_object_id'][0]['#'];
257 $presentation->blocks = $quest['#']['presentation'][0]['#']['flow'][0]['#']['flow'];
259 foreach($presentation->blocks as $pblock) {
261 $block = NULL;
262 $block->type = $pblock['@']['class'];
264 switch($block->type) {
265 case 'QUESTION_BLOCK':
266 $sub_blocks = $pblock['#']['flow'];
267 foreach($sub_blocks as $sblock) {
268 //echo "Calling process_block from line 263<br>";
269 $this->process_block($sblock, $block);
271 break;
273 case 'RESPONSE_BLOCK':
274 $choices = NULL;
275 switch($question->qtype) {
276 case 'Matching':
277 $bb_subquestions = $pblock['#']['flow'];
278 $sub_questions = array();
279 foreach($bb_subquestions as $bb_subquestion) {
280 $sub_question = NULL;
281 $sub_question->ident = $bb_subquestion['#']['response_lid'][0]['@']['ident'];
282 $this->process_block($bb_subquestion['#']['flow'][0], $sub_question);
283 $bb_choices = $bb_subquestion['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
284 $choices = array();
285 $this->process_choices($bb_choices, $choices);
286 $sub_question->choices = $choices;
287 if (!isset($block->subquestions)) {
288 $block->subquestions = array();
290 $block->subquestions[] = $sub_question;
292 break;
293 case 'Multiple Answer':
294 $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
295 $choices = array();
296 $this->process_choices($bb_choices, $choices);
297 $block->choices = $choices;
298 break;
299 case 'Essay':
300 // Doesn't apply since the user responds with text input
301 break;
302 case 'Multiple Choice':
303 $mc_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
304 foreach($mc_choices as $mc_choice) {
305 $choices = NULL;
306 $choices = $this->process_block($mc_choice, $choices);
307 $block->choices[] = $choices;
309 break;
310 case 'Short Response':
311 // do nothing?
312 break;
313 case 'Fill in the Blank':
314 // do nothing?
315 break;
316 default:
317 $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
318 $choices = array();
319 $this->process_choices($bb_choices, $choices);
320 $block->choices = $choices;
322 break;
323 case 'RIGHT_MATCH_BLOCK':
324 $matching_answerset = $pblock['#']['flow'];
326 $answerset = array();
327 foreach($matching_answerset as $answer) {
328 // $answerset[] = $this->process_block($answer, $bb_answer);
329 $bb_answer = null;
330 $bb_answer->text = $answer['#']['flow'][0]['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
331 $answerset[] = $bb_answer;
333 $block->matching_answerset = $answerset;
334 break;
335 default:
336 print "UNHANDLED PRESENTATION BLOCK";
337 break;
339 $question->{$block->type} = $block;
342 // determine response processing
343 // there is a section called 'outcomes' that I don't know what to do with
344 $resprocessing = $quest['#']['resprocessing'];
345 $respconditions = $resprocessing[0]['#']['respcondition'];
346 $reponses = array();
347 if ($question->qtype == 'Matching') {
348 $this->process_matching_responses($respconditions, $responses);
350 else {
351 $this->process_responses($respconditions, $responses);
353 $question->responses = $responses;
354 $feedbackset = $quest['#']['itemfeedback'];
355 $feedbacks = array();
356 $this->process_feedback($feedbackset, $feedbacks);
357 $question->feedback = $feedbacks;
358 return $question;
361 function process_block($cur_block, &$block) {
362 global $COURSE, $CFG;
364 $cur_type = $cur_block['@']['class'];
365 switch($cur_type) {
366 case 'FORMATTED_TEXT_BLOCK':
367 $block->text = $this->strip_applet_tags_get_mathml($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#']);
368 break;
369 case 'FILE_BLOCK':
370 //revisit this to make sure it is working correctly
371 // Commented out ['matapplication']..., etc. because I
372 // noticed that when I imported a new Blackboard 6 file
373 // and printed out the block, the tree did not extend past ['material'][0]['#'] - CT 8/3/06
374 $block->file = $cur_block['#']['material'][0]['#'];//['matapplication'][0]['@']['uri'];
375 if ($block->file != '') {
376 // if we have a file copy it to the course dir and adjust its name to be visible over the web.
377 $block->file = $this->copy_file_to_course($block->file);
378 $block->file = $CFG->wwwroot.'/file.php/'.$COURSE->id.'/bb_import/'.basename($block->file);
380 break;
381 case 'Block':
382 if (isset($cur_block['#']['material'][0]['#']['mattext'][0]['#'])) {
383 $block->text = $cur_block['#']['material'][0]['#']['mattext'][0]['#'];
385 else if (isset($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'])) {
386 $block->text = $cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
388 else if (isset($cur_block['#']['response_label'])) {
389 // this is a response label block
390 $sub_blocks = $cur_block['#']['response_label'][0];
391 if(!isset($block->ident)) {
392 if(isset($sub_blocks['@']['ident'])) {
393 $block->ident = $sub_blocks['@']['ident'];
396 foreach($sub_blocks['#']['flow_mat'] as $sub_block) {
397 $this->process_block($sub_block, $block);
400 else {
401 if (isset($cur_block['#']['flow_mat']) || isset($cur_block['#']['flow'])) {
402 if (isset($cur_block['#']['flow_mat'])) {
403 $sub_blocks = $cur_block['#']['flow_mat'];
405 elseif (isset($cur_block['#']['flow'])) {
406 $sub_blocks = $cur_block['#']['flow'];
408 foreach ($sub_blocks as $sblock) {
409 // this will recursively grab the sub blocks which should be of one of the other types
410 $this->process_block($sblock, $block);
414 break;
415 case 'LINK_BLOCK':
416 // not sure how this should be included
417 if (!empty($cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'])) {
418 $block->link = $cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'];
420 else {
421 $block->link = '';
423 break;
425 return $block;
428 function process_choices($bb_choices, &$choices) {
429 foreach($bb_choices as $choice) {
430 if (isset($choice['@']['ident'])) {
431 $cur_choice = $choice['@']['ident'];
433 else { //for multiple answer
434 $cur_choice = $choice['#']['response_label'][0];//['@']['ident'];
436 if (isset($choice['#']['flow_mat'][0])) { //for multiple answer
437 $cur_block = $choice['#']['flow_mat'][0];
438 // Reset $cur_choice to NULL because process_block is expecting an object
439 // for the second argument and not a string, which is what is was set as
440 // originally - CT 8/7/06
441 $cur_choice = null;
442 $this->process_block($cur_block, $cur_choice);
444 elseif (isset($choice['#']['response_label'])) {
445 // Reset $cur_choice to NULL because process_block is expecting an object
446 // for the second argument and not a string, which is what is was set as
447 // originally - CT 8/7/06
448 $cur_choice = null;
449 $this->process_block($choice, $cur_choice);
451 $choices[] = $cur_choice;
455 function process_matching_responses($bb_responses, &$responses) {
456 foreach($bb_responses as $bb_response) {
457 $response = NULL;
458 if (isset($bb_response['#']['conditionvar'][0]['#']['varequal'])) {
459 $response->correct = $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['#'];
460 $response->ident = $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['@']['respident'];
462 else {
463 $response->correct = 'Broken Question?';
464 $response->ident = 'Broken Question?';
466 $response->feedback = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
467 $responses[] = $response;
471 function process_responses($bb_responses, &$responses) {
472 foreach($bb_responses as $bb_response) {
473 //Added this line to instantiate $response.
474 // Without instantiating the $response variable, the same object
475 // gets added to the array
476 $response = null;
477 if (isset($bb_response['@']['title'])) {
478 $response->title = $bb_response['@']['title'];
480 else {
481 $reponse->title = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
483 $reponse->ident = array();
484 if (isset($bb_response['#']['conditionvar'][0]['#'])){//['varequal'][0]['#'])) {
485 $response->ident[0] = $bb_response['#']['conditionvar'][0]['#'];//['varequal'][0]['#'];
487 else if (isset($bb_response['#']['conditionvar'][0]['#']['other'][0]['#'])) {
488 $response->ident[0] = $bb_response['#']['conditionvar'][0]['#']['other'][0]['#'];
491 if (isset($bb_response['#']['conditionvar'][0]['#']['and'])){//[0]['#'])) {
492 $responseset = $bb_response['#']['conditionvar'][0]['#']['and'];//[0]['#']['varequal'];
493 foreach($responseset as $rs) {
494 $response->ident[] = $rs['#'];
495 if(!isset($response->feedback) and isset( $rs['@'] ) ) {
496 $response->feedback = $rs['@']['respident'];
500 else {
501 $response->feedback = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
504 // determine what point value to give response
505 if (isset($bb_response['#']['setvar'])) {
506 switch ($bb_response['#']['setvar'][0]['#']) {
507 case "SCORE.max":
508 $response->fraction = 1;
509 break;
510 default:
511 // I have only seen this being 0 or unset
512 // there are probably fractional values of SCORE.max, but I'm not sure what they look like
513 $response->fraction = 0;
514 break;
517 else {
518 // just going to assume this is the case this is probably not correct.
519 $response->fraction = 0;
522 $responses[] = $response;
526 function process_feedback($feedbackset, &$feedbacks) {
527 foreach($feedbackset as $bb_feedback) {
528 // Added line $feedback=null so that $feedback does not get reused in the loop
529 // and added the the $feedbacks[] array multiple times
530 $feedback = null;
531 $feedback->ident = $bb_feedback['@']['ident'];
532 if (isset($bb_feedback['#']['flow_mat'][0])) {
533 $this->process_block($bb_feedback['#']['flow_mat'][0], $feedback);
535 elseif (isset($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0])) {
536 $this->process_block($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0], $feedback);
538 $feedbacks[] = $feedback;
543 * Create common parts of question
545 function process_common( $quest ) {
546 $question = $this->defaultquestion();
547 $question->questiontext = addslashes($quest->QUESTION_BLOCK->text);
548 $question->name = shorten_text( $quest->id, 250 );
550 return $question;
553 //----------------------------------------
554 // Process True / False Questions
555 //----------------------------------------
556 function process_tf($quest, &$questions) {
557 $question = $this->process_common( $quest );
559 $question->qtype = TRUEFALSE;
560 $question->single = 1; // Only one answer is allowed
562 // first choice is true, second is false.
563 if ($quest->responses[0]->fraction == 1) {
564 $correct = true;
566 else {
567 $correct = false;
570 foreach($quest->feedback as $fb) {
571 $fback->{$fb->ident} = $fb->text;
574 if ($correct) { // true is correct
575 $question->answer = 1;
576 $question->feedbacktrue = addslashes($fback->correct);
577 $question->feedbackfalse = addslashes($fback->incorrect);
578 } else { // false is correct
579 $question->answer = 0;
580 $question->feedbacktrue = addslashes($fback->incorrect);
581 $question->feedbackfalse = addslashes($fback->correct);
583 $question->correctanswer = $question->answer;
584 $questions[] = $question;
588 //----------------------------------------
589 // Process Fill in the Blank
590 //----------------------------------------
591 function process_fblank($quest, &$questions) {
592 $question = $this->process_common( $quest );
593 $question->qtype = SHORTANSWER;
594 $question->single = 1;
596 $answers = array();
597 $fractions = array();
598 $feedbacks = array();
600 // extract the feedback
601 $feedback = array();
602 foreach($quest->feedback as $fback) {
603 if (isset($fback->ident)) {
604 if ($fback->ident == 'correct' || $fback->ident == 'incorrect') {
605 $feedback[$fback->ident] = $fback->text;
610 foreach($quest->responses as $response) {
611 if(isset($response->title)) {
612 if (isset($response->ident[0]['varequal'][0]['#'])) {
613 //for BB Fill in the Blank, only interested in correct answers
614 if ($response->feedback = 'correct') {
615 $answers[] = addslashes($response->ident[0]['varequal'][0]['#']);
616 $fractions[] = 1;
617 if (isset($feedback['correct'])) {
618 $feedbacks[] = addslashes($feedback['correct']);
620 else {
621 $feedbacks[] = '';
629 //Adding catchall to so that students can see feedback for incorrect answers when they enter something the
630 //instructor did not enter
631 $answers[] = '*';
632 $fractions[] = 0;
633 if (isset($feedback['incorrect'])) {
634 $feedbacks[] = addslashes($feedback['incorrect']);
636 else {
637 $feedbacks[] = '';
640 $question->answer = $answers;
641 $question->fraction = $fractions;
642 $question->feedback = $feedbacks; // Changed to assign $feedbacks to $question->feedback instead of
644 if (!empty($question)) {
645 $questions[] = $question;
650 //----------------------------------------
651 // Process Multiple Choice Questions
652 //----------------------------------------
653 function process_mc($quest, &$questions) {
654 $question = $this->process_common( $quest );
655 $question->qtype = MULTICHOICE;
656 $question->single = 1;
658 $feedback = array();
659 foreach($quest->feedback as $fback) {
660 $feedback[$fback->ident] = addslashes($fback->text);
663 foreach($quest->responses as $response) {
664 if (isset($response->title)) {
665 if ($response->title == 'correct') {
666 // only one answer possible for this qtype so first index is correct answer
667 $correct = $response->ident[0]['varequal'][0]['#'];
670 else {
671 // fallback method for when the title is not set
672 if ($response->feedback == 'correct') {
673 // only one answer possible for this qtype so first index is correct answer
674 $correct = $response->ident[0]['varequal'][0]['#']; // added [0]['varequal'][0]['#'] to $response->ident - CT 8/9/06
679 $i = 0;
680 foreach($quest->RESPONSE_BLOCK->choices as $response) {
681 $question->answer[$i] = addslashes($response->text);
682 if ($correct == $response->ident) {
683 $question->fraction[$i] = 1;
684 // this is a bit of a hack to catch the feedback... first we see if a 'correct' feedback exists
685 // then specific feedback for this question (maybe this should be switched?, but from my example
686 // question pools I have not seen response specific feedback, only correct or incorrect feedback
687 if (!empty($feedback['correct'])) {
688 $question->feedback[$i] = $feedback['correct'];
690 elseif (!empty($feedback[$i])) {
691 $question->feedback[$i] = $feedback[$i];
693 else {
694 // failsafe feedback (should be '' instead?)
695 $question->feedback[$i] = "correct";
698 else {
699 $question->fraction[$i] = 0;
700 if (!empty($feedback['incorrect'])) {
701 $question->feedback[$i] = $feedback['incorrect'];
703 elseif (!empty($feedback[$i])) {
704 $question->feedback[$i] = $feedback[$i];
706 else {
707 // failsafe feedback (should be '' instead?)
708 $question->feedback[$i] = 'incorrect';
711 $i++;
714 if (!empty($question)) {
715 $questions[] = $question;
719 //----------------------------------------
720 // Process Multiple Choice Questions With Multiple Answers
721 //----------------------------------------
722 function process_ma($quest, &$questions) {
723 $question = $this->process_common( $quest ); // copied this from process_mc
724 $question->qtype = MULTICHOICE;
725 $question->single = 0; // More than one answer allowed
727 $answers = $quest->responses;
728 $correct_answers = array();
729 foreach($answers as $answer) {
730 if($answer->title == 'correct') {
731 $answerset = $answer->ident[0]['and'][0]['#']['varequal'];
732 foreach($answerset as $ans) {
733 $correct_answers[] = $ans['#'];
738 foreach ($quest->feedback as $fb) {
739 $feedback->{$fb->ident} = addslashes(trim($fb->text));
742 $correct_answer_count = count($correct_answers);
743 $choiceset = $quest->RESPONSE_BLOCK->choices;
744 $i = 0;
745 foreach($choiceset as $choice) {
746 $question->answer[$i] = addslashes(trim($choice->text));
747 if (in_array($choice->ident, $correct_answers)) {
748 // correct answer
749 $question->fraction[$i] = floor(100000/$correct_answer_count)/100000; // strange behavior if we have more than 5 decimal places
750 $question->feedback[$i] = $feedback->correct;
752 else {
753 // wrong answer
754 $question->fraction[$i] = 0;
755 $question->feedback[$i] = $feedback->incorrect;
757 $i++;
760 $questions[] = $question;
763 //----------------------------------------
764 // Process Essay Questions
765 //----------------------------------------
766 function process_essay($quest, &$questions) {
767 // this should be rewritten to accomodate moodle 1.6 essay question type eventually
769 if (defined("ESSAY")) {
770 // treat as short answer
771 $question = $this->process_common( $quest ); // copied this from process_mc
772 $question->qtype = ESSAY;
774 $question->feedback = array();
775 // not sure where to get the correct answer from
776 foreach($quest->feedback as $feedback) {
777 // Added this code to put the possible solution that the
778 // instructor gives as the Moodle answer for an essay question
779 if ($feedback->ident == 'solution') {
780 $question->feedback = addslashes($feedback->text);
783 //Added because essay/questiontype.php:save_question_option is expecting a
784 //fraction property - CT 8/10/06
785 $question->fraction[] = 1;
786 if (!empty($question)) {
787 $questions[]=$question;
790 else {
791 print "Essay question types are not handled because the quiz question type 'Essay' does not exist in this installation of Moodle<br/>";
792 print "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
796 //----------------------------------------
797 // Process Matching Questions
798 //----------------------------------------
799 function process_matching($quest, &$questions) {
800 global $QTYPES;
802 // renderedmatch is an optional plugin, so we need to check if it is defined
803 if (array_key_exists('renderedmatch', $QTYPES)) {
804 $question = $this->process_common( $quest );
805 $question->valid = true;
806 $question->qtype = 'renderedmatch';
808 foreach($quest->RESPONSE_BLOCK->subquestions as $qid => $subq) {
809 foreach($quest->responses as $rid => $resp) {
810 if ($resp->ident == $subq->ident) {
811 $correct = addslashes($resp->correct);
812 $feedback = addslashes($resp->feedback);
816 foreach($subq->choices as $cid => $choice) {
817 if ($choice == $correct) {
818 $question->subquestions[] = addslashes($subq->text);
819 $question->subanswers[] = addslashes($quest->RIGHT_MATCH_BLOCK->matching_answerset[$cid]->text);
824 // check format
825 $status = true;
826 if ( count($quest->RESPONSE_BLOCK->subquestions) > count($quest->RIGHT_MATCH_BLOCK->matching_answerset) || count($question->subquestions) < 2) {
827 $status = false;
829 else {
830 // need to redo to make sure that no two questions have the same answer (rudimentary now)
831 foreach($question->subanswers as $qstn) {
832 if(isset($previous)) {
833 if ($qstn == $previous) {
834 $status = false;
837 $previous = $qstn;
838 if ($qstn == '') {
839 $status = false;
844 if ($status) {
845 $questions[] = $question;
847 else {
848 global $COURSE, $CFG;
849 print '<table class="boxaligncenter" border="1">';
850 print '<tr><td colspan="2" style="background-color:#FF8888;">This matching question is malformed. Please ensure there are no blank answers, no two questions have the same answer, and/or there are correct answers for each question. There must be at least as many subanswers as subquestions, and at least one subquestion.</td></tr>';
852 print "<tr><td>Question:</td><td>".$quest->QUESTION_BLOCK->text;
853 if (isset($quest->QUESTION_BLOCK->file)) {
854 print '<br/><font color="red">There is a subfile contained in the zipfile that has been copied to course files: bb_import/'.basename($quest->QUESTION_BLOCK->file).'</font>';
855 if (preg_match('/(gif|jpg|jpeg|png)$/i', $quest->QUESTION_BLOCK->file)) {
856 print '<img src="'.$CFG->wwwroot.'/file.php/'.$COURSE->id.'/bb_import/'.basename($quest->QUESTION_BLOCK->file).'" />';
859 print "</td></tr>";
860 print "<tr><td>Subquestions:</td><td><ul>";
861 foreach($quest->responses as $rs) {
862 $correct_responses->{$rs->ident} = $rs->correct;
864 foreach($quest->RESPONSE_BLOCK->subquestions as $subq) {
865 print '<li>'.$subq->text.'<ul>';
866 foreach($subq->choices as $id=>$choice) {
867 print '<li>';
868 if ($choice == $correct_responses->{$subq->ident}) {
869 print '<font color="green">';
871 else {
872 print '<font color="red">';
874 print $quest->RIGHT_MATCH_BLOCK->matching_answerset[$id]->text.'</font></li>';
876 print '</ul>';
878 print '</ul></td></tr>';
880 print '<tr><td>Feedback:</td><td><ul>';
881 foreach($quest->feedback as $fb) {
882 print '<li>'.$fb->ident.': '.$fb->text.'</li>';
884 print '</ul></td></tr></table>';
887 else {
888 print "Matching question types are not handled because the quiz question type 'Rendered Matching' does not exist in this installation of Moodle<br/>";
889 print "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
894 function strip_applet_tags_get_mathml($string) {
895 if(stristr($string, '</APPLET>') === FALSE) {
896 return $string;
898 else {
899 // strip all applet tags keeping stuff before/after and inbetween (if mathml) them
900 while (stristr($string, '</APPLET>') !== FALSE) {
901 preg_match("/(.*)\<applet.*value=\"(\<math\>.*\<\/math\>)\".*\<\/applet\>(.*)/i",$string, $mathmls);
902 $string = $mathmls[1].$mathmls[2].$mathmls[3];
904 return $string;
908 } // close object