/** * test qti file parsing: validation and loading in a non-persistant context */ public function testFileParsing() { //check if wrong packages are not validated correctly foreach (glob(dirname(__FILE__) . '/samples/parser/wrong/*.zip') as $file) { $qtiParser = new PackageParser($file); $qtiParser->validate(); $this->assertFalse($qtiParser->isValid(), 'failed with: ' . $file); $this->assertTrue(count($qtiParser->getErrors()) > 0); $this->assertTrue(strlen($qtiParser->displayErrors()) > 0); } //check if package samples are valid foreach (glob(dirname(__FILE__) . '/samples/package/QTI/*.zip') as $file) { $qtiParser = new PackageParser($file); $qtiParser->validate(); if (!$qtiParser->isValid()) { echo $qtiParser->displayErrors(); } $this->assertTrue($qtiParser->isValid()); } //check if wrong manifest files are not validated correctly foreach (glob(dirname(__FILE__) . '/samples/package/wrong/*.xml') as $file) { $qtiParser = new ManifestParser($file); $qtiParser->validate(); $this->assertFalse($qtiParser->isValid()); $this->assertTrue(count($qtiParser->getErrors()) > 0); $this->assertTrue(strlen($qtiParser->displayErrors()) > 0); } //check if manifest samples are valid $basePath = common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir(); foreach (glob(dirname(__FILE__) . '/samples/package/*.xml') as $file) { $qtiParser = new ManifestParser($file); $schema = ''; $fileContent = file_get_contents($file); if (strpos($fileContent, 'xmlns="http://www.imsglobal.org/xsd/apip/apipv1p0/imscp_v1p1"')) { $schema = $basePath . 'model/qti/data/apipv1p0/Core_Level/Package/apipv1p0_imscpv1p2_v1p0.xsd'; } $qtiParser->validate($schema); if (!$qtiParser->isValid()) { echo $qtiParser->displayErrors(); } $this->assertTrue($qtiParser->isValid()); } }
/** * imports a qti package and * returns the number of items imported * * @access public * @author Joel Bout, <*****@*****.**> * @param $file * @param core_kernel_classes_Class $itemClass * @param bool $validate * @param core_kernel_versioning_Repository $repository * @param bool $rollbackOnError * @param bool $rollbackOnWarning * @throws Exception * @throws ExtractException * @throws ParsingException * @throws \common_Exception * @throws \common_ext_ExtensionException * @throws common_exception_Error * @return common_report_Report */ public function importQTIPACKFile($file, core_kernel_classes_Class $itemClass, $validate = true, core_kernel_versioning_Repository $repository = null, $rollbackOnError = false, $rollbackOnWarning = false, $extractApip = false) { //load and validate the package $qtiPackageParser = new PackageParser($file); if ($validate) { $qtiPackageParser->validate(); if (!$qtiPackageParser->isValid()) { throw new ParsingException('Invalid QTI package format'); } } //extract the package $folder = $qtiPackageParser->extract(); if (!is_dir($folder)) { throw new ExtractException(); } $report = new common_report_Report(common_report_Report::TYPE_SUCCESS, ''); $successItems = array(); try { $qtiItemResources = $this->createQtiManifest($folder . 'imsmanifest.xml'); $itemCount = 0; foreach ($qtiItemResources as $qtiItemResource) { $itemCount++; $itemReport = $this->importQtiItem($folder, $qtiItemResource, $itemClass, $extractApip); $rdfItem = $itemReport->getData(); if ($rdfItem) { $successItems[$qtiItemResource->getIdentifier()] = $rdfItem; } $report->add($itemReport); } } catch (ValidationException $ve) { $validationReport = \common_report_Report::createFailure("The IMS Manifest file could not be validated"); $validationReport->add($ve->getReport()); $report->setMessage(__("No Items could be imported from the given IMS QTI package.")); $report->setType(common_report_Report::TYPE_ERROR); $report->add($validationReport); } catch (common_exception_UserReadableException $e) { $report = new common_report_Report(common_report_Report::TYPE_ERROR, __($e->getUserMessage())); $report->add($e); } if (!empty($successItems)) { // Some items were imported from the package. $report->setMessage(__('%d Item(s) of %d imported from the given IMS QTI Package.', count($successItems), $itemCount)); if (count($successItems) !== $itemCount) { $report->setType(common_report_Report::TYPE_WARNING); } } else { $report->setMessage(__('No Items could be imported from the given IMS QTI package.')); $report->setType(common_report_Report::TYPE_ERROR); } if ($rollbackOnError === true) { if ($report->getType() === common_report_Report::TYPE_ERROR || $report->contains(common_report_Report::TYPE_ERROR)) { $this->rollback($successItems, $report); } } elseif ($rollbackOnWarning === true) { if ($report->getType() === common_report_Report::TYPE_WARNING || $report->contains(common_report_Report::TYPE_WARNING)) { $this->rollback($successItems, $report); } } // cleanup tao_helpers_File::delTree($folder); return $report; }
/** * imports a qti package and * returns the number of items imported * * @access public * @author Joel Bout, <*****@*****.**> * @param $file * @param core_kernel_classes_Class $itemClass * @param bool $validate * @param core_kernel_versioning_Repository $repository * @param bool $rollbackOnError * @param bool $rollbackOnWarning * @throws Exception * @throws ExtractException * @throws ParsingException * @throws \common_Exception * @throws \common_ext_ExtensionException * @throws common_exception_Error * @return common_report_Report */ public function importQTIPACKFile($file, core_kernel_classes_Class $itemClass, $validate = true, core_kernel_versioning_Repository $repository = null, $rollbackOnError = false, $rollbackOnWarning = false) { //repository $repository = is_null($repository) ? taoItems_models_classes_ItemsService::singleton()->getDefaultFileSource() : $repository; $report = new common_report_Report(common_report_Report::TYPE_SUCCESS, ''); //load and validate the package $qtiPackageParser = new PackageParser($file); if ($validate) { $qtiPackageParser->validate(); if (!$qtiPackageParser->isValid()) { throw new ParsingException('Invalid QTI package format'); } } //extract the package $folder = $qtiPackageParser->extract(); if (!is_dir($folder)) { throw new ExtractException(); } //load and validate the manifest $qtiManifestParser = new ManifestParser($folder . 'imsmanifest.xml'); if ($validate) { $basePath = common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir(); $this->validateMultiple($qtiManifestParser, array($basePath . 'models/classes/QTI/data/imscp_v1p1.xsd', $basePath . 'models/classes/QTI/data/apipv1p0/Core_Level/Package/apipv1p0_imscpv1p2_v1p0.xsd')); if (!$qtiManifestParser->isValid()) { tao_helpers_File::delTree($folder); $eStrs = array(); foreach ($qtiManifestParser->getErrors() as $libXmlError) { $eStrs[] = __('XML error at line %1$d "%2$s".', $libXmlError['line'], str_replace('[LibXMLError] ', '', trim($libXmlError['message']))); } $report->add(new common_report_Report(common_report_Report::TYPE_ERROR, __("The IMS Manifest file could not be validated:\n%s", implode($eStrs, "\n")))); $report->setType(common_report_Report::TYPE_ERROR); $report->setMessage(__("No Items could be imported from the given IMS QTI package.")); return $report; } } try { //load the information about resources in the manifest $qtiItemResources = $qtiManifestParser->load(); $itemService = taoItems_models_classes_ItemsService::singleton(); $qtiService = Service::singleton(); // The metadata import feature needs a DOM representation of the manifest. $domManifest = new DOMDocument('1.0', 'UTF-8'); $domManifest->load($qtiManifestParser->getSource()); $metadataMapping = $qtiService->getMetadataRegistry()->getMapping(); $metadataInjectors = array(); $metadataValues = array(); foreach ($metadataMapping['injectors'] as $injector) { $metadataInjectors[] = new $injector(); } foreach ($metadataMapping['extractors'] as $extractor) { $metadataExtractor = new $extractor(); $metadataValues = array_merge($metadataValues, $metadataExtractor->extract($domManifest)); } $successItems = array(); $successCount = 0; $itemCount = 0; foreach ($qtiItemResources as $qtiItemResource) { $itemCount++; try { $qtiFile = $folder . $qtiItemResource->getFile(); $itemReport = $this->importQTIFile($qtiFile, $itemClass, $validate, $repository); $rdfItem = $itemReport->getData(); if ($rdfItem) { $itemPath = taoItems_models_classes_ItemsService::singleton()->getItemFolder($rdfItem); $itemContent = $itemService->getItemContent($rdfItem); foreach ($qtiItemResource->getAuxiliaryFiles() as $auxResource) { // $auxResource is a relativ URL, so we need to replace the slashes with directory separators $auxPath = $folder . str_replace('/', DIRECTORY_SEPARATOR, $auxResource); $relPath = helpers_File::getRelPath($qtiFile, $auxPath); //prevent directory traversal: $relPathSafe = str_replace('..' . DIRECTORY_SEPARATOR, '', $relPath, $count); if ($count) { $itemContent = str_replace($relPath, $relPathSafe, $itemContent); } $destPath = $itemPath . $relPathSafe; tao_helpers_File::copy($auxPath, $destPath, true); } // Finally, import metadata. $this->importItemMetadata($metadataValues, $qtiItemResource, $rdfItem, $metadataInjectors); $itemService->setItemContent($rdfItem, $itemContent); $successItems[$qtiItemResource->getIdentifier()] = $rdfItem; $successCount++; } // Modify the message of the item report to include more specific // information e.g. the item identifier. if ($itemReport->containsError() === false) { $itemReport->setMessage(__('The IMS QTI Item referenced as "%s" in the IMS Manifest file was successfully imported.', $qtiItemResource->getIdentifier())); } else { $itemReport->setMessage(__('The IMS QTI Item referenced as "%s" in the IMS Manifest file could not be imported.', $qtiItemResource->getIdentifier())); } $report->add($itemReport); } catch (ParsingException $e) { $report->add(new common_report_Report(common_report_Report::TYPE_ERROR, $e->getUserMessage())); } catch (Exception $e) { // an error occured during a specific item $report->add(new common_report_Report(common_report_Report::TYPE_ERROR, __("An unknown error occured while importing the IMS QTI Package."))); common_Logger::e($e->getMessage()); } } if ($successCount > 0) { // Some items were imported from the package. $report->setMessage(__('%d Item(s) of %d imported from the given IMS QTI Package.', $successCount, $itemCount)); if ($successCount !== $itemCount) { $report->setType(common_report_Report::TYPE_WARNING); } } else { $report->setMessage(__('No Items could be imported from the given IMS QTI package.')); $report->setType(common_report_Report::TYPE_ERROR); } if ($rollbackOnError === true) { if ($report->getType() === common_report_Report::TYPE_ERROR || $report->contains(common_report_Report::TYPE_ERROR)) { $this->rollback($successItems, $report); } } elseif ($rollbackOnWarning === true) { if ($report->getType() === common_report_Report::TYPE_WARNING || $report->contains(common_report_Report::TYPE_WARNING)) { $this->rollback($successItems, $report); } } } catch (common_exception_UserReadableException $e) { $report = new common_report_Report(common_report_Report::TYPE_ERROR, __($e->getUserMessage())); $report->add($e); } // cleanup tao_helpers_File::delTree($folder); return $report; }
/** * imports a qti package and * returns the number of items imported * * @access public * @author Joel Bout, <*****@*****.**> * @param $file * @param core_kernel_classes_Class $itemClass * @param bool $validate * @param core_kernel_versioning_Repository $repository * @param bool $rollbackOnError * @param bool $rollbackOnWarning * @throws Exception * @throws ExtractException * @throws ParsingException * @throws \common_Exception * @throws \common_ext_ExtensionException * @throws common_exception_Error * @return common_report_Report */ public function importQTIPACKFile($file, core_kernel_classes_Class $itemClass, $validate = true, core_kernel_versioning_Repository $repository = null, $rollbackOnError = false, $rollbackOnWarning = false) { //load and validate the package $qtiPackageParser = new PackageParser($file); if ($validate) { $qtiPackageParser->validate(); if (!$qtiPackageParser->isValid()) { throw new ParsingException('Invalid QTI package format'); } } //extract the package $folder = $qtiPackageParser->extract(); if (!is_dir($folder)) { throw new ExtractException(); } $report = new common_report_Report(common_report_Report::TYPE_SUCCESS, ''); $successItems = array(); $allCreatedClasses = array(); try { // -- Initializing metadata services. $metadataMapping = Service::singleton()->getMetadataRegistry()->getMapping(); $metadataInjectors = array(); $metadataGuardians = array(); $metadataClassLookups = array(); $metadataValues = array(); // The metadata import feature needs a DOM representation of the manifest. $domManifest = new DOMDocument('1.0', 'UTF-8'); $domManifest->load($folder . 'imsmanifest.xml'); foreach ($metadataMapping['injectors'] as $injector) { $metadataInjectors[] = new $injector(); \common_Logger::i("Metadata Injector '{$injector}' registered."); } foreach ($metadataMapping['guardians'] as $guardian) { $metadataGuardians[] = new $guardian(); \common_Logger::i("Metadata Guardian '{$guardian}' registered."); } foreach ($metadataMapping['classLookups'] as $classLookup) { $metadataClassLookups[] = new $classLookup(); \common_Logger::i("Metadata Class Lookup '{$classLookup}' registered."); } $qtiItemResources = $this->createQtiManifest($folder . 'imsmanifest.xml'); foreach ($metadataMapping['extractors'] as $extractor) { $metadataExtractor = new $extractor(); \common_Logger::i("Metatada Extractor '{$extractor}' registered."); $metadataValues = array_merge($metadataValues, $metadataExtractor->extract($domManifest)); } $metadataCount = count($metadataValues, COUNT_RECURSIVE); \common_Logger::i("{$metadataCount} Metadata Values found in manifest by extractor(s)."); $itemCount = 0; $sharedFiles = array(); $createdClasses = array(); foreach ($qtiItemResources as $qtiItemResource) { $itemCount++; $itemReport = $this->importQtiItem($folder, $qtiItemResource, $itemClass, array(), $metadataValues, $metadataInjectors, $metadataGuardians, $metadataClassLookups, $sharedFiles, $createdClasses); $allCreatedClasses = array_merge($allCreatedClasses, $createdClasses); $rdfItem = $itemReport->getData(); if ($rdfItem) { $successItems[$qtiItemResource->getIdentifier()] = $rdfItem; } $report->add($itemReport); } } catch (ValidationException $ve) { $validationReport = \common_report_Report::createFailure("The IMS Manifest file could not be validated"); $validationReport->add($ve->getReport()); $report->setMessage(__("No Items could be imported from the given IMS QTI package.")); $report->setType(common_report_Report::TYPE_ERROR); $report->add($validationReport); } catch (common_exception_UserReadableException $e) { $report = new common_report_Report(common_report_Report::TYPE_ERROR, __($e->getUserMessage())); $report->add($e); } if (!empty($successItems)) { // Some items were imported from the package. $report->setMessage(__('%d Item(s) of %d imported from the given IMS QTI Package.', count($successItems), $itemCount)); if (count($successItems) !== $itemCount) { $report->setType(common_report_Report::TYPE_WARNING); } } else { $report->setMessage(__('No Items could be imported from the given IMS QTI package.')); $report->setType(common_report_Report::TYPE_ERROR); } if ($rollbackOnError === true) { if ($report->getType() === common_report_Report::TYPE_ERROR || $report->contains(common_report_Report::TYPE_ERROR)) { $this->rollback($successItems, $report, $allCreatedClasses); } } elseif ($rollbackOnWarning === true) { if ($report->contains(common_report_Report::TYPE_WARNING)) { $this->rollback($successItems, $report, $allCreatedClasses); } } // cleanup tao_helpers_File::delTree($folder); return $report; }