public function testSeparateStylesheetTwo()
 {
     // The loaded component is still a rubricBlock but this
     // time with two (YES, TWO!) stylesheets.
     $doc = new XmlDocument('2.1');
     $doc->load(self::samplesDir() . 'rendering/rubricblock_3.xml');
     $this->assertEquals(2, count($doc->getDocumentComponent()->getStylesheets()));
     $renderingEngine = new XhtmlRenderingEngine();
     $renderingEngine->setStylesheetPolicy(XhtmlRenderingEngine::STYLESHEET_SEPARATE);
     $rendering = $renderingEngine->render($doc->getDocumentComponent());
     $linkElts = $rendering->getElementsByTagName('link');
     $this->assertEquals(0, $linkElts->length);
     $linksFragment = $renderingEngine->getStylesheets();
     $this->assertInstanceOf('\\DOMDocumentFragment', $linksFragment);
     $this->assertEquals(2, $linksFragment->childNodes->length);
     // Test first <link> element.
     $linkElt = $linksFragment->childNodes->item(0);
     $this->assertEquals('link', $linkElt->localName);
     $this->assertEquals('style1.css', $linkElt->getAttribute('href'));
     $this->assertEquals('text/css', $linkElt->getAttribute('type'));
     $this->assertEquals('screen', $linkElt->getAttribute('media'));
     $this->assertEquals('\\0_ !HOURRAY! _0/', $linkElt->getAttribute('title'));
     // Test second <link> element.
     $linkElt = $linksFragment->childNodes->item(1);
     $this->assertEquals('link', $linkElt->localName);
     $this->assertEquals('style2.css', $linkElt->getAttribute('href'));
     $this->assertEquals('text/css', $linkElt->getAttribute('type'));
     $this->assertEquals('screen', $linkElt->getAttribute('media'));
     $this->assertEquals('0/*\\0 (Jedi duel)', $linkElt->getAttribute('title'));
 }
 /**
  * Converts the test from the document to an array
  * @return array the test data as array
  * @throws taoQtiTest_models_classes_QtiTestConverterException
  */
 public function toArray()
 {
     try {
         return $this->componentToArray($this->doc->getDocumentComponent());
     } catch (ReflectionException $re) {
         common_Logger::e($re->getMessage());
         common_Logger::d($re->getTraceAsString());
         throw new taoQtiTest_models_classes_QtiTestConverterException('Unable to convert the QTI Test to json: ' . $re->getMessage());
     }
 }
 public function testItemSessionControls()
 {
     $doc = new XmlDocument('2.1');
     $doc->load(self::samplesDir() . 'custom/runtime/routeitem_itemsessioncontrols.xml');
     // Q01.
     $q01 = $doc->getDocumentComponent()->getComponentByIdentifier('Q01');
     $this->assertInstanceOf('qtism\\data\\AssessmentItemRef', $q01);
     $this->assertEquals(2, $q01->getItemSessionControl()->getMaxAttempts());
     // P02.
     $p02 = $doc->getDocumentComponent()->getComponentByIdentifier('P02');
     $this->assertInstanceOf('qtism\\data\\TestPart', $p02);
     $this->assertEquals(4, $p02->getItemSessionControl()->getMaxAttempts());
 }
 /**
  * @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());
 }
Example #5
0
 public function parse($xmlString, $validate = true)
 {
     // TODO: Remove this, and move it higher up
     LogService::flush();
     $xmlDocument = new XmlDocument();
     if ($validate === false) {
         LogService::log('QTI pre-validation is turned off, some invalid attributes might be stripped from XML content upon conversion');
     }
     $xmlDocument->loadFromString($xmlString, $validate);
     /** @var AssessmentItem $assessmentItem */
     $assessmentTest = $xmlDocument->getDocumentComponent();
     if (!$assessmentTest instanceof AssessmentTest) {
         throw new MappingException('XML is not a valid <assessmentItem> document');
     }
     // Ignore `testPart` and `assessmentSection`. Grab every item references and merge in array
     $itemReferences = [];
     foreach ($assessmentTest->getComponentsByClassName('assessmentItemRef', true) as $assessmentItemRef) {
         $itemReferences[] = $assessmentItemRef->getIdentifier();
     }
     LogService::log('Support for mapping is very limited. Elements such `testPart`, `assessmentSections`, `seclection`, `rubricBlock`, ' . 'etc are ignored. Please see developer docs for more details');
     $data = new activity_data();
     $data->set_items($itemReferences);
     $activity = new activity($assessmentTest->getIdentifier(), $data);
     // Flush out all the error messages stored in this static class, also ensure they are unique
     $messages = array_values(array_unique(LogService::flush()));
     return [$activity, $messages];
 }
 public function testAssigningScoresAndCorrectResponses()
 {
     $doc = new XmlDocument();
     $doc->load(self::samplesDir() . 'custom/items/template_processing.xml');
     $session = new AssessmentItemSession($doc->getDocumentComponent());
     $itemSessionControl = new ItemSessionControl();
     $itemSessionControl->setMaxAttempts(0);
     $session->setItemSessionControl($itemSessionControl);
     $session->beginItemSession();
     // Check that the templateProcessing was correctly processed.
     $this->assertEquals('ChoiceA', $session->getVariable('RESPONSE')->getCorrectResponse()->getValue());
     $this->assertEquals(1.0, $session['GOODSCORE']->getValue());
     $this->assertEquals(0.0, $session['WRONGSCORE']->getValue());
     // Check that it really works...
     // With a correct response.
     $session->beginAttempt();
     $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new QtiIdentifier('ChoiceA'))));
     $session->endAttempt($responses);
     $this->assertEquals(1.0, $session['SCORE']->getValue());
     // With an incorrect response.
     $session->beginAttempt();
     $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new QtiIdentifier('ChoiceB'))));
     $session->endAttempt($responses);
     $this->assertEquals(0.0, $session['SCORE']->getValue());
 }
 public function testWrite()
 {
     $uri = self::samplesDir() . 'custom/standalone_assessmentsection.xml';
     $doc = new XmlDocument();
     $doc->load($uri);
     $assessmentSection = $doc->getDocumentComponent();
     // Write the file.
     $uri = tempnam('/tmp', 'qsm');
     $doc->save($uri);
     $this->assertTrue(file_exists($uri));
     // Reload it.
     $doc->load($uri);
     $this->assertInstanceOf('qtism\\data\\AssessmentSection', $doc->getDocumentComponent());
     // Retest.
     $this->testLoad($doc->getDocumentComponent());
     unlink($uri);
 }
 public function testGetShuffledChoiceIdentifierAtInvalidShuffledChoiceIndex()
 {
     $doc = new XmlDocument();
     $doc->load(self::samplesDir() . 'ims/items/2_1/choice_fixed.xml');
     $session = new AssessmentItemSession($doc->getDocumentComponent());
     $session->beginItemSession();
     $this->setExpectedException('\\OutOfBoundsException', 'No identifier at index 1337.');
     $session->getShuffledChoiceIdentifierAt(0, 1337);
 }
