/**
  * 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;
 }