tao-test/app/taoQtiItem/model/Export/QTIPackedItemExporter.php

243 lines
9.0 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2008-2010 (original work) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER);
* 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV);
* 2013-2016 (update and modification) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
*/
namespace oat\taoQtiItem\model\Export;
use oat\taoQtiItem\model\portableElement\exception\PortableElementException;
use oat\taoQtiItem\model\qti\exception\ExportException;
use oat\taoQtiItem\model\qti\Service;
use \core_kernel_classes_Resource;
use \ZipArchive;
use \DOMDocument;
use \tao_helpers_Uri;
use \taoItems_models_classes_TemplateRenderer;
use \tao_helpers_Display;
use \common_Exception;
class QTIPackedItemExporter extends AbstractQTIItemExporter
{
private $manifest;
/**
* Creates a new instance of QtiPackedItemExporter for a particular item.
*
* @param core_kernel_classes_Resource $item The item to be exported.
* @param ZipArchive $zip The ZIP archive were the files have to be exported.
* @param DOMDocument $manifest A Manifest to be reused to reference item components (e.g. auxilliary files).
*/
public function __construct(core_kernel_classes_Resource $item, ZipArchive $zip, DOMDocument $manifest = null)
{
parent::__construct($item, $zip);
$this->setManifest($manifest);
}
public function getManifest()
{
return $this->manifest;
}
public function setManifest(DOMDocument $manifest = null)
{
$this->manifest = $manifest;
}
public function hasManifest()
{
return $this->getManifest() !== null;
}
public function export($options = [])
{
if (!$this->containsItem()) {
$report = parent::export($options);
if ($report->getType() !== \common_report_Report::TYPE_ERROR || !$report->containsError()) {
try {
$exportResult = [];
if (is_array($report->getData())) {
$exportResult = $report->getData();
}
$this->exportManifest($options, $exportResult);
} catch (ExportException $e) {
$report->setType(\common_report_Report::TYPE_ERROR);
$report->setMessage($e->getUserMessage());
}
}
return $report;
}
return \common_report_Report::createSuccess();
}
/**
* Whenever the item is already in the manifest
* @return boolean
*/
protected function containsItem()
{
$found = false;
if ($this->hasManifest()) {
foreach ($this->getManifest()->getElementsByTagName('resource') as $resourceNode) {
/** @var \DOMElement $resourceNode */
if ($resourceNode->getAttribute('identifier') == $this->buildIdentifier()) {
$found = true;
break;
}
}
}
return $found;
}
public function buildBasePath()
{
return tao_helpers_Uri::getUniqueId($this->getItem()->getUri());
}
public function buildIdentifier()
{
return tao_helpers_Uri::getUniqueId($this->getItem()->getUri());
}
/**
* Build, merge and export the IMS Manifest into the target ZIP archive.
*
* @throws
*/
public function exportManifest($options = [], $exportResult = [])
{
$base = $this->buildBasePath();
$zipArchive = $this->getZip();
$qtiFile = '';
$qtiResources = [];
$sharedAssets = isset($exportResult['portableAssets']) ? $exportResult['portableAssets'] : [];
for ($i = 0; $i < $zipArchive->numFiles; $i++) {
$fileName = $zipArchive->getNameIndex($i);
//shared assets are authorized to be added at the root of the package
if (preg_match("@^" . preg_quote($base) . "@", $fileName) || in_array($fileName, $sharedAssets)) {
if (basename($fileName) == 'qti.xml') {
$qtiFile = $fileName;
} else {
if (!empty($fileName)) {
$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 = [];
// 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'] = [];
foreach ($qtiItem->getInteractions() as $interaction) {
$interactionData = [];
$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);
}
$manifest = $this->getManifest();
$this->getMetadataExporter()->export($this->getItem(), $manifest);
$this->setManifest($manifest);
// -- Overwrite manifest in the current ZIP archive.
$zipArchive->addFromString('imsmanifest.xml', $this->getManifest()->saveXML());
} else {
//the item has no item content, there are 2 possibilities:
$itemLabel = $this->getItem()->getLabel();
if (empty($itemLabel)) {
//it has no label at all: the item does not exist anymore
throw new ExportException($this->getItem()->getUri(), 'item not found');
} else {
//there is one, so the item does exist but might not have any content
throw new ExportException($itemLabel, 'no item content');
}
}
}
protected function renderManifest(array $options, array $qtiItemData)
{
$asApip = isset($options['apip']) && $options['apip'] === true;
$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, [
'qtiItems' => [$qtiItemData],
'manifestIdentifier' => 'MANIFEST-' . tao_helpers_Display::textCleaner(uniqid('tao', true), '-')
]);
$renderedManifest = $templateRenderer->render();
$newManifest = new DOMDocument('1.0', TAO_DEFAULT_ENCODING);
$newManifest->loadXML($renderedManifest);
return $newManifest;
}
protected function itemContentPostProcessing($content)
{
return $content;
}
}