Example #9
0
 public function testTimeLimits()
 {
     $doc = new XmlDocument();
     $doc->load(self::samplesDir() . 'custom/runtime/timelimits.xml');
     $testPart = $doc->getDocumentComponent()->getComponentByIdentifier('testPartId');
     $this->assertTrue($testPart->hasTimeLimits());
     $timeLimits = $testPart->getTimeLimits();
     $this->assertTrue($timeLimits->getMinTime()->equals(new Duration('PT60S')));
     $this->assertTrue($timeLimits->getMaxTime()->equals(new Duration('PT120S')));
     $this->assertTrue($timeLimits->doesAllowLateSubmission());
 }
Example #10
0
 public function testBasicSelection()
 {
     $doc = new XmlDocument();
     $doc->load(self::samplesDir() . 'custom/runtime/selection_and_ordering.xml');
     $testPart = $doc->getDocumentComponent()->getComponentByIdentifier('testPart');
     $this->assertEquals('testPart', $testPart->getIdentifier());
     $s01 = $doc->getDocumentComponent()->getComponentByIdentifier('S01', true);
     $this->assertEquals('S01', $s01->getIdentifier());
     // Prepare route selection of S01A.
     $s01a = $doc->getDocumentComponent()->getComponentByIdentifier('S01A', true);
     $this->assertEquals('S01A', $s01a->getIdentifier());
     $s01aRoute = new SelectableRoute();
     foreach ($s01a->getSectionParts() as $sectionPart) {
         $s01aRoute->addRouteItem($sectionPart, $s01a, $testPart, $doc->getDocumentComponent());
     }
     // Prepare route selection of S01B.
     $s01b = $doc->getDocumentComponent()->getComponentByIdentifier('S01B', true);
     $this->assertEquals('S01B', $s01b->getIdentifier());
     $s01bRoute = new SelectableRoute();
     foreach ($s01b->getSectionParts() as $sectionPart) {
         $s01bRoute->addRouteItem($sectionPart, $s01b, $testPart, $doc->getDocumentComponent());
     }
     $selection = new BasicSelection($s01, new SelectableRouteCollection(array($s01aRoute, $s01bRoute)));
     $selectedRoutes = $selection->select();
     $selectedRoute = new SelectableRoute();
     foreach ($selectedRoutes as $r) {
         $selectedRoute->appendRoute($r);
     }
     $routeCheck1 = self::isRouteCorrect($selectedRoute, array('Q1', 'Q2', 'Q3'));
     $routeCheck2 = self::isRouteCorrect($selectedRoute, array('Q4', 'Q5', 'Q6'));
     $this->assertFalse($routeCheck1 === true && $routeCheck2 === true);
     $this->assertTrue($routeCheck1 === true || $routeCheck2 === true);
 }
