* @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; } }