tao-test/app/taoQtiItem/model/qti/XIncludeLoader.php

195 lines
6.7 KiB
PHP
Raw Normal View History

2022-08-29 20:14:13 +02:00
<?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) 2015 (original work) Open Assessment Technologies SA
*
*/
namespace oat\taoQtiItem\model\qti;
use DOMDocument;
use oat\taoItems\model\media\ItemMediaResolver;
use oat\taoQtiItem\model\qti\Item;
use oat\taoQtiItem\model\qti\XInclude;
use oat\taoQtiItem\model\qti\interaction\PortableCustomInteraction;
use oat\taoQtiItem\model\qti\interaction\CustomInteraction;
use oat\taoQtiItem\model\qti\ParserFactory;
use oat\taoQtiItem\model\qti\exception\XIncludeException;
use oat\taoQtiItem\model\qti\exception\ParsingException;
/**
*
* @access public
* @author Sam, <sam@taotesting.com>
* @package taoQTI
*/
class XIncludeLoader
{
protected $qtiItem = null;
protected $resolver = null;
public function __construct(Item $qtiItem, ItemMediaResolver $resolver)
{
$this->qtiItem = $qtiItem;
$this->resolver = $resolver;
}
/**
* Load parse the item and resolve all xinclude
*
* @param boolean $removeUnfoundHref
* @return array
* @throws XIncludeException when the href cannot be resolved
*/
public function load($removeUnfoundHref = false)
{
$xincludes = $this->getXIncludes();
//load xincludes in standard element
foreach ($xincludes as $xinclude) {
//retrive the xinclude from href
$href = $xinclude->attr('href');
if (!empty($href)) {
try {
$asset = $this->resolver->resolve($href);
$filePath = $asset->getMediaSource()->download($asset->getMediaIdentifier());
$this->loadXInclude($xinclude, $filePath);
} catch (\tao_models_classes_FileNotFoundException $exception) {
if ($removeUnfoundHref) {
$xinclude->attr('href', '');
} else {
throw new XIncludeException('The file referenced by href does not exist : ' . $href, $xinclude);
}
}
}
}
//load xinclude in portable element markup
$customElements = $this->getCustomElements();
foreach ($customElements as $customElement) {
$xincludes = array_merge($xincludes, $this->parseCustomElementMarkup($customElement));
}
return $xincludes;
}
/**
* Parse and load xinclude located in custom element markup
*
* @param CustomInteraction $customElement
* @return array
* @throws XIncludeException when the file in href cannot be resolved
* @throws ParsingException when the markup cannot be loaded as xml document
*/
private function parseCustomElementMarkup(CustomInteraction $customElement)
{
$xincludes = [];
$xml = new DOMDocument();
$xml->formatOutput = true;
$loadSuccess = $xml->loadXML($customElement->getMarkup());
$node = $xml->documentElement;
if ($loadSuccess && !is_null($node)) {
$parser = new ParserFactory($xml);
$xincludesNodes = $parser->queryXPath(".//*[name(.)='include']");
foreach ($xincludesNodes as $xincludeNode) {
$href = $xincludeNode->getAttribute('href');
$asset = $this->resolver->resolve($href);
$filePath = $asset->getMediaSource()->download($asset->getMediaIdentifier());
if (file_exists($filePath)) {
$fileContent = file_get_contents($filePath);
$xmlInclude = new DOMDocument();
$xmlInclude->formatOutput = true;
$xmlInclude->loadXML($fileContent);
foreach ($xmlInclude->documentElement->childNodes as $node) {
$importNode = $xml->importNode($node, true);
$xincludeNode->parentNode->insertBefore($importNode, $xincludeNode);
}
} else {
throw new XIncludeException('The file referenced by href does not exist : ' . $href, $xincludeNode);
}
$xincludeNode->parentNode->removeChild($xincludeNode);
$xincludes[] = $href;
}
} else {
throw new ParsingException('cannot parse pci markup');
}
$customElement->setMarkup($xml->saveXML($xml->documentElement, LIBXML_NOEMPTYTAG));
return $xincludes;
}
/**
* load an xml string into the body of the XInclude
*
* @param \oat\taoQtiItem\model\qti\XInclude $xinclude
* @param string $filePath
* @throws XIncludeException
*/
private function loadXInclude(XInclude $xinclude, $filePath)
{
//load DOMDocument
$xml = new DOMDocument();
$loadSuccess = $xml->load($filePath);
$node = $xml->documentElement;
if ($loadSuccess && !is_null($node)) {
//parse the href content
$parser = new ParserFactory($xml);
$parser->loadContainerStatic($node, $xinclude->getBody());
} else {
throw new XIncludeException('Cannot load the XInclude DOM XML', $xinclude);
}
}
/**
* Find the xinclude elements in the qti item
*
* @return \oat\taoQtiItem\model\qti\XInclude[]
*/
private function getXIncludes()
{
$xincludes = [];
foreach ($this->qtiItem->getComposingElements() as $element) {
if ($element instanceof XInclude) {
$xincludes[] = $element;
}
}
return $xincludes;
}
/**
* Find the custom elements in the qti item
*
* @return \oat\taoQtiItem\model\qti\interaction\PortableCustomInteraction[]
*/
private function getCustomElements()
{
$customElements = [];
foreach ($this->qtiItem->getComposingElements() as $element) {
if ($element instanceof PortableCustomInteraction) {
$customElements[] = $element;
}
}
return $customElements;
}
}