Example #11
0
function testAssessmentTests(array $files, $validate = false)
{
    $loaded = 0;
    $totalSpent = 0;
    foreach ($files as $f) {
        $start = microtime();
        $testDoc = new XmlDocument();
        $testDoc->load($f, $validate);
        $end = microtime();
        $spent = spentTime($start, $end);
        $totalSpent += $spent;
        output("Test '" . pathinfo($f, PATHINFO_BASENAME) . "' loaded in " . sprintf("%.8f", $spent) . " seconds.");
        $partCount = count($testDoc->getDocumentComponent()->getComponentsByClassName('testPart'));
        $sectionCount = count($testDoc->getDocumentComponent()->getComponentsByClassName('assessmentSection'));
        $itemCount = count($testDoc->getDocumentComponent()->getComponentsByClassName('assessmentItemRef'));
        outputDescription("{$partCount} testPart(s), {$sectionCount} assessmentSection(s), {$itemCount} assessmentItemRef(s)");
        outputDescription("Memory usage is " . memory_get_usage() / pow(1024, 2) . " MB");
        output('');
        $loaded++;
    }
    outputAverage($totalSpent / $loaded);
}
 /**
  * Item's ResponseProcessing.
  *
  * @param core_kernel_classes_Resource $item The Item you want to apply ResponseProcessing.
  * @throws \RuntimeException If an error occurs while processing responses or transmitting results
  */
 protected function processResponses(core_kernel_classes_Resource $item)
 {
     $jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
     try {
         $qtiXmlFilePath = QtiFile::getQtiFilePath($item);
         $qtiXmlDoc = new XmlDocument();
         $qtiXmlDoc->load($qtiXmlFilePath);
     } catch (StorageException $e) {
         $msg = "An error occurred while loading QTI-XML file at expected location '{$qtiXmlFilePath}'.";
         common_Logger::e($e->getPrevious()->getMessage());
         throw new \RuntimeException($msg, 0, $e);
     }
     $itemSession = new AssessmentItemSession($qtiXmlDoc->getDocumentComponent(), new SessionManager());
     $itemSession->beginItemSession();
     $variables = array();
     $filler = new taoQtiCommon_helpers_PciVariableFiller($qtiXmlDoc->getDocumentComponent());
     // Convert client-side data as QtiSm Runtime Variables.
     foreach ($jsonPayload as $id => $response) {
         try {
             $var = $filler->fill($id, $response);
             // Do not take into account QTI Files at preview time.
             // Simply delete the created file.
             if (taoQtiCommon_helpers_Utils::isQtiFile($var, false) === true) {
                 $fileManager = taoQtiCommon_helpers_Utils::getFileDatatypeManager();
                 $fileManager->delete($var->getValue());
             } else {
                 $variables[] = $var;
             }
         } catch (OutOfRangeException $e) {
             // A variable value could not be converted, ignore it.
             // Developer's note: QTI Pairs with a single identifier (missing second identifier of the pair) are transmitted as an array of length 1,
             // this might cause problem. Such "broken" pairs are simply ignored.
             common_Logger::d("Client-side value for variable '{$id}' is ignored due to data malformation.");
         } catch (OutOfBoundsException $e) {
             // No such identifier found in item.
             common_Logger::d("The variable with identifier '{$id}' is not declared in the item definition.");
         }
     }
     try {
         $itemSession->beginAttempt();
         $itemSession->endAttempt(new State($variables));
         // Return the item session state to the client-side.
         echo json_encode(array('success' => true, 'displayFeedback' => true, 'itemSession' => self::buildOutcomeResponse($itemSession)));
     } catch (AssessmentItemSessionException $e) {
         $msg = "An error occurred while processing the responses.";
         throw new \RuntimeException($msg, 0, $e);
     } catch (taoQtiCommon_helpers_ResultTransmissionException $e) {
         $msg = "An error occurred while transmitting a result to the target Result Server.";
         throw new \RuntimeException($msg, 0, $e);
     }
 }
 public function testLoadMatchCorrect()
 {
     $xml = new XmlDocument('2.1');
     $xml->load(self::getTemplatesPath() . '2_1/match_correct.xml');
     $this->assertInstanceOf('qtism\\data\\processing\\ResponseProcessing', $xml->getDocumentComponent());
     $this->assertFalse($xml->getDocumentComponent()->hasTemplateLocation());
     $this->assertFalse($xml->getDocumentComponent()->hasTemplate());
     $responseRules = $xml->getDocumentComponent()->getResponseRules();
     $this->assertEquals(1, count($responseRules));
     $responseCondition = $responseRules[0];
     $this->assertInstanceOf('qtism\\data\\rules\\ResponseCondition', $responseCondition);
     $responseIf = $responseCondition->getResponseIf();
     $match = $responseIf->getExpression();
     $this->assertInstanceOf('qtism\\data\\expressions\\operators\\Match', $match);
     $matchExpressions = $match->getExpressions();
     $this->assertEquals(2, count($matchExpressions));
     $variable = $matchExpressions[0];
     $this->assertInstanceOf('qtism\\data\\expressions\\Variable', $variable);
     $this->assertEquals('RESPONSE', $variable->getIdentifier());
     $correct = $matchExpressions[1];
     $this->assertInstanceOf('qtism\\data\\expressions\\Correct', $correct);
     $this->assertEquals('RESPONSE', $correct->getIdentifier());
     // To be continued...
 }
 public function testModerateXmlBase()
 {
     $doc = new XmlDocument();
     $doc->load(self::samplesDir() . 'rendering/xmlbase_2.xml');
     $div = $doc->getDocumentComponent();
     $this->assertInstanceOf('qtism\\data\\content\\xhtml\\text\\Div', $div);
     $this->assertFalse($div->hasXmlBase());
     $this->assertEquals('', $div->getXmlBase());
     $subDivs = $div->getComponentsByClassName('div');
     $this->assertEquals(2, count($subDivs));
     $this->assertTrue($subDivs[0]->hasXmlBase());
     $this->assertEquals('http://www.qtism-project.org/farm/', $subDivs[0]->getXmlBase());
     $this->assertTrue($subDivs[1]->hasXmlBase());
     $this->assertEquals('http://www.qtism-project.org/birds/', $subDivs[1]->getXmlBase());
 }
