/**
  * Desploy all the required files into the provided directories
  *
  * @param core_kernel_classes_Resource $item
  * @param string $language
  * @param tao_models_classes_service_StorageDirectory $publicDirectory
  * @param tao_models_classes_service_StorageDirectory $privateDirectory
  * @return common_report_Report
  */
 protected function deployQtiItem(core_kernel_classes_Resource $item, $language, tao_models_classes_service_StorageDirectory $publicDirectory, tao_models_classes_service_StorageDirectory $privateDirectory)
 {
     $qtiService = Service::singleton();
     // retrieve the media assets
     try {
         $qtiItem = $this->retrieveAssets($item, $language, $publicDirectory);
         //store variable qti elements data into the private directory
         $variableElements = $qtiService->getVariableElements($qtiItem);
         $privateDirectory->write($language . DIRECTORY_SEPARATOR . self::VAR_ELT_FILE_NAME, json_encode($variableElements));
         //create the item.json file in private directory
         $itemPacker = new QtiItemPacker();
         $itemPacker->setReplaceXinclude(false);
         $itemPack = $itemPacker->packQtiItem($item, $language, $qtiItem, $publicDirectory);
         $this->itemJson = $itemPack->JsonSerialize();
         //get the filtered data to avoid cheat
         $data = $qtiItem->getDataForDelivery();
         $this->itemJson['data'] = $data['core'];
         $privateDirectory->write($language . DIRECTORY_SEPARATOR . self::ITEM_FILE_NAME, json_encode($this->itemJson));
         return new common_report_Report(common_report_Report::TYPE_SUCCESS, __('Successfully compiled "%s"', $language));
     } catch (\tao_models_classes_FileNotFoundException $e) {
         return new common_report_Report(common_report_Report::TYPE_ERROR, __('Unable to retrieve asset "%s"', $e->getFilePath()));
     } catch (XIncludeException $e) {
         return new common_report_Report(common_report_Report::TYPE_ERROR, $e->getUserMessage());
     } catch (\Exception $e) {
         return new common_report_Report(common_report_Report::TYPE_ERROR, $e->getMessage());
     }
 }
 /**
  * Resolve the given TAO Item URI in the path to
  * the related QTI-XML file.
  * 
  * @param string $url The URI of the TAO Item to resolve.
  * @return string The path to the related QTI-XML file.
  * @throws ResolutionException If an error occurs during the resolution of $url.
  */
 public function resolve($url)
 {
     $taoItem = new core_kernel_classes_Resource($url);
     if ($taoItem->exists() === false) {
         $msg = "The QTI Item with URI '{$url}' cannot be found.";
         throw new ResolutionException($msg);
     }
     // The item is retrieved from the database.
     // We can try to reach the QTI-XML file by detecting
     // where it is supposed to be located.
     // strip xinclude, we don't need that at the moment.
     $raw = $this->service->getXmlByRdfItem($this->getResource($url));
     $tmpfile = sys_get_temp_dir() . '/' . md5($url) . '.xml';
     $raw = preg_replace("/<xi:include(?:.*)>/u", '', $raw);
     file_put_contents($tmpfile, $raw);
     return $tmpfile;
 }
 public static function tearDownAfterClass()
 {
     \taoItems_models_classes_ItemsService::singleton()->deleteItem(self::$itemResource);
     // Unegister Metadata Injector.
     \oat\taoQtiItem\model\qti\Service::singleton()->getMetadataRegistry()->unregisterMetadataInjector('oat\\taoQtiItem\\model\\qti\\metadata\\ontology\\LomInjector');
     // Unregister Metadata Extractor.
     \oat\taoQtiItem\model\qti\Service::singleton()->getMetadataRegistry()->unregisterMetadataExtractor('oat\\taoQtiItem\\model\\qti\\metadata\\imsManifest\\ImsManifestMetadataExtractor');
     // Unregister Metadata Guardian.
     \oat\taoQtiItem\model\qti\Service::singleton()->getMetadataRegistry()->unregisterMetadataGuardian('oat\\taoQtiItem\\model\\qti\\metadata\\guardians\\LomIdentifierGuardian');
 }
 public static function tearDownAfterClass()
 {
     \taoItems_models_classes_ItemsService::singleton()->deleteItem(self::$itemResource);
     // Unregister Metadata ClassLookup.
     \oat\taoQtiItem\model\qti\Service::singleton()->getMetadataRegistry()->unregisterMetadataClassLookup('oat\\taoQtiItem\\model\\qti\\metadata\\classLookups\\LabelClassLookup');
     // Unregister Metadata Extractor.
     \oat\taoQtiItem\model\qti\Service::singleton()->getMetadataRegistry()->unregisterMetadataExtractor('oat\\taoQtiItem\\model\\qti\\metadata\\imsManifest\\ImsManifestMetadataExtractor');
     // Delete fake class
     $class = new \core_kernel_classes_Class('http://www.test.com#mytestclass');
     $class->delete(true);
 }
 /**
  * render used for deploy and preview
  *
  * @access public
  * @author Joel Bout, <*****@*****.**>
  * @param core_kernel_classes_Resource $item
  * @param $langCode
  * @throws \common_Exception
  * @return string
  */
 public function render(core_kernel_classes_Resource $item, $langCode)
 {
     $returnValue = (string) '';
     $qitService = Service::singleton();
     $qtiItem = $qitService->getDataItemByRdfItem($item, $langCode);
     if (!is_null($qtiItem)) {
         $returnValue = $qitService->renderQTIItem($qtiItem, $langCode);
     } else {
         common_Logger::w('No qti data for item ' . $item->getUri() . ' in ' . __FUNCTION__, 'taoQtiItem');
     }
     return (string) $returnValue;
 }
 protected function getAssets(\core_kernel_classes_Resource $item, $lang)
 {
     $qtiItem = Service::singleton()->getDataItemByRdfItem($item, $lang);
     $assetParser = new AssetParser($qtiItem);
     $assetParser->setGetSharedLibraries(false);
     $returnValue = array();
     foreach ($assetParser->extract() as $type => $assets) {
         foreach ($assets as $assetUrl) {
             foreach (self::$BLACKLIST as $blacklist) {
                 if (preg_match($blacklist, $assetUrl) === 1) {
                     continue 2;
                 }
             }
             $returnValue[] = $assetUrl;
         }
     }
     return $returnValue;
 }
 /**
  * Build, merge and export the IMS Manifest into the target ZIP archive.
  * 
  * @throws 
  */
 public function exportManifest($options = array())
 {
     $base = $this->buildBasePath();
     $zipArchive = $this->getZip();
     $qtiFile = '';
     $qtiResources = array();
     for ($i = 0; $i < $zipArchive->numFiles; $i++) {
         $fileName = $zipArchive->getNameIndex($i);
         if (preg_match("@^" . preg_quote($base) . "@", $fileName)) {
             if (basename($fileName) == 'qti.xml') {
                 $qtiFile = $fileName;
             } else {
                 $qtiResources[] = $fileName;
             }
         }
     }
     $qtiItemService = Service::singleton();
     //@todo add support of multi language packages
     $rdfItem = $this->getItem();
     $qtiItem = $qtiItemService->getDataItemByRdfItem($rdfItem);
     if (!is_null($qtiItem)) {
         // -- Prepare data transfer to the imsmanifest.tpl template.
         $qtiItemData = array();
         // alter identifier for export to avoid any "clash".
         $qtiItemData['identifier'] = $this->buildIdentifier();
         $qtiItemData['filePath'] = $qtiFile;
         $qtiItemData['medias'] = $qtiResources;
         $qtiItemData['adaptive'] = $qtiItem->getAttributeValue('adaptive') === 'adaptive' ? true : false;
         $qtiItemData['timeDependent'] = $qtiItem->getAttributeValue('timeDependent') === 'timeDependent' ? true : false;
         $qtiItemData['toolName'] = $qtiItem->getAttributeValue('toolVendor');
         $qtiItemData['toolVersion'] = $qtiItem->getAttributeValue('toolVersion');
         $qtiItemData['interactions'] = array();
         foreach ($qtiItem->getInteractions() as $interaction) {
             $interactionData = array();
             $interactionData['type'] = $interaction->getQtiTag();
             $qtiItemData['interactions'][] = $interactionData;
         }
         // -- Build a brand new IMS Manifest.
         $newManifest = $this->renderManifest($options, $qtiItemData);
         if ($this->hasManifest()) {
             // Merge old manifest and new one.
             $dom1 = $this->getManifest();
             $dom2 = $newManifest;
             $resourceNodes = $dom2->getElementsByTagName('resource');
             $resourcesNodes = $dom1->getElementsByTagName('resources');
             foreach ($resourcesNodes as $resourcesNode) {
                 foreach ($resourceNodes as $resourceNode) {
                     $newResourceNode = $dom1->importNode($resourceNode, true);
                     $resourcesNode->appendChild($newResourceNode);
                 }
             }
             // rendered manifest is now useless.
             unset($dom2);
         } else {
             // Brand new manifest.
             $this->setManifest($newManifest);
         }
         // -- Overwrite manifest in the current ZIP archive.
         $zipArchive->addFromString('imsmanifest.xml', $this->getManifest()->saveXML());
     } else {
         $itemLabel = $this->getItem()->getLabel();
         throw new common_Exception("the item '{$itemLabel}' involved in the export process has no content.");
     }
 }
 /**
  * (non-PHPdoc)
  * @see taoItems_actions_ItemPreview::getRenderedItem()
  */
 protected function getRenderedItem($item)
 {
     //@todo make getRenderedItem language dependent
     $lang = \common_session_SessionManager::getSession()->getDataLanguage();
     $qtiItem = Service::singleton()->getDataItemByRdfItem($item, $lang, true);
     $contentVariableElements = array_merge($this->getModalFeedbacks($qtiItem), $this->getRubricBlocks($qtiItem));
     $taoBaseUrl = common_ext_ExtensionsManager::singleton()->getExtensionById('tao')->getConstant('BASE_WWW');
     $qtiBaseUrl = common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getConstant('BASE_WWW');
     $taoLibUrl = $taoBaseUrl . 'js/lib/';
     $taoQtiItemLibUrl = $qtiBaseUrl . 'js/runtime/';
     $xhtml = $qtiItem->toXHTML(array('contentVariableElements' => $contentVariableElements, 'js_var' => array('view' => $this->getRequestView()), 'path' => array('tao' => $taoLibUrl, 'taoQtiItem' => $taoQtiItemLibUrl)));
     return $xhtml;
 }
 public function getItemData()
 {
     $returnValue = array('itemData' => null);
     if ($this->hasRequestParameter('uri')) {
         $lang = taoItems_models_classes_ItemsService::singleton()->getSessionLg();
         $itemUri = tao_helpers_Uri::decode($this->getRequestParameter('uri'));
         $itemResource = new core_kernel_classes_Resource($itemUri);
         $item = Service::singleton()->getDataItemByRdfItem($itemResource, $lang, false);
         //do not resolve xinclude here, leave it to the client side
         if (!is_null($item)) {
             $returnValue['itemData'] = $item->toArray();
         }
     }
     $this->returnJson($returnValue);
 }
 /**
  * Build, merge and export the IMS Manifest into the target ZIP archive.
  * 
  * @throws 
  */
 public function exportManifest($options = array())
 {
     $asApip = isset($options['apip']) && $options['apip'] === true;
     $base = $this->buildBasePath();
     $zipArchive = $this->getZip();
     $qtiFile = '';
     $qtiResources = array();
     for ($i = 0; $i < $zipArchive->numFiles; $i++) {
         $fileName = $zipArchive->getNameIndex($i);
         if (preg_match("@^" . preg_quote($base) . "@", $fileName)) {
             if (basename($fileName) == 'qti.xml') {
                 $qtiFile = $fileName;
             } else {
                 $qtiResources[] = $fileName;
             }
         }
     }
     $qtiItemService = Service::singleton();
     //@todo add support of multi language packages
     $rdfItem = $this->getItem();
     $qtiItem = $qtiItemService->getDataItemByRdfItem($rdfItem);
     if (!is_null($qtiItem)) {
         // -- Prepare data transfer to the imsmanifest.tpl template.
         $qtiItemData = array();
         // alter identifier for export to avoid any "clash".
         $qtiItemData['identifier'] = $this->buildIdentifier();
         $qtiItemData['filePath'] = $qtiFile;
         $qtiItemData['medias'] = $qtiResources;
         $qtiItemData['adaptive'] = $qtiItem->getAttributeValue('adaptive') === 'adaptive' ? true : false;
         $qtiItemData['timeDependent'] = $qtiItem->getAttributeValue('timeDependent') === 'timeDependent' ? true : false;
         $qtiItemData['toolName'] = $qtiItem->getAttributeValue('toolVendor');
         $qtiItemData['toolVersion'] = $qtiItem->getAttributeValue('toolVersion');
         $qtiItemData['interactions'] = array();
         foreach ($qtiItem->getInteractions() as $interaction) {
             $interactionData = array();
             $interactionData['type'] = $interaction->getQtiTag();
             $qtiItemData['interactions'][] = $interactionData;
         }
         // -- Build a brand new IMS Manifest.
         $dir = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir();
         $tpl = $asApip === false ? $dir . 'model/qti/templates/imsmanifest.tpl.php' : $dir . 'model/qti/templates/imsmanifestApip.tpl.php';
         $templateRenderer = new taoItems_models_classes_TemplateRenderer($tpl, array('qtiItems' => array($qtiItemData), 'manifestIdentifier' => 'MANIFEST-' . tao_helpers_Display::textCleaner(uniqid('tao', true), '-')));
         $renderedManifest = $templateRenderer->render();
         $newManifest = new DOMDocument('1.0', TAO_DEFAULT_ENCODING);
         $newManifest->loadXML($renderedManifest);
         if ($this->hasManifest()) {
             // Merge old manifest and new one.
             $dom1 = $this->getManifest();
             $dom2 = $newManifest;
             $dom2->loadXML($renderedManifest);
             $resourceNodes = $dom2->getElementsByTagName('resource');
             $resourcesNodes = $dom1->getElementsByTagName('resources');
             foreach ($resourcesNodes as $resourcesNode) {
                 foreach ($resourceNodes as $resourceNode) {
                     $newResourceNode = $dom1->importNode($resourceNode, true);
                     $resourcesNode->appendChild($newResourceNode);
                 }
             }
             // rendered manifest is now useless.
             unset($dom2);
         } else {
             // Brand new manifest.
             $this->setManifest($newManifest);
         }
         // -- Overwrite manifest in the current ZIP archive.
         $zipArchive->addFromString('imsmanifest.xml', $this->getManifest()->saveXML());
     } else {
         $itemLabel = $this->getItem()->getLabel();
         throw new common_Exception("the item '{$itemLabel}' involved in the export process has no content.");
     }
 }
 /**
  * @param core_kernel_classes_Resource $item
  * @param string $lang
  * @return string
  */
 protected function getXmlByItem(core_kernel_classes_Resource $item, $lang = '')
 {
     return Service::singleton()->getXmlByRdfItem($item, $lang);
 }
 /**
  * @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;
 }
 /**
  * Load Dom & Xpath of xml item content & register xpath namespace
  *
  * @param \core_kernel_classes_Resource $item
  * @return $this
  * @throws ExtractorException
  */
 private function loadXml(\core_kernel_classes_Resource $item)
 {
     $itemService = Service::singleton();
     $xml = $itemService->getXmlByRdfItem($item);
     if (empty($xml)) {
         throw new ExtractorException('No content found for item ' . $item->getUri());
     }
     $this->dom = new \DOMDocument();
     $this->dom->loadXml($xml);
     $this->xpath = new \DOMXpath($this->dom);
     $this->xpath->registerNamespace('qti', 'http://www.imsglobal.org/xsd/imsqti_v2p1');
     return $this;
 }
 /**
  * constructor
  */
 public function __construct()
 {
     $this->baseDevDir = $this->getBaseDevDir();
     $this->baseDevUrl = $this->getBaseDevUrl();
     $this->sharedLibRegistry = Service::singleton()->getSharedLibrariesRegistry();
 }
 /**
  * Desploy all the required files into the provided directories
  *
  * @param core_kernel_classes_Resource $item
  * @param string $language
  * @param string $publicDirectory
  * @param string $privateFolder
  * @return common_report_Report
  */
 protected function deployQtiItem(core_kernel_classes_Resource $item, $language, $publicDirectory, $privateFolder)
 {
     //        start debugging here
     common_Logger::d('destination original ' . $publicDirectory . ' ' . $privateFolder);
     $itemService = taoItems_models_classes_ItemsService::singleton();
     $qtiService = Service::singleton();
     //copy item.xml file to private directory
     $itemFolder = $itemService->getItemFolder($item, $language);
     tao_helpers_File::copy($itemFolder . 'qti.xml', $privateFolder . 'qti.xml', false);
     //copy client side resources (javascript loader)
     $qtiItemDir = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir();
     $taoDir = \common_ext_ExtensionsManager::singleton()->getExtensionById('tao')->getDir();
     $assetPath = $qtiItemDir . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR;
     $assetLibPath = $taoDir . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR;
     if (\tao_helpers_Mode::is('production')) {
         tao_helpers_File::copy($assetPath . 'qtiLoader.min.js', $publicDirectory . 'qtiLoader.min.js', false);
     } else {
         tao_helpers_File::copy($assetPath . 'qtiLoader.js', $publicDirectory . 'qtiLoader.js', false);
         tao_helpers_File::copy($assetLibPath . 'require.js', $publicDirectory . 'require.js', false);
     }
     // retrieve the media assets
     try {
         $qtiItem = $this->retrieveAssets($item, $language, $publicDirectory);
         //store variable qti elements data into the private directory
         $variableElements = $qtiService->getVariableElements($qtiItem);
         $serializedVariableElements = json_encode($variableElements);
         file_put_contents($privateFolder . 'variableElements.json', $serializedVariableElements);
         // render item based on the modified QtiItem
         $xhtml = $qtiService->renderQTIItem($qtiItem, $language);
         //note : no need to manually copy qti or other third party lib files, all dependencies are managed by requirejs
         // write index.html
         file_put_contents($publicDirectory . 'index.html', $xhtml);
         return new common_report_Report(common_report_Report::TYPE_SUCCESS, __('Successfully compiled "%s"', $language));
     } catch (\tao_models_classes_FileNotFoundException $e) {
         return new common_report_Report(common_report_Report::TYPE_ERROR, __('Unable to retrieve asset "%s"', $e->getFilePath()));
     }
 }
 /**
  * @param core_kernel_classes_Resource $item
  * @param string $lang
  * @param Directory $publicDirectory
  * @return qti\Item
  * @throws taoItems_models_classes_CompilationFailedException
  */
 protected function retrieveAssets(core_kernel_classes_Resource $item, $lang, Directory $publicDirectory)
 {
     $qtiItem = Service::singleton()->getDataItemByRdfItem($item, $lang);
     $assetParser = new AssetParser($qtiItem, $publicDirectory);
     $assetParser->setGetSharedLibraries(false);
     $assetParser->setGetXinclude(false);
     $resolver = new ItemMediaResolver($item, $lang);
     $replacementList = array();
     foreach ($assetParser->extract() as $type => $assets) {
         foreach ($assets as $assetUrl) {
             foreach (self::$BLACKLIST as $blacklist) {
                 if (preg_match($blacklist, $assetUrl) === 1) {
                     continue 2;
                 }
             }
             $mediaAsset = $resolver->resolve($assetUrl);
             $mediaSource = $mediaAsset->getMediaSource();
             $basename = $mediaSource->getBaseName($mediaAsset->getMediaIdentifier());
             $replacement = $basename;
             $count = 0;
             while (in_array($replacement, $replacementList)) {
                 $dot = strrpos($basename, '.');
                 $replacement = $dot !== false ? substr($basename, 0, $dot) . '_' . $count . substr($basename, $dot) : $basename . $count;
                 $count++;
             }
             $replacementList[$assetUrl] = $replacement;
             $tmpfile = $mediaSource->download($mediaAsset->getMediaIdentifier());
             $fh = fopen($tmpfile, 'r');
             $publicDirectory->writeStream($lang . '/' . $replacement, $fh);
             fclose($fh);
             unlink($tmpfile);
             //$fileStream = $mediaSource->getFileStream($mediaAsset->getMediaIdentifier());
             //$publicDirectory->writeStream($lang.'/'.$replacement, $fileStream);
         }
     }
     $dom = new \DOMDocument('1.0', 'UTF-8');
     if ($dom->loadXML($qtiItem->toXml()) === true) {
         $xpath = new \DOMXPath($dom);
         $attributeNodes = $xpath->query('//@*');
         foreach ($attributeNodes as $node) {
             if (isset($replacementList[$node->value])) {
                 $node->value = $replacementList[$node->value];
             }
         }
         $attributeNodes = $xpath->query("//*[local-name()='entry']") ?: [];
         unset($xpath);
         foreach ($attributeNodes as $node) {
             $node->nodeValue = strtr(htmlentities($node->nodeValue, ENT_XML1), $replacementList);
         }
     } else {
         throw new taoItems_models_classes_CompilationFailedException('Unable to load XML');
     }
     $qtiParser = new Parser($dom->saveXML());
     $assetRetrievedQtiItem = $qtiParser->load();
     //loadxinclude
     $xincludeLoader = new XIncludeLoader($assetRetrievedQtiItem, $resolver);
     $xincludeLoader->load(true);
     return $assetRetrievedQtiItem;
 }
 protected function getPortableElementAssets(\core_kernel_classes_Resource $item, $lang)
 {
     $qtiItem = Service::singleton()->getDataItemByRdfItem($item, $lang);
     $directory = $this->getStorageDirectory($item, $lang);
     $assetParser = new AssetParser($qtiItem, $directory);
     $assetParser->setGetCustomElementDefinition(true);
     return $assetParser->extractPortableAssetElements();
 }
 /**
  * @param $folder
  * @param \oat\taoQtiItem\model\qti\Resource $qtiItemResource
  * @param $itemClass
  * @param array $dependencies
  * @param array $metadataValues
  * @param array $metadataInjectors
  * @param array $metadataGuardians
  * @param array $metadataClassLookups
  * @param array $sharedFiles
  * @param array $createdClass
  * @return common_report_Report
  * @throws common_exception_Error
  */
 public function importQtiItem($folder, Resource $qtiItemResource, $itemClass, array $dependencies = array(), array $metadataValues = array(), array $metadataInjectors = array(), array $metadataGuardians = array(), array $metadataClassLookups = array(), array $sharedFiles = array(), &$createdClasses = array())
 {
     try {
         $qtiService = Service::singleton();
         //load the information about resources in the manifest
         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) {
                         \common_Logger::i("Resource '{$resourceIdentifier}' is already stored in the database and will not be imported.");
                         $msg = __('The IMS QTI Item referenced as "%s" in the IMS Manifest file was already stored in the Item Bank.', $resourceIdentifier);
                         $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) {
                     \common_Logger::i("Target Class Lookup for resource '{$resourceIdentifier}' ...");
                     if (($targetClass = $classLookup->lookup($metadataValues[$resourceIdentifier])) !== false) {
                         \common_Logger::i("Class Lookup Successful. Resource '{$resourceIdentifier}' will be stored in RDFS Class '" . $targetClass->getUri() . "'.");
                         if ($classLookup instanceof MetadataClassLookupClassCreator) {
                             $createdClasses = $classLookup->createdClasses();
                         }
                         break;
                     }
                 }
             }
             $qtiFile = $folder . helpers_File::urlToPath($qtiItemResource->getFile());
             common_Logger::i('file :: ' . $qtiItemResource->getFile());
             $qtiModel = $this->createQtiItemModel($qtiFile);
             $rdfItem = $this->createRdfItem($targetClass !== false ? $targetClass : $itemClass, $qtiModel);
             $itemAssetManager = new AssetManager();
             $itemAssetManager->setItemContent($qtiModel->toXML());
             $itemAssetManager->setSource($folder);
             /**
              * Load asset handler following priority handler defined by you
              * The first applicable will be used to import assets
              */
             /** Portable element handler */
             $peHandler = new PortableAssetHandler($qtiModel, dirname($qtiFile));
             $itemAssetManager->loadAssetHandler($peHandler);
             /** Shared stimulus handler */
             $sharedStimulusHandler = new SharedStimulusAssetHandler();
             $sharedStimulusHandler->setQtiModel($qtiModel)->setItemSource(new ItemMediaResolver($rdfItem, ''))->setSharedFiles($sharedFiles)->setParentPath($rdfItem->getLabel());
             $itemAssetManager->loadAssetHandler($sharedStimulusHandler);
             /** Local storage handler */
             $localHandler = new LocalAssetHandler();
             $localHandler->setItemSource(new LocalItemSource(array('item' => $rdfItem)));
             $itemAssetManager->loadAssetHandler($localHandler);
             $itemAssetManager->importAuxiliaryFiles($qtiItemResource)->importDependencyFiles($qtiItemResource, $dependencies);
             $itemAssetManager->finalize();
             $qtiModel = $this->createQtiItemModel($itemAssetManager->getItemContent(), false);
             $qtiService->saveDataItemToRdfItem($qtiModel, $rdfItem);
             // Finally, import metadata.
             $this->importResourceMetadata($metadataValues, $qtiItemResource, $rdfItem, $metadataInjectors);
             $eventManager = ServiceManager::getServiceManager()->get(EventManager::CONFIG_ID);
             $eventManager->trigger(new ItemImported($rdfItem, $qtiModel));
             $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) {
             $message = $e->getUserMessage();
             $report = new common_report_Report(common_report_Report::TYPE_ERROR, $message);
         } 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 (XmlStorageException $e) {
             $files = array();
             $message = __('There are errors in the following shared stimulus : ') . PHP_EOL;
             /** @var \LibXMLError $error */
             foreach ($e->getErrors() as $error) {
                 if (!in_array($error->file, $files)) {
                     $files[] = $error->file;
                     $message .= '- ' . basename($error->file) . ' :' . PHP_EOL;
                 }
                 $message .= $error->message . ' at line : ' . $error->line . PHP_EOL;
             }
             $report = new common_report_Report(common_report_Report::TYPE_ERROR, $message);
         } catch (PortableElementInvalidModelException $pe) {
             $report = common_report_Report::createFailure(__('IMS QTI Item referenced as "%s" contains a portable element and cannot be imported.', $qtiItemResource->getIdentifier()));
             $report->add($pe->getReport());
             if (isset($rdfItem) && !is_null($rdfItem) && $rdfItem->exists()) {
                 $rdfItem->delete();
             }
         } catch (PortableElementException $e) {
             // an error occured during a specific item
             if ($e instanceof common_exception_UserReadableException) {
                 $msg = __('Error on item %1$s : %2$s', $qtiItemResource->getIdentifier(), $e->getUserMessage());
             } else {
                 $msg = __('Error on item %s', $qtiItemResource->getIdentifier());
                 common_Logger::d($e->getMessage());
             }
             $report = new common_report_Report(common_report_Report::TYPE_ERROR, $msg);
             if (isset($rdfItem) && !is_null($rdfItem) && $rdfItem->exists()) {
                 $rdfItem->delete();
             }
         } 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."));
             if (isset($rdfItem) && !is_null($rdfItem) && $rdfItem->exists()) {
                 $rdfItem->delete();
             }
             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;
 }
 public function testImportPCI()
 {
     $itemClass = $this->itemService->getRootClass();
     $report = $this->importService->importQTIPACKFile($this->getSamplePath('/package/PCI/pcisample.zip'), $itemClass);
     $this->assertEquals(\common_report_Report::TYPE_SUCCESS, $report->getType());
     $items = array();
     foreach ($report as $itemReport) {
         $this->assertEquals(\common_report_Report::TYPE_SUCCESS, $itemReport->getType());
         $data = $itemReport->getData();
         if (!is_null($data)) {
             $items[] = $data;
         }
     }
     $this->assertEquals(1, count($items));
     $item = \oat\taoQtiItem\model\qti\Service::singleton()->getDataItemByRdfItem($items[0], DEFAULT_LANG, false);
     $itemData = $item->toArray();
     $itemDataElemetns = current($itemData['body']['elements']);
     //ensure that path prefixed with interaction identifies was not changed;
     $this->assertEquals($itemDataElemetns['entryPoint'], "adaptiveChoiceInteraction/runtime/adaptiveChoiceInteraction.js");
     //ensure that interaction properties imported properly
     $this->assertTrue(isset($itemDataElemetns['properties']['choices'][0]['label']));
     foreach ($items as $item) {
         $this->itemService->deleteItem($item);
     }
 }
 /**
  * Compact the test and items in a single QTI-XML Compact Document.
  * 
  * @return XmlCompactDocument.
  */
 protected function compactTest()
 {
     $testService = taoQtiTest_models_classes_QtiTestService::singleton();
     $test = $this->getResource();
     common_Logger::t('Compacting QTI test ' . $test->getLabel() . '...');
     $resolver = new taoQtiTest_helpers_ItemResolver(Service::singleton());
     $originalDoc = $testService->getDoc($test);
     $compiledDoc = XmlCompactDocument::createFromXmlAssessmentTestDocument($originalDoc, $resolver, $resolver);
     common_Logger::t("QTI Test XML transformed in a compact version.");
     return $compiledDoc;
 }
 public function saveItem()
 {
     $returnValue = array('success' => false);
     if ($this->hasRequestParameter('uri')) {
         $uri = urldecode($this->getRequestParameter('uri'));
         $xml = file_get_contents('php://input');
         $rdfItem = new core_kernel_classes_Resource($uri);
         /** @var Service $itemService */
         $itemService = Service::singleton();
         //check if the item is QTI item
         if ($itemService->hasItemModel($rdfItem, array(ItemModel::MODEL_URI))) {
             try {
                 $returnValue['success'] = $itemService->saveXmlItemToRdfItem($xml, $rdfItem);
             } catch (QtiModelException $e) {
                 $returnValue = array('success' => false, 'type' => 'Error', 'message' => $e->getUserMessage());
             }
         }
     }
     $this->returnJson($returnValue);
 }
 /**
  * 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;
 }
 /**
  * tests initialization
  */
 public function setUp()
 {
     TaoPhpUnitTestRunner::initTest();
     $this->qtiService = Service::singleton();
 }
 /**
  * Desploy all the required files into the provided directories
  * 
  * @param core_kernel_classes_Resource $item
  * @param string $language
  * @param string $publicDirectory
  * @param string $privateFolder
  * @return common_report_Report
  */
 protected function deployQtiItem(core_kernel_classes_Resource $item, $language, $publicDirectory, $privateFolder)
 {
     //        start debugging here
     common_Logger::d('destination original ' . $publicDirectory . ' ' . $privateFolder);
     $itemService = taoItems_models_classes_ItemsService::singleton();
     $qtiService = Service::singleton();
     //copy all item folder (but the qti.xml)
     $itemFolder = $itemService->getItemFolder($item, $language);
     taoItems_helpers_Deployment::copyResources($itemFolder, $publicDirectory, array('qti.xml'));
     //copy item.xml file to private directory
     tao_helpers_File::copy($itemFolder . 'qti.xml', $privateFolder . 'qti.xml', false);
     //copy client side resources (javascript loader)
     $qtiItemDir = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir();
     $taoDir = \common_ext_ExtensionsManager::singleton()->getExtensionById('tao')->getDir();
     $assetPath = $qtiItemDir . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR;
     $assetLibPath = $taoDir . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR;
     if (\tao_helpers_Mode::is('production')) {
         tao_helpers_File::copy($assetPath . 'qtiLoader.min.js', $publicDirectory . 'qtiLoader.min.js', false);
     } else {
         tao_helpers_File::copy($assetPath . 'qtiLoader.js', $publicDirectory . 'qtiLoader.js', false);
         tao_helpers_File::copy($assetLibPath . 'require.js', $publicDirectory . 'require.js', false);
     }
     //load item in php object :
     //warning:  use the same instance of php qti item because the serial is regenerated each time:
     $qtiItem = $qtiService->getDataItemByRdfItem($item, $language);
     //store variable qti elements data into the private directory
     $variableElements = $qtiService->getVariableElements($qtiItem);
     $serializedVariableElements = json_encode($variableElements);
     file_put_contents($privateFolder . 'variableElements.json', $serializedVariableElements);
     // render item
     $xhtml = $qtiService->renderQTIItem($qtiItem, $language);
     // retrieve external resources
     $report = taoItems_helpers_Deployment::retrieveExternalResources($xhtml, $publicDirectory);
     //@todo (optional) : exclude 'require.js' from copying
     if ($report->getType() == common_report_Report::TYPE_SUCCESS) {
         $xhtml = $report->getData();
     } else {
         return $report;
     }
     //note : no need to manually copy qti or other third party lib files, all dependencies are managed by requirejs
     // write index.html
     file_put_contents($publicDirectory . 'index.html', $xhtml);
     //copy the event.xml if not present
     $eventsXmlFile = $publicDirectory . 'events.xml';
     if (!file_exists($eventsXmlFile)) {
         $qtiItemDir = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir();
         $eventsReference = $qtiItemDir . 'model' . DIRECTORY_SEPARATOR . 'qti' . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'events_ref.xml';
         $eventXml = file_get_contents($eventsReference);
         if (is_string($eventXml) && !empty($eventXml)) {
             $eventXml = str_replace('{ITEM_URI}', $item->getUri(), $eventXml);
             @file_put_contents($eventsXmlFile, $eventXml);
         }
     }
     // --- Include QTI specific compilation stuff here.
     // At this moment, we should have compiled raw Items. We now have
     // to put the qti.xml file for each language in the compilation folder.
     return new common_report_Report(common_report_Report::TYPE_SUCCESS, __('Successfully compiled "%s"', $language));
 }
 /**
  * Returns the JSON encoded map of availabled Portable Shared Libraries.
  * 
  * Example: consider that the 'IMSGlobal/jquery_2_1_1' and 'OAT/lodash' shared
  * libraries are registered on the platform. This action will return the following
  * JSON structure:
  * 
  * {
  *     "IMSGlobal/jquery_2_1_1": "http://platformhost/taoQtiItem/views/js/portableSharedLibraries/IMSGlobal/jquery_2_1_1.js",
  *     "OAT/lodash": "http://platformhost/taoQtiItem/views/js/portableSharedLibraries/OAT/lodash.js"    
  * }
  */
 public function index()
 {
     $registry = Service::singleton()->getSharedLibrariesRegistry();
     $this->returnJson($registry->getMapping(), 200);
 }