From 66e9b66865fb47ff546ccdbd370f29a0e02a8fa9 Mon Sep 17 00:00:00 2001 From: cmoyon Date: Thu, 28 Apr 2016 13:17:59 +0200 Subject: [PATCH 1/8] Working uploaded file as state storage --- actions/class.TestRunner.php | 13 ++- models/classes/StateStorageQtiFile.php | 103 ++++++++++++++++++ models/classes/StateStorageQtiFileManager.php | 87 +++++++++++++++ 3 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 models/classes/StateStorageQtiFile.php create mode 100644 models/classes/StateStorageQtiFileManager.php diff --git a/actions/class.TestRunner.php b/actions/class.TestRunner.php index 6733dbaa2a..31a4cbb6c6 100644 --- a/actions/class.TestRunner.php +++ b/actions/class.TestRunner.php @@ -35,6 +35,8 @@ use qtism\data\NavigationMode; use oat\taoQtiItem\helpers\QtiRunner; use oat\taoQtiTest\models\TestSessionMetaData; +use oat\taoQtiTest\models\StateStorageQtiFileManager; + /** * Runs a QTI Test. * @@ -261,8 +263,10 @@ protected function beforeAction($notifyError = true) { $sessionManager = new taoQtiTest_helpers_SessionManager($resultServer, $testResource); $userUri = common_session_SessionManager::getSession()->getUserUri(); $seeker = new BinaryAssessmentTestSeeker($this->getTestDefinition()); - - $this->setStorage(new taoQtiTest_helpers_TestSessionStorage($sessionManager, $seeker, $userUri)); + + $storage = new taoQtiTest_helpers_TestSessionStorage($sessionManager, $seeker, $userUri); + // $storage-> + $this->setStorage($storage); $this->retrieveTestSession(); // @TODO: use some storage to get the potential reason of the state (close/suspended) @@ -712,7 +716,12 @@ public function storeItemVariableSet() common_Logger::e($msg); } + $qtiFileManager = new StateStorageQtiFileManager( + $this->getTestSession(), + \common_session_SessionManager::getSession() + ); $filler = new taoQtiCommon_helpers_PciVariableFiller($currentItem); + $filler->setFileManager($qtiFileManager); if (is_array($jsonPayload)) { foreach ($jsonPayload as $id => $response) { diff --git a/models/classes/StateStorageQtiFile.php b/models/classes/StateStorageQtiFile.php new file mode 100644 index 0000000000..0136fa0f1c --- /dev/null +++ b/models/classes/StateStorageQtiFile.php @@ -0,0 +1,103 @@ +key = $key; + } + + public function getData() + { + // TODO: Implement getData() method. + } + + public function getMimeType() + { + // TODO: Implement getMimeType() method. + } + + public function hasFilename() + { + // TODO: Implement hasFilename() method. + } + + public function getFilename() + { + return $this->key; + } + + public function getStream() + { + // TODO: Implement getStream() method. + } + + public function getIdentifier() + { + // TODO: Implement getIdentifier() method. + } + + public function equals($obj) + { + // TODO: Implement equals() method. + } + + /** + * Return base type e.q. 9 + * @return int + */ + public function getBaseType() + { + return BaseType::FILE; + } + + /** + * Return cardinality type e.q. 0 + * @return int + */ + public function getCardinality() + { + return Cardinality::SINGLE; + } + + public function getPath() + { + \common_Logger::e(__FUNCTION__); + } + + /** + * Return filename + * @return string|void + */ + public function __toString() + { + return $this->getFilename(); + } +} \ No newline at end of file diff --git a/models/classes/StateStorageQtiFileManager.php b/models/classes/StateStorageQtiFileManager.php new file mode 100644 index 0000000000..806587f8cb --- /dev/null +++ b/models/classes/StateStorageQtiFileManager.php @@ -0,0 +1,87 @@ +storageService = ServiceManager::getServiceManager()->get('tao/stateStorage'); + $this->testSession = $testSession; + $this->userSession = $userSession; + } + + public function createFromFile($path, $mimeType, $filename = '') + { + \common_Logger::i(__FUNCTION__); + } + + public function createFromData($data, $mimeType, $filename = '') + { + $data = base64_encode($data); + $key = $this->generateUniqKey($this->testSession->getSessionId()); + + $this->storageService->set($this->userSession->getUserUri(), $key, $data); + \common_Logger::e(base64_decode($this->storageService->get($this->userSession->getUserUri(), $key))); + return new StateStorageQtiFile($key); +// return new StateStorageQtiFile($key); + } + + public function retrieve($identifier) + { + \common_Logger::i(__FUNCTION__); + } + + public function delete(File $file) + { + \common_Logger::i(__FUNCTION__); + } + + protected function generateUniqKey($prefix='') + { + return md5(uniqid($prefix, true)); + } + +} \ No newline at end of file From f6e17cd86c106f31eb20d2a4d7bab8d552663428 Mon Sep 17 00:00:00 2001 From: cmoyon Date: Mon, 2 May 2016 09:16:13 +0200 Subject: [PATCH 2/8] Write file as state storage working --- actions/class.TestRunner.php | 24 ++++++--- helpers/class.TestSessionStorage.php | 39 +++++++++++++-- models/classes/StateStorageQtiFile.php | 50 +++++++++++++++---- models/classes/StateStorageQtiFileManager.php | 26 +++++----- 4 files changed, 104 insertions(+), 35 deletions(-) diff --git a/actions/class.TestRunner.php b/actions/class.TestRunner.php index 31a4cbb6c6..1e35500874 100644 --- a/actions/class.TestRunner.php +++ b/actions/class.TestRunner.php @@ -223,6 +223,19 @@ protected function getTestMeta() { return $this->testMeta; } + /** + * Return the State storage + * @return StateStorageQtiFileManager + * @throws common_exception_Error + */ + protected function getStateStorageQtiFileManager() + { + return new StateStorageQtiFileManager( + $this->getServiceCallId(), + \common_session_SessionManager::getSession()->getUserUri() + ); + } + /** * Print an error report into the response. * After you have called this method, you must prevent other actions to be processed and must close the response. @@ -265,7 +278,8 @@ protected function beforeAction($notifyError = true) { $seeker = new BinaryAssessmentTestSeeker($this->getTestDefinition()); $storage = new taoQtiTest_helpers_TestSessionStorage($sessionManager, $seeker, $userUri); - // $storage-> + + $storage->setFileManager($this->getStateStorageQtiFileManager()); $this->setStorage($storage); $this->retrieveTestSession(); @@ -344,7 +358,7 @@ protected function afterAction($withContext = true) { } common_Logger::i("Persisting QTI Assessment Test Session '${sessionId}'..."); - $this->getStorage()->persist($testSession); + $this->getStorage()->persist($testSession); } /** @@ -716,12 +730,8 @@ public function storeItemVariableSet() common_Logger::e($msg); } - $qtiFileManager = new StateStorageQtiFileManager( - $this->getTestSession(), - \common_session_SessionManager::getSession() - ); $filler = new taoQtiCommon_helpers_PciVariableFiller($currentItem); - $filler->setFileManager($qtiFileManager); + $filler->setFileManager($this->getStateStorageQtiFileManager()); if (is_array($jsonPayload)) { foreach ($jsonPayload as $id => $response) { diff --git a/helpers/class.TestSessionStorage.php b/helpers/class.TestSessionStorage.php index afa49c8880..7b97a66def 100644 --- a/helpers/class.TestSessionStorage.php +++ b/helpers/class.TestSessionStorage.php @@ -26,7 +26,8 @@ use qtism\runtime\storage\common\StorageException; use qtism\data\AssessmentTest; use qtism\runtime\tests\AssessmentTestSession; -use qtism\runtime\storage\binary\QtiBinaryStreamAccessFsFile; +use qtism\runtime\storage\binary\QtiBinaryStreamAccess; +use qtism\common\datatypes\files\FileManager; /** * A QtiSm AssessmentTestSession Storage Service implementation for TAO. @@ -52,7 +53,13 @@ class taoQtiTest_helpers_TestSessionStorage extends AbstractQtiBinaryStorage { * @var string */ private $userUri; - + + /** + * File manager to pass to QtiBinaryStreamAccess + * @var FileManager + */ + protected $fileManager; + /** * Create a new TestSessionStorage object. * @@ -159,8 +166,30 @@ public function exists($sessionId) { return $storageService->has($userUri, $sessionId); } - - protected function createBinaryStreamAccess(IStream $stream) { - return new QtiBinaryStreamAccessFsFile($stream); + + /** + * @throws StorageException + * @return FileManager + */ + public function getFileManager() + { + if (!isset($this->fileManager)) { + throw new StorageException('Missing file manager for test session storage.', StorageException::UNKNOWN); + } + return $this->fileManager; + } + + /** + * @param FileManager $fileManager + */ + public function setFileManager(FileManager $fileManager) + { + $this->fileManager = $fileManager; + } + + + protected function createBinaryStreamAccess(IStream $stream) + { + return new QtiBinaryStreamAccess($stream, $this->getFileManager()); } } \ No newline at end of file diff --git a/models/classes/StateStorageQtiFile.php b/models/classes/StateStorageQtiFile.php index 0136fa0f1c..6816707767 100644 --- a/models/classes/StateStorageQtiFile.php +++ b/models/classes/StateStorageQtiFile.php @@ -25,52 +25,81 @@ use qtism\common\enums\BaseType; use qtism\common\enums\Cardinality; -class StateStorageQtiFile implements File +class StateStorageQtiFile implements File { protected $key; + protected $mimeType; + protected $filename; + protected $data; - public function __construct($key) + /** + * StateStorageQtiFile constructor. + * @param $key + * @param string $mimeType + * @param string $filename + * @param string $data + */ + public function __construct($key, $mimeType='', $filename='', $data='') { $this->key = $key; + $this->mimeType = $mimeType; + $this->filename = $filename; + $this->data = $data; } public function getData() { - // TODO: Implement getData() method. + \common_Logger::i(__FUNCTION__); + return $this->data; } + /** + * Get the mime type + * @return mixed + */ public function getMimeType() { - // TODO: Implement getMimeType() method. + return $this->mimeType; } + /** + * Check if filename is set + * @return bool + */ public function hasFilename() { - // TODO: Implement hasFilename() method. + return $this->getFilename() !== ''; } + /** + * Get filename + * @return mixed + */ public function getFilename() { - return $this->key; + return $this->filename; } public function getStream() { + \common_Logger::i(__FUNCTION__); // TODO: Implement getStream() method. } public function getIdentifier() { - // TODO: Implement getIdentifier() method. + \common_Logger::i(__FUNCTION__); + return $this->key; } public function equals($obj) { + \common_Logger::i(__FUNCTION__); // TODO: Implement equals() method. } /** - * Return base type e.q. 9 + * Return base type for file e.q. 9 * @return int */ public function getBaseType() @@ -79,7 +108,7 @@ public function getBaseType() } /** - * Return cardinality type e.q. 0 + * Return cardinality type for file e.q. 0 * @return int */ public function getCardinality() @@ -98,6 +127,7 @@ public function getPath() */ public function __toString() { - return $this->getFilename(); + \common_Logger::i(__FUNCTION__); + return $this->getIdentifier(); } } \ No newline at end of file diff --git a/models/classes/StateStorageQtiFileManager.php b/models/classes/StateStorageQtiFileManager.php index 806587f8cb..2779cbbd9e 100644 --- a/models/classes/StateStorageQtiFileManager.php +++ b/models/classes/StateStorageQtiFileManager.php @@ -36,21 +36,21 @@ class StateStorageQtiFileManager implements FileManager /** * Test session - * @var AssessmentTestSession + * @var Uri string */ - protected $testSession; + protected $testId; /** * User session - * @var \common_session_Session + * @var Uri string */ - protected $userSession; + protected $userId; - public function __construct(AssessmentTestSession $testSession, \common_session_Session $userSession) + public function __construct($testId, $userId) { $this->storageService = ServiceManager::getServiceManager()->get('tao/stateStorage'); - $this->testSession = $testSession; - $this->userSession = $userSession; + $this->testId = $testId; + $this->userId = $userId; } public function createFromFile($path, $mimeType, $filename = '') @@ -61,17 +61,17 @@ public function createFromFile($path, $mimeType, $filename = '') public function createFromData($data, $mimeType, $filename = '') { $data = base64_encode($data); - $key = $this->generateUniqKey($this->testSession->getSessionId()); + $key = $this->generateUniqKey($this->testId); + \common_Logger::i($key); - $this->storageService->set($this->userSession->getUserUri(), $key, $data); - \common_Logger::e(base64_decode($this->storageService->get($this->userSession->getUserUri(), $key))); - return new StateStorageQtiFile($key); -// return new StateStorageQtiFile($key); + $this->storageService->set($this->userId, $key, $data); + \common_Logger::e(base64_decode($this->storageService->get($this->userId, $key))); + return new StateStorageQtiFile($key, $mimeType, $filename); } public function retrieve($identifier) { - \common_Logger::i(__FUNCTION__); + return new StateStorageQtiFile($identifier); } public function delete(File $file) From d3dbd015ad64d93dfef0ffa1ebe789cfa05780d9 Mon Sep 17 00:00:00 2001 From: cmoyon Date: Tue, 3 May 2016 17:23:07 +0200 Subject: [PATCH 3/8] Completing StateStorageQtiFileManager functions --- actions/class.TestRunner.php | 4 +- models/classes/StateStorageQtiFile.php | 99 +++++++++++++-- models/classes/StateStorageQtiFileManager.php | 118 +++++++++++++++--- 3 files changed, 192 insertions(+), 29 deletions(-) diff --git a/actions/class.TestRunner.php b/actions/class.TestRunner.php index 1e35500874..f36fc25854 100644 --- a/actions/class.TestRunner.php +++ b/actions/class.TestRunner.php @@ -230,10 +230,12 @@ protected function getTestMeta() { */ protected function getStateStorageQtiFileManager() { - return new StateStorageQtiFileManager( + $service = new StateStorageQtiFileManager( $this->getServiceCallId(), \common_session_SessionManager::getSession()->getUserUri() ); + $service->setServiceLocator($this->getServiceManager()); + return $service; } /** diff --git a/models/classes/StateStorageQtiFile.php b/models/classes/StateStorageQtiFile.php index 6816707767..782ea7b5c2 100644 --- a/models/classes/StateStorageQtiFile.php +++ b/models/classes/StateStorageQtiFile.php @@ -20,6 +20,7 @@ namespace oat\taoQtiTest\models; +use Psr\Http\Message\StreamInterface; use qtism\common\datatypes\File; use qtism\common\datatypes\RuntimeException; use qtism\common\enums\BaseType; @@ -27,9 +28,36 @@ class StateStorageQtiFile implements File { + /** + * When dealing with files, compare, read, write, ... + * in CHUNK_SIZE to not eat up memory. + * + * @var integer + */ + const CHUNK_SIZE = 2048; + + /** + * Key to identify file variable + * @var string + */ protected $key; - protected $mimeType; + + /** + * File name + * @var string + */ protected $filename; + + /** + * Mime type of file content + * @var string + */ + protected $mimeType; + + /** + * Content + * @var string + */ protected $data; /** @@ -47,6 +75,10 @@ public function __construct($key, $mimeType='', $filename='', $data='') $this->data = $data; } + /** + * Get data + * @return string + */ public function getData() { \common_Logger::i(__FUNCTION__); @@ -80,22 +112,71 @@ public function getFilename() return $this->filename; } + /** + * Return the stream of file $data + * @todo use PSR7 stream, but not respect File::getStream signature + * @return StreamInterface + */ public function getStream() { - \common_Logger::i(__FUNCTION__); - // TODO: Implement getStream() method. + if (empty($this->data)) { + return false; + } + + $temp = tmpfile(); + fwrite($temp, $this->getData()); + fseek($temp, 0); + + return $temp; } + /** + * Get identifier of the file e.q. $key + * @return string + */ public function getIdentifier() { - \common_Logger::i(__FUNCTION__); return $this->key; } + /** + * Compare two File by checking filename, mime type & content + * @param mixed $obj + * @return bool + */ public function equals($obj) { - \common_Logger::i(__FUNCTION__); - // TODO: Implement equals() method. + if (!$obj instanceof File) { + return false; + } + + if ($this->getFilename() !== $obj->getFilename()) { + return false; + } + + if ($this->getMimeType() !== $obj->getMimeType()) { + return false; + } + + // We have to check the content of the file. + $myStream = $this->getStream(); + $objStream = $obj->getStream(); + + while (feof($myStream) === false && feof($objStream) === false) { + $myChunk = fread($myStream, self::CHUNK_SIZE); + $objChjunk = fread($objStream, self::CHUNK_SIZE); + + if ($myChunk !== $objChjunk) { + @fclose($myStream); + @fclose($objStream); + return false; + } + } + + @fclose($myStream); + @fclose($objStream); + + return true; } /** @@ -116,18 +197,12 @@ public function getCardinality() return Cardinality::SINGLE; } - public function getPath() - { - \common_Logger::e(__FUNCTION__); - } - /** * Return filename * @return string|void */ public function __toString() { - \common_Logger::i(__FUNCTION__); return $this->getIdentifier(); } } \ No newline at end of file diff --git a/models/classes/StateStorageQtiFileManager.php b/models/classes/StateStorageQtiFileManager.php index 2779cbbd9e..6159b504fd 100644 --- a/models/classes/StateStorageQtiFileManager.php +++ b/models/classes/StateStorageQtiFileManager.php @@ -23,11 +23,16 @@ use oat\oatbox\service\ServiceManager; use qtism\common\datatypes\File; use qtism\common\datatypes\files\FileManager; +use qtism\common\datatypes\files\FileManagerException; use qtism\common\datatypes\files\FileSystemFile; use qtism\runtime\tests\AssessmentTestSession; +use Zend\ServiceManager\ServiceLocatorAwareInterface; +use Zend\ServiceManager\ServiceLocatorAwareTrait; -class StateStorageQtiFileManager implements FileManager +class StateStorageQtiFileManager implements FileManager, ServiceLocatorAwareInterface { + use ServiceLocatorAwareTrait; + /** * Service to store file variable * @var \tao_models_classes_service_StateStorage @@ -36,39 +41,112 @@ class StateStorageQtiFileManager implements FileManager /** * Test session - * @var Uri string + * @var string */ protected $testId; /** * User session - * @var Uri string + * @var string */ protected $userId; + /** + * StateStorageQtiFileManager constructor. + * @param $testId + * @param $userId + */ public function __construct($testId, $userId) { - $this->storageService = ServiceManager::getServiceManager()->get('tao/stateStorage'); + $this->storageService = $this->getServiceLocator()->get('tao/stateStorage'); $this->testId = $testId; $this->userId = $userId; } - public function createFromFile($path, $mimeType, $filename = '') + /** + * Create a StateStorageQtiFile by storing data in key=>value persistence + * Compact metadata at the begining of data string + * @param $filename + * @param $mimeType + * @param $data + * @return StateStorageQtiFile + */ + protected function create($filename, $mimeType, $data) { - \common_Logger::i(__FUNCTION__); + $key = $this->generateUniqKey($this->testId); +// +// // Filename +// $len = strlen($filename); +// $packedFilename = pack('S', $len) . $filename; +// +// // MIME type. +// $len = strlen($mimeType); +// $packedMimeType = pack('S', $len) . $mimeType; +// +// // Data +// //$data = $packedFilename . $packedMimeType . $data; + + //State storage set + if (!$this->storageService->set($this->userId, $key, $data)) { + throw new \RuntimeException('Unable to store filename in key=>value system'); + } + return new StateStorageQtiFile($key, $mimeType, $filename, $data); } - public function createFromData($data, $mimeType, $filename = '') + /** + * Return a StateStorageQtiFile from content of file located at $path + * @todo Access file as stream + * @todo Check mime type? + * @param string $path + * @param string $mimeType + * @param string $filename + * @return StateStorageQtiFile + * @throws FileManagerException + */ + public function createFromFile($path, $mimeType, $filename='') { - $data = base64_encode($data); - $key = $this->generateUniqKey($this->testId); - \common_Logger::i($key); + try { + if (!is_file($path)) { + throw new \RuntimeException('Unable to find source file at "' . $path . '".'); + } + + if (!is_readable($path)) { + throw new \RuntimeException('Source file "' . $path . '" found but not readable.'); + } - $this->storageService->set($this->userId, $key, $data); - \common_Logger::e(base64_decode($this->storageService->get($this->userId, $key))); - return new StateStorageQtiFile($key, $mimeType, $filename); + $pathinfo = pathinfo($path); + $filename = ($filename=='') ? $pathinfo['filename'] . '.' . $pathinfo['extension'] : $filename; + $data = file_get_contents($path); + + return $this->create($filename, $mimeType, $data); + } catch (\RuntimeException $e) { + throw new FileManagerException('An error occured while creating a StateStorageQtiFile object', 0, $e); + } + } + + /** + * Return a StateStorageQtiFile from $data + * @param string $data + * @param string $mimeType + * @param string $filename + * @return StateStorageQtiFile + * @throws FileManagerException + */ + public function createFromData($data, $mimeType, $filename = '') + { + try { + return $this->create($filename, $mimeType, $data); + } catch (\RuntimeException $e) { + throw new FileManagerException('An error occured while creating a StateStorageQtiFile object', 0, $e); + } } + /** + * Create a StateStorageQtiFile with identifier + * @todo delete reference into test state storage + * @param string $identifier + * @return StateStorageQtiFile + */ public function retrieve($identifier) { return new StateStorageQtiFile($identifier); @@ -76,12 +154,20 @@ public function retrieve($identifier) public function delete(File $file) { - \common_Logger::i(__FUNCTION__); + $key = $file->getIdentifier(); + if (!$this->storageService->del($this->userId, $key)) { + throw new \RuntimeException('Unable to delete filename in key=>value system'); + } + return true; } + /** + * Generate a key as identifier to track file in state storage + * @param string $prefix + * @return string + */ protected function generateUniqKey($prefix='') { - return md5(uniqid($prefix, true)); + return uniqid($prefix, true); } - } \ No newline at end of file From 2bf2e4e657935b8aa702b794ce6b840301d44495 Mon Sep 17 00:00:00 2001 From: cmoyon Date: Tue, 3 May 2016 17:29:45 +0200 Subject: [PATCH 4/8] Updating version to 2.8.0 --- models/classes/StateStorageQtiFileManager.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/models/classes/StateStorageQtiFileManager.php b/models/classes/StateStorageQtiFileManager.php index 6159b504fd..37d4c82f17 100644 --- a/models/classes/StateStorageQtiFileManager.php +++ b/models/classes/StateStorageQtiFileManager.php @@ -74,6 +74,10 @@ public function __construct($testId, $userId) protected function create($filename, $mimeType, $data) { $key = $this->generateUniqKey($this->testId); + + if ($filename=='') { + $filename = $key; + } // // // Filename // $len = strlen($filename); @@ -132,7 +136,7 @@ public function createFromFile($path, $mimeType, $filename='') * @return StateStorageQtiFile * @throws FileManagerException */ - public function createFromData($data, $mimeType, $filename = '') + public function createFromData($data, $mimeType, $filename='') { try { return $this->create($filename, $mimeType, $data); From 71289e535ac8e8e8753264a1f2f036920f010b66 Mon Sep 17 00:00:00 2001 From: cmoyon Date: Tue, 3 May 2016 18:31:37 +0200 Subject: [PATCH 5/8] Adding setStateStorage, split binary transformation to File function --- actions/class.TestRunner.php | 2 +- models/classes/StateStorageQtiFile.php | 21 ++++++- models/classes/StateStorageQtiFileManager.php | 56 ++++++++++++------- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/actions/class.TestRunner.php b/actions/class.TestRunner.php index f36fc25854..30ad74e545 100644 --- a/actions/class.TestRunner.php +++ b/actions/class.TestRunner.php @@ -274,7 +274,7 @@ protected function beforeAction($notifyError = true) { // Initialize storage and test session. $testResource = new core_kernel_classes_Resource($this->getRequestParameter('QtiTestDefinition')); - + $sessionManager = new taoQtiTest_helpers_SessionManager($resultServer, $testResource); $userUri = common_session_SessionManager::getSession()->getUserUri(); $seeker = new BinaryAssessmentTestSeeker($this->getTestDefinition()); diff --git a/models/classes/StateStorageQtiFile.php b/models/classes/StateStorageQtiFile.php index 782ea7b5c2..023719d414 100644 --- a/models/classes/StateStorageQtiFile.php +++ b/models/classes/StateStorageQtiFile.php @@ -81,7 +81,6 @@ public function __construct($key, $mimeType='', $filename='', $data='') */ public function getData() { - \common_Logger::i(__FUNCTION__); return $this->data; } @@ -115,7 +114,7 @@ public function getFilename() /** * Return the stream of file $data * @todo use PSR7 stream, but not respect File::getStream signature - * @return StreamInterface + * @return Resource */ public function getStream() { @@ -205,4 +204,22 @@ public function __toString() { return $this->getIdentifier(); } + + /** + * Transform current file to binary content + * @return string + */ + public function toBinary() + { + // Filename + $len = strlen($this->filename); + $packedFilename = pack('S', $len) . $this->filename; + + // MIME type. + $len = strlen($this->mimeType); + $packedMimeType = pack('S', $len) . $this->mimeType; + + // Data + return $packedFilename . $packedMimeType . $this->data; + } } \ No newline at end of file diff --git a/models/classes/StateStorageQtiFileManager.php b/models/classes/StateStorageQtiFileManager.php index 37d4c82f17..f0e5ffe806 100644 --- a/models/classes/StateStorageQtiFileManager.php +++ b/models/classes/StateStorageQtiFileManager.php @@ -20,7 +20,6 @@ namespace oat\taoQtiTest\models; -use oat\oatbox\service\ServiceManager; use qtism\common\datatypes\File; use qtism\common\datatypes\files\FileManager; use qtism\common\datatypes\files\FileManagerException; @@ -58,11 +57,31 @@ class StateStorageQtiFileManager implements FileManager, ServiceLocatorAwareInte */ public function __construct($testId, $userId) { - $this->storageService = $this->getServiceLocator()->get('tao/stateStorage'); $this->testId = $testId; $this->userId = $userId; } + /** + * Get state storage, retrieve it if empty + * @return array|object|\tao_models_classes_service_StateStorage + */ + public function getStateStorage() + { + if (empty($this->storageService)) { + $this->storageService = $this->getServiceLocator()->get('tao/stateStorage'); + } + return $this->storageService; + } + + /** + * Set storage service + * @param $storageService + */ + public function setStateStorage($storageService) + { + $this->storageService = $storageService; + } + /** * Create a StateStorageQtiFile by storing data in key=>value persistence * Compact metadata at the begining of data string @@ -78,23 +97,15 @@ protected function create($filename, $mimeType, $data) if ($filename=='') { $filename = $key; } -// -// // Filename -// $len = strlen($filename); -// $packedFilename = pack('S', $len) . $filename; -// -// // MIME type. -// $len = strlen($mimeType); -// $packedMimeType = pack('S', $len) . $mimeType; -// -// // Data -// //$data = $packedFilename . $packedMimeType . $data; - - //State storage set - if (!$this->storageService->set($this->userId, $key, $data)) { - throw new \RuntimeException('Unable to store filename in key=>value system'); + + $stateStorageFile = new StateStorageQtiFile($key, $mimeType, $filename, $data); + $content = $stateStorageFile->toBinary(); + + //State storage update + if (!$this->getStateStorage()->set($this->userId, $key, $content)) { + throw new \RuntimeException('Unable to store file in state storage system'); } - return new StateStorageQtiFile($key, $mimeType, $filename, $data); + return $stateStorageFile; } /** @@ -156,11 +167,16 @@ public function retrieve($identifier) return new StateStorageQtiFile($identifier); } + /** + * Delete file in key=>value storage + * @param File $file + * @return bool + */ public function delete(File $file) { $key = $file->getIdentifier(); - if (!$this->storageService->del($this->userId, $key)) { - throw new \RuntimeException('Unable to delete filename in key=>value system'); + if (!$this->getStateStorage()->del($this->userId, $key)) { + throw new \RuntimeException('Unable to delete file in state storage system'); } return true; } From c964c0967dd85ddaff901d0dd8da2a751af99cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Bogaerts?= Date: Wed, 4 May 2016 10:01:52 +0200 Subject: [PATCH 6/8] Update composer.json Dependency to development version of tao-lib-qti for future integration. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7353099d21..6cfdb21465 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ }, "minimum-stability": "dev", "require": { - "oat-sa/lib-tao-qti": "dev-master", + "oat-sa/lib-tao-qti": "dev-refactor/qtism-dependencies-to-feature-branch", "oat-sa/oatbox-extension-installer": "dev-master" }, "autoload": { From dae573115f31f08d7fadc49329005fcf6f1f6b88 Mon Sep 17 00:00:00 2001 From: cmoyon Date: Wed, 4 May 2016 11:35:36 +0200 Subject: [PATCH 7/8] Instantiate state storage only if not exists --- actions/class.TestRunner.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/actions/class.TestRunner.php b/actions/class.TestRunner.php index 30ad74e545..291709dc16 100644 --- a/actions/class.TestRunner.php +++ b/actions/class.TestRunner.php @@ -29,6 +29,7 @@ use qtism\common\enums\BaseType; use qtism\common\enums\Cardinality; use qtism\common\datatypes\String as QtismString; +use qtism\common\datatypes\files\FileManager; use qtism\runtime\storage\binary\BinaryAssessmentTestSeeker; use qtism\runtime\storage\common\AbstractStorage; use qtism\data\SubmissionMode; @@ -75,6 +76,13 @@ class taoQtiTest_actions_TestRunner extends tao_actions_ServiceModule { * @var AbstractStorage */ private $storage = null; + + /** + * The service state storage of storage engine. + * + * @var FileManager + */ + private $serviceStateStorage = null; /** * The error that occured during the current request. @@ -230,12 +238,14 @@ protected function getTestMeta() { */ protected function getStateStorageQtiFileManager() { - $service = new StateStorageQtiFileManager( - $this->getServiceCallId(), - \common_session_SessionManager::getSession()->getUserUri() - ); - $service->setServiceLocator($this->getServiceManager()); - return $service; + if (!$this->serviceStateStorage) { + $this->serviceStateStorage = new StateStorageQtiFileManager( + $this->getServiceCallId(), + \common_session_SessionManager::getSession()->getUserUri() + ); + $this->serviceStateStorage->setServiceLocator($this->getServiceManager()); + } + return $this->serviceStateStorage; } /** From d5f8178b60c95870b3f1f840c437edb4843d4c8e Mon Sep 17 00:00:00 2001 From: = Date: Wed, 4 May 2016 11:53:36 +0200 Subject: [PATCH 8/8] Meaningful error message in case of unknown exception in TAO logs. --- actions/class.TestRunner.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/actions/class.TestRunner.php b/actions/class.TestRunner.php index 30ad74e545..c90eaa10f9 100644 --- a/actions/class.TestRunner.php +++ b/actions/class.TestRunner.php @@ -15,7 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Copyright (c) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); + * Copyright (c) 2013-2016 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); * * */ @@ -881,6 +881,16 @@ protected function handleAssessmentTestSessionException(AssessmentTestSessionExc case AssessmentTestSessionException::ASSESSMENT_ITEM_DURATION_OVERFLOW: $this->onTimeout($e); break; + default: + $msg = "An unexpected error occured in the QTI Test Runner: \n"; + while ($e) { + $msg .= $e->getMessage(); + if (($e = $e->getPrevious()) !== null) { + $msg .= "\nCaused by:\n"; + } + } + common_Logger::e($msg); + break; } } -} \ No newline at end of file +}