/** * Update the item content by wrapping the modalfeedback body into a new wrapper * * @param oat\taoQtiItem\modal\Item $item * @param string $itemFile * @return boolean */ protected function updateItem(\oat\taoQtiItem\model\qti\Item $item, $itemFile) { $changed = false; $responses = $item->getResponses(); foreach ($responses as $response) { $responseIdentifier = $response->attr('identifier'); $rules = $response->getFeedbackRules(); foreach ($rules as $rule) { $modalFeedbacks = array(); if ($rule->getFeedbackThen()) { $modalFeedbacks[] = $rule->getFeedbackThen(); } if ($rule->getFeedbackElse()) { $modalFeedbacks[] = $rule->getFeedbackElse(); } foreach ($modalFeedbacks as $modalFeedback) { $feedbackXml = simplexml_load_string($modalFeedback->toQti()); if ($feedbackXml->div[0] && $feedbackXml->div[0]['class'] && preg_match('/^x-tao-wrapper/', $feedbackXml->div[0]['class'])) { //the item body has not already been wrapped by the new wrapper <div class="x-tao-wrapper w-tao-relatedOutcome-{{response.identifier}}"> continue; } $message = $modalFeedback->getBody()->getBody(); $modalFeedback->getBody()->edit('<div class="x-tao-wrapper x-tao-relatedOutcome-' . $responseIdentifier . '">' . $message . '</div>', true); $changed = true; } } } return $changed; }
/** * Remove unused response declaration from the items and rp template misuse * * @param oat\taoQtiItem\modal\Item $item * @param string $itemFile * @return boolean */ protected function updateItem(\oat\taoQtiItem\model\qti\Item $item, $itemFile) { $changed = false; $responses = $item->getResponses(); $interactions = $item->getInteractions(); $usedResponses = []; foreach ($interactions as $interaction) { $usedResponses[] = $interaction->attr('responseIdentifier'); } foreach ($responses as $response) { $responseIdentifier = $response->attr('identifier'); if (!in_array($responseIdentifier, $usedResponses)) { $changed = true; $item->removeResponse($response); } } $xml = simplexml_load_file($itemFile); $rpTemplate = (string) $xml->responseProcessing['template']; //detect wrong usage for standard standard response declaration $rp = $item->getResponseProcessing(); if ($rp instanceof \oat\taoQtiItem\model\qti\response\TemplatesDriven && $rpTemplate) { if (count($interactions) > 1) { $changed = true; } else { $interaction = reset($interactions); if ($interaction && $interaction->attr('responseIdentifier') != 'RESPONSE') { $changed = true; } } } return $changed; }
/** * Remove unused response declaration from the items and rp template misuse * * @param oat\taoQtiItem\modal\Item $item * @param string $itemFile * @return boolean */ protected function updateItem(Item $item, $itemFile) { $changed = false; $requireFix = false; $interactions = $item->getInteractions(); foreach ($interactions as $interaction) { if ($interaction instanceof PortableCustomInteraction && $interaction->getTypeIdentifier() === 'textReaderInteraction') { $response = $interaction->getResponse(); $currentDefaultVal = $response->getDefaultValue(); if (!is_array($currentDefaultVal) || empty($currentDefaultVal) || count($currentDefaultVal) !== 1) { $requireFix = true; } else { $val = $currentDefaultVal[0]; $requireFix = !($val instanceof Value && $val->getValue() === 'true'); } if ($requireFix) { $defaultValue = new Value(); $defaultValue->setValue('true'); $response->setDefaultValue([$defaultValue]); $changed = true; } } } return $changed; }
/** * Extract all assets from the current item * @return array the assets by type */ public function extract() { foreach ($this->item->getComposingElements() as $element) { $this->extractImg($element); $this->extractObject($element); $this->extractStyleSheet($element); $this->extractCustomElement($element); } return $this->assets; }
public function testModel() { $myItem = new Item(); $myItem->setAttribute('title', 'My Coolest Item'); $myItem->getBody()->edit('sth'); $myInteraction = new ChoiceInteraction(); $myInteraction->getPrompt()->edit('Prompt you'); $myChoice1 = $myInteraction->createChoice(array('fixed' => true), 'This is correct'); $myChoice2 = $myInteraction->createChoice(array('fixed' => true), 'This is not correct'); $this->assertInstanceOf('\\oat\\taoQtiItem\\model\\qti\\choice\\SimpleChoice', $myChoice2); $this->assertEquals(count($myInteraction->getChoices()), 2); $myChoice1->setContent('answer #1'); $myChoice2->setContent('answer #2'); $myInteraction->removeChoice($myChoice1); $this->assertEquals(count($myInteraction->getChoices()), 1); $myItem->addInteraction($myInteraction, "Adding my interaction here {$myInteraction->getPlaceholder()}. And not there."); $this->assertNotNull($myInteraction->getRelatedItem()); $this->assertEquals($myInteraction->getRelatedItem()->getSerial(), $myItem->getSerial()); $myResponse = new ResponseDeclaration(); $myItem->addResponse($myResponse); $this->assertNotNull($myResponse->getRelatedItem()); $this->assertEquals($myResponse->getRelatedItem()->getSerial(), $myItem->getSerial()); $myItem->removeResponse($myResponse); $responses = $myItem->getResponses(); $this->assertTrue(empty($responses)); }
public function extractPortableAssetElements() { foreach ($this->item->getComposingElements() as $element) { $this->extractCustomElement($element); } return $this->assets; }
/** * @param core_kernel_classes_Resource $item the item to pack * @param string $lang * @param Item $qtiItem * @param \oat\oatbox\filesystem\Directory $directory * @return ItemPack $itemPack * @throws common_Exception */ public function packQtiItem($item, $lang, $qtiItem, Directory $directory) { //use the QtiParser to transform the QTI XML into an assoc array representation try { //build the ItemPack from the parsed data if ($this->replaceXinclude) { $resolver = new ItemMediaResolver($item, $lang); $xincludeLoader = new XIncludeLoader($qtiItem, $resolver); $xincludeLoader->load(true); } $itemPack = new ItemPack(self::$itemType, $qtiItem->toArray()); $itemPack->setAssetEncoders($this->getAssetEncoders()); $assetParser = new AssetParser($qtiItem, $directory); $assetParser->setDeepParsing($this->isNestedResourcesInclusion()); $assetParser->setGetXinclude(!$this->replaceXinclude); $storageDirectory = new \tao_models_classes_service_StorageDirectory($item->getUri(), $directory->getFileSystemId(), $directory->getPrefix() . '/' . $lang); $storageDirectory->setServiceLocator($directory->getServiceLocator()); foreach ($assetParser->extract($itemPack) as $type => $assets) { $resolver = new ItemMediaResolver($item, $lang); foreach ($assets as &$asset) { $mediaAsset = $resolver->resolve($asset); $mediaSource = $mediaAsset->getMediaSource(); $asset = $mediaSource->getBaseName($mediaAsset->getMediaIdentifier()); } $itemPack->setAssets($type, $assets, $storageDirectory); } } catch (common_Exception $e) { throw new common_Exception('Unable to pack item ' . $item->getUri() . ' : ' . $e->getMessage()); } return $itemPack; }
protected function getResponse($identifier) { foreach ($this->item->getResponses() as $response) { if ($response->getIdentifier() == $identifier) { return $response; } } throw new ParsingException('cannot found the response with identifier ' . $identifier); }
/** * Short description of method takeOverFrom * * @access public * @author Joel Bout, <*****@*****.**> * @param ResponseProcessing responseProcessing * @param Item item * @return oat\taoQtiItem\model\qti\response\Composite */ public static function takeOverFrom(ResponseProcessing $responseProcessing, Item $item) { $returnValue = null; if ($responseProcessing instanceof static) { // already good $returnValue = $responseProcessing; } elseif ($responseProcessing instanceof Template) { // IMS Template $rp = new Summation($item, 'SCORE'); foreach ($item->getInteractions() as $interaction) { $response = $interaction->getResponse(); try { $irp = IrpTemplate::createByTemplate($responseProcessing->getUri(), $response, $item); } catch (Exception $e) { throw new TakeoverFailedException(); } $rp->add($irp, $item); } $returnValue = $rp; } elseif ($responseProcessing instanceof TemplatesDriven) { // TemplateDriven $rp = new Summation($item, 'SCORE'); foreach ($item->getInteractions() as $interaction) { $response = $interaction->getResponse(); try { $irp = IrpTemplate::createByTemplate($responseProcessing->getTemplate($response), $response, $item); } catch (Exception $e) { throw new TakeoverFailedException(); } $rp->add($irp, $item); } $returnValue = $rp; } else { common_Logger::d('Composite ResponseProcessing can not takeover from ' . get_class($responseProcessing) . ' yet'); throw new TakeoverFailedException(); } common_Logger::i('Converted to Composite', array('TAOITEMS', 'QTI')); return $returnValue; }
/** * Short description of method buildRule * * @deprecated * @access public * @author Joel Bout, <*****@*****.**> * @param Item item * @return string */ public function buildRule(Item $item) { $returnValue = (string) ''; foreach ($item->getInteractions() as $interaction) { $response = $interaction->getResponse(); $uri = $response->getHowMatch(); $templateName = substr($uri, strrpos($uri, '/') + 1); $matchingTemplate = dirname(__FILE__) . '/rpTemplate/rule.' . $templateName . '.tpl.php'; $tplRenderer = new taoItems_models_classes_TemplateRenderer($matchingTemplate, array('responseIdentifier' => $response->getIdentifier(), 'outcomeIdentifier' => 'SCORE')); $returnValue .= $tplRenderer->render(); } return (string) $returnValue; }
public function getVariableElements(Item $item) { $allData = $item->getDataForDelivery(); return $allData['variable']; }
protected function getModalFeedbacks(Item $qtiItem) { $returnValue = array(); $feedbacks = $qtiItem->getModalFeedbacks(); foreach ($feedbacks as $feedback) { $returnValue[$feedback->getSerial()] = $feedback->toArray(); } return $returnValue; }
/** * Short description of method create * * @access public * @author Joel Bout, <*****@*****.**> * @param int classID * @param Response response * @param Item item * @return oat\taoQtiItem\model\qti\response\interactionResponseProcessing\InteractionResponseProcessing */ public static function create($classID, ResponseDeclaration $response, Item $item) { switch ($classID) { case None::CLASS_ID: $className = 'oat\\taoQtiItem\\model\\qti\\response\\interactionResponseProcessing\\None'; break; case MatchCorrectTemplate::CLASS_ID: $className = 'oat\\taoQtiItem\\model\\qti\\response\\interactionResponseProcessing\\MatchCorrectTemplate'; break; case MapResponseTemplate::CLASS_ID: $className = 'oat\\taoQtiItem\\model\\qti\\response\\interactionResponseProcessing\\MapResponseTemplate'; break; case MapResponsePointTemplate::CLASS_ID: $className = 'oat\\taoQtiItem\\model\\qti\\response\\interactionResponseProcessing\\MapResponsePointTemplate'; break; case Custom::CLASS_ID: $className = 'oat\\taoQtiItem\\model\\qti\\response\\interactionResponseProcessing\\Custom'; break; default: throw new common_exception_Error('Unknown InteractionResponseProcessing Class ID "' . $classID . '"'); } $outcome = self::generateOutcomeDefinition(); $outcomes = $item->getOutcomes(); $outcomes[] = $outcome; $item->setOutcomes($outcomes); $returnValue = new $className($response, $outcome); return $returnValue; }
/** * Set the item the current Qti Element belongs to. * The related item assignment is propagated to all containing Qti Element of the current one. * The "force" option allows changing the associated item (even if it has already been defined) * * @param oat\taoQtiItem\model\qti\Item $item * @param boolean $force * @return boolean * @throws oat\taoQtiItem\model\qti\exception\QtiModelException */ public function setRelatedItem(Item $item, $force = false) { $returnValue = false; if (!is_null($this->relatedItem) && $this->relatedItem->getSerial() == $item->getSerial()) { $returnValue = true; // identical } elseif (!$force && !is_null($this->relatedItem)) { throw new QtiModelException('attempt to change item reference for a QTI element'); } else { // propagate the assignation of item to all included objects $reflection = new ReflectionClass($this); foreach ($reflection->getProperties() as $property) { if (!$property->isStatic() && !$property->isPrivate()) { $propertyName = $property->getName(); $value = $this->{$propertyName}; if (is_array($value)) { foreach ($value as $subvalue) { if (is_object($subvalue) && $subvalue instanceof Element) { $subvalue->setRelatedItem($item); } elseif (is_object($subvalue) && $subvalue instanceof ResponseIdentifier) { // manage the reference of identifier $idenfierBaseType = $subvalue->getValue(true); if (!is_null($idenfierBaseType)) { $idenfierBaseType->getReferencedObject()->setRelatedItem($item); } } } } elseif (is_object($value) && $value instanceof Element) { $value->setRelatedItem($item); } } } // set item reference to current object $this->relatedItem = $item; $returnValue = true; } return $returnValue; }
/** * Short description of method buildCompositeResponseProcessing * * @access public * @author Joel Bout, <*****@*****.**> * @param DOMElement data * @param Item item * @return oat\taoQtiItem\model\qti\response\ResponseProcessing */ protected function buildCompositeResponseProcessing(DOMElement $data, Item $item) { $returnValue = null; // STRONGLY simplified summation detection $patternCorrectTAO = '/responseCondition [count(./*) = 1 ] [name(./*[1]) = "responseIf" ] [count(./responseIf/*) = 2 ] [name(./responseIf/*[1]) = "match" ] [name(./responseIf/match/*[1]) = "variable" ] [name(./responseIf/match/*[2]) = "correct" ] [name(./responseIf/*[2]) = "setOutcomeValue" ] [count(./responseIf/setOutcomeValue/*) = 1 ] [name(./responseIf/setOutcomeValue/*[1]) = "baseValue"]'; $patternMapTAO = '/responseCondition [count(./*) = 1 ] [name(./*[1]) = "responseIf" ] [count(./responseIf/*) = 2 ] [name(./responseIf/*[1]) = "not" ] [count(./responseIf/not/*) = 1 ] [name(./responseIf/not/*[1]) = "isNull" ] [count(./responseIf/not/isNull/*) = 1 ] [name(./responseIf/not/isNull/*[1]) = "variable" ] [name(./responseIf/*[2]) = "setOutcomeValue" ] [count(./responseIf/setOutcomeValue/*) = 1 ] [name(./responseIf/setOutcomeValue/*[1]) = "mapResponse"]'; $patternMapPointTAO = '/responseCondition [count(./*) = 1 ] [name(./*[1]) = "responseIf" ] [count(./responseIf/*) = 2 ] [name(./responseIf/*[1]) = "not" ] [count(./responseIf/not/*) = 1 ] [name(./responseIf/not/*[1]) = "isNull" ] [count(./responseIf/not/isNull/*) = 1 ] [name(./responseIf/not/isNull/*[1]) = "variable" ] [name(./responseIf/*[2]) = "setOutcomeValue" ] [count(./responseIf/setOutcomeValue/*) = 1 ] [name(./responseIf/setOutcomeValue/*[1]) = "mapResponsePoint"]'; $patternNoneTAO = '/responseCondition [count(./*) = 1 ] [name(./*[1]) = "responseIf" ] [count(./responseIf/*) = 2 ] [name(./responseIf/*[1]) = "isNull" ] [count(./responseIf/isNull/*) = 1 ] [name(./responseIf/isNull/*[1]) = "variable" ] [name(./responseIf/*[2]) = "setOutcomeValue" ] [count(./responseIf/setOutcomeValue/*) = 1 ] [name(./responseIf/setOutcomeValue/*[1]) = "baseValue"]'; $possibleSummation = '/setOutcomeValue [count(./*) = 1 ] [name(./*[1]) = "sum" ]'; $irps = array(); $composition = null; $data = simplexml_import_dom($data); foreach ($data as $responseRule) { if (!is_null($composition)) { throw new UnexpectedResponseProcessing('Not composite, rules after composition'); } $subtree = new SimpleXMLElement($responseRule->asXML()); if (count($subtree->xpath($patternCorrectTAO)) > 0) { $responseIdentifier = (string) $subtree->responseIf->match->variable[0]['identifier']; $irps[$responseIdentifier] = array('class' => 'MatchCorrectTemplate', 'outcome' => (string) $subtree->responseIf->setOutcomeValue[0]['identifier']); } elseif (count($subtree->xpath($patternMapTAO)) > 0) { $responseIdentifier = (string) $subtree->responseIf->not->isNull->variable[0]['identifier']; $irps[$responseIdentifier] = array('class' => 'MapResponseTemplate', 'outcome' => (string) $subtree->responseIf->setOutcomeValue[0]['identifier']); } elseif (count($subtree->xpath($patternMapPointTAO)) > 0) { $responseIdentifier = (string) $subtree->responseIf->not->isNull->variable[0]['identifier']; $irps[$responseIdentifier] = array('class' => 'MapResponsePointTemplate', 'outcome' => (string) $subtree->responseIf->setOutcomeValue[0]['identifier']); } elseif (count($subtree->xpath($patternNoneTAO)) > 0) { $responseIdentifier = (string) $subtree->responseIf->isNull->variable[0]['identifier']; $irps[$responseIdentifier] = array('class' => 'None', 'outcome' => (string) $subtree->responseIf->setOutcomeValue[0]['identifier'], 'default' => (string) $subtree->responseIf->setOutcomeValue[0]->baseValue[0]); } elseif (count($subtree->xpath($possibleSummation)) > 0) { $composition = 'Summation'; $outcomesUsed = array(); foreach ($subtree->xpath('/setOutcomeValue/sum/variable') as $var) { $outcomesUsed[] = (string) $var[0]['identifier']; } } else { throw new UnexpectedResponseProcessing('Not composite, unknown rule'); } } if (is_null($composition)) { throw new UnexpectedResponseProcessing('Not composit, Composition rule missing'); } $responses = array(); foreach ($item->getInteractions() as $interaction) { $responses[$interaction->getResponse()->getIdentifier()] = $interaction->getResponse(); } if (count(array_diff(array_keys($irps), array_keys($responses))) > 0) { throw new UnexpectedResponseProcessing('Not composit, no responses for rules: ' . implode(',', array_diff(array_keys($irps), array_keys($responses)))); } if (count(array_diff(array_keys($responses), array_keys($irps))) > 0) { common_Logger::w('Some responses have no processing'); throw new UnexpectedResponseProcessing('Not composit, no support for unmatched variables yet'); } //assuming sum is correct $compositonRP = new Summation($item); foreach ($responses as $id => $response) { $outcome = null; foreach ($item->getOutcomes() as $possibleOutcome) { if ($possibleOutcome->getIdentifier() == $irps[$id]['outcome']) { $outcome = $possibleOutcome; break; } } if (is_null($outcome)) { throw new ParsingException('Undeclared Outcome in ResponseProcessing'); } $classname = '\\oat\\taoQtiItem\\model\\qti\\response\\interactionResponseProcessing\\' . $irps[$id]['class']; $irp = new $classname($response, $outcome); if ($irp instanceof \oat\taoQtiItem\model\qti\response\interactionResponseProcessing\None && isset($irps[$id]['default'])) { $irp->setDefaultValue($irps[$id]['default']); } $compositonRP->add($irp); } $returnValue = $compositonRP; return $returnValue; }
/** * * @param core_kernel_classes_Class $itemClass * @param oat\taoQtiItem\model\qti\Item $qtiModel * @throws common_exception_Error * @throws \common_Exception * @return core_kernel_classes_Resource */ protected function createRdfItem(core_kernel_classes_Class $itemClass, Item $qtiModel) { $itemService = taoItems_models_classes_ItemsService::singleton(); $qtiService = Service::singleton(); if (!$itemService->isItemClass($itemClass)) { throw new common_exception_Error('provided non Itemclass for ' . __FUNCTION__); } $rdfItem = $itemService->createInstance($itemClass); //set the QTI type $itemService->setItemModel($rdfItem, new core_kernel_classes_Resource(ItemModel::MODEL_URI)); //set the label $label = ''; if ($qtiModel->hasAttribute('label')) { $label = $qtiModel->getAttributeValue('label'); } if (empty($label)) { $label = $qtiModel->getAttributeValue('title'); } $rdfItem->setLabel($label); //save itemcontent if (!$qtiService->saveDataItemToRdfItem($qtiModel, $rdfItem)) { throw new \common_Exception('Unable to save item'); } return $rdfItem; }
/** * Feed the instance with portable related data extracted from the item * * @param Item $item * @throws \common_Exception */ protected function feedRequiredFiles(Item $item) { $this->requiredFiles = []; $this->portableObjects = []; $this->picModels = []; $models = $this->getPortableFactory()->getModels(); foreach ($models as $model) { $className = $model->getQtiElementClassName(); $portableElementsXml = $item->getComposingElements($className); foreach ($portableElementsXml as $portableElementXml) { $this->parsePortableElement($model, $portableElementXml); } } }