-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from oat-sa/backport/xinclude
Backport/xinclude
- Loading branch information
Showing
13 changed files
with
428 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
|
||
namespace qtism\data; | ||
|
||
use qtism\data\rules\ResponseRule; | ||
use qtism\data\rules\OutcomeRule; | ||
use qtism\data\content\InlineStatic; | ||
use qtism\data\content\FlowStatic; | ||
use qtism\data\content\BlockStatic; | ||
use qtism\common\utils\Format; | ||
use \InvalidArgumentException; | ||
|
||
/** | ||
* From IMS QTI: | ||
* | ||
* Fragments are included using the Xinclude mechanism. (See [XINCLUDE].) The instance of include is treated as if | ||
* it was actually an instance of the root element of the fragment referred to by the href attribute of the include | ||
* element. For the purposes of this specification the xpointer mechanism defined by the XInclude specification must | ||
* not be used. Also, all included fragments must be treated as parsed xml. | ||
* | ||
* This technique is similar to the inclusion of media objects (using object) but allows the inclusion of data that | ||
* conforms to this specification, specifically, it allows the inclusion of interactions, static content, processing | ||
* rules or, at test level whole sections, to be included from externally defined fragments. | ||
* | ||
* When including externally defined fragments the content of the fragment must satisfy the requirements of the | ||
* specification in the context of the item in which it is being included. For example, interactions included | ||
* from fragments must be correctly bound to response variables declared in the items. | ||
* | ||
* @author Jérôme Bogaerts <[email protected]> | ||
* | ||
*/ | ||
class XInclude extends ExternalQtiComponent implements BlockStatic, FlowStatic, InlineStatic, OutcomeRule, ResponseRule { | ||
|
||
/** | ||
* A base URI. | ||
* | ||
* @var string | ||
* @qtism-bean-property | ||
*/ | ||
private $xmlBase = ''; | ||
|
||
/** | ||
* Create a new XInclude object. | ||
* | ||
* @param string $xmlString The XML Content of the node. | ||
*/ | ||
public function __construct($xmlString) { | ||
parent::__construct($xmlString); | ||
} | ||
|
||
/** | ||
* Get the value of the xi:href attribute in the XML content. | ||
* | ||
* This is a convenience method. | ||
* | ||
* @return string | ||
*/ | ||
public function getHref() { | ||
$xml = $this->getXml(); | ||
return $xml->documentElement->getAttribute('href'); | ||
} | ||
|
||
/** | ||
* @see \qtism\data\content\Flow::setXmlBase() | ||
*/ | ||
public function setXmlBase($xmlBase = '') | ||
{ | ||
if (is_string($xmlBase) && (empty($xmlBase) || Format::isUri($xmlBase))) { | ||
$this->xmlBase = $xmlBase; | ||
} else { | ||
$msg = "The 'xmlBase' argument must be an empty string or a valid URI, '" . $xmlBase . "' given"; | ||
throw new InvalidArgumentException($msg); | ||
} | ||
} | ||
|
||
/** | ||
* @see \qtism\data\content\Flow::getXmlBase() | ||
*/ | ||
public function getXmlBase() | ||
{ | ||
return $this->xmlBase; | ||
} | ||
|
||
/** | ||
* @see \qtism\data\content\Flow::hasXmlBase() | ||
*/ | ||
public function hasXmlBase() | ||
{ | ||
return $this->getXmlBase() !== ''; | ||
} | ||
|
||
/** | ||
* @see \qtism\data\ExternalQtiComponent::getQtiClassName() | ||
*/ | ||
public function getQtiClassName() | ||
{ | ||
return 'include'; | ||
} | ||
|
||
/** | ||
* @see \qtism\data\ExternalQtiComponent::buildTargetNamespace() | ||
*/ | ||
protected function buildTargetNamespace() { | ||
$this->setTargetNamespace('http://www.w3.org/2001/XInclude'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?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) 2013-2015 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); | ||
* | ||
* @author Jérôme Bogaerts <[email protected]> | ||
* @license GPLv2 | ||
*/ | ||
|
||
namespace qtism\data\storage\xml\marshalling; | ||
|
||
use qtism\data\XInclude; | ||
use qtism\data\QtiComponent; | ||
use \DOMElement; | ||
|
||
/** | ||
* Marshalling/Unmarshalling implementation for Include. | ||
* | ||
* @author Jérôme Bogaerts <[email protected]> | ||
* | ||
*/ | ||
class XIncludeMarshaller extends Marshaller | ||
{ | ||
/** | ||
* Marshall an XInclude object into a DOMElement object. | ||
* | ||
* @param \qtism\data\QtiComponent $component An XInclude object. | ||
* @return \DOMElement The according DOMElement object. | ||
* @throws \qtism\data\storage\marshalling\MarshallingException | ||
*/ | ||
protected function marshall(QtiComponent $component) | ||
{ | ||
return self::getDOMCradle()->importNode($component->getXml()->documentElement, true); | ||
} | ||
|
||
/** | ||
* Unmarshall a DOMElement object corresponding to a math element. | ||
* | ||
* @param \DOMElement $element A DOMElement object. | ||
* @return \qtism\data\QtiComponent A Math object. | ||
* @throws \qtism\data\storage\marshalling\UnmarshallingException | ||
*/ | ||
protected function unmarshall(DOMElement $element) | ||
{ | ||
$node = $element->cloneNode(true); | ||
|
||
return new XInclude($element->ownerDocument->saveXML($node)); | ||
} | ||
|
||
/** | ||
* @see \qtism\data\storage\xml\marshalling\Marshaller::getExpectedQtiClassName() | ||
*/ | ||
public function getExpectedQtiClassName() | ||
{ | ||
return 'include'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
namespace qtismtest\data\storage\xml; | ||
|
||
use qtism\data\storage\xml\XmlDocument; | ||
use qtism\data\storage\xml\XmlStorageException; | ||
|
||
class XmlDocumentXIncludeTest extends \QtiSmTestCase { | ||
|
||
public function testLoadAndSaveXIncludeNsInTag() { | ||
$doc = new XmlDocument(); | ||
$doc->load(self::samplesDir() . 'custom/items/xinclude/xinclude_ns_in_tag.xml', true); | ||
|
||
$includes = $doc->getDocumentComponent()->getComponentsByClassName('include'); | ||
$this->assertEquals(1, count($includes)); | ||
$this->assertEquals('xinclude_ns_in_tag_content1.xml', $includes[0]->getHref()); | ||
|
||
$file = tempnam('/tmp', 'qsm'); | ||
$doc->save($file); | ||
|
||
// Does it validate again? | ||
$doc = new XmlDocument(); | ||
try { | ||
$doc->load($file, true); | ||
$this->assertTrue(true); | ||
} catch (XmlStorageException $e) { | ||
$this->assertFalse(true, "The document using xinclude should validate after being saved."); | ||
} | ||
} | ||
|
||
/** | ||
* @depends testLoadAndSaveXIncludeNsInTag | ||
*/ | ||
public function testLoadAndResolveXIncludeSameBase() { | ||
$doc = new XmlDocument(); | ||
$doc->load(self::samplesDir() . 'custom/items/xinclude/xinclude_ns_in_tag.xml', true); | ||
|
||
// At this moment, includes are not resolved. | ||
$includes = $doc->getDocumentComponent()->getComponentsByClassName('include'); | ||
$this->assertEquals(1, count($includes)); | ||
// So no img components can be found... | ||
$imgs = $doc->getDocumentComponent()->getComponentsByClassName('img'); | ||
$this->assertEquals(0, count($imgs)); | ||
|
||
$doc->xInclude(); | ||
|
||
// Now they are! | ||
$includes = $doc->getDocumentComponent()->getComponentsByClassName('include'); | ||
$this->assertEquals(0, count($includes)); | ||
|
||
// And we should find an img component then! | ||
$imgs = $doc->getDocumentComponent()->getComponentsByClassName('img'); | ||
$this->assertEquals(1, count($imgs)); | ||
|
||
// Check that xml:base was appropriately resolved. In this case, | ||
// no content for xml:base because 'xinclude_ns_in_tag_content1.xml' is in the | ||
// same directory as the main xml file. | ||
$this->assertEquals('', $imgs[0]->getXmlBase()); | ||
} | ||
|
||
/** | ||
* @depends testLoadAndResolveXIncludeSameBase | ||
*/ | ||
public function testLoadAndResolveXIncludeDifferentBase() { | ||
$doc = new XmlDocument(); | ||
$doc->load(self::samplesDir() . 'custom/items/xinclude/xinclude_ns_in_tag_subfolder.xml', true); | ||
$doc->xInclude(); | ||
|
||
$includes = $doc->getDocumentComponent()->getComponentsByClassName('include'); | ||
$this->assertEquals(0, count($includes)); | ||
|
||
// And we should find an img component then! | ||
$imgs = $doc->getDocumentComponent()->getComponentsByClassName('img'); | ||
$this->assertEquals(1, count($imgs)); | ||
|
||
// Check that xml:base was appropriately resolved. In this case, | ||
// no content for xml:base because 'xinclude_ns_in_tag_content1.xml' is in the | ||
// same directory as the main xml file. | ||
$this->assertEquals('subfolder/', $imgs[0]->getXmlBase()); | ||
} | ||
} |
Oops, something went wrong.