/**
  * Handle the process to manage shared stimulus files
  *
  * @param $absolutePath
  * @param $relativePath
  * @return array
  * @throws \qtism\data\storage\xml\XmlStorageException
  */
 public function handle($absolutePath, $relativePath)
 {
     $sharedFiles = $this->getSharedFiles();
     $md5 = md5_file($absolutePath);
     if (isset($sharedFiles[$md5])) {
         \common_Logger::i('Auxiliary file \'' . $absolutePath . '\' linked to shared storage.');
         return $sharedFiles[$md5];
     }
     SharedStimulusImporter::isValidSharedStimulus($absolutePath);
     $newXmlFile = SharedStimulusPackageImporter::embedAssets($absolutePath);
     $itemContent = $this->sharedStorage->add($newXmlFile, basename($relativePath), $this->parentPath);
     if (method_exists($this->sharedStorage, 'forceMimeType')) {
         $asset = $this->itemSource->resolve($itemContent['uri']);
         $this->sharedStorage->forceMimeType($asset->getMediaIdentifier(), 'application/qti+xml');
     }
     $this->addSharedFile($md5, $itemContent);
     \common_Logger::i('Auxiliary file \'' . $absolutePath . '\' added to shared storage.');
     return $itemContent;
 }
 public function testEditPackage()
 {
     $packageImporter = $this->getMockBuilder('oat\\taoMediaManager\\model\\SharedStimulusPackageImporter')->getMock();
     $instance = new \core_kernel_classes_Resource('http://fancyDomain.com/tao.rdf#fancyInstanceUri');
     $sharedImporter = new SharedStimulusImporter($instance->getUri());
     $filename = dirname(__DIR__) . '/sample/sharedStimulus/stimulusPackage.zip';
     $myClass = new \core_kernel_classes_Class('http://fancyDomain.com/tao.rdf#fancyUri');
     $file['type'] = 'application/zip';
     $file['uploaded_file'] = $filename;
     $form = $sharedImporter->getForm();
     $form->setValues(array('source' => $file, 'lang' => 'EN_en'));
     $returnReport = \common_report_Report::createSuccess('Success');
     $packageImporter->expects($this->once())->method('edit')->with($instance, $form)->willReturn($returnReport);
     $sharedImporter->setZipImporter($packageImporter);
     $report = $sharedImporter->import($myClass, $form);
     $this->assertEquals($returnReport->getMessage(), $report->getMessage(), __('Report message is wrong'));
     $this->assertEquals($returnReport->getType(), $report->getType(), __('Report should be success'));
 }
 /**
  *
  * @param string $initialVersion
  * @return string $versionUpdatedTo
  */
 public function update($initialVersion)
 {
     $currentVersion = $initialVersion;
     //migrate from 0.1 to 0.1.1
     if ($currentVersion == '0.1') {
         // mediaSources set in 0.2
         $currentVersion = '0.1.1';
     }
     if ($currentVersion == '0.1.1') {
         FileManager::setFileManagementModel(new SimpleFileManagement());
         // mediaSources unset in 0.2
         $currentVersion = '0.1.2';
     }
     if ($currentVersion == '0.1.2') {
         //add alt text to media manager
         $file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'alt_text.rdf';
         $adapter = new \tao_helpers_data_GenerisAdapterRdf();
         if ($adapter->import($file)) {
             $currentVersion = '0.1.3';
         } else {
             \common_Logger::w('Import failed for ' . $file);
         }
     }
     if ($currentVersion == '0.1.3') {
         OntologyUpdater::correctModelId(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'alt_text.rdf');
         $currentVersion = '0.1.4';
     }
     if ($currentVersion == '0.1.4') {
         //modify config files due to the new interfaces relation
         $tao = \common_ext_ExtensionsManager::singleton()->getExtensionById('tao');
         $tao->unsetConfig('mediaManagementSources');
         $tao->unsetConfig('mediaBrowserSources');
         TaoMediaService::singleton()->addMediaSource(new MediaSource());
         //modify links in item content
         $service = \taoItems_models_classes_ItemsService::singleton();
         $items = $service->getAllByModel('http://www.tao.lu/Ontologies/TAOItem.rdf#QTI');
         foreach ($items as $item) {
             $itemContent = $service->getItemContent($item);
             $itemContent = preg_replace_callback('/src="mediamanager\\/([^"]+)"/', function ($matches) {
                 $mediaClass = MediaService::singleton()->getRootClass();
                 $medias = $mediaClass->searchInstances(array(MEDIA_LINK => $matches[1]), array('recursive' => true));
                 $media = array_pop($medias);
                 $uri = '';
                 if (!is_null($media) && $media->exists()) {
                     $uri = \tao_helpers_Uri::encode($media->getUri());
                 }
                 return 'src="taomedia://mediamanager/' . $uri . '"';
             }, $itemContent);
             $itemContent = preg_replace_callback('/src="local\\/([^"]+)"/', function ($matches) {
                 return 'src="' . $matches[1] . '"';
             }, $itemContent);
             $service->setItemContent($item, $itemContent);
         }
         $currentVersion = '0.2.0';
     }
     if ($currentVersion === '0.2.0') {
         $accessService = \funcAcl_models_classes_AccessService::singleton();
         //revoke access right to back office
         $backOffice = new \core_kernel_classes_Resource('http://www.tao.lu/Ontologies/TAO.rdf#BackOfficeRole');
         $accessService->revokeExtensionAccess($backOffice, 'taoMediaManager');
         //grant access right to media manager
         $mediaManager = new \core_kernel_classes_Resource('http://www.tao.lu/Ontologies/TAOMedia.rdf#MediaManagerRole');
         $accessService->grantExtensionAccess($mediaManager, 'taoMediaManager');
         $currentVersion = '0.2.1';
     }
     if ($currentVersion === '0.2.1') {
         //include mediamanager into globalmanager
         $mediaManager = new \core_kernel_classes_Resource('http://www.tao.lu/Ontologies/TAOMedia.rdf#MediaManagerRole');
         $globalManager = new \core_kernel_Classes_Resource('http://www.tao.lu/Ontologies/TAO.rdf#GlobalManagerRole');
         \tao_models_classes_RoleService::singleton()->includeRole($globalManager, $mediaManager);
         $currentVersion = '0.2.2';
     }
     if ($currentVersion === '0.2.2') {
         //copy file from /media to data/taoMediaManager/media and delete /media
         $dataPath = FILES_PATH . 'taoMediaManager' . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR;
         $dir = dirname(dirname(__DIR__)) . '/media';
         if (file_exists($dir)) {
             if (\tao_helpers_File::copy($dir, $dataPath)) {
                 $it = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS);
                 $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
                 foreach ($files as $file) {
                     if ($file->isDir()) {
                         rmdir($file->getRealPath());
                     } else {
                         unlink($file->getRealPath());
                     }
                 }
                 rmdir($dir);
                 $currentVersion = '0.2.3';
             }
         } else {
             $currentVersion = '0.2.3';
         }
     }
     if ($currentVersion === '0.2.3') {
         $accessService = \funcAcl_models_classes_AccessService::singleton();
         //grant access to item author
         $itemAuthor = new \core_kernel_classes_Resource('http://www.tao.lu/Ontologies/TAOItem.rdf#ItemAuthor');
         $accessService->grantExtensionAccess($itemAuthor, 'taoMediaManager');
         $currentVersion = '0.2.4';
     }
     if ($currentVersion === '0.2.4') {
         $mediaClass = MediaService::singleton()->getRootClass();
         $fileManager = FileManager::getFileManagementModel();
         foreach ($mediaClass->getInstances(true) as $media) {
             $fileLink = $media->getUniquePropertyValue(new \core_kernel_classes_Property(MEDIA_LINK));
             $fileLink = $fileLink instanceof \core_kernel_classes_Resource ? $fileLink->getUri() : (string) $fileLink;
             $filePath = $fileManager->retrieveFile($fileLink);
             $mimeType = \tao_helpers_File::getMimeType($filePath);
             $mimeType = $mimeType === 'application/xhtml+xml' ? 'application/qti+xml' : $mimeType;
             $media->setPropertyValue(new \core_kernel_classes_Property(MEDIA_MIME_TYPE), $mimeType);
         }
         $currentVersion = '0.2.5';
     }
     if ($currentVersion === '0.2.5') {
         $fileManager = FileManager::getFileManagementModel();
         $iterator = new \core_kernel_classes_ResourceIterator(array(MediaService::singleton()->getRootClass()));
         foreach ($iterator as $media) {
             $fileLink = $media->getUniquePropertyValue(new \core_kernel_classes_Property(MEDIA_LINK));
             $fileLink = $fileLink instanceof \core_kernel_classes_Resource ? $fileLink->getUri() : (string) $fileLink;
             $filePath = $fileManager->retrieveFile($fileLink);
             try {
                 SharedStimulusImporter::isValidSharedStimulus($filePath);
                 $media->editPropertyValues(new \core_kernel_classes_Property(MEDIA_MIME_TYPE), 'application/qti+xml');
             } catch (\Exception $e) {
                 $mimeType = \tao_helpers_File::getMimeType($filePath);
                 $media->editPropertyValues(new \core_kernel_classes_Property(MEDIA_MIME_TYPE), $mimeType);
             }
         }
         $currentVersion = '0.3.0';
     }
     $this->skip('0.3.0', '0.5.1');
 }
 /**
  * Validate an xml file, convert file linked inside and store it into media manager
  * @param \core_kernel_classes_Resource $instance the instance to edit
  * @param string $lang language of the shared stimulus
  * @param string $xmlFile File to store
  * @return \common_report_Report
  */
 protected function replaceSharedStimulus($instance, $lang, $xmlFile)
 {
     //if the class does not belong to media classes create a new one with its name (for items)
     $mediaClass = new core_kernel_classes_Class(MediaService::ROOT_CLASS_URI);
     if (!$instance->isInstanceOf($mediaClass)) {
         $report = \common_report_Report::createFailure('The instance ' . $instance->getUri() . ' is not a Media instance');
         return $report;
     }
     SharedStimulusImporter::isValidSharedStimulus($xmlFile);
     $name = basename($xmlFile, '.xml');
     $name .= '.xhtml';
     $filepath = dirname($xmlFile) . '/' . $name;
     \tao_helpers_File::copy($xmlFile, $filepath);
     $service = MediaService::singleton();
     if (!$service->editMediaInstance($filepath, $instance->getUri(), $lang)) {
         $report = \common_report_Report::createFailure(__('Fail to edit Shared Stimulus'));
     } else {
         $report = \common_report_Report::createSuccess(__('Shared Stimulus edited successfully'));
     }
     return $report;
 }
 /**
  * @param $folder
  * @param \taoQtiTest_models_classes_QtiResource $qtiItemResource
  * @param $itemClass
  * @param bool|false $extractApip
  * @param array $dependencies
  * @return common_report_Report
  * @throws common_exception_Error
  */
 public function importQtiItem($folder, Resource $qtiItemResource, $itemClass, $extractApip = false, $dependencies = array())
 {
     try {
         //load the information about resources in the manifest
         $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($folder . 'imsmanifest.xml');
         $metadataMapping = $qtiService->getMetadataRegistry()->getMapping();
         $metadataInjectors = array();
         $metadataGuardians = array();
         $metadataClassLookups = array();
         $metadataValues = array();
         foreach ($metadataMapping['injectors'] as $injector) {
             $metadataInjectors[] = new $injector();
         }
         foreach ($metadataMapping['guardians'] as $guardian) {
             $metadataGuardians[] = new $guardian();
         }
         foreach ($metadataMapping['classLookups'] as $classLookup) {
             $metadataClassLookups[] = new $classLookup();
         }
         foreach ($metadataMapping['extractors'] as $extractor) {
             $metadataExtractor = new $extractor();
             $metadataValues = array_merge($metadataValues, $metadataExtractor->extract($domManifest));
         }
         $sources = MediaService::singleton()->getWritableSources();
         $sharedStorage = array_shift($sources);
         $sharedFiles = array();
         try {
             $resourceIdentifier = $qtiItemResource->getIdentifier();
             // Use the guardians to check whether or not the item has to be imported.
             foreach ($metadataGuardians as $guardian) {
                 if (isset($metadataValues[$resourceIdentifier]) === true) {
                     if (($guard = $guardian->guard($metadataValues[$resourceIdentifier])) !== false) {
                         $msg = __('The IMS QTI Item referenced as "%s" in the IMS Manifest file was already stored in the Item Bank.', $qtiItemResource->getIdentifier());
                         $report = common_report_Report::createInfo($msg, $guard);
                         // Simply do not import again.
                         return $report;
                     }
                 }
             }
             $targetClass = false;
             // Use the classLookups to determine where the item has to go.
             foreach ($metadataClassLookups as $classLookup) {
                 if (isset($metadataValues[$resourceIdentifier]) === true) {
                     if (($targetClass = $classLookup->lookup($metadataValues[$resourceIdentifier])) !== false) {
                         break;
                     }
                 }
             }
             $qtiFile = $folder . $qtiItemResource->getFile();
             $qtiModel = $this->createQtiItemModel($qtiFile);
             $rdfItem = $this->createRdfItem($targetClass !== false ? $targetClass : $itemClass, $qtiModel);
             $name = $rdfItem->getLabel();
             $itemContent = $itemService->getItemContent($rdfItem);
             $xincluded = array();
             foreach ($qtiModel->getBody()->getComposingElements('oat\\taoQtiItem\\model\\qti\\Xinclude') as $xincludeEle) {
                 $xincluded[] = $xincludeEle->attr('href');
             }
             $local = new LocalItemSource(array('item' => $rdfItem));
             foreach ($qtiItemResource->getAuxiliaryFiles() as $auxResource) {
                 // file on FS
                 $auxFile = $folder . str_replace('/', DIRECTORY_SEPARATOR, $auxResource);
                 // rel path in item
                 $auxPath = str_replace(DIRECTORY_SEPARATOR, '/', helpers_File::getRelPath($qtiFile, $auxFile));
                 if (!empty($sharedStorage) && in_array($auxPath, $xincluded)) {
                     $md5 = md5_file($auxFile);
                     if (isset($sharedFiles[$md5])) {
                         $info = $sharedFiles[$md5];
                         \common_Logger::i('Auxiliary file \'' . $auxPath . '\' linked to shared storage.');
                     } else {
                         // TODO cleanup sharedstimulus import/export
                         // move to taoQti item or library
                         // validate the shared stimulus
                         SharedStimulusImporter::isValidSharedStimulus($auxFile);
                         // embed assets in the shared stimulus
                         $newXmlFile = SharedStimulusPackageImporter::embedAssets($auxFile);
                         $info = $sharedStorage->add($newXmlFile, basename($auxFile), $name);
                         if (method_exists($sharedStorage, 'forceMimeType')) {
                             // add() does not return link, so we need to parse it
                             $resolver = new ItemMediaResolver($rdfItem, '');
                             $asset = $resolver->resolve($info['uri']);
                             $sharedStorage->forceMimeType($asset->getMediaIdentifier(), 'application/qti+xml');
                         }
                         $sharedFiles[$md5] = $info;
                         \common_Logger::i('Auxiliary file \'' . $auxPath . '\' added to shared storage.');
                     }
                 } else {
                     // store locally, in a safe directory
                     $safePath = '';
                     if (dirname($auxPath) !== '.') {
                         $safePath = str_replace('../', '', dirname($auxPath)) . '/';
                     }
                     $info = $local->add($auxFile, basename($auxFile), $safePath);
                     \common_Logger::i('Auxiliary file \'' . $auxPath . '\' copied.');
                 }
                 // replace uri if changed
                 if ($auxPath != ltrim($info['uri'], '/')) {
                     $itemContent = str_replace($auxPath, $info['uri'], $itemContent);
                 }
             }
             foreach ($qtiItemResource->getDependencies() as $dependency) {
                 // file on FS
                 if (isset($dependencies[$dependency])) {
                     $auxFile = $dependencies[$dependency]->getFile();
                     $auxFile = $folder . str_replace('/', DIRECTORY_SEPARATOR, $auxFile);
                     // rel path in item
                     $auxPath = str_replace(DIRECTORY_SEPARATOR, '/', helpers_File::getRelPath($qtiFile, $auxFile));
                     if (!empty($sharedStorage) && in_array($auxPath, $xincluded)) {
                         $md5 = md5_file($auxFile);
                         if (isset($sharedFiles[$md5])) {
                             $info = $sharedFiles[$md5];
                             \common_Logger::i('Auxiliary file \'' . $auxPath . '\' linked to shared storage.');
                         } else {
                             // TODO cleanup sharedstimulus import/export
                             // move to taoQti item or library
                             // validate the shared stimulus
                             SharedStimulusImporter::isValidSharedStimulus($auxFile);
                             // embed assets in the shared stimulus
                             $newXmlFile = SharedStimulusPackageImporter::embedAssets($auxFile);
                             $info = $sharedStorage->add($newXmlFile, basename($auxFile), $name);
                             if (method_exists($sharedStorage, 'forceMimeType')) {
                                 // add() does not return link, so we need to parse it
                                 $resolver = new ItemMediaResolver($rdfItem, '');
                                 $asset = $resolver->resolve($info['uri']);
                                 $sharedStorage->forceMimeType($asset->getMediaIdentifier(), 'application/qti+xml');
                             }
                             $sharedFiles[$md5] = $info;
                             \common_Logger::i('Auxiliary file \'' . $auxPath . '\' added to shared storage.');
                         }
                     } else {
                         // store locally, in a safe directory
                         $safePath = '';
                         if (dirname($auxPath) !== '.') {
                             $safePath = str_replace('../', '', dirname($auxPath)) . '/';
                         }
                         $info = $local->add($auxFile, basename($auxFile), $safePath);
                         \common_Logger::i('Auxiliary file \'' . $auxPath . '\' copied.');
                     }
                     // replace uri if changed
                     if ($auxPath != ltrim($info['uri'], '/')) {
                         $itemContent = str_replace($auxPath, $info['uri'], $itemContent);
                     }
                 }
             }
             // Finally, import metadata.
             $this->importItemMetadata($metadataValues, $qtiItemResource, $rdfItem, $metadataInjectors);
             // And Apip if wanted
             if ($extractApip) {
                 $this->storeApip($qtiFile, $rdfItem);
             }
             $itemService->setItemContent($rdfItem, $itemContent);
             $msg = __('The IMS QTI Item referenced as "%s" in the IMS Manifest file was successfully imported.', $qtiItemResource->getIdentifier());
             $report = common_report_Report::createSuccess($msg, $rdfItem);
         } catch (ParsingException $e) {
             $report = new common_report_Report(common_report_Report::TYPE_ERROR, $e->getUserMessage());
         } catch (ValidationException $ve) {
             $report = \common_report_Report::createFailure(__('IMS QTI Item referenced as "%s" in the IMS Manifest file could not be imported.', $qtiItemResource->getIdentifier()));
             $report->add($ve->getReport());
         } catch (Exception $e) {
             // an error occured during a specific item
             $report = new common_report_Report(common_report_Report::TYPE_ERROR, __("An unknown error occured while importing the IMS QTI Package."));
             common_Logger::e($e->getMessage());
         }
     } 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);
     }
     return $report;
 }