* */ class taoQtiTest_models_classes_export_QtiTestExporter extends taoItems_models_classes_ItemExporter { /** * The QTISM XmlDocument representing the Test to be * exported. * * @var XmlDocument */ private $testDocument; /** * A reference to the QTI Test Service. * * @var taoQtiTest_models_classes_QtiTestService */ private $testService; /** * An array of items related to the current Test Export. The array is associative. Its * keys are actually the assessmentItemRef identifiers. * * @var array */ private $items; /** * A DOMDocument representing the IMS Manifest to be * populated while exporting the Test. * * @var DOMDocument */ private $manifest = null; /** * @var MetadataExporter Service to export metadata to IMSManifest */ protected $metadataExporter; /** * Create a new instance of QtiTestExport. * * @param core_kernel_classes_Resource $test The Resource in the ontology representing the QTI Test to be exported. * @param ZipArchive $zip An instance of ZipArchive were components of the QTI Test will be stored into. * @param DOMDocument $manifest A DOMDocument representing the IMS Manifest to be populated during the Test Export. */ public function __construct(core_kernel_classes_Resource $test, ZipArchive $zip, DOMDocument $manifest) { parent::__construct($test, $zip); $this->setTestService(taoQtiTest_models_classes_QtiTestService::singleton()); $this->setTestDocument($this->getTestService()->getDoc($test)); $this->setItems($this->getTestService()->getItems($test)); $this->setManifest($manifest); } /** * Set the QTISM XmlDocument which holds the QTI Test definition to be exported. * * @param XmlDocument $testDocument */ protected function setTestDocument(XmlDocument $testDocument) { $this->testDocument = $testDocument; } /** * Get the QTISM XmlDocument which holds the QTI Test definition to be exported. * * @return XmlDocument */ protected function getTestDocument() { return $this->testDocument; } /** * Set a reference on the QTI Test Service. * * @param taoQtiTest_models_classes_QtiTestService $service */ protected function setTestService(taoQtiTest_models_classes_QtiTestService $service) { $this->testService = $service; } /** * Get a reference on the QTI Test Service. * * @return taoQtiTest_models_classes_QtiTestService */ protected function getTestService() { return $this->testService; } /** * Set the array of items that are involved in the QTI Test Definition to * be exported. * * @param array $items An associative array where keys are assessmentItemRef identifiers and values are core_kernel_classes_Resource objects representing the items in the knowledge base. */ protected function setItems(array $items) { $this->items = $items; } /** * Get the array of items that are involved in the QTI Test Definition * to be exported. * * @return array An associative array where keys are assessmentItemRef identifiers and values are core_kernel_classes_Resource objects representing the items in the knowledge base. */ protected function getItems() { return $this->items; } /** * Set the DOMDocument representing the IMS Manifest to be * populated during Test Export. * * @param DOMDocument $manifest */ protected function setManifest(DOMDocument $manifest) { $this->manifest = $manifest; } /** * Get the DOMDocument representing the IMS Manifest to * be populated during Test Export. * * @return DOMDocument */ public function getManifest() { return $this->manifest; } public function preProcessing() { if ($this->getServiceManager()->has(AssessmentItemRefPreProcessor::SERVICE_ID)) { /** @var AssessmentItemRefPreProcessor $preprocessor */ $preprocessor = $this->getServiceManager()->get(AssessmentItemRefPreProcessor::SERVICE_ID); $items = $preprocessor->process($this->testDocument); $this->setItems($items); } } /** * Export the test definition and all its dependencies (media, items, ...) into * the related ZIP archive. * * @param array $options An array of options (not used by this implementation). * @return common_report_Report */ public function export($options = []) { $this->preProcessing(); // 1. Export the items bound to the test. $report = $this->exportItems(); $itemIdentifiers = $report->getData(); // 2. Export the test definition itself. $this->exportTest($itemIdentifiers); // 3. Export test metadata to manifest $this->getMetadataExporter()->export($this->getItem(), $this->getManifest()); // 4. Persist manifest in archive. $this->getZip()->addFromString('imsmanifest.xml', $this->getManifest()->saveXML()); return $report; } /** * Export the dependent items into the ZIP archive. * * @return common_report_Report that contains An array of identifiers that were assigned to exported items into the IMS Manifest. */ protected function exportItems() { $report = common_report_Report::createSuccess(__('Export successful for the test "%s"', $this->getItem()->getLabel())); $identifiers = []; foreach ($this->getItems() as $refIdentifier => $item) { $itemExporter = $this->createItemExporter($item); if (!in_array($itemExporter->buildIdentifier(), $identifiers)) { $identifiers[] = $itemExporter->buildIdentifier(); $subReport = $itemExporter->export(); } // Modify the reference to the item in the test definition. $newQtiItemXmlPath = '../../items/' . tao_helpers_Uri::getUniqueId($item->getUri()) . '/qti.xml'; $itemRef = $this->getTestDocument()->getDocumentComponent()->getComponentByIdentifier($refIdentifier); $itemRef->setHref($newQtiItemXmlPath); if ( $report->getType() !== common_report_Report::TYPE_ERROR && ($subReport->containsError() || $subReport->getType() === common_report_Report::TYPE_ERROR) ) { //only report erros otherwise the list of report can be very long $report->setType(common_report_Report::TYPE_ERROR); $report->setMessage(__('Export failed for the test "%s"', $this->getItem()->getLabel())); $report->add($subReport); } } $report->setData($identifiers); return $report; } /** * Export the Test definition itself and its related media. * * @param array $itemIdentifiers An array of identifiers that were assigned to exported items into the IMS Manifest. */ protected function exportTest(array $itemIdentifiers) { $testXmlDocument = $this->postProcessing($this->getTestDocument()->saveToString()); $newTestDir = 'tests/' . tao_helpers_Uri::getUniqueId($this->getItem()->getUri()) . '/'; $testRootDir = $this->getTestService()->getQtiTestDir($this->getItem()); $testHref = $newTestDir . 'test.xml'; common_Logger::t('TEST DEFINITION AT: ' . $testHref); $this->getZip()->addFromString($testHref, $testXmlDocument); $this->referenceTest($testHref, $itemIdentifiers); $iterator = $testRootDir->getFlyIterator(Directory::ITERATOR_RECURSIVE | Directory::ITERATOR_FILE); $indexFile = pathinfo(taoQtiTest_models_classes_QtiTestService::QTI_TEST_DEFINITION_INDEX, PATHINFO_BASENAME); foreach ($iterator as $f) { // Only add dependency files... if ($f->getBasename() !== taoQtiTest_models_classes_QtiTestService::TAOQTITEST_FILENAME && $f->getBasename() !== $indexFile) { // Add the file to the archive. $fileHref = $newTestDir . $f->getBaseName(); common_Logger::t('AUXILIARY FILE AT: ' . $fileHref); $this->getZip()->addFromString($fileHref, $f->read()); $this->referenceAuxiliaryFile($fileHref); } } } /** * Reference the test into the IMS Manifest. * * @param string $href The path (base path is the ZIP archive) to the test definition. * @param array $itemIdentifiers An array of identifiers that were assigned to exported items into the IMS Manifest. */ protected function referenceTest($href, array $itemIdentifiers) { $identifier = tao_helpers_Uri::getUniqueId($this->getItem()->getUri()); $manifest = $this->getManifest(); // Identifiy the target node. $resources = $manifest->getElementsByTagName('resources'); $targetElt = $resources->item(0); // Create the IMS Manifest element. $resourceElt = $manifest->createElement('resource'); $resourceElt->setAttribute('identifier', $identifier); $resourceElt->setAttribute('type', $this->getTestResourceType()); $resourceElt->setAttribute('href', $href); $targetElt->appendChild($resourceElt); // Append an IMS Manifest element referencing the test definition. $fileElt = $manifest->createElement('file'); $fileElt->setAttribute('href', $href); $resourceElt->appendChild($fileElt); foreach ($itemIdentifiers as $itemIdentifier) { $this->referenceDependency($itemIdentifier); } } /** * Reference a test dependency (i.e. Items related to the test) in the IMS Manifest. * * @param string $identifierRef The identifier of the item resource in the IMS Manifest. */ protected function referenceDependency($identifierRef) { $xpath = new DOMXpath($this->getManifest()); $identifier = tao_helpers_Uri::getUniqueId($this->getItem()->getUri()); $manifest = $this->getManifest(); $search = $xpath->query("//resource[@identifier='${identifier}']"); $resourceElt = $search->item(0); // Append IMS Manifest elements referencing $identifierRef. $dependencyElt = $manifest->createElement('dependency'); $dependencyElt->setAttribute('identifierref', $identifierRef); $resourceElt->appendChild($dependencyElt); } /** * Reference a Test Auxiliary File (e.g. media, stylesheet, ...) in the IMS Manifest. * * @param string $href The path (base path is the ZIP archive) to the auxiliary file in the ZIP archive. */ protected function referenceAuxiliaryFile($href) { $manifest = $this->getManifest(); $testIdentifier = tao_helpers_Uri::getUniqueId($this->getItem()->getUri()); $xpath = new DOMXPath($manifest); // Find the first element. $dependencies = $xpath->query("//resource[@identifier='${testIdentifier}']/dependency"); $firstDependencyElt = $dependencies->item(0); // Create an IMS Manifest element. $fileElt = $manifest->createElement('file'); $fileElt->setAttribute('href', ltrim($href, '/')); $firstDependencyElt->parentNode->insertBefore($fileElt, $firstDependencyElt); } protected function createItemExporter(core_kernel_classes_Resource $item) { return new taoQtiTest_models_classes_export_QtiItemExporter($item, $this->getZip(), $this->getManifest()); } protected function getTestResourceType() { return 'imsqti_test_xmlv2p1'; } protected function postProcessing($testXmlDocument) { return $testXmlDocument; } /** * Get the service to export Metadata * * @return MetadataExporter */ protected function getMetadataExporter() { if (! $this->metadataExporter) { $this->metadataExporter = $this->getServiceManager()->get(MetadataService::SERVICE_ID)->getExporter(); } return $this->metadataExporter; } protected function getServiceManager() { return ServiceManager::getServiceManager(); } }