/** * It is sometimes necessary to identify the link between assessmentItemRefs described in a QTI Test definition and the resources * describing items in IMS Manifest file. This utility method helps you to achieve this. * * The method will return an array describing the IMS Manifest resources that were found in an IMS Manifest file on basis of * the assessmentItemRefs found in an AssessmentTest definition. The keys of the arrays are assessmentItemRef identifiers and * values are IMS Manifest Resources. * * If an IMS Manifest Resource cannot be found for a given assessmentItemRef, the value in the returned array will be false. * * @param XmlDocument $test A QTI Test Definition. * @param taoQtiTest_models_classes_ManifestParser $manifestParser A Manifest Parser. * @param string $basePath The base path of the folder the IMS archive is exposed as a file system component. * @return array An array containing two arrays (items and dependencies) where keys are identifiers and values are oat\taoQtiItem\model\qti\Resource objects or false. */ public static function buildAssessmentItemRefsTestMap(XmlDocument $test, taoQtiTest_models_classes_ManifestParser $manifestParser, $basePath) { $assessmentItemRefs = $test->getDocumentComponent()->getComponentsByClassName('assessmentItemRef'); $map = array('items' => array(), 'dependencies' => array()); $itemResources = $manifestParser->getResources(array('imsqti_item_xmlv2p1', 'imsqti_apipitem_xmlv2p1'), taoQtiTest_models_classes_ManifestParser::FILTER_RESOURCE_TYPE); $allResources = $manifestParser->getResources(); // cleanup $basePath. $basePath = rtrim($basePath, "/\\"); $basePath = helpers_File::truePath($basePath); $basePath .= DIRECTORY_SEPARATOR; $documentURI = preg_replace('/^file:\\//', '', $test->getDomDocument()->documentURI); $testPathInfo = pathinfo($documentURI); $testBasePath = tao_helpers_File::truePath($testPathInfo['dirname']) . DIRECTORY_SEPARATOR; foreach ($assessmentItemRefs as $itemRef) { // Find the QTI Resource (in IMS Manifest) related to the item ref. // To achieve this, we compare their path. $itemRefRelativeHref = str_replace('/', DIRECTORY_SEPARATOR, $itemRef->getHref()); $itemRefRelativeHref = ltrim($itemRefRelativeHref, "/\\"); $itemRefCanonicalHref = helpers_File::truePath($testBasePath . $itemRefRelativeHref); $map['items'][$itemRef->getIdentifier()] = false; // Compare with items referenced in the manifest. foreach ($itemResources as $itemResource) { $itemResourceRelativeHref = str_replace('/', DIRECTORY_SEPARATOR, $itemResource->getFile()); $itemResourceRelativeHref = ltrim($itemResourceRelativeHref, "/\\"); $itemResourceCanonicalHref = helpers_File::truePath($basePath . $itemResourceRelativeHref); // With some Windows flavours (Win7, Win8), the $itemRefCanonicalHref comes out with // a leading 'file:\' component. Let's clean this. (str_replace is binary-safe \0/) $os = tao_helpers_Environment::getOperatingSystem(); if ($os === 'WINNT' || $os === 'WIN32' || $os === 'Windows') { $itemRefCanonicalHref = str_replace('file:\\', '', $itemRefCanonicalHref); // And moreover, it sometimes refer the temp directory as Windows\TEMP instead of Windows\Temp. $itemRefCanonicalHref = str_replace('\\TEMP\\', '\\Temp\\', $itemRefCanonicalHref); } // With some MacOS flavours, the $itemRefCanonicalHref comes out with // a leading '/private' component. Clean it! if ($os === 'Darwin') { $itemRefCanonicalHref = str_replace('/private', '', $itemRefCanonicalHref); } if ($itemResourceCanonicalHref == $itemRefCanonicalHref && is_file($itemResourceCanonicalHref)) { // assessmentItemRef <-> IMS Manifest resource successful binding! $map['items'][$itemRef->getIdentifier()] = $itemResource; //get dependencies for each item foreach ($itemResource->getDependencies() as $dependencyIdentifier) { /** @var taoQtiTest_models_classes_QtiResource $resource */ foreach ($allResources as $resource) { if ($dependencyIdentifier == $resource->getIdentifier()) { $map['dependencies'][$dependencyIdentifier] = $resource; break; } } } break; } } } return $map; }
/** * Import a QTI Test Package containing one or more QTI Test definitions. * * @param core_kernel_classes_Class $targetClass The Target RDFS class where you want the Test Resources to be created. * @param string $file The path to the IMS archive you want to import tests from. * @return common_report_Report An import report. */ public function importMultipleTests(core_kernel_classes_Class $targetClass, $file) { $testClass = $targetClass; $report = new common_report_Report(common_report_Report::TYPE_INFO); $validPackage = false; $validManifest = false; $testsFound = false; // Validate the given IMS Package itself (ZIP integrity, presence of an 'imsmanifest.xml' file. $invalidArchiveMsg = __("The provided archive is invalid. Make sure it is not corrupted and that it contains an 'imsmanifest.xml' file."); try { $qtiPackageParser = new taoQtiTest_models_classes_PackageParser($file); $qtiPackageParser->validate(); $validPackage = true; } catch (Exception $e) { $report->add(common_report_Report::createFailure($invalidArchiveMsg)); } // Validate the manifest (well formed XML, valid against the schema). if ($validPackage === true) { $folder = $qtiPackageParser->extract(); if (is_dir($folder) === false) { $report->add(common_report_Report::createFailure($invalidArchiveMsg)); } else { $qtiManifestParser = new taoQtiTest_models_classes_ManifestParser($folder . 'imsmanifest.xml'); $qtiManifestParser->validate(); if ($qtiManifestParser->isValid() === true) { $validManifest = true; $tests = array(); foreach (Resource::getTestTypes() as $type) { $tests = array_merge($tests, $qtiManifestParser->getResources($type)); } $testsFound = count($tests) !== 0; if ($testsFound !== true) { $report->add(common_report_Report::createFailure(__("Package is valid but no tests were found. Make sure that it contains valid QTI tests."))); } else { foreach ($tests as $qtiTestResource) { $report->add($this->importTest($testClass, $qtiTestResource, $qtiManifestParser, $folder)); } } } else { $msg = __("The 'imsmanifest.xml' file found in the archive is not valid."); $report->add(common_report_Report::createFailure($msg)); } // Cleanup the folder where the archive was extracted. tao_helpers_File::deltree($folder); } } if ($report->containsError() === true) { $report->setMessage(__('The IMS QTI Test Package could not be imported.')); $report->setType(common_report_Report::TYPE_ERROR); } else { $report->setMessage(__('IMS QTI Test Package successfully imported.')); $report->setType(common_report_Report::TYPE_SUCCESS); } if ($report->containsError() === true && $validPackage === true && $validManifest === true && $testsFound === true) { // We consider a test package as an atomic component, we then rollback it. $itemService = taoItems_models_classes_ItemsService::singleton(); foreach ($report as $r) { $data = $r->getData(); // Delete all imported items. foreach ($data->items as $item) { common_Logger::i("Rollbacking item '" . $item->getLabel() . "'..."); @$itemService->deleteItem($item); } // Delete the target Item RDFS class. common_Logger::i("Rollbacking Items target RDFS class '" . $data->itemClass->getLabel() . "'..."); @$data->itemClass->delete(); // Delete test definition. common_Logger::i("Rollbacking test '" . $data->rdfsResource->getLabel() . "..."); @$this->deleteTest($data->rdfsResource); if (count($data->items) > 0) { $msg = __("The resources related to the IMS QTI Test referenced as \"%s\" in the IMS Manifest file were rolled back.", $data->manifestResource->getIdentifier()); $report->add(new common_report_Report(common_report_Report::TYPE_WARNING, $msg)); } } } return $report; }
public function addItems($directory) { $qtiManifestParser = new \taoQtiTest_models_classes_ManifestParser($directory . 'imsmanifest.xml'); $itemTypes = array('imsqti_item_xmlv2p1', 'imsqti_apipitem_xmlv2p2', 'imsqti_apipitem_xmlv2p1'); $items = $qtiManifestParser->getResources(); $todo = array(); foreach ($items as $res) { if (in_array($res->getType(), $itemTypes)) { $this->addXml($directory . $res->getFile()); } foreach ($res->getAuxiliaryFiles() as $file) { $mime = \tao_helpers_File::getMimeType($directory . $file); $prefix = substr($mime, 0, strpos($mime, '/')); if ($prefix == 'image') { $this->replaceImage($directory . $file); } } } }