3 ////////////////////////////////////////////////////////////////////////////
4 /// Blackboard 6.x Format
6 /// This Moodle class provides all functions necessary to import and export
9 ////////////////////////////////////////////////////////////////////////////
11 // Based on default.php, included by ../import.php
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() {
24 //Function to check and create the needed dir to unzip file to
25 function check_and_create_import_dir($unique_code) {
29 $status = $this->check_dir_exists($CFG->dataroot
."/temp",true);
31 $status = $this->check_dir_exists($CFG->dataroot
."/temp/bbquiz_import",true);
34 $status = $this->check_dir_exists($CFG->dataroot
."/temp/bbquiz_import/".$unique_code,true);
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
46 $dir = $this->temp_dir
;
50 // Create arrays to store files and directories
52 $dir_subdirs = array();
54 // Make sure we can delete it
57 if ((($handle = opendir($dir))) == FALSE) {
58 // The directory could not be opened
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) {
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) {
87 if (rmdir($dir_subdirs[$i]) == FALSE) {
95 if (rmdir($this->temp_dir
) == FALSE) {
98 // Success, every thing is gone return true
102 //Function to check if a directory exists and, optionally, create it
103 function check_dir_exists($dir,$create=false) {
113 $status = mkdir ($dir,$CFG->directorypermissions
);
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)) {
152 function readdata($filename) {
153 /// Returns complete file with an array, one item per line
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]);
181 error("Could not find question data file in zip");
185 print "filename: $filename<br />tempdir: $temp_dir <br />";
186 error("Could not unzip file.");
190 error ("Could not read uploaded file");
194 error("Could not create temporary directory");
198 function save_question_options($question) {
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
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
) {
220 $this->process_matching($question, $questions);
222 case "Multiple Choice":
223 $this->process_mc($question, $questions);
226 $this->process_essay($question, $questions);
228 case "Multiple Answer":
229 $this->process_ma($question, $questions);
232 $this->process_tf($question, $questions);
234 case 'Fill in the Blank':
235 $this->process_fblank($question, $questions);
237 case 'Short Response':
238 $this->process_essay($question, $questions);
241 print "Unknown or unhandled question type: \"$question->qtype\"<br />";
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 = $this->defaultquestion();
255 $question->qtype
= $quest['#']['itemmetadata'][0]['#']['bbmd_questiontype'][0]['#'];
256 $presentation->blocks
= $quest['#']['presentation'][0]['#']['flow'][0]['#']['flow'];
258 foreach($presentation->blocks
as $pblock) {
261 $block->type
= $pblock['@']['class'];
263 switch($block->type
) {
264 case 'QUESTION_BLOCK':
265 $sub_blocks = $pblock['#']['flow'];
266 foreach($sub_blocks as $sblock) {
267 //echo "Calling process_block from line 263<br>";
268 $this->process_block($sblock, $block);
272 case 'RESPONSE_BLOCK':
274 switch($question->qtype
) {
276 $bb_subquestions = $pblock['#']['flow'];
277 $sub_questions = array();
278 foreach($bb_subquestions as $bb_subquestion) {
279 $sub_question = NULL;
280 $sub_question->ident
= $bb_subquestion['#']['response_lid'][0]['@']['ident'];
281 $this->process_block($bb_subquestion['#']['flow'][0], $sub_question);
282 $bb_choices = $bb_subquestion['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
284 $this->process_choices($bb_choices, $choices);
285 $sub_question->choices
= $choices;
286 if (!isset($block->subquestions
)) {
287 $block->subquestions
= array();
289 $block->subquestions
[] = $sub_question;
292 case 'Multiple Answer':
293 $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
295 $this->process_choices($bb_choices, $choices);
296 $block->choices
= $choices;
299 // Doesn't apply since the user responds with text input
301 case 'Multiple Choice':
302 $mc_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
303 foreach($mc_choices as $mc_choice) {
305 $choices = $this->process_block($mc_choice, $choices);
306 $block->choices
[] = $choices;
309 case 'Short Response':
312 case 'Fill in the Blank':
316 $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
318 $this->process_choices($bb_choices, $choices);
319 $block->choices
= $choices;
322 case 'RIGHT_MATCH_BLOCK':
323 $matching_answerset = $pblock['#']['flow'];
324 $answerset = array();
325 foreach($matching_answerset as $answer) {
326 $this->process_block($answer, $bb_answer);
327 $answerset[] = $bb_answer;
329 $block->matching_answerset
= $answerset;
332 print "UNHANDLED PRESENTATION BLOCK";
335 $question->{$block->type
} = $block;
338 // determine response processing
339 // there is a section called 'outcomes' that I don't know what to do with
340 $resprocessing = $quest['#']['resprocessing'];
341 $respconditions = $resprocessing[0]['#']['respcondition'];
343 if ($question->qtype
== 'Matching') {
344 $this->process_matching_responses($respconditions, $responses);
347 $this->process_responses($respconditions, $responses);
349 $question->responses
= $responses;
350 $feedbackset = $quest['#']['itemfeedback'];
351 $feedbacks = array();
352 $this->process_feedback($feedbackset, $feedbacks);
353 $question->feedback
= $feedbacks;
357 function process_block($cur_block, &$block) {
358 global $COURSE, $CFG;
360 $cur_type = $cur_block['@']['class'];
362 case 'FORMATTED_TEXT_BLOCK':
363 $block->text
= $this->strip_applet_tags_get_mathml($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#']);
366 //revisit this to make sure it is working correctly
367 // Commented out ['matapplication']..., etc. because I
368 // noticed that when I imported a new Blackboard 6 file
369 // and printed out the block, the tree did not extend past ['material'][0]['#'] - CT 8/3/06
370 $block->file
= $cur_block['#']['material'][0]['#'];//['matapplication'][0]['@']['uri'];
371 if ($block->file
!= '') {
372 // if we have a file copy it to the course dir and adjust its name to be visible over the web.
373 $block->file
= $this->copy_file_to_course($block->file
);
374 $block->file
= $CFG->wwwroot
.'/file.php/'.$COURSE->id
.'/bb_import/'.basename($block->file
);
378 if (isset($cur_block['#']['material'][0]['#']['mattext'][0]['#'])) {
379 $block->text
= $cur_block['#']['material'][0]['#']['mattext'][0]['#'];
381 else if (isset($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'])) {
382 $block->text
= $cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
384 else if (isset($cur_block['#']['response_label'])) {
385 // this is a response label block
386 $sub_blocks = $cur_block['#']['response_label'][0];
387 if(!isset($block->ident
)) {
388 if(isset($sub_blocks['@']['ident'])) {
389 $block->ident
= $sub_blocks['@']['ident'];
392 foreach($sub_blocks['#']['flow_mat'] as $sub_block) {
393 $this->process_block($sub_block, $block);
397 if (isset($cur_block['#']['flow_mat']) ||
isset($cur_block['#']['flow'])) {
398 if (isset($cur_block['#']['flow_mat'])) {
399 $sub_blocks = $cur_block['#']['flow_mat'];
401 elseif (isset($cur_block['#']['flow'])) {
402 $sub_blocks = $cur_block['#']['flow'];
404 foreach ($sub_blocks as $sblock) {
405 // this will recursively grab the sub blocks which should be of one of the other types
406 $this->process_block($sblock, $block);
412 // not sure how this should be included
413 if (!empty($cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'])) {
414 $block->link
= $cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'];
424 function process_choices($bb_choices, &$choices) {
425 foreach($bb_choices as $choice) {
426 if (isset($choice['@']['ident'])) {
427 $cur_choice = $choice['@']['ident'];
429 else { //for multiple answer
430 $cur_choice = $choice['#']['response_label'][0];//['@']['ident'];
432 if (isset($choice['#']['flow_mat'][0])) { //for multiple answer
433 $cur_block = $choice['#']['flow_mat'][0];
434 // Reset $cur_choice to NULL because process_block is expecting an object
435 // for the second argument and not a string, which is what is was set as
436 // originally - CT 8/7/06
438 $this->process_block($cur_block, $cur_choice);
440 elseif (isset($choice['#']['response_label'])) {
441 // Reset $cur_choice to NULL because process_block is expecting an object
442 // for the second argument and not a string, which is what is was set as
443 // originally - CT 8/7/06
445 $this->process_block($choice, $cur_choice);
447 $choices[] = $cur_choice;
451 function process_matching_responses($bb_responses, &$responses) {
452 foreach($bb_responses as $bb_response) {
454 if (isset($bb_response['#']['conditionvar'][0]['#']['varequal'])) {
455 $response->correct
= $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['#'];
456 $response->ident
= $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['@']['respident'];
459 $response->correct
= 'Broken Question?';
460 $response->ident
= 'Broken Question?';
462 $response->feedback
= $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
463 $responses[] = $response;
467 function process_responses($bb_responses, &$responses) {
468 foreach($bb_responses as $bb_response) {
469 //Added this line to instantiate $response.
470 // Without instantiating the $response variable, the same object
471 // gets added to the array
473 if (isset($bb_response['@']['title'])) {
474 $response->title
= $bb_response['@']['title'];
477 $reponse->title
= $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
479 $reponse->ident
= array();
480 if (isset($bb_response['#']['conditionvar'][0]['#'])){//['varequal'][0]['#'])) {
481 $response->ident
[0] = $bb_response['#']['conditionvar'][0]['#'];//['varequal'][0]['#'];
483 else if (isset($bb_response['#']['conditionvar'][0]['#']['other'][0]['#'])) {
484 $response->ident
[0] = $bb_response['#']['conditionvar'][0]['#']['other'][0]['#'];
487 if (isset($bb_response['#']['conditionvar'][0]['#']['and'])){//[0]['#'])) {
488 $responseset = $bb_response['#']['conditionvar'][0]['#']['and'];//[0]['#']['varequal'];
489 foreach($responseset as $rs) {
490 $response->ident
[] = $rs['#'];
491 if(!isset($response->feedback
) and isset( $rs['@'] ) ) {
492 $response->feedback
= $rs['@']['respident'];
497 $response->feedback
= $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
500 // determine what point value to give response
501 if (isset($bb_response['#']['setvar'])) {
502 switch ($bb_response['#']['setvar'][0]['#']) {
504 $response->fraction
= 1;
507 // I have only seen this being 0 or unset
508 // there are probably fractional values of SCORE.max, but I'm not sure what they look like
509 $response->fraction
= 0;
514 // just going to assume this is the case this is probably not correct.
515 $response->fraction
= 0;
518 $responses[] = $response;
522 function process_feedback($feedbackset, &$feedbacks) {
523 foreach($feedbackset as $bb_feedback) {
524 // Added line $feedback=null so that $feedback does not get reused in the loop
525 // and added the the $feedbacks[] array multiple times
527 $feedback->ident
= $bb_feedback['@']['ident'];
528 if (isset($bb_feedback['#']['flow_mat'][0])) {
529 $this->process_block($bb_feedback['#']['flow_mat'][0], $feedback);
531 elseif (isset($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0])) {
532 $this->process_block($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0], $feedback);
534 $feedbacks[] = $feedback;
538 //----------------------------------------
539 // Process True / False Questions
540 //----------------------------------------
541 function process_tf($quest, &$questions) {
542 $question = $this->defaultquestion();
544 $question->qtype
= TRUEFALSE
;
545 $question->defaultgrade
= 1;
546 $question->single
= 1; // Only one answer is allowed
547 $question->image
= ""; // No images with this format
548 $question->questiontext
= addslashes($quest->QUESTION_BLOCK
->text
);
549 // put name in question object
550 $question->name
= shorten_text($question->questiontext
, 250);
552 // first choice is true, second is false.
553 if ($quest->responses
[0]->fraction
== 1) {
560 foreach($quest->feedback
as $fb) {
561 $fback->{$fb->ident
} = $fb->text
;
564 if ($correct) { // true is correct
565 $question->answer
= 1;
566 $question->feedbacktrue
= addslashes($fback->correct
);
567 $question->feedbackfalse
= addslashes($fback->incorrect
);
568 } else { // false is correct
569 $question->answer
= 0;
570 $question->feedbacktrue
= addslashes($fback->incorrect
);
571 $question->feedbackfalse
= addslashes($fback->correct
);
573 $question->correctanswer
= $question->answer
;
574 $questions[] = $question;
578 //----------------------------------------
579 // Process Fill in the Blank
580 //----------------------------------------
581 function process_fblank($quest, &$questions) {
582 $question = $this->defaultquestion();
583 $question->qtype
= SHORTANSWER
;
584 $question->defaultgrade
= 1;
585 $question->single
= 1;
586 $question->usecase
= 0;
587 $question->image
= '';
588 $question->questiontext
= addslashes($quest->QUESTION_BLOCK
->text
);
589 $question->name
= shorten_text($question->questiontext
, 250);
591 $fractions = array();
592 $feedbacks = array();
594 // extract the feedback
596 foreach($quest->feedback
as $fback) {
597 if (isset($fback->ident
)) {
598 if ($fback->ident
== 'correct' ||
$fback->ident
== 'incorrect') {
599 $feedback[$fback->ident
] = $fback->text
;
604 foreach($quest->responses
as $response) {
605 if(isset($response->title
)) {
606 if (isset($response->ident
[0]['varequal'][0]['#'])) {
607 //for BB Fill in the Blank, only interested in correct answers
608 if ($response->feedback
= 'correct') {
609 $answers[] = addslashes($response->ident
[0]['varequal'][0]['#']);
611 if (isset($feedback['correct'])) {
612 $feedbacks[] = addslashes($feedback['correct']);
623 //Adding catchall to so that students can see feedback for incorrect answers when they enter something the
624 //instructor did not enter
627 if (isset($feedback['incorrect'])) {
628 $feedbacks[] = addslashes($feedback['incorrect']);
634 $question->answer
= $answers;
635 $question->fraction
= $fractions;
636 $question->feedback
= $feedbacks; // Changed to assign $feedbacks to $question->feedback instead of
638 if (!empty($question)) {
639 $questions[] = $question;
644 //----------------------------------------
645 // Process Multiple Choice Questions
646 //----------------------------------------
647 function process_mc($quest, &$questions) {
648 $question = $this->defaultquestion();
649 $question->qtype
= MULTICHOICE
;
650 $question->defaultgrade
= 1;
651 $question->single
= 1;
652 $question->image
= "";
653 $question->questiontext
= addslashes($quest->QUESTION_BLOCK
->text
);
654 $question->name
= shorten_text($question->questiontext
, 250);
657 foreach($quest->feedback
as $fback) {
658 $feedback[$fback->ident
] = addslashes($fback->text
);
661 foreach($quest->responses
as $response) {
662 if (isset($response->title
)) {
663 if ($response->title
== 'correct') {
664 // only one answer possible for this qtype so first index is correct answer
665 $correct = $response->ident
[0]['varequal'][0]['#'];
669 // fallback method for when the title is not set
670 if ($response->feedback
== 'correct') {
671 // only one answer possible for this qtype so first index is correct answer
672 $correct = $response->ident
[0]['varequal'][0]['#']; // added [0]['varequal'][0]['#'] to $response->ident - CT 8/9/06
678 foreach($quest->RESPONSE_BLOCK
->choices
as $response) {
679 $question->answer
[$i] = addslashes($response->text
);
680 if ($correct == $response->ident
) {
681 $question->fraction
[$i] = 1;
682 // this is a bit of a hack to catch the feedback... first we see if a 'correct' feedback exists
683 // then specific feedback for this question (maybe this should be switched?, but from my example
684 // question pools I have not seen response specific feedback, only correct or incorrect feedback
685 if (!empty($feedback['correct'])) {
686 $question->feedback
[$i] = $feedback['correct'];
688 elseif (!empty($feedback[$i])) {
689 $question->feedback
[$i] = $feedback[$i];
692 // failsafe feedback (should be '' instead?)
693 $question->feedback
[$i] = "correct";
697 $question->fraction
[$i] = 0;
698 if (!empty($feedback['incorrect'])) {
699 $question->feedback
[$i] = $feedback['incorrect'];
701 elseif (!empty($feedback[$i])) {
702 $question->feedback
[$i] = $feedback[$i];
705 // failsafe feedback (should be '' instead?)
706 $question->feedback
[$i] = 'incorrect';
712 if (!empty($question)) {
713 $questions[] = $question;
717 //----------------------------------------
718 // Process Multiple Choice Questions With Multiple Answers
719 //----------------------------------------
720 function process_ma($quest, &$questions) {
721 $question = $this->defaultquestion(); // copied this from process_mc
722 $question->questiontext
= addslashes($quest->QUESTION_BLOCK
->text
);
723 $question->name
= shorten_text($question->questiontext
, 250);
724 $question->qtype
= MULTICHOICE
;
725 $question->defaultgrade
= 1;
726 $question->single
= 0; // More than one answer allowed
727 $question->image
= ""; // No images with this format
729 $answers = $quest->responses
;
730 $correct_answers = array();
731 foreach($answers as $answer) {
732 if($answer->title
== 'correct') {
733 $answerset = $answer->ident
[0]['and'][0]['#']['varequal'];
734 foreach($answerset as $ans) {
735 $correct_answers[] = $ans['#'];
740 foreach ($quest->feedback
as $fb) {
741 $feedback->{$fb->ident
} = addslashes(trim($fb->text
));
744 $correct_answer_count = count($correct_answers);
745 $choiceset = $quest->RESPONSE_BLOCK
->choices
;
747 foreach($choiceset as $choice) {
748 $question->answer
[$i] = addslashes(trim($choice->text
));
749 if (in_array($choice->ident
, $correct_answers)) {
751 $question->fraction
[$i] = floor(100000/$correct_answer_count)/100000; // strange behavior if we have more than 5 decimal places
752 $question->feedback
[$i] = $feedback->correct
;
756 $question->fraction
[$i] = 0;
757 $question->feedback
[$i] = $feedback->incorrect
;
762 $questions[] = $question;
765 //----------------------------------------
766 // Process Essay Questions
767 //----------------------------------------
768 function process_essay($quest, &$questions) {
769 // this should be rewritten to accomodate moodle 1.6 essay question type eventually
771 if (defined("ESSAY")) {
772 // treat as short answer
773 $question = $this->defaultquestion(); // copied this from process_mc
774 $question->qtype
= ESSAY
;
775 $question->defaultgrade
= 1;
776 $question->usecase
= 0; // Ignore case
777 $question->image
= ""; // No images with this format
778 $question->questiontext
= addslashes(trim($quest->QUESTION_BLOCK
->text
));
779 $question->name
= shorten_text($question->questiontext
, 250);
781 $question->feedback
= array();
782 // not sure where to get the correct answer from
783 foreach($quest->feedback
as $feedback) {
784 // Added this code to put the possible solution that the
785 // instructor gives as the Moodle answer for an essay question
786 if ($feedback->ident
== 'solution') {
787 $question->feedback
= addslashes($feedback->text
);
790 //Added because essay/questiontype.php:save_question_option is expecting a
791 //fraction property - CT 8/10/06
792 $question->fraction
[] = 1;
793 if (!empty($question)) {
794 $questions[]=$question;
798 print "Essay question types are not handled because the quiz question type 'Essay' does not exist in this installation of Moodle<br/>";
799 print " Omitted Question: ".$quest->QUESTION_BLOCK
->text
.'<br/><br/>';
803 //----------------------------------------
804 // Process Matching Questions
805 //----------------------------------------
806 function process_matching($quest, &$questions) {
807 if (defined("RENDEREDMATCH")) {
808 $question = $this->defaultquestion($this->defaultquestion());
809 $question->valid
= true;
810 $question->qtype
= RENDEREDMATCH
;
811 $question->defaultgrade
= 1;
812 $question->questiontext
= addslashes($quest->QUESTION_BLOCK
->text
);
813 $question->name
= shorten_text($question->questiontext
, 250);
815 foreach($quest->RESPONSE_BLOCK
->subquestions
as $qid => $subq) {
816 foreach($quest->responses
as $rid => $resp) {
817 if ($resp->ident
== $subq->ident
) {
818 $correct = addslashes($resp->correct
);
819 $feedback = addslashes($resp->feedback
);
823 foreach($subq->choices
as $cid => $choice) {
824 if ($choice == $correct) {
825 $question->subquestions
[] = addslashes($subq->text
);
826 $question->subanswers
[] = addslashes($quest->RIGHT_MATCH_BLOCK
->matching_answerset
[$cid]->text
);
833 if ( count($quest->RESPONSE_BLOCK
->subquestions
) > count($quest->RIGHT_MATCH_BLOCK
->matching_answerset
) ||
count($question->subquestions
) < 2) {
837 // need to redo to make sure that no two questions have the same answer (rudimentary now)
838 foreach($question->subanswers
as $qstn) {
839 if(isset($previous)) {
840 if ($qstn == $previous) {
852 $questions[] = $question;
855 global $COURSE, $CFG;
856 print '<table class="boxaligncenter" border="1">';
857 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>';
859 print "<tr><td>Question:</td><td>".$quest->QUESTION_BLOCK
->text
;
860 if (isset($quest->QUESTION_BLOCK
->file
)) {
861 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>';
862 if (preg_match('/(gif|jpg|jpeg|png)$/i', $quest->QUESTION_BLOCK
->file
)) {
863 print '<img src="'.$CFG->wwwroot
.'/file.php/'.$COURSE->id
.'/bb_import/'.basename($quest->QUESTION_BLOCK
->file
).'" />';
867 print "<tr><td>Subquestions:</td><td><ul>";
868 foreach($quest->responses
as $rs) {
869 $correct_responses->{$rs->ident
} = $rs->correct
;
871 foreach($quest->RESPONSE_BLOCK
->subquestions
as $subq) {
872 print '<li>'.$subq->text
.'<ul>';
873 foreach($subq->choices
as $id=>$choice) {
875 if ($choice == $correct_responses->{$subq->ident
}) {
876 print '<font color="green">';
879 print '<font color="red">';
881 print $quest->RIGHT_MATCH_BLOCK
->matching_answerset
[$id]->text
.'</font></li>';
885 print '</ul></td></tr>';
887 print '<tr><td>Feedback:</td><td><ul>';
888 foreach($quest->feedback
as $fb) {
889 print '<li>'.$fb->ident
.': '.$fb->text
.'</li>';
891 print '</ul></td></tr></table>';
895 print "Matching question types are not handled because the quiz question type 'Rendered Matching' does not exist in this installation of Moodle<br/>";
896 print " Omitted Question: ".$quest->QUESTION_BLOCK
->text
.'<br/><br/>';
901 function strip_applet_tags_get_mathml($string) {
902 if(stristr($string, '</APPLET>') === FALSE) {
906 // strip all applet tags keeping stuff before/after and inbetween (if mathml) them
907 while (stristr($string, '</APPLET>') !== FALSE) {
908 preg_match("/(.*)\<applet.*value=\"(\<math\>.*\<\/math\>)\".*\<\/applet\>(.*)/i",$string, $mathmls);
909 $string = $mathmls[1].$mathmls[2].$mathmls[3];