Example #15
0
 public function parse($xmlString, $validate = true)
 {
     // TODO: Remove this, and move it higher up
     LogService::flush();
     $xmlDocument = new XmlDocument();
     if ($validate === false) {
         LogService::log('QTI pre-validation is turned off, some invalid attributes might be stripped from XML content upon conversion');
     }
     $xmlDocument->loadFromString($xmlString, $validate);
     /** @var AssessmentItem $assessmentItem */
     $assessmentItem = $xmlDocument->getDocumentComponent();
     if (!$assessmentItem instanceof AssessmentItem) {
         throw new MappingException('XML is not a valid <assessmentItem> document');
     }
     return $this->parseWithAssessmentItemComponent($assessmentItem);
 }
 protected function convertToAssessmentItem(array $data)
 {
     list($xml, $manifest) = Converter::convertLearnosityToQtiItem($data);
     // Assert the XML string is formed and not empty
     // Also, assert manifest is in form of array, regardless it was empty or not
     $this->assertTrue(is_string($xml) && !empty($xml));
     $this->assertTrue(is_array($manifest));
     $document = new XmlDocument();
     $document->loadFromString($xml);
     /** @var AssessmentItem $assessmentItem */
     $assessmentItem = $document->getDocumentComponent();
     // Basic assert on <assessmentItem> object
     $this->assertNotNull($assessmentItem);
     $this->assertTrue($assessmentItem instanceof AssessmentItem);
     return $assessmentItem;
 }
 public function testTemplateVariableDefault()
 {
     // This test aims at testing whether template variables
     // are correctly instantiated as part of the item session and
     // they can be used in response processing.
     $doc = new XmlDocument('2.1.0');
     $doc->load(self::samplesDir() . 'custom/items/template_declaration_default.xml');
     $itemSession = new AssessmentItemSession($doc->getDocumentComponent());
     $itemSessionControl = new ItemSessionControl();
     $itemSessionControl->setMaxAttempts(0);
     $itemSession->setItemSessionControl($itemSessionControl);
     $itemSession->beginItemSession();
     $this->assertTrue($itemSession['WRONGSCORE']->equals(new Float(0.0)));
     $this->assertTrue($itemSession['GOODSCORE']->equals(new Float(1.0)));
     // 1st attempt to get 'GOODSCORE' as 'SCORE'.
     $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new Identifier('ChoiceA'))));
     $itemSession->beginAttempt();
     $itemSession->endAttempt($responses);
     $this->assertTrue($itemSession['SCORE']->equals($itemSession['GOODSCORE']));
     // 2nd attempt to get 'WRONGSCORE' as 'SCORE'.
     $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new Identifier('ChoiceB'))));
     $itemSession->beginAttempt();
     $itemSession->endAttempt($responses);
     $this->assertTrue($itemSession['SCORE']->equals($itemSession['WRONGSCORE']));
 }
 /**
  * Item's ResponseProcessing.
  * 
  * @param core_kernel_file_File $itemPath The Item file resource you want to apply ResponseProcessing.
  * @throws RuntimeException If an error occurs while processing responses or transmitting results
  */
 protected function processResponses(core_kernel_classes_Resource $item)
 {
     $jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
     try {
         $qtiXmlFileContent = QtiFile::getQtiFileContent($item);
         $qtiXmlDoc = new XmlDocument();
         $qtiXmlDoc->loadFromString($qtiXmlFileContent);
     } catch (StorageException $e) {
         $msg = "An error occured while loading QTI-XML file at expected location '{$qtiXmlFilePath}'.";
         throw new \RuntimeException($msg, 0, $e);
     }
     $itemSession = new AssessmentItemSession($qtiXmlDoc->getDocumentComponent(), new SessionManager());
     $itemSession->beginItemSession();
     $variables = array();
     // Convert client-side data as QtiSm Runtime Variables.
     foreach ($jsonPayload as $identifier => $response) {
         $filler = new taoQtiCommon_helpers_PciVariableFiller($qtiXmlDoc->getDocumentComponent());
         try {
             $var = $filler->fill($identifier, $response);
             // Do not take into account QTI File placeholders.
             if (taoQtiCommon_helpers_Utils::isQtiFilePlaceHolder($var) === false) {
                 $variables[] = $var;
             }
         } catch (\OutOfRangeException $e) {
             // A variable value could not be converted, ignore it.
             // Developer's note: QTI Pairs with a single identifier (missing second identifier of the pair) are transmitted as an array of length 1,
             // this might cause problem. Such "broken" pairs are simply ignored.
             common_Logger::d("Client-side value for variable '{$identifier}' is ignored due to data malformation.");
         } catch (\OutOfBoundsException $e) {
             // The response identifier does not match any response declaration.
             common_Logger::d("Uknown item variable declaration '{$identifier}.");
         }
     }
     try {
         $itemSession->beginAttempt();
         $itemSession->endAttempt(new State($variables));
         // Transmit results to the Result Server.
         $this->transmitResults($item, $itemSession);
         // Return the item session state to the client-side.
         echo json_encode(array('success' => true, 'displayFeedback' => true, 'itemSession' => self::buildOutcomeResponse($itemSession), 'feedbacks' => $this->getFeedbacks($itemSession)));
     } catch (AssessmentItemSessionException $e) {
         $msg = "An error occured while processing the responses.";
         throw new \RuntimeException($msg, 0, $e);
     } catch (taoQtiCommon_helpers_ResultTransmissionException $e) {
         $msg = "An error occured while transmitting variable '{$identifier}' to the target Result Server.";
         throw new \RuntimeException($msg, 0, $e);
     }
 }
 /**
  * Compile the RubricBlocRefs' contents into a separate rubric block PHP template.
  * 
  * @param AssessmentTest $assessmentTest The AssessmentTest object you want to compile the rubrickBlocks.
  */
 protected function compileRubricBlocks(AssessmentTest $assessmentTest)
 {
     $rubricBlockRefs = $assessmentTest->getComponentsByClassName('rubricBlockRef');
     $testService = taoQtiTest_models_classes_QtiTestService::singleton();
     $sourceDir = $testService->getQtiTestDir($this->getResource());
     foreach ($rubricBlockRefs as $rubricRef) {
         $rubricRefHref = $rubricRef->getHref();
         $cssScoper = $this->getCssScoper();
         $renderingEngine = $this->getRenderingEngine();
         $markupPostRenderer = $this->getMarkupPostRenderer();
         $publicCompiledDocDir = $this->getPublicDirectory();
         $privateCompiledDocDir = $this->getPrivateDirectory();
         // -- loading...
         common_Logger::t("Loading rubricBlock '" . $rubricRefHref . "'...");
         $rubricDoc = new XmlDocument();
         $rubricDoc->loadFromString($this->getPrivateDirectory()->read($rubricRefHref));
         common_Logger::t("rubricBlock '" . $rubricRefHref . "' successfully loaded.");
         // -- rendering...
         common_Logger::t("Rendering rubricBlock '" . $rubricRefHref . "'...");
         $pathinfo = pathinfo($rubricRefHref);
         $renderingFile = $pathinfo['filename'] . '.php';
         $rubric = $rubricDoc->getDocumentComponent();
         $rubricStylesheets = $rubric->getStylesheets();
         $stylesheets = new StylesheetCollection();
         // In any case, include the base QTI Stylesheet.
         $stylesheets->merge($rubricStylesheets);
         $rubric->setStylesheets($stylesheets);
         // -- If the rubricBlock has no id, give it a auto-generated one in order
         // to be sure that CSS rescoping procedure works fine (it needs at least an id
         // to target its scoping).
         if ($rubric->hasId() === false) {
             // Prepend 'tao' to the generated id because the CSS
             // ident token must begin by -|[a-zA-Z]
             $rubric->setId('tao' . uniqid());
         }
         // -- Copy eventual remote resources of the rubricBlock.
         $this->copyRemoteResources($rubric);
         $domRendering = $renderingEngine->render($rubric);
         $mainStringRendering = $markupPostRenderer->render($domRendering);
         // Prepend stylesheets rendering to the main rendering.
         $styleRendering = $renderingEngine->getStylesheets();
         $mainStringRendering = $styleRendering->ownerDocument->saveXML($styleRendering) . $mainStringRendering;
         foreach ($stylesheets as $rubricStylesheet) {
             $relPath = trim($this->getExtraPath(), '/');
             $relPath = (empty($relPath) ? '' : $relPath . DIRECTORY_SEPARATOR) . $rubricStylesheet->getHref();
             $sourceFile = $sourceDir->getFile($relPath);
             if (!$publicCompiledDocDir->has($relPath)) {
                 try {
                     $data = $sourceFile->read();
                     $tmpDir = \tao_helpers_File::createTempDir();
                     $tmpFile = $tmpDir . 'tmp.css';
                     file_put_contents($tmpFile, $data);
                     $scopedCss = $cssScoper->render($tmpFile, $rubric->getId());
                     unlink($tmpFile);
                     rmdir($tmpDir);
                     $publicCompiledDocDir->write($relPath, $scopedCss);
                 } catch (\InvalidArgumentException $e) {
                     common_Logger::e('Unable to copy file into public directory: ' . $relPath);
                 }
             }
         }
         // -- Replace the artificial 'tao://qti-directory' base path with a runtime call to the delivery time base path.
         $mainStringRendering = str_replace(TAOQTITEST_PLACEHOLDER_BASE_URI, '<?php echo $' . TAOQTITEST_BASE_PATH_NAME . '; ?>', $mainStringRendering);
         if (!$privateCompiledDocDir->has($renderingFile)) {
             try {
                 $privateCompiledDocDir->write($renderingFile, $mainStringRendering);
                 common_Logger::t("rubricBlockRef '" . $rubricRefHref . "' successfully rendered.");
             } catch (\InvalidArgumentException $e) {
                 common_Logger::e('Unable to copy file into public directory: ' . $renderingFile);
             }
         }
         // -- Clean up old rubric block and reference the new rubric block template.
         $privateCompiledDocDir->delete($rubricRefHref);
         $rubricRef->setHref('./' . $pathinfo['filename'] . '.php');
     }
 }
 /**
  * Assign items to a QTI test.
  * @param XmlDocument $doc
  * @param array $items
  * @return type
  * @throws taoQtiTest_models_classes_QtiTestServiceException
  */
 private function setItemsToDoc(XmlDocument $doc, array $items, $sectionIndex = 0)
 {
     $sections = $doc->getDocumentComponent()->getComponentsByClassName('assessmentSection');
     if (!isset($sections[$sectionIndex])) {
         throw new taoQtiTest_models_classes_QtiTestServiceException('No section found in test at index : ' . $sectionIndex, taoQtiTest_models_classes_QtiTestServiceException::TEST_READ_ERROR);
     }
     $section = $sections[$sectionIndex];
     $itemContentProperty = new core_kernel_classes_Property(TAO_ITEM_CONTENT_PROPERTY);
     $itemRefs = new SectionPartCollection();
     $itemRefIdentifiers = array();
     foreach ($items as $itemResource) {
         $itemContent = new core_kernel_file_File($itemResource->getUniquePropertyValue($itemContentProperty));
         $itemDoc = new XmlDocument();
         try {
             $itemDoc->load($itemContent->getAbsolutePath());
         } catch (StorageException $e) {
             // We consider the item not compliant with QTI, let's try the next one.
             continue;
         }
         $itemRefIdentifier = $itemDoc->getDocumentComponent()->getIdentifier();
         //enable more than one reference
         if (array_key_exists($itemRefIdentifier, $itemRefIdentifiers)) {
             $itemRefIdentifiers[$itemRefIdentifier] += 1;
             $itemRefIdentifier .= '-' . $itemRefIdentifiers[$itemRefIdentifier];
         } else {
             $itemRefIdentifiers[$itemRefIdentifier] = 0;
         }
         $itemRefs[] = new AssessmentItemRef($itemRefIdentifier, $itemResource->getUri());
     }
     $section->setSectionParts($itemRefs);
     return count($itemRefs);
 }
 /**
  * 
  * @dataProvider metaProvider
  * @param string $testFile
  * @param array $expectedMeta
  */
 public function testTestMeta($testFile, $expectedMeta)
 {
     $xml = new XmlDocument();
     $xml->load($testFile);
     $this->assertEquals($expectedMeta, taoQtiTest_helpers_TestCompilerUtils::testMeta($xml->getDocumentComponent()));
 }
