Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/format/blackboard_six/format.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 require_once ($CFG->libdir . '/xmlize.php');
00030 
00031 
00038 class qformat_blackboard_six extends qformat_default {
00039     function provide_import() {
00040         return true;
00041     }
00042 
00043 
00044     //Function to check and create the needed dir to unzip file to
00045     function check_and_create_import_dir($unique_code) {
00046 
00047         global $CFG;
00048 
00049         $status = $this->check_dir_exists($CFG->tempdir."",true);
00050         if ($status) {
00051             $status = $this->check_dir_exists($CFG->tempdir."/bbquiz_import",true);
00052         }
00053         if ($status) {
00054             $status = $this->check_dir_exists($CFG->tempdir."/bbquiz_import/".$unique_code,true);
00055         }
00056 
00057         return $status;
00058     }
00059 
00060     function clean_temp_dir($dir='') {
00061         global $CFG;
00062 
00063         // for now we will just say everything happened okay note
00064         // that a mess may be piling up in $CFG->tempdir/bbquiz_import
00065         // TODO return true at top of the function renders all the following code useless
00066         return true;
00067 
00068         if ($dir == '') {
00069             $dir = $this->temp_dir;
00070         }
00071         $slash = "/";
00072 
00073         // Create arrays to store files and directories
00074         $dir_files      = array();
00075         $dir_subdirs    = array();
00076 
00077         // Make sure we can delete it
00078         chmod($dir, $CFG->directorypermissions);
00079 
00080         if ((($handle = opendir($dir))) == FALSE) {
00081             // The directory could not be opened
00082             return false;
00083         }
00084 
00085         // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
00086         while(false !== ($entry = readdir($handle))) {
00087             if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != ".") {
00088                 $dir_subdirs[] = $dir. $slash .$entry;
00089             }
00090             else if ($entry != ".." && $entry != ".") {
00091                 $dir_files[] = $dir. $slash .$entry;
00092             }
00093         }
00094 
00095         // Delete all files in the curent directory return false and halt if a file cannot be removed
00096         $countdir_files = count($dir_files);
00097         for($i=0; $i<$countdir_files; $i++) {
00098             chmod($dir_files[$i], $CFG->directorypermissions);
00099             if (((unlink($dir_files[$i]))) == FALSE) {
00100                 return false;
00101             }
00102         }
00103 
00104         // Empty sub directories and then remove the directory
00105         $countdir_subdirs = count($dir_subdirs);
00106         for($i=0; $i<$countdir_subdirs; $i++) {
00107             chmod($dir_subdirs[$i], $CFG->directorypermissions);
00108             if ($this->clean_temp_dir($dir_subdirs[$i]) == FALSE) {
00109                 return false;
00110             }
00111             else {
00112                 if (rmdir($dir_subdirs[$i]) == FALSE) {
00113                 return false;
00114                 }
00115             }
00116         }
00117 
00118         // Close directory
00119         closedir($handle);
00120         if (rmdir($this->temp_dir) == FALSE) {
00121             return false;
00122         }
00123         // Success, every thing is gone return true
00124         return true;
00125     }
00126 
00127     //Function to check if a directory exists and, optionally, create it
00128     function check_dir_exists($dir,$create=false) {
00129 
00130         global $CFG;
00131 
00132         $status = true;
00133         if(!is_dir($dir)) {
00134             if (!$create) {
00135                 $status = false;
00136             } else {
00137                 umask(0000);
00138                 $status = mkdir ($dir,$CFG->directorypermissions);
00139             }
00140         }
00141         return $status;
00142     }
00143 
00144     function importpostprocess() {
00148 
00149         // need to clean up temporary directory
00150         return $this->clean_temp_dir();
00151     }
00152 
00153     function copy_file_to_course($filename) {
00154         global $CFG, $COURSE;
00155         $filename = str_replace('\\','/',$filename);
00156         $fullpath = $this->temp_dir.'/res00001/'.$filename;
00157         $basename = basename($filename);
00158 
00159         $copy_to = $CFG->dataroot.'/'.$COURSE->id.'/bb_import';
00160 
00161         if ($this->check_dir_exists($copy_to,true)) {
00162             if(is_readable($fullpath)) {
00163                 $copy_to.= '/'.$basename;
00164                 if (!copy($fullpath, $copy_to)) {
00165                     return false;
00166                 }
00167                 else {
00168                     return $copy_to;
00169                 }
00170             }
00171         }
00172         else {
00173             return false;
00174         }
00175     }
00176 
00177     function readdata($filename) {
00179         global $CFG;
00180 
00181         // if the extension is .dat we just return that,
00182         // if .zip we unzip the file and get the data
00183         $ext = substr($this->realfilename, strpos($this->realfilename,'.'), strlen($this->realfilename)-1);
00184         if ($ext=='.dat') {
00185             if (!is_readable($filename)) {
00186                 print_error('filenotreadable', 'error');
00187             }
00188             return file($filename);
00189         }
00190 
00191         $unique_code = time();
00192         $temp_dir = $CFG->tempdir."/bbquiz_import/".$unique_code;
00193         $this->temp_dir = $temp_dir;
00194         if ($this->check_and_create_import_dir($unique_code)) {
00195             if(is_readable($filename)) {
00196                 if (!copy($filename, "$temp_dir/bboard.zip")) {
00197                     print_error('cannotcopybackup', 'question');
00198                 }
00199                 if(unzip_file("$temp_dir/bboard.zip", '', false)) {
00200                     // assuming that the information is in res0001.dat
00201                     // after looking at 6 examples this was always the case
00202                     $q_file = "$temp_dir/res00001.dat";
00203                     if (is_file($q_file)) {
00204                         if (is_readable($q_file)) {
00205                             $filearray = file($q_file);
00207                             if (preg_match("~\r~", $filearray[0]) AND !preg_match("~\n~", $filearray[0])) {
00208                                 return explode("\r", $filearray[0]);
00209                             } else {
00210                                 return $filearray;
00211                             }
00212                         }
00213                     }
00214                     else {
00215                         print_error('cannotfindquestionfile', 'questioni');
00216                     }
00217                 }
00218                 else {
00219                     print "filename: $filename<br />tempdir: $temp_dir <br />";
00220                     print_error('cannotunzip', 'question');
00221                 }
00222             }
00223             else {
00224                 print_error('cannotreaduploadfile');
00225             }
00226         }
00227         else {
00228             print_error('cannotcreatetempdir');
00229         }
00230     }
00231 
00232     function save_question_options($question) {
00233         return true;
00234     }
00235 
00236 
00237 
00238   function readquestions ($lines) {
00242 
00243     $text = implode($lines, " ");
00244     $xml = xmlize($text, 0);
00245 
00246     $raw_questions = $xml['questestinterop']['#']['assessment'][0]['#']['section'][0]['#']['item'];
00247     $questions = array();
00248 
00249     foreach($raw_questions as $quest) {
00250         $question = $this->create_raw_question($quest);
00251 
00252         switch($question->qtype) {
00253             case "Matching":
00254                 $this->process_matching($question, $questions);
00255                 break;
00256             case "Multiple Choice":
00257                 $this->process_mc($question, $questions);
00258                 break;
00259             case "Essay":
00260                 $this->process_essay($question, $questions);
00261                 break;
00262             case "Multiple Answer":
00263                 $this->process_ma($question, $questions);
00264                 break;
00265             case "True/False":
00266                 $this->process_tf($question, $questions);
00267                 break;
00268             case 'Fill in the Blank':
00269                 $this->process_fblank($question, $questions);
00270                 break;
00271             case 'Short Response':
00272                 $this->process_essay($question, $questions);
00273                 break;
00274             default:
00275                 print "Unknown or unhandled question type: \"$question->qtype\"<br />";
00276                 break;
00277         }
00278 
00279     }
00280     return $questions;
00281   }
00282 
00283 
00284 // creates a cleaner object to deal with for processing into moodle
00285 // the object created is NOT a moodle question object
00286 function create_raw_question($quest) {
00287 
00288     $question = new stdClass();
00289     $question->qtype = $quest['#']['itemmetadata'][0]['#']['bbmd_questiontype'][0]['#'];
00290     $question->id = $quest['#']['itemmetadata'][0]['#']['bbmd_asi_object_id'][0]['#'];
00291     $presentation->blocks = $quest['#']['presentation'][0]['#']['flow'][0]['#']['flow'];
00292 
00293     foreach($presentation->blocks as $pblock) {
00294 
00295         $block = NULL;
00296         $block->type = $pblock['@']['class'];
00297 
00298         switch($block->type) {
00299             case 'QUESTION_BLOCK':
00300                 $sub_blocks = $pblock['#']['flow'];
00301                 foreach($sub_blocks as $sblock) {
00302                     //echo "Calling process_block from line 263<br>";
00303                     $this->process_block($sblock, $block);
00304                 }
00305                 break;
00306 
00307             case 'RESPONSE_BLOCK':
00308                 $choices = NULL;
00309                 switch($question->qtype) {
00310                     case 'Matching':
00311                         $bb_subquestions = $pblock['#']['flow'];
00312                         $sub_questions = array();
00313                         foreach($bb_subquestions as $bb_subquestion) {
00314                             $sub_question = NULL;
00315                             $sub_question->ident = $bb_subquestion['#']['response_lid'][0]['@']['ident'];
00316                             $this->process_block($bb_subquestion['#']['flow'][0], $sub_question);
00317                             $bb_choices = $bb_subquestion['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
00318                             $choices = array();
00319                             $this->process_choices($bb_choices, $choices);
00320                             $sub_question->choices = $choices;
00321                             if (!isset($block->subquestions)) {
00322                                 $block->subquestions = array();
00323                             }
00324                             $block->subquestions[] = $sub_question;
00325                         }
00326                         break;
00327                     case 'Multiple Answer':
00328                         $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
00329                         $choices = array();
00330                         $this->process_choices($bb_choices, $choices);
00331                         $block->choices = $choices;
00332                         break;
00333                     case 'Essay':
00334                         // Doesn't apply since the user responds with text input
00335                         break;
00336                     case 'Multiple Choice':
00337                         $mc_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
00338                             foreach($mc_choices as $mc_choice) {
00339                             $choices = NULL;
00340                             $choices = $this->process_block($mc_choice, $choices);
00341                             $block->choices[] = $choices;
00342                         }
00343                         break;
00344                     case 'Short Response':
00345                         // do nothing?
00346                         break;
00347                     case 'Fill in the Blank':
00348                         // do nothing?
00349                         break;
00350                     default:
00351                         $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
00352                         $choices = array();
00353                         $this->process_choices($bb_choices, $choices);
00354                         $block->choices = $choices;
00355                 }
00356                 break;
00357             case 'RIGHT_MATCH_BLOCK':
00358                 $matching_answerset = $pblock['#']['flow'];
00359 
00360                 $answerset = array();
00361                 foreach($matching_answerset as $answer) {
00362                     // $answerset[] = $this->process_block($answer, $bb_answer);
00363                     $bb_answer = null;
00364                     $bb_answer->text = $answer['#']['flow'][0]['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
00365                     $answerset[] = $bb_answer;
00366                 }
00367                 $block->matching_answerset = $answerset;
00368                 break;
00369             default:
00370                 print "UNHANDLED PRESENTATION BLOCK";
00371                 break;
00372         }
00373         $question->{$block->type} = $block;
00374     }
00375 
00376     // determine response processing
00377     // there is a section called 'outcomes' that I don't know what to do with
00378     $resprocessing = $quest['#']['resprocessing'];
00379     $respconditions = $resprocessing[0]['#']['respcondition'];
00380     $reponses = array();
00381     if ($question->qtype == 'Matching') {
00382         $this->process_matching_responses($respconditions, $responses);
00383     }
00384     else {
00385         $this->process_responses($respconditions, $responses);
00386     }
00387     $question->responses = $responses;
00388     $feedbackset = $quest['#']['itemfeedback'];
00389     $feedbacks = array();
00390     $this->process_feedback($feedbackset, $feedbacks);
00391     $question->feedback = $feedbacks;
00392     return $question;
00393 }
00394 
00395 function process_block($cur_block, &$block) {
00396     global $COURSE, $CFG;
00397 
00398     $cur_type = $cur_block['@']['class'];
00399     switch($cur_type) {
00400         case 'FORMATTED_TEXT_BLOCK':
00401             $block->text = $this->strip_applet_tags_get_mathml($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#']);
00402             break;
00403         case 'FILE_BLOCK':
00404             //revisit this to make sure it is working correctly
00405             // Commented out ['matapplication']..., etc. because I
00406             // noticed that when I imported a new Blackboard 6 file
00407             // and printed out the block, the tree did not extend past ['material'][0]['#'] - CT 8/3/06
00408             $block->file = $cur_block['#']['material'][0]['#'];//['matapplication'][0]['@']['uri'];
00409             if ($block->file != '') {
00410                 // if we have a file copy it to the course dir and adjust its name to be visible over the web.
00411                 $block->file = $this->copy_file_to_course($block->file);
00412                 $block->file = $CFG->wwwroot.'/file.php/'.$COURSE->id.'/bb_import/'.basename($block->file);
00413             }
00414             break;
00415         case 'Block':
00416             if (isset($cur_block['#']['material'][0]['#']['mattext'][0]['#'])) {
00417             $block->text = $cur_block['#']['material'][0]['#']['mattext'][0]['#'];
00418             }
00419             else if (isset($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'])) {
00420                 $block->text = $cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
00421             }
00422             else if (isset($cur_block['#']['response_label'])) {
00423                 // this is a response label block
00424                 $sub_blocks = $cur_block['#']['response_label'][0];
00425                 if(!isset($block->ident)) {
00426                     if(isset($sub_blocks['@']['ident'])) {
00427                         $block->ident = $sub_blocks['@']['ident'];
00428                     }
00429                 }
00430                 foreach($sub_blocks['#']['flow_mat'] as $sub_block) {
00431                     $this->process_block($sub_block, $block);
00432                 }
00433             }
00434             else {
00435                 if (isset($cur_block['#']['flow_mat']) || isset($cur_block['#']['flow'])) {
00436                     if (isset($cur_block['#']['flow_mat'])) {
00437                         $sub_blocks = $cur_block['#']['flow_mat'];
00438                     }
00439                     elseif (isset($cur_block['#']['flow'])) {
00440                         $sub_blocks = $cur_block['#']['flow'];
00441                     }
00442                    foreach ($sub_blocks as $sblock) {
00443                         // this will recursively grab the sub blocks which should be of one of the other types
00444                         $this->process_block($sblock, $block);
00445                     }
00446                 }
00447             }
00448             break;
00449         case 'LINK_BLOCK':
00450             // not sure how this should be included
00451             if (!empty($cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'])) {
00452                 $block->link = $cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'];
00453             }
00454             else {
00455                $block->link = '';
00456             }
00457             break;
00458     }
00459     return $block;
00460 }
00461 
00462 function process_choices($bb_choices, &$choices) {
00463     foreach($bb_choices as $choice) {
00464             if (isset($choice['@']['ident'])) {
00465             $cur_choice = $choice['@']['ident'];
00466         }
00467         else { //for multiple answer
00468             $cur_choice = $choice['#']['response_label'][0];//['@']['ident'];
00469         }
00470         if (isset($choice['#']['flow_mat'][0])) { //for multiple answer
00471             $cur_block = $choice['#']['flow_mat'][0];
00472             // Reset $cur_choice to NULL because process_block is expecting an object
00473             // for the second argument and not a string, which is what is was set as
00474             // originally - CT 8/7/06
00475             $cur_choice = null;
00476             $this->process_block($cur_block, $cur_choice);
00477         }
00478         elseif (isset($choice['#']['response_label'])) {
00479             // Reset $cur_choice to NULL because process_block is expecting an object
00480             // for the second argument and not a string, which is what is was set as
00481             // originally - CT 8/7/06
00482             $cur_choice = null;
00483             $this->process_block($choice, $cur_choice);
00484         }
00485         $choices[] = $cur_choice;
00486     }
00487 }
00488 
00489 function process_matching_responses($bb_responses, &$responses) {
00490     foreach($bb_responses as $bb_response) {
00491         $response = NULL;
00492         if (isset($bb_response['#']['conditionvar'][0]['#']['varequal'])) {
00493             $response->correct = $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['#'];
00494             $response->ident = $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['@']['respident'];
00495         }
00496         else {
00497             $response->correct =  'Broken Question?';
00498             $response->ident = 'Broken Question?';
00499         }
00500         $response->feedback = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
00501         $responses[] = $response;
00502     }
00503 }
00504 
00505 function process_responses($bb_responses, &$responses) {
00506     foreach($bb_responses as $bb_response) {
00507         //Added this line to instantiate $response.
00508         // Without instantiating the $response variable, the same object
00509         // gets added to the array
00510         $response = new stdClass();
00511         if (isset($bb_response['@']['title'])) {
00512                 $response->title = $bb_response['@']['title'];
00513             }
00514             else {
00515                 $reponse->title = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
00516             }
00517             $reponse->ident = array();
00518             if (isset($bb_response['#']['conditionvar'][0]['#'])){//['varequal'][0]['#'])) {
00519                 $response->ident[0] = $bb_response['#']['conditionvar'][0]['#'];//['varequal'][0]['#'];
00520             }
00521             else if (isset($bb_response['#']['conditionvar'][0]['#']['other'][0]['#'])) {
00522                 $response->ident[0] = $bb_response['#']['conditionvar'][0]['#']['other'][0]['#'];
00523             }
00524 
00525             if (isset($bb_response['#']['conditionvar'][0]['#']['and'])){//[0]['#'])) {
00526                 $responseset = $bb_response['#']['conditionvar'][0]['#']['and'];//[0]['#']['varequal'];
00527                 foreach($responseset as $rs) {
00528                     $response->ident[] = $rs['#'];
00529                     if(!isset($response->feedback) and isset( $rs['@'] ) ) {
00530                         $response->feedback = $rs['@']['respident'];
00531                     }
00532                 }
00533             }
00534             else {
00535                 $response->feedback = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
00536             }
00537 
00538             // determine what point value to give response
00539             if (isset($bb_response['#']['setvar'])) {
00540                 switch ($bb_response['#']['setvar'][0]['#']) {
00541                     case "SCORE.max":
00542                         $response->fraction = 1;
00543                         break;
00544                     default:
00545                         // I have only seen this being 0 or unset
00546                         // there are probably fractional values of SCORE.max, but I'm not sure what they look like
00547                         $response->fraction = 0;
00548                         break;
00549                 }
00550             }
00551             else {
00552                // just going to assume this is the case this is probably not correct.
00553                $response->fraction = 0;
00554             }
00555 
00556             $responses[] = $response;
00557         }
00558 }
00559 
00560 function process_feedback($feedbackset, &$feedbacks) {
00561     foreach($feedbackset as $bb_feedback) {
00562         // Added line $feedback=null so that $feedback does not get reused in the loop
00563         // and added the the $feedbacks[] array multiple times
00564         $feedback = null;
00565         $feedback->ident = $bb_feedback['@']['ident'];
00566         if (isset($bb_feedback['#']['flow_mat'][0])) {
00567             $this->process_block($bb_feedback['#']['flow_mat'][0], $feedback);
00568         }
00569         elseif (isset($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0])) {
00570             $this->process_block($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0], $feedback);
00571         }
00572         $feedbacks[] = $feedback;
00573     }
00574 }
00575 
00579 function process_common( $quest ) {
00580     $question = $this->defaultquestion();
00581     $question->questiontext = $quest->QUESTION_BLOCK->text;
00582     $question->name = shorten_text( $quest->id, 250 );
00583 
00584     return $question;
00585 }
00586 
00587 //----------------------------------------
00588 // Process True / False Questions
00589 //----------------------------------------
00590 function process_tf($quest, &$questions) {
00591     $question = $this->process_common( $quest );
00592 
00593     $question->qtype = TRUEFALSE;
00594     $question->single = 1; // Only one answer is allowed
00595     // 0th [response] is the correct answer.
00596     $responses = $quest->responses;
00597     $correctresponse = $responses[0]->ident[0]['varequal'][0]['#'];
00598     if ($correctresponse != 'false') {
00599         $correct = true;
00600     }
00601     else {
00602         $correct = false;
00603     }
00604 
00605     foreach($quest->feedback as $fb) {
00606         $fback->{$fb->ident} = $fb->text;
00607     }
00608 
00609     if ($correct) {  // true is correct
00610         $question->answer = 1;
00611         $question->feedbacktrue = $fback->correct;
00612         $question->feedbackfalse = $fback->incorrect;
00613     } else {  // false is correct
00614         $question->answer = 0;
00615         $question->feedbacktrue = $fback->incorrect;
00616         $question->feedbackfalse = $fback->correct;
00617     }
00618     $question->correctanswer = $question->answer;
00619     $questions[] = $question;
00620 }
00621 
00622 
00623 //----------------------------------------
00624 // Process Fill in the Blank
00625 //----------------------------------------
00626 function process_fblank($quest, &$questions) {
00627     $question = $this->process_common( $quest );
00628     $question->qtype = SHORTANSWER;
00629     $question->single = 1;
00630 
00631     $answers = array();
00632     $fractions = array();
00633     $feedbacks = array();
00634 
00635     // extract the feedback
00636     $feedback = array();
00637     foreach($quest->feedback as $fback) {
00638         if (isset($fback->ident)) {
00639             if ($fback->ident == 'correct' || $fback->ident == 'incorrect') {
00640                 $feedback[$fback->ident] = $fback->text;
00641             }
00642         }
00643     }
00644 
00645     foreach($quest->responses as $response) {
00646         if(isset($response->title)) {
00647             if (isset($response->ident[0]['varequal'][0]['#'])) {
00648                 //for BB Fill in the Blank, only interested in correct answers
00649                 if ($response->feedback = 'correct') {
00650                     $answers[] = $response->ident[0]['varequal'][0]['#'];
00651                     $fractions[] = 1;
00652                     if (isset($feedback['correct'])) {
00653                         $feedbacks[] = $feedback['correct'];
00654                     }
00655                     else {
00656                         $feedbacks[] = '';
00657                     }
00658                 }
00659             }
00660 
00661         }
00662     }
00663 
00664     //Adding catchall to so that students can see feedback for incorrect answers when they enter something the
00665     //instructor did not enter
00666     $answers[] = '*';
00667     $fractions[] = 0;
00668     if (isset($feedback['incorrect'])) {
00669         $feedbacks[] = $feedback['incorrect'];
00670     }
00671     else {
00672         $feedbacks[] = '';
00673     }
00674 
00675     $question->answer = $answers;
00676     $question->fraction = $fractions;
00677     $question->feedback = $feedbacks; // Changed to assign $feedbacks to $question->feedback instead of
00678 
00679     if (!empty($question)) {
00680         $questions[] = $question;
00681     }
00682 
00683 }
00684 
00685 //----------------------------------------
00686 // Process Multiple Choice Questions
00687 //----------------------------------------
00688 function process_mc($quest, &$questions) {
00689     $question = $this->process_common( $quest );
00690     $question->qtype = MULTICHOICE;
00691     $question->single = 1;
00692 
00693     $feedback = array();
00694     foreach($quest->feedback as $fback) {
00695         $feedback[$fback->ident] = $fback->text;
00696     }
00697 
00698     foreach($quest->responses as $response) {
00699         if (isset($response->title)) {
00700             if ($response->title == 'correct') {
00701                 // only one answer possible for this qtype so first index is correct answer
00702                 $correct = $response->ident[0]['varequal'][0]['#'];
00703             }
00704         }
00705         else {
00706             // fallback method for when the title is not set
00707             if ($response->feedback == 'correct') {
00708                // only one answer possible for this qtype so first index is correct answer
00709                $correct = $response->ident[0]['varequal'][0]['#']; // added [0]['varequal'][0]['#'] to $response->ident - CT 8/9/06
00710             }
00711         }
00712     }
00713 
00714     $i = 0;
00715     foreach($quest->RESPONSE_BLOCK->choices as $response) {
00716         $question->answer[$i] = $response->text;
00717         if ($correct == $response->ident) {
00718             $question->fraction[$i] = 1;
00719             // this is a bit of a hack to catch the feedback... first we see if a 'correct' feedback exists
00720             // then specific feedback for this question (maybe this should be switched?, but from my example
00721             // question pools I have not seen response specific feedback, only correct or incorrect feedback
00722             if (!empty($feedback['correct'])) {
00723                 $question->feedback[$i] = $feedback['correct'];
00724             }
00725             elseif (!empty($feedback[$i])) {
00726                 $question->feedback[$i] = $feedback[$i];
00727             }
00728             else {
00729                 // failsafe feedback (should be '' instead?)
00730                 $question->feedback[$i] = "correct";
00731             }
00732         }
00733         else {
00734             $question->fraction[$i] = 0;
00735             if (!empty($feedback['incorrect'])) {
00736                 $question->feedback[$i] = $feedback['incorrect'];
00737             }
00738             elseif (!empty($feedback[$i])) {
00739                 $question->feedback[$i] = $feedback[$i];
00740             }
00741             else {
00742                 // failsafe feedback (should be '' instead?)
00743                 $question->feedback[$i] = 'incorrect';
00744             }
00745         }
00746         $i++;
00747     }
00748 
00749     if (!empty($question)) {
00750         $questions[] = $question;
00751     }
00752 }
00753 
00754 //----------------------------------------
00755 // Process Multiple Choice Questions With Multiple Answers
00756 //----------------------------------------
00757 function process_ma($quest, &$questions) {
00758     $question = $this->process_common( $quest ); // copied this from process_mc
00759     $question->qtype = MULTICHOICE;
00760     $question->single = 0; // More than one answer allowed
00761 
00762     $answers = $quest->responses;
00763     $correct_answers = array();
00764     foreach($answers as $answer) {
00765         if($answer->title == 'correct') {
00766             $answerset = $answer->ident[0]['and'][0]['#']['varequal'];
00767             foreach($answerset as $ans) {
00768                 $correct_answers[] = $ans['#'];
00769             }
00770         }
00771     }
00772 
00773     foreach ($quest->feedback as $fb) {
00774         $feedback->{$fb->ident} = trim($fb->text);
00775     }
00776 
00777     $correct_answer_count = count($correct_answers);
00778     $choiceset = $quest->RESPONSE_BLOCK->choices;
00779     $i = 0;
00780     foreach($choiceset as $choice) {
00781         $question->answer[$i] = trim($choice->text);
00782         if (in_array($choice->ident, $correct_answers)) {
00783             // correct answer
00784             $question->fraction[$i] = floor(100000/$correct_answer_count)/100000; // strange behavior if we have more than 5 decimal places
00785             $question->feedback[$i] = $feedback->correct;
00786         }
00787         else {
00788             // wrong answer
00789             $question->fraction[$i] = 0;
00790             $question->feedback[$i] = $feedback->incorrect;
00791         }
00792         $i++;
00793     }
00794 
00795     $questions[] = $question;
00796 }
00797 
00798 //----------------------------------------
00799 // Process Essay Questions
00800 //----------------------------------------
00801 function process_essay($quest, &$questions) {
00802 // this should be rewritten to accomodate moodle 1.6 essay question type eventually
00803 
00804     if (defined("ESSAY")) {
00805         // treat as short answer
00806         $question = $this->process_common( $quest ); // copied this from process_mc
00807         $question->qtype = ESSAY;
00808 
00809         $question->feedback = array();
00810         // not sure where to get the correct answer from
00811         foreach($quest->feedback as $feedback) {
00812             // Added this code to put the possible solution that the
00813             // instructor gives as the Moodle answer for an essay question
00814             if ($feedback->ident == 'solution') {
00815                 $question->feedback = $feedback->text;
00816             }
00817         }
00818         //Added because essay/questiontype.php:save_question_option is expecting a
00819         //fraction property - CT 8/10/06
00820         $question->fraction[] = 1;
00821         if (!empty($question)) {
00822             $questions[]=$question;
00823         }
00824     }
00825     else {
00826         print "Essay question types are not handled because the quiz question type 'Essay' does not exist in this installation of Moodle<br/>";
00827         print "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
00828     }
00829 }
00830 
00831 //----------------------------------------
00832 // Process Matching Questions
00833 //----------------------------------------
00834 function process_matching($quest, &$questions) {
00835     // renderedmatch is an optional plugin, so we need to check if it is defined
00836     if (question_bank::is_qtype_installed('renderedmatch')) {
00837         $question = $this->process_common($quest);
00838         $question->valid = true;
00839         $question->qtype = 'renderedmatch';
00840 
00841         foreach($quest->RESPONSE_BLOCK->subquestions as $qid => $subq) {
00842             foreach($quest->responses as $rid => $resp) {
00843                 if ($resp->ident == $subq->ident) {
00844                     $correct = $resp->correct;
00845                     $feedback = $resp->feedback;
00846                 }
00847             }
00848 
00849             foreach($subq->choices as $cid => $choice) {
00850                 if ($choice == $correct) {
00851                     $question->subquestions[] = $subq->text;
00852                     $question->subanswers[] = $quest->RIGHT_MATCH_BLOCK->matching_answerset[$cid]->text;
00853                 }
00854             }
00855         }
00856 
00857         // check format
00858         $status = true;
00859         if ( count($quest->RESPONSE_BLOCK->subquestions) > count($quest->RIGHT_MATCH_BLOCK->matching_answerset) || count($question->subquestions) < 2) {
00860             $status = false;
00861         }
00862         else {
00863             // need to redo to make sure that no two questions have the same answer (rudimentary now)
00864             foreach($question->subanswers as $qstn) {
00865                 if(isset($previous)) {
00866                     if ($qstn == $previous) {
00867                         $status = false;
00868                     }
00869                 }
00870                 $previous = $qstn;
00871                 if ($qstn == '') {
00872                     $status = false;
00873                 }
00874             }
00875         }
00876 
00877         if ($status) {
00878             $questions[] = $question;
00879         }
00880         else {
00881             global $COURSE, $CFG;
00882             print '<table class="boxaligncenter" border="1">';
00883             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>';
00884 
00885             print "<tr><td>Question:</td><td>".$quest->QUESTION_BLOCK->text;
00886             if (isset($quest->QUESTION_BLOCK->file)) {
00887                 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>';
00888                 if (preg_match('/(gif|jpg|jpeg|png)$/i', $quest->QUESTION_BLOCK->file)) {
00889                     print '<img src="'.$CFG->wwwroot.'/file.php/'.$COURSE->id.'/bb_import/'.basename($quest->QUESTION_BLOCK->file).'" />';
00890                 }
00891             }
00892             print "</td></tr>";
00893             print "<tr><td>Subquestions:</td><td><ul>";
00894             foreach($quest->responses as $rs) {
00895                 $correct_responses->{$rs->ident} = $rs->correct;
00896             }
00897             foreach($quest->RESPONSE_BLOCK->subquestions as $subq) {
00898                 print '<li>'.$subq->text.'<ul>';
00899                 foreach($subq->choices as $id=>$choice) {
00900                     print '<li>';
00901                     if ($choice == $correct_responses->{$subq->ident}) {
00902                         print '<font color="green">';
00903                     }
00904                     else {
00905                         print '<font color="red">';
00906                     }
00907                     print $quest->RIGHT_MATCH_BLOCK->matching_answerset[$id]->text.'</font></li>';
00908                 }
00909                 print '</ul>';
00910             }
00911             print '</ul></td></tr>';
00912 
00913             print '<tr><td>Feedback:</td><td><ul>';
00914             foreach($quest->feedback as $fb) {
00915                 print '<li>'.$fb->ident.': '.$fb->text.'</li>';
00916             }
00917             print '</ul></td></tr></table>';
00918         }
00919     }
00920     else {
00921         print "Matching question types are not handled because the quiz question type 'Rendered Matching' does not exist in this installation of Moodle<br/>";
00922         print "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
00923     }
00924 }
00925 
00926 
00927 function strip_applet_tags_get_mathml($string) {
00928     if(stristr($string, '</APPLET>') === FALSE) {
00929         return $string;
00930     }
00931     else {
00932         // strip all applet tags keeping stuff before/after and inbetween (if mathml) them
00933         while (stristr($string, '</APPLET>') !== FALSE) {
00934             preg_match("/(.*)<applet.*value=\"(<math>.*<\/math>)\".*<\/applet>(.*)/i",$string, $mathmls);
00935             $string = $mathmls[1].$mathmls[2].$mathmls[3];
00936         }
00937         return $string;
00938     }
00939 }
00940 
00941 } // close object
00942 
 All Data Structures Namespaces Files Functions Variables Enumerations