internalCompile(); if ($report->getType() == common_report_Report::TYPE_SUCCESS) { // replace instances with strign identifiers list($item, $publicDirectory, $privateDirectory) = $report->getData(); $report->setData([$item->getUri(), $publicDirectory->getId(), $privateDirectory->getId()]); } return $report; } /** * Deploy 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(); try { $qtiItem = $this->createQtiItem($item, $language); $resolver = new ItemMediaResolver($item, $language); $publicLangDirectory = $publicDirectory->getDirectory($language); // retrieve the media assets $packedAssets = $this->parseAndReplaceAssetByPlaceholder($qtiItem, $resolver, $publicLangDirectory); $this->compileItemIndex($item->getUri(), $qtiItem, $language); //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(); $itemPack = $itemPacker->packQtiItem($item, $language, $qtiItem, $publicDirectory); $this->itemJson = $itemPack->JsonSerialize(); //get the filtered data to avoid cheat $data = $qtiItem->getDataForDelivery(); $data = $this->convertXmlAttributes($data); $this->itemJson['data'] = $data['core']; $metadata = $this->getMetadataProperties(); $privateDirectory->write($language . DIRECTORY_SEPARATOR . self::ITEM_FILE_NAME, json_encode($this->itemJson)); $privateDirectory->write($language . DIRECTORY_SEPARATOR . self::METADATA_FILE_NAME, json_encode($metadata)); $privateDirectory->write($language . DIRECTORY_SEPARATOR . self::PORTABLE_ELEMENT_FILE_NAME, json_encode($this->getItemPortableElements($qtiItem))); 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() ); } } private function createQtiItem(core_kernel_classes_Resource $item, $lang): Item { $qtiItem = $this->getServiceLocator()->get(Service::class)->getDataItemByRdfItem($item, $lang); if (is_null($qtiItem)) { throw new taoItems_models_classes_CompilationFailedException( __('Unable to retrieve item : "%s"', $item->getLabel()) ); } return $qtiItem; } private function parseAndReplaceAssetByPlaceholder(Item &$qtiItem, ItemMediaResolver $resolver, Directory $publicLangDirectory) { $packedAssets = $this->getQtiItemAssetCompiler()->extractAndCopyAssetFiles($qtiItem, $publicLangDirectory, $resolver); $dom = new DOMDocument('1.0', 'UTF-8'); try { if ($dom->loadXML($qtiItem->toXML()) === false) { throw new \InvalidArgumentException(); } } catch (Throwable $e) { throw new taoItems_models_classes_CompilationFailedException( sprintf('Unable to load XML for item %s', $qtiItem->getIdentifier()) ); } $this->getXIncludeXmlInjector()->injectSharedStimulus($dom, $packedAssets); $this->getItemAssetXmlReplacer()->replaceAssetNodeValue($dom, $packedAssets); $qtiParser = new Parser($dom->saveXML()); $qtiItem = $qtiParser->load(); return $packedAssets; } /** * Convert internal parameters to json if needed * @param $data * @return mixed * @throws common_exception_Error */ protected function convertXmlAttributes($data) { if ( is_array($data) && array_key_exists('core', $data) && is_array($data['core']) && array_key_exists('apipAccessibility', $data['core']) && $data['core']['apipAccessibility'] ) { $data['core']['apipAccessibility'] = tao_helpers_Xml::to_array($data['core']['apipAccessibility']); } return $data; } /** * Get the portable elements data in use in the item * @param Element $qtiItem * @return array */ private function getItemPortableElements(Element $qtiItem) { $portableElementService = new PortableElementService(); $portableElementService->setServiceLocator($this->getServiceLocator()); return [ 'pci' => $portableElementService->getPortableElementByClass(PortableElementService::PORTABLE_CLASS_INTERACTION, $qtiItem, true), 'pic' => $portableElementService->getPortableElementByClass(PortableElementService::PORTABLE_CLASS_INFOCONTROL, $qtiItem, true) ]; } /** * Get the item properties as compiled metadata * @return array */ private function getMetadataProperties() { $triples = $this->getResource()->getRdfTriples(); $properties = []; foreach ($triples as $triple) { $properties[$triple->predicate] = $triple->object; } //we also include a shortcut to the item URI $properties['@uri'] = $this->getResource()->getUri(); return $properties; } private function getQtiItemAssetCompiler(): QtiItemAssetCompiler { return $this->getServiceLocator()->get(QtiItemAssetCompiler::class); } private function getXIncludeXmlInjector(): XIncludeXmlInjector { return $this->getServiceLocator()->get(XIncludeXmlInjector::class); } private function getItemAssetXmlReplacer(): QtiItemAssetXmlReplacer { return $this->getServiceLocator()->get(QtiItemAssetXmlReplacer::class); } }