/**
  * temporary helper until proper servicemanager integration
  * @return ExtendedStateService
  */
 public static function getExtendedStateService()
 {
     if (!isset(self::$extendedStateService)) {
         self::$extendedStateService = new ExtendedStateService();
     }
     return self::$extendedStateService;
 }
 /**
  * Returns the options related to the current test context
  * @param RunnerServiceContext $context The test context
  * @return mixed
  */
 public function getOptions(RunnerServiceContext $context)
 {
     $session = $context->getTestSession();
     // Comment allowed? Skipping allowed? Logout or Exit allowed ?
     $options = ['allowComment' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowComment($session), 'allowSkipping' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowSkipping($session), 'exitButton' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowExit($session), 'logoutButton' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowLogout($session)];
     // get the options from the categories owned by the current item
     $categories = \taoQtiTest_helpers_TestRunnerUtils::getCategories($session);
     $prefixCategory = 'x-tao-option-';
     $prefixCategoryLen = strlen($prefixCategory);
     foreach ($categories as $category) {
         if (!strncmp($category, $prefixCategory, $prefixCategoryLen)) {
             // extract the option name from the category, transform to camelCase if needed
             $optionName = lcfirst(str_replace(' ', '', ucwords(strtr(substr($category, $prefixCategoryLen), ['-' => ' ', '_' => ' ']))));
             // the options added by the categories are just flags
             $options[$optionName] = true;
         }
     }
     return $options;
 }
 /**
  * Save session metadata.
  * 
  * @param array $metaData Meta data array to be saved.
  * Example:
  * array(
  *   'TEST' => array('TEST_EXIT_CODE' => 'IC'),
  *   'SECTION' => array('SECTION_EXIT_CODE' => 701),
  * )
  */
 public function save(array $metaData)
 {
     $testUri = $this->session->getTest()->getUri();
     $resultServer = \taoResultServer_models_classes_ResultServerStateFull::singleton();
     foreach ($metaData as $type => $data) {
         foreach ($data as $key => $value) {
             $metaVariable = $this->getVariable($key, $value);
             if (strcasecmp($type, 'ITEM') === 0) {
                 $itemUri = \taoQtiTest_helpers_TestRunnerUtils::getCurrentItemUri($this);
                 $occurence = $this->session->getCurrentAssessmentItemRefOccurence();
                 $transmissionId = "{$sessionId}.{$item}.{$occurence}";
                 $resultServer->storeItemVariable($testUri, $itemUri, $metaVariable, $transmissionId);
             } elseif (strcasecmp($type, 'TEST') === 0) {
                 $resultServer->storeTestVariable($testUri, $metaVariable, $this->session->getSessionId());
             } elseif (strcasecmp($type, 'SECTION') === 0) {
                 //suffix section variables with _{SECTION_IDENTIFIER}
                 $assessmentSectionId = $this->session->getCurrentAssessmentSection()->getIdentifier();
                 $metaVariable->setIdentifier($key . '_' . $assessmentSectionId);
                 $resultServer->storeTestVariable($testUri, $metaVariable, $this->session->getSessionId());
             }
         }
     }
 }
 /**
  * Retrieve the current test session as an AssessmentTestSession object from
  * persistent storage.
  * 
  */
 protected function retrieveTestSession()
 {
     $qtiStorage = $this->getStorage();
     $sessionId = $this->getServiceCallId();
     if ($qtiStorage->exists($sessionId) === false) {
         common_Logger::i("Instantiating QTI Assessment Test Session");
         $this->setTestSession($qtiStorage->instantiate($this->getTestDefinition(), $sessionId));
         $testTaker = \common_session_SessionManager::getSession()->getUser();
         taoQtiTest_helpers_TestRunnerUtils::setInitialOutcomes($this->getTestSession(), $testTaker);
     } else {
         common_Logger::i("Retrieving QTI Assessment Test Session '{$sessionId}'...");
         $this->setTestSession($qtiStorage->retrieve($this->getTestDefinition(), $sessionId));
     }
     taoQtiTest_helpers_TestRunnerUtils::preserveOutcomes($this->getTestSession());
 }
 /**
  * Builds the map of an assessment test
  * @param RunnerServiceContext $context The test context
  * @param RunnerConfig $config The runner config
  * @return mixed
  */
 public function getMap(RunnerServiceContext $context, RunnerConfig $config)
 {
     $map = ['parts' => [], 'jumps' => []];
     // get config for the sequence number option
     $reviewConfig = $config->getConfigValue('review');
     $checkInformational = $config->getConfigValue('checkInformational');
     $forceTitles = !empty($reviewConfig['forceTitle']);
     $uniqueTitle = isset($reviewConfig['itemTitle']) ? $reviewConfig['itemTitle'] : '%d';
     /* @var AssessmentTestSession $session */
     $session = $context->getTestSession();
     if ($session->isRunning() !== false) {
         $route = $session->getRoute();
         $store = $session->getAssessmentItemSessionStore();
         $routeItems = $route->getAllRouteItems();
         $offset = $route->getRouteItemPosition($routeItems[0]);
         $offsetPart = 0;
         $offsetSection = 0;
         $lastPart = null;
         $lastSection = null;
         /** @var \qtism\runtime\tests\RouteItem $routeItem */
         foreach ($routeItems as $routeItem) {
             // access the item reference
             $itemRef = $routeItem->getAssessmentItemRef();
             $occurrence = $routeItem->getOccurence();
             // get the jump definition
             $itemSession = $store->getAssessmentItemSession($itemRef, $occurrence);
             // load item infos
             $testPart = $routeItem->getTestPart();
             $partId = $testPart->getIdentifier();
             $section = $routeItem->getAssessmentSection();
             $sectionId = $section->getIdentifier();
             $itemId = $itemRef->getIdentifier();
             $itemUri = strstr($itemRef->getHref(), '|', true);
             $item = new \core_kernel_classes_Resource($itemUri);
             if ($lastPart != $partId) {
                 $offsetPart = 0;
                 $lastPart = $partId;
             }
             if ($lastSection != $sectionId) {
                 $offsetSection = 0;
                 $lastSection = $sectionId;
             }
             if ($forceTitles) {
                 $label = sprintf($uniqueTitle, $offsetSection + 1);
             } else {
                 $label = $item->getLabel();
             }
             $itemInfos = ['id' => $itemId, 'uri' => $itemUri, 'label' => $label, 'position' => $offset, 'positionInPart' => $offsetPart, 'positionInSection' => $offsetSection, 'index' => $offsetSection + 1, 'occurrence' => $occurrence, 'remainingAttempts' => $itemSession->getRemainingAttempts(), 'answered' => TestRunnerUtils::isItemCompleted($routeItem, $itemSession), 'flagged' => TestRunnerUtils::getItemFlag($session, $routeItem), 'viewed' => $itemSession->isPresented()];
             if ($checkInformational) {
                 $itemInfos['informational'] = TestRunnerUtils::isItemInformational($routeItem, $itemSession);
             }
             // update the map
             $map['jumps'][] = ['identifier' => $itemId, 'section' => $sectionId, 'part' => $partId, 'position' => $offset, 'uri' => $itemUri];
             if (!isset($map['parts'][$partId])) {
                 $map['parts'][$partId]['id'] = $partId;
                 $map['parts'][$partId]['label'] = $partId;
                 $map['parts'][$partId]['position'] = $offset;
                 $map['parts'][$partId]['isLinear'] = $testPart->getNavigationMode() == NavigationMode::LINEAR;
             }
             if (!isset($map['parts'][$partId]['sections'][$sectionId])) {
                 $map['parts'][$partId]['sections'][$sectionId]['id'] = $sectionId;
                 $map['parts'][$partId]['sections'][$sectionId]['label'] = $section->getTitle();
                 $map['parts'][$partId]['sections'][$sectionId]['position'] = $offset;
             }
             $map['parts'][$partId]['sections'][$sectionId]['items'][$itemId] = $itemInfos;
             // update the stats
             $this->updateStats($map, $itemInfos);
             $this->updateStats($map['parts'][$partId], $itemInfos);
             $this->updateStats($map['parts'][$partId]['sections'][$sectionId], $itemInfos);
             $offset++;
             $offsetPart++;
             $offsetSection++;
         }
     }
     return $map;
 }
 /**
  * Action to call to comment an item.
  * 
  */
 public function comment()
 {
     $this->beforeAction();
     $testSession = $this->getTestSession();
     $resultServer = taoResultServer_models_classes_ResultServerStateFull::singleton();
     $transmitter = new taoQtiCommon_helpers_ResultTransmitter($resultServer);
     // prepare transmission Id for result server.
     $item = $testSession->getCurrentAssessmentItemRef()->getIdentifier();
     $occurence = $testSession->getCurrentAssessmentItemRefOccurence();
     $sessionId = $testSession->getSessionId();
     $transmissionId = "{$sessionId}.{$item}.{$occurence}";
     // retrieve comment's intrinsic value.
     $comment = $this->getRequestParameter('comment');
     // build variable and send it.
     $itemUri = taoQtiTest_helpers_TestRunnerUtils::getCurrentItemUri($testSession);
     $testUri = $testSession->getTest()->getUri();
     $variable = new ResponseVariable('comment', Cardinality::SINGLE, BaseType::STRING, new String($comment));
     $transmitter->transmitItemVariable($variable, $transmissionId, $itemUri, $testUri);
 }
 /**
  * Flag an item
  */
 public function flagItem()
 {
     $code = 200;
     try {
         $serviceContext = $this->getServiceContext();
         $testSession = $serviceContext->getTestSession();
         if ($this->hasRequestParameter('position')) {
             $itemPosition = intval($this->getRequestParameter('position'));
         } else {
             $itemPosition = $testSession->getRoute()->getPosition();
         }
         if ($this->hasRequestParameter('flag')) {
             $flag = $this->getRequestParameter('flag');
             if (is_numeric($flag)) {
                 $flag = !!intval($flag);
             } else {
                 $flag = 'false' != strtolower($flag);
             }
         } else {
             $flag = true;
         }
         TestRunnerUtils::setItemFlag($testSession, $itemPosition, $flag);
         $response = ['success' => true];
     } catch (common_Exception $e) {
         $response = $this->getErrorResponse($e);
         $code = $this->getErrorCode($e);
     }
     $this->returnJson($response, $code);
 }