Example #22
0
 /**
  * Run the rendering behaviour related to the "XHTML" flavour.
  * 
  * @param \qtism\data\storage\xml\XmlDocument $doc The QTI XML document to be rendered.
  * @param \qtism\runtime\rendering\markup\xhtml\XhtmlRenderingEngine $renderer
  * @return string The raw rendering data.
  */
 private function runXhtml(XmlDocument $doc, XhtmlRenderingEngine $renderer)
 {
     $arguments = $this->getArguments();
     $profile = $arguments['flavour'];
     $xml = $renderer->render($doc->getDocumentComponent());
     $header = '';
     $footer = '';
     $indent = '';
     $nl = '';
     if ($arguments['format'] === true) {
         $xml->formatOutput = true;
         $indent .= "  ";
         $nl .= "\n";
     }
     if ($arguments['document'] === true) {
         $rootComponent = $doc->getDocumentComponent();
         $title = '';
         if ($rootComponent->getQtiClassName() === 'assessmentItem') {
             $title = XmlUtils::escapeXmlSpecialChars($rootComponent->getTitle());
         }
         $header .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
         $header .= "<html>{$nl}";
         $header .= "{$indent}<head>{$nl}";
         $header .= "{$indent}{$indent}<meta charset=\"utf-8\">{$nl}";
         if (empty($title) !== false) {
             $header .= "{$indent}{$indent}<title>" . $title . "</title>{$nl}";
         }
         $header .= "{$indent}{$indent}" . $renderer->getStylesheets()->ownerDocument->saveXML($renderer->getStylesheets());
         $header .= "{$indent}</head>{$nl}";
         $header .= "{$indent}<body>{$nl}";
         $footer = "{$indent}</body>{$nl}";
         $footer .= "</html>\n";
     }
     $body = $xml->saveXml($xml->documentElement) . "{$nl}";
     // Indent body...
     $indentBody = '';
     if ($arguments['document'] === null) {
         $indent = '';
     }
     foreach (preg_split('/\\n|\\r/u', $body, -1, PREG_SPLIT_NO_EMPTY) as $bodyLine) {
         // do stuff with $line
         $indentBody .= "{$indent}{$indent}{$bodyLine}{$nl}";
     }
     $body = $indentBody;
     return "{$header}{$indentBody}{$footer}";
 }
 public function testAppendNumberCorrectOutcomeProcessing()
 {
     $doc = new XmlDocument();
     $doc->load(self::samplesDir() . 'categories.xml');
     TestCategoryRulesUtils::appendNumberCorrectOutcomeProcessing($doc->getDocumentComponent(), 'math', 'MATH' . TestCategoryRulesUtils::NUMBER_CORRECT_SUFFIX);
     $this->assertInstanceOf('qtism\\data\\processing\\OutcomeProcessing', $doc->getDocumentComponent()->getOutcomeProcessing());
     $outcomeRules = $doc->getDocumentComponent()->getOutcomeProcessing()->getOutcomeRules();
     $this->assertCount(1, $outcomeRules);
     $this->assertInstanceOf('qtism\\data\\rules\\setOutcomeValue', $outcomeRules[0]);
     $this->assertEquals('MATH' . TestCategoryRulesUtils::NUMBER_CORRECT_SUFFIX, $outcomeRules[0]->getIdentifier());
     $this->assertInstanceOF('qtism\\data\\expressions\\NumberCorrect', $outcomeRules[0]->getExpression());
     $this->assertEquals(array('math'), $outcomeRules[0]->getExpression()->getIncludeCategories()->getArrayCopy());
     // If a second call to TestCategoryRulesUtils::appendNumberCorrectOutcomeProcessing occurs for a variable wich
     // is already targeted by a setOutcomeValue rule, no new outcome rule should appear to avoid duplicates.
     TestCategoryRulesUtils::appendNumberCorrectOutcomeProcessing($doc->getDocumentComponent(), 'math', 'MATH' . TestCategoryRulesUtils::NUMBER_CORRECT_SUFFIX);
     $outcomeRules = $doc->getDocumentComponent()->getOutcomeProcessing()->getOutcomeRules();
     $this->assertCount(1, $outcomeRules);
 }
