/** * This function will add the attributes that are extracted from the qti xml * into the database. * * @param array attributes that are extracted from the QTI XML. * @return int the question ids. */ function importQuestions($attributes) { global $supported_media_type, $msg; $qids = array(); foreach ($attributes as $resource => $attrs) { if (preg_match('/imsqti\\_(.*)/', $attrs['type'])) { //Instantiate class obj $xml = new QTIParser($attrs['type']); $xml_content = @file_get_contents($this->import_path . $attrs['href']); $xml->setRelativePath($package_base_name); if (!$xml->parse($xml_content)) { $msg->addError('QTI_WRONG_PACKAGE'); break; } //set test title $this->title = $xml->title; //if ($attrs[href] =='56B1BEDC-A820-7AA8-A21D-F32017189445/56B1BEDC-A820-7AA8-A21D-F32017189445.xml'){ // debug($xml, 'attributes'); //} //import file, should we use file href? or jsut this href? //Aug 25, use both, so then it can check for respondus media as well. foreach ($attrs['file'] as $file_id => $file_name) { $file_pathinfo = pathinfo($file_name); if ($file_pathinfo['basename'] == $attrs['href']) { //This file will be parsed later continue; } if (in_array(strtolower($file_pathinfo['extension']), $supported_media_type)) { //copy medias over. $this->copyMedia(array($file_name), $xml->items); } } for ($loopcounter = 0; $loopcounter < $xml->item_num; $loopcounter++) { //Create POST values. unset($test_obj); //clear cache $test_obj['required'] = 1; $test_obj['preset_num'] = 0; $test_obj['category_id'] = 0; $test_obj['question'] = $xml->question[$loopcounter]; $test_obj['feedback'] = $xml->feedback[$loopcounter]; $test_obj['groups'] = $xml->groups[$loopcounter]; $test_obj['property'] = intval($xml->attributes[$loopcounter]['render_fib']['property']); $test_obj['choice'] = array(); $test_obj['answers'] = array(); //assign choices $i = 0; //trim values if (is_array($xml->answers[$loopcounter])) { array_walk($xml->answers[$loopcounter], 'trim_value'); } //TODO: The groups is 1-0+ choices. So we should loop thru groups, not choices. if (is_array($xml->choices[$loopcounter])) { foreach ($xml->choices[$loopcounter] as $choiceNum => $choiceOpt) { if (sizeof($test_obj['groups']) > 0) { if (!empty($xml->answers[$loopcounter])) { foreach ($xml->answers[$loopcounter] as $ansNum => $ansOpt) { if ($choiceNum == $ansOpt) { //Not exactly efficient, worst case N^2 $test_obj['answers'][$ansNum] = $i; } } } } else { //save answer(s) if (is_array($xml->answers[$loopcounter]) && in_array($choiceNum, $xml->answers[$loopcounter])) { $test_obj['answers'][] = $i; } } $test_obj['choice'][] = $choiceOpt; $i++; } } // unset($qti_import); $this->constructParams($test_obj); //debug($xml->getQuestionType($loopcounter), 'lp_'.$loopcounter); //Create questions $this->getQuestionType($xml->getQuestionType($loopcounter)); //save question id $qids[] = $this->qid; //Dependency handling if (!empty($attrs['dependency'])) { $xml_items = array_merge($xml_items, $xml->items); } } //assign title if ($xml->title != '') { $this->title = $xml->title; } //assign marks/weights $this->weights = $xml->weights; $xml->close(); } elseif ($attrs['type'] == 'webcontent') { //webcontent, copy it over. $this->copyMedia($attrs['file'], $xml_items); } } //debug($qids, 'qids'); return $qids; }
/** * Validate all the XML in the package, including checking XSDs, missing data. * @param string the path of the directory that contains all the package files * @return boolean true if every file exists in the manifest, * false if any is missing. */ function checkResources($import_path) { global $items, $msg, $skip_ims_validation, $avail_dt; if (!is_dir($import_path)) { return; } //if the package has access for all content, skip validation for now. //todo: import the XSD into our validator if ($skip_ims_validation) { return true; } //generate a file tree $data = rscandir($import_path); //check if every file is presented in the manifest foreach ($data as $filepath) { $filepath = substr($filepath, strlen($import_path)); //validate xml via its xsd/dtds if (preg_match('/(.*)\\.xml/', $filepath)) { libxml_use_internal_errors(true); $dom = new DOMDocument(); $dom->load(realpath($import_path . $filepath)); if (!@$dom->schemaValidate('main.xsd')) { $errors = libxml_get_errors(); foreach ($errors as $error) { //suppress warnings if ($error->level == LIBXML_ERR_WARNING) { continue; } $msg->addError(array('IMPORT_CARTRIDGE_FAILED', libxml_display_error($error))); } libxml_clear_errors(); } //if this is the manifest file, we do not have //to check for its existance. // if (preg_match('/(.*)imsmanifest\.xml/', $filepath)){ // continue; // } } } //Create an array that mimics the structure of the data array, //based on the xml items $filearray = array(); foreach ($items as $name => $fileinfo) { if (isset($fileinfo['file']) && is_array($fileinfo['file']) && !empty($fileinfo['file'])) { foreach ($fileinfo['file'] as $fn) { if (!in_array(realpath($import_path . $fn), $filearray)) { //if url, skip if (preg_match('/^http[s]?\\:/', $fn) == 0) { $filearray[] = realpath($import_path . $fn); } } } } //validate the xml by its schema if (preg_match('/imsqti\\_(.*)/', $fileinfo['type'])) { $qti = new QTIParser($fileinfo['type']); $xml_content = @file_get_contents($import_path . $fileinfo['href']); $qti->parse($xml_content); //will add error to $msg if failed } //add all dependent discussion tools to a list if (isset($fileinfo['dependency']) && !empty($fileinfo['dependency'])) { $avail_dt = array_merge($avail_dt, $fileinfo['dependency']); } } //check if all files in the xml is presented in the archieve $result = array_diff($filearray, $data); //using sizeof because array_diff only //returns an array containing all the entries from array1 that //are not present in any of the //other arrays. //Using sizeof make sure it's not a subset of array2. //-1 on data because it always contain the imsmanifest.xml file if (!$skip_ims_validation) { if (!empty($result) || sizeof($data) - 1 > sizeof($filearray)) { $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references'))); } } return true; }
/** * Validate all the XML in the package, including checking XSDs, missing data. * @param string the path of the directory that contains all the package files * @return boolean true if every file exists in the manifest, false if any is missing. */ function checkResources($import_path) { global $items, $msg, $skip_ims_validation; if (!is_dir($import_path)) { return; } //if the package has access for all content, skip validation for now. //todo: import the XSD into our validator if ($skip_ims_validation) { return true; } //generate a file tree $data = rscandir($import_path); //check if every file is presented in the manifest foreach ($data as $filepath) { $filepath = substr($filepath, strlen($import_path)); //validate xml via its xsd/dtds if (preg_match('/(.*)\\.xml/', $filepath)) { $dom = new DOMDocument(); $dom->load(realpath($import_path . $filepath)); if (!@$dom->schemaValidate('main.xsd')) { $msg->addError('MANIFEST_FAILED_VALIDATION - ' . $filepath); } //if this is the manifest file, we do not have to check for its existance. if (preg_match('/(.*)imsmanifest\\.xml/', $filepath)) { continue; } } $flag = false; $file_exists_in_manifest = false; //check if every file in manifest indeed exists foreach ($items as $name => $fileinfo) { if (is_array($fileinfo['file'])) { if (in_array($filepath, $fileinfo['file'])) { $file_exists_in_manifest = true; //validate the xml by its schema if (preg_match('/imsqti\\_(.*)/', $fileinfo['type'])) { $qti = new QTIParser($fileinfo['type']); $xml_content = @file_get_contents($import_path . $fileinfo['href']); $qti->parse($xml_content); if ($msg->containsErrors()) { $flag = false; } else { $flag = true; } } else { $flag = true; } } } } //check if all the files exists in the manifest, if not, throw error. if (!$file_exists_in_manifest) { $msg->addError('MANIFEST_NOT_WELLFORM: MISSING REFERENCES'); break; } if ($flag == false) { //add an error message if it doesn't have any. if (!$msg->containsErrors()) { $msg->addError('MANIFEST_NOT_WELLFORM: MISSING REFERENCES'); } return false; } } return true; }