MDL-10240:
[moodle-linuxchix.git] / question / format / blackboard_6 / format.php
blob779282518c2bad86d2de23bb499da0511ce356df
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 = $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) {
260 $block = NULL;
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);
270 break;
272 case 'RESPONSE_BLOCK':
273 $choices = NULL;
274 switch($question->qtype) {
275 case 'Matching':
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'];
283 $choices = array();
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;
291 break;
292 case 'Multiple Answer':
293 $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
294 $choices = array();
295 $this->process_choices($bb_choices, $choices);
296 $block->choices = $choices;
297 break;
298 case 'Essay':
299 // Doesn't apply since the user responds with text input
300 break;
301 case 'Multiple Choice':
302 $mc_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
303 foreach($mc_choices as $mc_choice) {
304 $choices = NULL;
305 $choices = $this->process_block($mc_choice, $choices);
306 $block->choices[] = $choices;
308 break;
309 case 'Short Response':
310 // do nothing?
311 break;
312 case 'Fill in the Blank':
313 // do nothing?
314 break;
315 default:
316 $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
317 $choices = array();
318 $this->process_choices($bb_choices, $choices);
319 $block->choices = $choices;
321 break;
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;
330 break;
331 default:
332 print "UNHANDLED PRESENTATION BLOCK";
333 break;
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'];
342 $reponses = array();
343 if ($question->qtype == 'Matching') {
344 $this->process_matching_responses($respconditions, $responses);
346 else {
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;
354 return $question;
357 function process_block($cur_block, &$block) {
358 global $COURSE, $CFG;
360 $cur_type = $cur_block['@']['class'];
361 switch($cur_type) {
362 case 'FORMATTED_TEXT_BLOCK':
363 $block->text = $this->strip_applet_tags_get_mathml($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#']);
364 break;
365 case 'FILE_BLOCK':
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);
376 break;
377 case 'Block':
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);
396 else {
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);
410 break;
411 case 'LINK_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'];
416 else {
417 $block->link = '';
419 break;
421 return $block;
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
437 $cur_choice = null;
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
444 $cur_choice = null;
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) {
453 $response = NULL;
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'];
458 else {
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
472 $response = null;
473 if (isset($bb_response['@']['title'])) {
474 $response->title = $bb_response['@']['title'];
476 else {
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'];
496 else {
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]['#']) {
503 case "SCORE.max":
504 $response->fraction = 1;
505 break;
506 default:
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;
510 break;
513 else {
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
526 $feedback = null;
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) {
554 $correct = true;
556 else {
557 $correct = false;
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);
590 $answers = array();
591 $fractions = array();
592 $feedbacks = array();
594 // extract the feedback
595 $feedback = array();
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]['#']);
610 $fractions[] = 1;
611 if (isset($feedback['correct'])) {
612 $feedbacks[] = addslashes($feedback['correct']);
614 else {
615 $feedbacks[] = '';
623 //Adding catchall to so that students can see feedback for incorrect answers when they enter something the
624 //instructor did not enter
625 $answers[] = '*';
626 $fractions[] = 0;
627 if (isset($feedback['incorrect'])) {
628 $feedbacks[] = addslashes($feedback['incorrect']);
630 else {
631 $feedbacks[] = '';
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);
656 $feedback = array();
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]['#'];
668 else {
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
677 $i = 0;
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];
691 else {
692 // failsafe feedback (should be '' instead?)
693 $question->feedback[$i] = "correct";
696 else {
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];
704 else {
705 // failsafe feedback (should be '' instead?)
706 $question->feedback[$i] = 'incorrect';
709 $i++;
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;
746 $i = 0;
747 foreach($choiceset as $choice) {
748 $question->answer[$i] = addslashes(trim($choice->text));
749 if (in_array($choice->ident, $correct_answers)) {
750 // correct answer
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;
754 else {
755 // wrong answer
756 $question->fraction[$i] = 0;
757 $question->feedback[$i] = $feedback->incorrect;
759 $i++;
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;
797 else {
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 "&nbsp;&nbsp;&nbsp;&nbsp;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);
831 // check format
832 $status = true;
833 if ( count($quest->RESPONSE_BLOCK->subquestions) > count($quest->RIGHT_MATCH_BLOCK->matching_answerset) || count($question->subquestions) < 2) {
834 $status = false;
836 else {
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) {
841 $status = false;
844 $previous = $qstn;
845 if ($qstn == '') {
846 $status = false;
851 if ($status) {
852 $questions[] = $question;
854 else {
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).'" />';
866 print "</td></tr>";
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) {
874 print '<li>';
875 if ($choice == $correct_responses->{$subq->ident}) {
876 print '<font color="green">';
878 else {
879 print '<font color="red">';
881 print $quest->RIGHT_MATCH_BLOCK->matching_answerset[$id]->text.'</font></li>';
883 print '</ul>';
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>';
894 else {
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 "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
901 function strip_applet_tags_get_mathml($string) {
902 if(stristr($string, '</APPLET>') === FALSE) {
903 return $string;
905 else {
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];
911 return $string;
915 } // close object