Example #24
0
 public function testLoadInteractionMixSaschsen()
 {
     $xmlDoc = new XmlDocument('2.1');
     $xmlDoc->load(self::samplesDir() . 'ims/tests/interaction_mix_sachsen/interaction_mix_sachsen.xml');
     $phpDoc = new PhpDocument();
     $phpDoc->setDocumentComponent($xmlDoc->getDocumentComponent());
     $file = tempnam('/tmp', 'qsm');
     $phpDoc->save($file);
     $phpDoc = new PhpDocument();
     $phpDoc->load($file);
     $this->assertEquals('InteractionMixSachsen_1901710679', $phpDoc->getDocumentComponent()->getIdentifier());
     unlink($file);
     $this->assertFalse(file_exists($file));
 }
 public function testLoadPCIItem($url = '')
 {
     $doc = new XmlDocument();
     $doc->load(empty($url) === true ? self::samplesDir() . 'custom/interactions/custom_interaction_pci.xml' : $url, true);
     $item = $doc->getDocumentComponent();
     $this->assertInstanceOf('qtism\\data\\AssessmentItem', $item);
     $this->assertEquals('SimpleExample', $item->getIdentifier());
     $this->assertEquals('Example', $item->getTitle());
     $this->assertFalse($item->isAdaptive());
     $this->assertFalse($item->isTimeDependent());
     // responseDeclaration
     $responseDeclarations = $item->getComponentsByClassName('responseDeclaration');
     $this->assertEquals(1, count($responseDeclarations));
     $this->assertEquals(BaseType::POINT, $responseDeclarations[0]->getBaseType());
     $this->assertEquals(Cardinality::SINGLE, $responseDeclarations[0]->getCardinality());
     $this->assertEquals('RESPONSE', $responseDeclarations[0]->getIdentifier());
     // templateDeclarations
     $templateDeclarations = $item->getComponentsByClassName('templateDeclaration');
     $this->assertEquals(2, count($templateDeclarations));
     $this->assertEquals(BaseType::INTEGER, $templateDeclarations[0]->getBaseType());
     $this->assertEquals(Cardinality::SINGLE, $templateDeclarations[0]->getCardinality());
     $this->assertEquals('X', $templateDeclarations[0]->getIdentifier());
     $this->assertEquals(BaseType::INTEGER, $templateDeclarations[1]->getBaseType());
     $this->assertEquals(Cardinality::SINGLE, $templateDeclarations[1]->getCardinality());
     $this->assertEquals('Y', $templateDeclarations[1]->getIdentifier());
     // customInteraction
     $customInteractions = $item->getComponentsByClassName('customInteraction');
     $this->assertEquals(1, count($customInteractions));
     $customInteraction = $customInteractions[0];
     $this->assertEquals('RESPONSE', $customInteraction->getResponseIdentifier());
     $this->assertEquals('graph1', $customInteraction->getId());
     // xml content
     $customInteractionElt = $customInteraction->getXml()->documentElement;
     $this->assertEquals('RESPONSE', $customInteractionElt->getAttribute('responseIdentifier'));
     $this->assertEquals('graph1', $customInteractionElt->getAttribute('id'));
     $pci = 'http://www.imsglobal.org/xsd/portableCustomInteraction';
     // -- pci:portableCustomInteraction
     $portableCustomInteractionElts = $customInteractionElt->getElementsByTagNameNS($pci, 'portableCustomInteraction');
     $this->assertEquals(1, $portableCustomInteractionElts->length);
     $this->assertEquals('IW30MX6U48JF9120GJS', $portableCustomInteractionElts->item(0)->getAttribute('customInteractionTypeIdentifier'));
     // --pci:templateVariableMapping
     $templateVariableMappingElts = $customInteractionElt->getElementsByTagNameNS($pci, 'templateVariableMapping');
     $this->assertEquals(2, $templateVariableMappingElts->length);
     $this->assertEquals('X', $templateVariableMappingElts->item(0)->getAttribute('templateIdentifier'));
     $this->assertEquals('areaX', $templateVariableMappingElts->item(0)->getAttribute('configurationProperty'));
     $this->assertEquals('Y', $templateVariableMappingElts->item(1)->getAttribute('templateIdentifier'));
     $this->assertEquals('areaY', $templateVariableMappingElts->item(1)->getAttribute('configurationProperty'));
     // --pci:instance
     $instanceElts = $customInteractionElt->getElementsByTagNameNS($pci, 'instance');
     $this->assertEquals(1, $instanceElts->length);
     // --xhtml:script
     $xhtml = 'http://www.w3.org/1999/xhtml';
     $scriptElts = $instanceElts->item(0)->getElementsByTagNameNS($xhtml, 'script');
     $this->assertEquals(2, $scriptElts->length);
     $this->assertEquals('text/javascript', $scriptElts->item(0)->getAttribute('type'));
     $this->assertEquals('js/graph.js', $scriptElts->item(0)->getAttribute('src'));
     $this->assertEquals('text/javascript', $scriptElts->item(1)->getAttribute('type'));
     $this->assertEquals(7, mb_strpos($scriptElts->item(1)->nodeValue, 'qtiCustomInteractionContext.setConfiguration(', 0, 'UTF-8'));
     // --xhtml:div
     $divElts = $instanceElts->item(0)->getElementsByTagNameNS($xhtml, 'div');
     $this->assertEquals(1, $divElts->length);
     $this->assertEquals('graph1_box', $divElts->item(0)->getAttribute('id'));
     $this->assertEquals('graph', $divElts->item(0)->getAttribute('class'));
     $this->assertEquals('width:500px; height:500px;', $divElts->item(0)->getAttribute('style'));
 }
 /**
  * It is sometimes necessary to identify the link between assessmentItemRefs described in a QTI Test definition and the resources
  * describing items in IMS Manifest file. This utility method helps you to achieve this.
  * 
  * The method will return an array describing the IMS Manifest resources that were found in an IMS Manifest file on basis of
  * the assessmentItemRefs found in an AssessmentTest definition. The keys of the arrays are assessmentItemRef identifiers and
  * values are IMS Manifest Resources. 
  * 
  * If an IMS Manifest Resource cannot be found for a given assessmentItemRef, the value in the returned array will be false.
  * 
  * @param XmlDocument $test A QTI Test Definition.
  * @param taoQtiTest_models_classes_ManifestParser $manifestParser A Manifest Parser.
  * @param string $basePath The base path of the folder the IMS archive is exposed as a file system component.
  * @return array An array containing two arrays (items and dependencies) where keys are identifiers and values are oat\taoQtiItem\model\qti\Resource objects or false.
  */
 public static function buildAssessmentItemRefsTestMap(XmlDocument $test, taoQtiTest_models_classes_ManifestParser $manifestParser, $basePath)
 {
     $assessmentItemRefs = $test->getDocumentComponent()->getComponentsByClassName('assessmentItemRef');
     $map = array('items' => array(), 'dependencies' => array());
     $itemResources = $manifestParser->getResources(array('imsqti_item_xmlv2p1', 'imsqti_apipitem_xmlv2p1'), taoQtiTest_models_classes_ManifestParser::FILTER_RESOURCE_TYPE);
     $allResources = $manifestParser->getResources();
     // cleanup $basePath.
     $basePath = rtrim($basePath, "/\\");
     $basePath = helpers_File::truePath($basePath);
     $basePath .= DIRECTORY_SEPARATOR;
     $documentURI = preg_replace('/^file:\\//', '', $test->getDomDocument()->documentURI);
     $testPathInfo = pathinfo($documentURI);
     $testBasePath = tao_helpers_File::truePath($testPathInfo['dirname']) . DIRECTORY_SEPARATOR;
     foreach ($assessmentItemRefs as $itemRef) {
         // Find the QTI Resource (in IMS Manifest) related to the item ref.
         // To achieve this, we compare their path.
         $itemRefRelativeHref = str_replace('/', DIRECTORY_SEPARATOR, $itemRef->getHref());
         $itemRefRelativeHref = ltrim($itemRefRelativeHref, "/\\");
         $itemRefCanonicalHref = helpers_File::truePath($testBasePath . $itemRefRelativeHref);
         $map['items'][$itemRef->getIdentifier()] = false;
         // Compare with items referenced in the manifest.
         foreach ($itemResources as $itemResource) {
             $itemResourceRelativeHref = str_replace('/', DIRECTORY_SEPARATOR, $itemResource->getFile());
             $itemResourceRelativeHref = ltrim($itemResourceRelativeHref, "/\\");
             $itemResourceCanonicalHref = helpers_File::truePath($basePath . $itemResourceRelativeHref);
             // With some Windows flavours (Win7, Win8), the $itemRefCanonicalHref comes out with
             // a leading 'file:\' component. Let's clean this. (str_replace is binary-safe \0/)
             $os = tao_helpers_Environment::getOperatingSystem();
             if ($os === 'WINNT' || $os === 'WIN32' || $os === 'Windows') {
                 $itemRefCanonicalHref = str_replace('file:\\', '', $itemRefCanonicalHref);
                 // And moreover, it sometimes refer the temp directory as Windows\TEMP instead of Windows\Temp.
                 $itemRefCanonicalHref = str_replace('\\TEMP\\', '\\Temp\\', $itemRefCanonicalHref);
             }
             // With some MacOS flavours, the $itemRefCanonicalHref comes out with
             // a leading '/private' component. Clean it!
             if ($os === 'Darwin') {
                 $itemRefCanonicalHref = str_replace('/private', '', $itemRefCanonicalHref);
             }
             if ($itemResourceCanonicalHref == $itemRefCanonicalHref && is_file($itemResourceCanonicalHref)) {
                 // assessmentItemRef <-> IMS Manifest resource successful binding!
                 $map['items'][$itemRef->getIdentifier()] = $itemResource;
                 //get dependencies for each item
                 foreach ($itemResource->getDependencies() as $dependencyIdentifier) {
                     /** @var taoQtiTest_models_classes_QtiResource $resource */
                     foreach ($allResources as $resource) {
                         if ($dependencyIdentifier == $resource->getIdentifier()) {
                             $map['dependencies'][$dependencyIdentifier] = $resource;
                             break;
                         }
                     }
                 }
                 break;
             }
         }
     }
     return $map;
 }
 public function testExplodeTestFeedbacks()
 {
     $src = self::samplesDir() . 'custom/runtime/testfeedbackrefs_explosion.xml';
     $doc = new XmlCompactDocument();
     $doc->load($src, true);
     $doc->setExplodeTestFeedbacks(true);
     $file = tempnam('/tmp', 'qsm');
     $doc->save($file);
     $pathinfo = pathinfo($file);
     $path = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . 'testFeedback_TF_P01_1.xml';
     $this->assertTrue(file_exists($path));
     $tfDoc = new XmlDocument();
     $tfDoc->load($path);
     $this->assertEquals('feedback1', $tfDoc->getDocumentComponent()->getIdentifier());
     $path = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . 'testFeedback_TF_P01_2.xml';
     $this->assertTrue(file_exists($path));
     $tfDoc = new XmlDocument();
     $tfDoc->load($path);
     $this->assertEquals('feedback2', $tfDoc->getDocumentComponent()->getIdentifier());
     $path = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . 'testFeedback_TF_testfeedbackrefs_explosion_1.xml';
     $this->assertTrue(file_exists($path));
     $tfDoc = new XmlDocument();
     $tfDoc->load($path);
     $this->assertEquals('mainfeedback1', $tfDoc->getDocumentComponent()->getIdentifier());
     $path = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . 'testFeedback_TF_testfeedbackrefs_explosion_2.xml';
     $this->assertTrue(file_exists($path));
     $tfDoc = new XmlDocument();
     $tfDoc->load($path);
     $this->assertEquals('mainfeedback2', $tfDoc->getDocumentComponent()->getIdentifier());
     $this->assertEquals(0, $doc->getDocumentComponent()->containsComponentWithClassName('testFeedback'));
 }
 /**
  * Embed external resources into the XML
  *
  * @param string $originalXml
  * @throws \tao_models_classes_FileNotFoundException
  * @return string
  */
 public static function embedAssets($originalXml)
 {
     $basedir = dirname($originalXml) . DIRECTORY_SEPARATOR;
     $xmlDocument = new XmlDocument();
     $xmlDocument->load($originalXml, true);
     //get images and object to base64 their src/data
     $images = $xmlDocument->getDocumentComponent()->getComponentsByClassName('img');
     $objects = $xmlDocument->getDocumentComponent()->getComponentsByClassName('object');
     /** @var $image \qtism\data\content\xhtml\Img */
     foreach ($images as $image) {
         $source = $image->getSrc();
         $image->setSrc(self::secureEncode($basedir, $source));
     }
     /** @var $object \qtism\data\content\xhtml\Object */
     foreach ($objects as $object) {
         $data = $object->getData();
         $object->setData(self::secureEncode($basedir, $data));
     }
     // save the document to a tempfile
     $newXml = tempnam(sys_get_temp_dir(), 'sharedStimulus_') . '.xml';
     $xmlDocument->save($newXml);
     return $newXml;
 }
<?php

/**
 * I don't even understand how it is correctly parsed...
 * But it's safe!
 */
use qtism\data\storage\xml\XmlDocument;
use qtism\runtime\rendering\markup\xhtml\XhtmlRenderingEngine;
require_once dirname(__FILE__) . '/../../qtism/qtism.php';
$doc = new XmlDocument();
$doc->load('../samples/rendering/php_highjacking_3.xml', true);
$renderer = new XhtmlRenderingEngine();
$rendering = $renderer->render($doc->getDocumentComponent());
$rendering->formatOutput = true;
echo $rendering->saveXML();
 public function testAmps()
 {
     $file = self::samplesDir() . 'custom/amps.xml';
     $doc = new XmlDocument();
     $doc->load($file);
     $root = $doc->getDocumentComponent();
     $divs = $root->getComponentsByClassName('div');
     $this->assertEquals(1, count($divs));
     $divContent = $divs[0]->getContent();
     $divText = $divContent[0];
     $this->assertEquals('Hello there & there! I am trying to make <you> "crazy"', $divText->getcontent());
 }