public function convert(BaseQuestionType $questionType, $interactionIdentifier, $interactionLabel) { /** @var clozetext $question */ $question = $questionType; // Extra text that can't be mapped since we are in textEntryInteraction which does not have prompt $this->extraContent = $question->get_stimulus(); // Replace {{ response }} with `textEntryInteraction` elements $maxLength = !is_null($question->get_max_length()) ? intval($question->get_max_length()) : 15; // Set default to `15` if not set $index = 0; $template = preg_replace_callback('/{{response}}/', function ($match) use(&$index, $interactionIdentifier, $interactionLabel, $maxLength) { $interaction = new TextEntryInteraction($interactionIdentifier . '_' . $index); $interaction->setLabel($interactionLabel); $interaction->setExpectedLength($maxLength); $index++; $replacement = QtiMarshallerUtil::marshall($interaction); return $replacement; }, $question->get_template()); // Wrap this interaction in a block since our `clozetext` `template` meant to be blocky and not inline $div = new Div(); $div->setClass('lrn-template'); $div->setContent(ContentCollectionBuilder::buildFlowCollectionContent(QtiMarshallerUtil::unmarshallElement($template))); // Build validation $isCaseSensitive = is_null($question->get_case_sensitive()) ? true : $question->get_case_sensitive(); $validationBuilder = new ClozetextValidationBuilder($isCaseSensitive); list($responseDeclaration, $responseProcessing) = $validationBuilder->buildValidation($interactionIdentifier, $question->get_validation(), $isCaseSensitive); return [$div, $responseDeclaration, $responseProcessing]; }
public function buildItemBody(array $interactions, $content = '') { // Try to build the <itemBody> according to items` content if exists if (empty($content)) { return $this->buildItemBodySimple($interactions); } try { return $this->buildItemBodyWithItemContent($interactions, $content); // If anything fails, <itemBody> can't be mapped due to whatever reasons // Probably simply due to its being wrapped in a tag which only accept inline content // Simply build it without considering items` content and put the content on the top } catch (\Exception $e) { $itemBody = $this->buildItemBodySimple($interactions); $itemBodyContent = new BlockCollection(); // Build the div bundle that contains all the item`s content // minus those questions and features `span` $html = new SimpleHtmlDom(); $html->load($content); foreach ($html->find('span.learnosity-response') as &$span) { $span->outertext = ''; } $div = new Div(); $contentCollection = QtiMarshallerUtil::unmarshallElement($html->save()); $div->setContent(ContentCollectionBuilder::buildFlowCollectionContent($contentCollection)); $itemBodyContent->attach($div); $itemBodyContent->merge($itemBody->getComponents()); $itemBody->setContent($itemBodyContent); LogService::log('Interactions are failed to be mapped with `item` content: ' . $e->getMessage() . '. Thus, interactions are separated from its actual `item` content and appended in the bottom'); return $itemBody; } }
protected function marshallChildrenKnown(QtiComponent $component, array $elements) { /** @var Object $component */ switch ($this->getMIMEType($component->getType())) { case self::MIME_IMAGE: $this->checkObjectComponents($component, '<img> tag'); $element = self::getDOMCradle()->createElement('img'); $element->setAttribute('src', $component->getData()); return $element; break; case self::MIME_AUDIO: $this->checkObjectComponents($component, '`audioplayer` feature'); $element = self::getDOMCradle()->createElement('span'); $element->setAttribute('class', 'learnosity-feature'); $element->setAttribute('data-type', 'audioplayer'); $element->setAttribute('data-src', $component->getData()); return $element; break; case self::MIME_VIDEO: $this->checkObjectComponents($component, '`videoplayer` feature'); $element = self::getDOMCradle()->createElement('span'); $element->setAttribute('class', 'learnosity-feature'); $element->setAttribute('data-type', 'videoplayer'); $element->setAttribute('data-src', $component->getData()); return $element; break; case self::MIME_HTML: $fragment = self::getDOMCradle()->createDocumentFragment(); $fragment->appendXML(QtiMarshallerUtil::marshallCollection(ContentCollectionBuilder::buildFlowCollectionContent($component->getComponents()))); $element = self::getDOMCradle()->createElement('div'); $element->setAttribute('data-type', 'sharedpassage'); $element->appendChild($fragment); return $element; break; default: // TODO: Need to think external HTML object file, what we are going to do with them? // Just parse <object> as default LogService::log('Unknown <object> MIME type, outputting <object> as it is'); return parent::marshallChildrenKnown($component, $elements); } }
public function convert(BaseQuestionType $questionType, $interactionIdentifier, $interactionLabel) { /** @var clozedropdown $question */ $question = $questionType; // Extra text that can't be mapped since we are in textEntryInteraction which does not have prompt $this->extraContent = $question->get_stimulus(); // Replace {{ response }} with `textEntryInteraction` elements $valueIdentifierMapPerInlineChoices = []; $index = 0; $possibleResponses = $question->get_possible_responses(); $template = preg_replace_callback('/{{response}}/', function ($match) use(&$index, &$valueIdentifierMapPerInlineChoices, $possibleResponses, $interactionIdentifier, $interactionLabel) { $inlineChoiceCollection = new InlineChoiceCollection(); if (!isset($possibleResponses[$index])) { throw new MappingException('Invalid `possible_responses`, missing entries'); } foreach ($possibleResponses[$index] as $choiceIndex => $choiceValue) { $inlineChoiceIdentifier = 'INLINECHOICE_' . $choiceIndex; $valueIdentifierMapPerInlineChoices[$index][$choiceValue] = $inlineChoiceIdentifier; // Update this map so can be used later upon building responseDeclaration objects $inlineChoice = new InlineChoice($inlineChoiceIdentifier); $inlineChoiceContent = new TextOrVariableCollection(); $inlineChoiceContent->attach(new TextRun($choiceValue)); $inlineChoice->setContent($inlineChoiceContent); $inlineChoiceCollection->attach($inlineChoice); } $interaction = new InlineChoiceInteraction($interactionIdentifier . '_' . $index, $inlineChoiceCollection); $interaction->setLabel($interactionLabel); $index++; $replacement = QtiMarshallerUtil::marshall($interaction); return $replacement; }, $question->get_template()); // Wrap this interaction in a block since our `clozedropdown` `template` meant to be blocky and not inline $div = new Div(); $div->setClass('lrn-template'); $div->setContent(ContentCollectionBuilder::buildFlowCollectionContent(QtiMarshallerUtil::unmarshallElement($template))); // Build validation $validationBuilder = new ClozedropdownValidationBuilder($valueIdentifierMapPerInlineChoices); list($responseDeclaration, $responseProcessing) = $validationBuilder->buildValidation($interactionIdentifier, $question->get_validation()); return [$div, $responseDeclaration, $responseProcessing]; }