*/ class TestSessionService extends ConfigurableService implements DeliveryExecutionDelete { use OntologyAwareTrait; const SERVICE_ID = 'taoQtiTest/TestSessionService'; const SESSION_PROPERTY_SESSION = 'session'; const SESSION_PROPERTY_STORAGE = 'storage'; const SESSION_PROPERTY_COMPILATION = 'compilation'; /** * Cache to store session instances * @var array */ protected static $cache = []; /** * Loads a test session into the memory cache * @param DeliveryExecution $deliveryExecution * @param bool $forReadingOnly * @throws QtiTestExtractionFailedException * @throws \common_Exception * @throws \common_exception_Error * @throws \common_exception_NotFound * @throws \common_ext_ExtensionException * @throws \oat\oatbox\service\exception\InvalidServiceManagerException */ protected function loadSession(DeliveryExecution $deliveryExecution, $forReadingOnly) { self::invalidateCache(); $session = null; $sessionId = $deliveryExecution->getIdentifier(); try { /** @var array $inputParameters */ $inputParameters = $this->getRuntimeInputParameters($deliveryExecution); /** @var AssessmentTest $testDefinition */ $testDefinition = $this->getServiceLocator()->get(QtiTestUtils::SERVICE_ID) ->getTestDefinition($inputParameters['QtiTestCompilation']); $testResource = new \core_kernel_classes_Resource($inputParameters['QtiTestDefinition']); } catch (common_exception_NoContent $e) { $sessionData = [ self::SESSION_PROPERTY_SESSION => null, self::SESSION_PROPERTY_STORAGE => null, self::SESSION_PROPERTY_COMPILATION => null ]; self::$cache[$sessionId] = $sessionData; return; } /** @var DeliveryServerService $deliveryServerService */ $deliveryServerService = $this->getServiceLocator()->get(DeliveryServerService::SERVICE_ID); $resultStore = $deliveryServerService->getResultStoreWrapper($deliveryExecution); $sessionManager = new \taoQtiTest_helpers_SessionManager($resultStore, $testResource); $userId = $deliveryExecution->getUserIdentifier(); $config = $this->getServiceLocator()->get(\common_ext_ExtensionsManager::SERVICE_ID) ->getExtensionById('taoQtiTest') ->getConfig('testRunner'); $storageClassName = $config['test-session-storage']; /** @var taoQtiTest_helpers_TestSessionStorage $qtiStorage */ $qtiStorage = new $storageClassName( $sessionManager, new BinaryAssessmentTestSeeker($testDefinition), $userId ); $this->propagate($qtiStorage); if ($qtiStorage->exists($sessionId)) { $session = $qtiStorage->retrieve($testDefinition, $sessionId, $forReadingOnly); if ($session instanceof UserUriAware) { $session->setUserUri($userId); } } /** @var \tao_models_classes_service_FileStorage $fileStorage */ $fileStorage = $this->getServiceLocator()->get(\tao_models_classes_service_FileStorage::SERVICE_ID); $directoryIds = explode('|', $inputParameters['QtiTestCompilation']); $directories = [ 'private' => $fileStorage->getDirectoryById($directoryIds[0]), 'public' => $fileStorage->getDirectoryById($directoryIds[1]) ]; self::$cache[$sessionId] = [ self::SESSION_PROPERTY_SESSION => $session, self::SESSION_PROPERTY_STORAGE => $qtiStorage, self::SESSION_PROPERTY_COMPILATION => $directories ]; } /** * Checks if a session has been loaded * @param $sessionId * @return bool */ protected function hasTestSession($sessionId) { return (isset(self::$cache[$sessionId]) && isset(self::$cache[$sessionId][self::SESSION_PROPERTY_SESSION])); } /** * Gets the test session for a particular deliveryExecution * * @param DeliveryExecution $deliveryExecution * @param bool $forReadingOnly * @return \qtism\runtime\tests\AssessmentTestSession * @throws QtiTestExtractionFailedException * @throws \common_Exception * @throws \common_exception_Error * @throws \common_exception_NotFound * @throws \common_ext_ExtensionException * @throws \oat\oatbox\service\exception\InvalidServiceManagerException */ public function getTestSession(DeliveryExecution $deliveryExecution, $forReadingOnly = false) { $sessionId = $deliveryExecution->getIdentifier(); if (!$this->hasTestSession($sessionId) || $this->accessModeChangedToWrite($forReadingOnly, $sessionId)) { $this->loadSession($deliveryExecution, $forReadingOnly); } return self::$cache[$sessionId][self::SESSION_PROPERTY_SESSION]; } /** * Register a test session * * @param AssessmentTestSession $session * @param \taoQtiTest_helpers_TestSessionStorage $storage * @param array $compilationDirectories */ public function registerTestSession(AssessmentTestSession $session, \taoQtiTest_helpers_TestSessionStorage $storage, array $compilationDirectories) { $sessionId = $session->getSessionId(); self::$cache[$sessionId] = [ self::SESSION_PROPERTY_SESSION => $session, self::SESSION_PROPERTY_STORAGE => $storage, self::SESSION_PROPERTY_COMPILATION => $compilationDirectories ]; } /** * Get a test session data by identifier. * * Get a session by $sessionId. In case it was previously registered using the TestSessionService::registerTestSession method, * an array with the following keys will be returned: * * * 'session': A qtism AssessmentTestSession object. * * 'storage': A taoQtiTest_helpers_TestSessionStorage. * * 'context': A RunnerServiceContext object (if not provided at TestSessionService::registerTestSession call time, it contains null). * * In case of no such session is found for $sessionId, false is returned. * * @param string $sessionId * @return false|array */ public function getTestSessionDataById($sessionId) { return $this->hasTestSession($sessionId) ? self::$cache[$sessionId] : false; } /** * Gets the test session storage for a particular deliveryExecution * * @param DeliveryExecutionInterface $deliveryExecution * @return taoQtiTest_helpers_TestSessionStorage|null * @throws QtiTestExtractionFailedException * @throws \common_Exception * @throws \common_exception_NotFound * @throws \common_ext_ExtensionException * @throws \oat\oatbox\service\exception\InvalidServiceManagerException */ public function getTestSessionStorage(DeliveryExecutionInterface $deliveryExecution) { $sessionId = $deliveryExecution->getIdentifier(); if (!$this->hasTestSession($sessionId)) { $this->loadSession($deliveryExecution, true); } return self::$cache[$sessionId][self::SESSION_PROPERTY_STORAGE]; } /** * * @param DeliveryExecution $deliveryExecution * @return array * Example: *
     * array(
     *   'QtiTestCompilation' => 'http://sample/first.rdf#i14369768868163155-|http://sample/first.rdf#i1436976886612156+',
     *   'QtiTestDefinition' => 'http://sample/first.rdf#i14369752345581135'
     * )
     * 
* @throws common_exception_NoContent */ public function getRuntimeInputParameters(DeliveryExecution $deliveryExecution) { try { $compiledDelivery = $deliveryExecution->getDelivery(); $runtime = $this->getServiceLocator()->get(RuntimeService::SERVICE_ID)->getRuntime($compiledDelivery->getUri()); return tao_models_classes_service_ServiceCallHelper::getInputValues($runtime, []); } catch (Throwable $exception) { throw new common_exception_NoContent($exception->getMessage()); } } /** * @param AssessmentTestSession $session * @throws \qtism\runtime\storage\common\StorageException */ public function persist(AssessmentTestSession $session) { $sessionId = $session->getSessionId(); if ($this->hasTestSession($sessionId)) { /** @var AbstractQtiBinaryStorage $storage */ $storage = self::$cache[$sessionId][self::SESSION_PROPERTY_STORAGE]; $storage->persist($session); } } /** * @inheritdoc */ public function deleteDeliveryExecutionData(DeliveryExecutionDeleteRequest $request) { $sessionId = $request->getDeliveryExecution()->getIdentifier(); try { $storage = $this->getTestSessionStorage($request->getDeliveryExecution(), false); if ($storage instanceof taoQtiTest_helpers_TestSessionStorage) { return $storage->delete($sessionId); } } catch (\Exception $exception) { return false; } return false; } /** * @param $forReadingOnly * @param string $sessionId * @return bool */ private function accessModeChangedToWrite($forReadingOnly, string $sessionId): bool { return $this->hasTestSession($sessionId) && !$forReadingOnly && self::$cache[$sessionId][self::SESSION_PROPERTY_SESSION]->isReadOnly(); } /** * Invalidate Cache. * * Invalidates the Test Session Cache. */ public function invalidateCache(): void { self::$cache = []; } }