/**
  * Store a file referenced by $qtiResource into the final $testContent folder. If the path provided
  * by $qtiResource contains sub-directories, they will be created before copying the file (even
  * if $copy = false).
  * 
  * @param Directory $testContent The pointer to the TAO Test Content folder.
  * @param oat\taoQtiItem\model\qti\Resource|string $qtiResource The QTI resource to be copied into $testContent. If given as a string, it must be the relative (to the IMS QTI Package) path to the resource file.
  * @param string $origin The path to the directory (root folder of extracted IMS QTI package) containing the QTI resource to be copied.
  * @param boolean $copy If set to false, the file will not be actually copied.
  * @param string $rename A new filename  e.g. 'file.css' to be used at storage time.
  * @return string The path were the file was copied/has to be copied (depending on the $copy argument).
  * @throws InvalidArgumentException If one of the above arguments is invalid.
  * @throws common_Exception If the copy fails.
  */
 public static function storeQtiResource(Directory $testContent, $qtiResource, $origin, $copy = true, $rename = '')
 {
     $fss = ServiceManager::getServiceManager()->get(FileSystemService::SERVICE_ID);
     $fs = $fss->getFileSystem($testContent->getFileSystem()->getId());
     $contentPath = $testContent->getPrefix();
     $ds = DIRECTORY_SEPARATOR;
     $contentPath = rtrim($contentPath, $ds);
     if ($qtiResource instanceof Resource) {
         $filePath = $qtiResource->getFile();
     } else {
         if (is_string($qtiResource) === true) {
             $filePath = $qtiResource;
         } else {
             throw new InvalidArgumentException("The 'qtiResource' argument must be a string or a taoQTI_models_classes_QTI_Resource object.");
         }
     }
     $resourcePathinfo = pathinfo($filePath);
     if (empty($resourcePathinfo['dirname']) === false && $resourcePathinfo['dirname'] !== '.') {
         // The resource file is not at the root of the archive but in a sub-folder.
         // Let's copy it in the same way into the Test Content folder.
         $breadCrumb = $contentPath . $ds . str_replace('/', $ds, $resourcePathinfo['dirname']);
         $breadCrumb = rtrim($breadCrumb, $ds);
         $finalName = empty($rename) === true ? $resourcePathinfo['filename'] . '.' . $resourcePathinfo['extension'] : $rename;
         $finalPath = $breadCrumb . $ds . $finalName;
     } else {
         // The resource file is at the root of the archive.
         // Overwrite template test.xml (created by self::createContent() method above) file with the new one.
         $finalName = empty($rename) === true ? $resourcePathinfo['filename'] . '.' . $resourcePathinfo['extension'] : $rename;
         $finalPath = $contentPath . $ds . $finalName;
     }
     if ($copy === true) {
         $origin = str_replace('/', $ds, $origin);
         $origin = rtrim($origin, $ds);
         $sourcePath = $origin . $ds . str_replace('/', $ds, $filePath);
         if (is_readable($sourcePath) === false) {
             throw new common_Exception("An error occured while copying the QTI resource from '{$sourcePath}' to '{$finalPath}'.");
         }
         $fh = fopen($sourcePath, 'r');
         $success = $fs->writeStream($finalPath, $fh);
         fclose($fh);
         if (!$success) {
             throw new common_Exception("An error occured while copying the QTI resource from '{$sourcePath}' to '{$finalPath}'.");
         }
     }
     return $finalPath;
 }
 /**
  * Enables you to build the QTI_Resources from a manifest xml data node
  * Content Packaging 1.1)
  *
  * @access public
  * @author Joel Bout, <*****@*****.**>
  * @param  SimpleXMLElement source
  * @return array
  * @see http://www.imsglobal.org/question/qti_v2p0/imsqti_intgv2p0.html#section10003
  */
 public static function getResourcesFromManifest(SimpleXMLElement $source)
 {
     $returnValue = array();
     //check of the root tag
     if ($source->getName() != 'manifest') {
         throw new ParsingException("incorrect manifest root tag");
     }
     $resourceNodes = $source->xpath("//*[name(.)='resource']");
     foreach ($resourceNodes as $resourceNode) {
         $type = (string) $resourceNode['type'];
         if (Resource::isAssessmentItem($type)) {
             $id = (string) $resourceNode['identifier'];
             $href = isset($resourceNode['href']) ? (string) $resourceNode['href'] : '';
             $auxFiles = array();
             //parse for auxiliary files
             foreach ($resourceNode->file as $fileNode) {
                 $fileHref = (string) $fileNode['href'];
                 if ($href != $fileHref) {
                     $auxFiles[] = $fileHref;
                 }
             }
             //include dependency files in item
             foreach ($resourceNode->dependency as $dependencyNode) {
                 $ref = (string) $dependencyNode['identifierref'];
                 //find referenced files within the current manifest:
                 $refResourceNodes = $source->xpath("//*[name(.)='resource' and @identifier='" . $ref . "']");
                 foreach ($refResourceNodes as $refResourceNode) {
                     if (isset($refResourceNode['href'])) {
                         $auxFiles[] = (string) $refResourceNode['href'];
                     }
                 }
             }
             $resource = new Resource($id, $type, $href);
             $resource->setAuxiliaryFiles($auxFiles);
             $returnValue[] = $resource;
         }
     }
     return (array) $returnValue;
 }
 /**
  * Imports the auxiliary files (file elements contained in the resource test element to be imported) into
  * the TAO Test Content directory.
  *
  * If some file cannot be copied, warnings will be committed.
  *
  * @param core_kernel_file_File $testContent The pointer to the TAO Test Content directory where auxilliary files will be stored.
  * @param oat\taoQtiItem\model\qti\Resource $qtiResource The manifest resource describing the test to be imported.
  * @param string $extractionFolder The absolute path to the temporary folder containing the content of the imported IMS QTI Package Archive.
  * @param common_report_Report A report about how the importation behaved.
  */
 protected function importTestAuxiliaryFiles(core_kernel_file_File $testContent, Resource $qtiResource, $extractionFolder, common_report_Report $report)
 {
     foreach ($qtiResource->getAuxiliaryFiles() as $aux) {
         try {
             taoQtiTest_helpers_Utils::storeQtiResource($testContent, $aux, $extractionFolder);
         } catch (common_Exception $e) {
             $report->add(new common_report_Report(common_report_Report::TYPE_WARNING, __('Auxiliary file not found at location "%s".', $aux)));
         }
     }
 }
 /**
  * Import metadata to a given QTI Item.
  *
  * @param oat\taoQtiItem\model\qti\metadata\MetadataValue[] $metadataValues An array of MetadataValue objects.
  * @param Resource $qtiResource The object representing the QTI Resource, from an IMS Manifest perspective.
  * @param core_kernel_classes_Resource $resource The object representing the target QTI Item in the Ontology.
  * @param oat\taoQtiItem\model\qti\metadata\MetadataInjector[] $ontologyInjectors Implementations of MetadataInjector that will take care to inject the metadata values in the appropriate Ontology Resource Properties.
  * @throws oat\taoQtiItem\model\qti\metadata\MetadataInjectionException If an error occurs while importing the metadata.
  */
 public function importItemMetadata(array $metadataValues, Resource $qtiResource, core_kernel_classes_Resource $resource, array $ontologyInjectors = array())
 {
     // Filter metadata values for this given item.
     $identifier = $qtiResource->getIdentifier();
     if (isset($metadataValues[$identifier]) === true) {
         $values = $metadataValues[$identifier];
         foreach ($ontologyInjectors as $injector) {
             $injector->inject($resource, array($identifier => $values));
         }
     }
 }
 /**
  * Import dependencies files
  *
  * @param QtiResource $qtiItemResource
  * @param $dependencies
  * @return $this
  * @throws AssetManagerException
  */
 public function importDependencyFiles(QtiResource $qtiItemResource, $dependencies)
 {
     $qtiFile = $this->getSource() . \helpers_File::urlToPath($qtiItemResource->getFile());
     foreach ($qtiItemResource->getDependencies() as $dependenciesFile) {
         if (!isset($dependencies[$dependenciesFile])) {
             continue;
         }
         $absolutePath = $this->getAbsolutePath($dependencies[$dependenciesFile]->getFile());
         $relativePath = $this->getRelativePath($qtiFile, $absolutePath);
         try {
             $this->importAsset($absolutePath, $relativePath);
         } catch (\common_Exception $e) {
             throw new AssetManagerException('Error occurs during dependency assets handling for item: ' . $qtiItemResource->getIdentifier() . ', assetFile: ' . $relativePath, 0, $e);
         }
     }
     return $this;
 }
 /**
  * Import metadata to a given QTI Item.
  *
  * @param oat\taoQtiItem\model\qti\metadata\MetadataValue[] $metadataValues An array of MetadataValue objects.
  * @param Resource $qtiResource The object representing the QTI Resource, from an IMS Manifest perspective.
  * @param core_kernel_classes_Resource $resource The object representing the target QTI Item in the Ontology.
  * @param oat\taoQtiItem\model\qti\metadata\MetadataInjector[] $ontologyInjectors Implementations of MetadataInjector that will take care to inject the metadata values in the appropriate Ontology Resource Properties.
  * @throws oat\taoQtiItem\model\qti\metadata\MetadataInjectionException If an error occurs while importing the metadata.
  */
 public function importResourceMetadata(array $metadataValues, Resource $qtiResource, core_kernel_classes_Resource $resource, array $ontologyInjectors = array())
 {
     // Filter metadata values for this given item.
     $identifier = $qtiResource->getIdentifier();
     if (isset($metadataValues[$identifier]) === true) {
         \common_Logger::i("Preparing Metadata Values for resource '{$identifier}'...");
         $values = $metadataValues[$identifier];
         foreach ($ontologyInjectors as $injector) {
             $valuesCount = count($values);
             $injectorClass = get_class($injector);
             \common_Logger::i("Attempting to inject {$valuesCount} Metadata Values in database for resource '{$identifier}' with Metadata Injector '{$injectorClass}'.");
             $injector->inject($resource, array($identifier => $values));
         }
     }
 }
 /**
  * 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(Resource::getItemTypes(), 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:\\/{1,3}/", '', $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;
 }