From 50cf68c6cce1fb4397564494b1b9b23f664d80a9 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 18 Jan 2016 14:55:55 +0100 Subject: [PATCH] Added capability to resolve response processing template locations --- .../data/storage/xml/XmlCompactDocument.php | 5 ++ src/qtism/data/storage/xml/XmlDocument.php | 49 ++++++++++++++++- .../xml/XmlDocumentTemplateLocationTest.php | 55 +++++++++++++++++++ .../template_location_item.xml | 25 +++++++++ .../template_location_item_invalid_target.xml | 25 +++++++++ .../template_location_item_wrong_target.xml | 26 +++++++++ .../template_location_rp.xml | 2 + .../template_location_rp_invalid.xml | 3 + 8 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 test/qtismtest/data/storage/xml/XmlDocumentTemplateLocationTest.php create mode 100644 test/samples/custom/items/template_location/template_location_item.xml create mode 100644 test/samples/custom/items/template_location/template_location_item_invalid_target.xml create mode 100644 test/samples/custom/items/template_location/template_location_item_wrong_target.xml create mode 100644 test/samples/custom/items/template_location/template_location_rp.xml create mode 100644 test/samples/custom/items/template_location/template_location_rp_invalid.xml diff --git a/src/qtism/data/storage/xml/XmlCompactDocument.php b/src/qtism/data/storage/xml/XmlCompactDocument.php index dd3e3ce25..5e571dec0 100644 --- a/src/qtism/data/storage/xml/XmlCompactDocument.php +++ b/src/qtism/data/storage/xml/XmlCompactDocument.php @@ -274,6 +274,11 @@ protected static function resolveAssessmentItemRef(ExtendedAssessmentItemRef $co $doc = new XmlDocument(); $doc->load($href); + + // Resolve external documents. + $doc->xInclude(); + $doc->resolveTemplateLocation(); + $item = $doc->getDocumentComponent(); foreach ($item->getResponseDeclarations() as $resp) { diff --git a/src/qtism/data/storage/xml/XmlDocument.php b/src/qtism/data/storage/xml/XmlDocument.php index e953cb55a..c5273fcd7 100644 --- a/src/qtism/data/storage/xml/XmlDocument.php +++ b/src/qtism/data/storage/xml/XmlDocument.php @@ -26,6 +26,8 @@ use qtism\data\QtiComponentCollection; use qtism\data\QtiComponentIterator; use qtism\data\QtiDocument; +use qtism\data\AssessmentItem; +use qtism\data\processing\ResponseProcessing; use qtism\data\storage\xml\marshalling\Qti20MarshallerFactory; use qtism\data\storage\xml\marshalling\Qti21MarshallerFactory; use qtism\data\storage\xml\marshalling\Qti211MarshallerFactory; @@ -365,13 +367,13 @@ public function schemaValidate($filename = '') * the include components can be resolved by calling this method. Files will * be included following the rules described by the XInclude specification. * - * @param boolean $validate Whether or not validate files being included. + * @param boolean $validate Whether or not validate files being included. Default is false. * @throws \LogicException If the method is called prior the load or loadFromString method was called. * @throws \qtism\data\storage\xml\XmlStorageException If an error occured while parsing or validating files to be included. */ public function xInclude($validate = false) { - if (($root = $this->getDocumentComponent()) !== false) { + if (($root = $this->getDocumentComponent()) !== null) { $baseUri = str_replace('\\', '/', $this->getDomDocument()->documentElement->baseURI); $pathinfo = pathinfo($baseUri); @@ -413,6 +415,49 @@ public function xInclude($validate = false) { throw new LogicException($msg); } } + + /** + * Resolve responseProcessing elements with template location. + * + * If the root element of the currently loaded QTI file is an assessmentItem element, + * this method will try to resolve responseProcessing fragments referenced by responseProcessing + * elements having a templateLocation attribute. + * + * @param boolean $validate Whether or not validate files being included. Default is false. + * @throws \LogicException If the method is called prior the load or loadFromString method was called. + * @throws \qtism\data\storage\xml\XmlStorageException If an error occured while parsing or validating files to be included. + */ + public function resolveTemplateLocation($validate = false) + { + if (($root = $this->getDocumentComponent()) !== null) { + + if ($root instanceof AssessmentItem && ($responseProcessing = $root->getResponseProcessing()) !== null && ($templateLocation = $responseProcessing->getTemplateLocation()) !== '') { + + if (Url::isRelative($templateLocation) === true) { + + $baseUri = str_replace('\\', '/', $this->getDomDocument()->documentElement->baseURI); + $pathinfo = pathinfo($baseUri); + $basePath = $pathinfo['dirname']; + $templateLocation = Url::rtrim($basePath) . '/' . Url::ltrim($templateLocation); + + $doc = new XmlDocument(); + $doc->load($templateLocation, $validate); + + $newResponseProcessing = $doc->getDocumentComponent(); + + if ($newResponseProcessing instanceof ResponseProcessing) { + $root->setResponseProcessing($newResponseProcessing); + } else { + $msg = "The template at location '${templateLocation}' is not a document containing a QTI responseProcessing element."; + throw new XmlStorageException($msg, XmlStorageException::RESOLUTION); + } + } + } + } else { + $msg = "Cannot resolve template location loading any file."; + throw new LogicException($msg); + } + } /** * Decorate the root element of the XmlAssessmentDocument with the appropriate diff --git a/test/qtismtest/data/storage/xml/XmlDocumentTemplateLocationTest.php b/test/qtismtest/data/storage/xml/XmlDocumentTemplateLocationTest.php new file mode 100644 index 000000000..934caabfc --- /dev/null +++ b/test/qtismtest/data/storage/xml/XmlDocumentTemplateLocationTest.php @@ -0,0 +1,55 @@ +load(self::samplesDir() . 'custom/items/template_location/template_location_item.xml', true); + + $responseProcessings = $doc->getDocumentComponent()->getComponentsByClassName('responseProcessing'); + $this->assertEquals(1, count($responseProcessings)); + $this->assertEquals('template_location_rp.xml', $responseProcessings[0]->getTemplateLocation()); + + $doc->resolveTemplateLocation(true); + + $responseProcessings = $doc->getDocumentComponent()->getComponentsByClassName('responseProcessing'); + $this->assertEquals(1, count($responseProcessings)); + $this->assertEquals('http://www.imsglobal.org/question/qti_v2p1/rptemplates/match_correct', $responseProcessings[0]->getTemplate()); + } + + public function testNotLoaded() { + $doc = new XmlDocument(); + + $this->setExpectedException('\\LogicException', 'Cannot resolve template location loading any file.'); + $doc->resolveTemplateLocation(); + } + + public function testWrongTarget() { + $doc = new XmlDocument(); + $doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item_wrong_target.xml', true); + + $this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException'); + $doc->resolveTemplateLocation(); + } + + public function testInvalidTargetNoValidation() { + $doc = new XmlDocument(); + $doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item_invalid_target.xml', true); + + $this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException', "'responseProcessingZ' components are not supported in QTI version '2.1.0'.", XmlStorageException::VERSION); + $doc->resolveTemplateLocation(); + } + + public function testInvalidTargetValidation() { + $doc = new XmlDocument(); + $doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item_invalid_target.xml', true); + + $this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException', null, XmlStorageException::XSD_VALIDATION); + $doc->resolveTemplateLocation(true); + } +} diff --git a/test/samples/custom/items/template_location/template_location_item.xml b/test/samples/custom/items/template_location/template_location_item.xml new file mode 100644 index 000000000..2b672a3d9 --- /dev/null +++ b/test/samples/custom/items/template_location/template_location_item.xml @@ -0,0 +1,25 @@ + + + + + ChoiceA + + + + + 0 + + + + + Who is the inventor of the telephone? + Alexander Graham Bell + Henri Owen Tudor + Bill Gates + + + + diff --git a/test/samples/custom/items/template_location/template_location_item_invalid_target.xml b/test/samples/custom/items/template_location/template_location_item_invalid_target.xml new file mode 100644 index 000000000..9023aa545 --- /dev/null +++ b/test/samples/custom/items/template_location/template_location_item_invalid_target.xml @@ -0,0 +1,25 @@ + + + + + ChoiceA + + + + + 0 + + + + + Who is the inventor of the telephone? + Alexander Graham Bell + Henri Owen Tudor + Bill Gates + + + + diff --git a/test/samples/custom/items/template_location/template_location_item_wrong_target.xml b/test/samples/custom/items/template_location/template_location_item_wrong_target.xml new file mode 100644 index 000000000..d170d6024 --- /dev/null +++ b/test/samples/custom/items/template_location/template_location_item_wrong_target.xml @@ -0,0 +1,26 @@ + + + + + ChoiceA + + + + + 0 + + + + + Who is the inventor of the telephone? + Alexander Graham Bell + Henri Owen Tudor + Bill Gates + + + + + diff --git a/test/samples/custom/items/template_location/template_location_rp.xml b/test/samples/custom/items/template_location/template_location_rp.xml new file mode 100644 index 000000000..64d5ea856 --- /dev/null +++ b/test/samples/custom/items/template_location/template_location_rp.xml @@ -0,0 +1,2 @@ + + diff --git a/test/samples/custom/items/template_location/template_location_rp_invalid.xml b/test/samples/custom/items/template_location/template_location_rp_invalid.xml new file mode 100644 index 000000000..513c0e3af --- /dev/null +++ b/test/samples/custom/items/template_location/template_location_rp_invalid.xml @@ -0,0 +1,3 @@ + + +