Skip to content

Commit

Permalink
Merge branch 'release-2.20.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
krampstudio committed Mar 18, 2016
2 parents 2b44dd8 + ddf21b6 commit d013984
Show file tree
Hide file tree
Showing 34 changed files with 1,258 additions and 1,019 deletions.
229 changes: 142 additions & 87 deletions actions/class.Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
use oat\taoQtiTest\models\runner\QtiRunnerClosedException;
use oat\taoQtiTest\models\runner\QtiRunnerPausedException;
use oat\taoQtiTest\models\event\TraceVariableStored;
use qtism\runtime\tests\AssessmentTestSessionState;
use \oat\taoTests\models\runner\CsrfToken;
use \oat\taoTests\models\runner\SessionCsrfToken;

/**
* Class taoQtiTest_actions_Runner
Expand All @@ -45,6 +48,12 @@ class taoQtiTest_actions_Runner extends tao_actions_ServiceModule
*/
protected $serviceContext;

/**
* The anti-CSRF manager
* @var CsrfToken;
*/
protected $csrf;

/**
* taoQtiTest_actions_Runner constructor.
*/
Expand All @@ -56,18 +65,62 @@ public function __construct()
taoQtiTest_helpers_TestRunnerUtils::noHttpClientCache();
}

/**
* Gets the anti-CSRF manager
* @return CsrfToken
*/
protected function getCsrf()
{
if (!$this->csrf) {
$this->csrf = new SessionCsrfToken('TEST_RUNNER');
}
return $this->csrf;
}

/**
* @param $data
* @param int [$httpStatus]
* @param bool [$token]
*/
protected function returnJson($data, $httpStatus = 200, $token = true)
{
// auto append the CSRF token to the result
if ($token) {
if (is_array($data)) {
if ($data['success'] || $httpStatus != 403) {
$data['token'] = $this->getCsrf()->getCsrfToken();
}
} else if (is_object($data)) {
if ($data->success || $httpStatus != 403) {
$data->token = $this->getCsrf()->getCsrfToken();
}
}
}

return parent::returnJson($data, $httpStatus);
}

/**
* Gets the test service context
* @param bool [$check] Checks the context after create. Default to true.
* @param bool [$checkToken] Checks the security token.
* @return QtiRunnerServiceContext
* @throws \common_Exception
*/
protected function getServiceContext($check = true)
protected function getServiceContext($check = true, $checkToken = true)
{
if (!$this->serviceContext) {
$testDefinition = $this->getRequestParameter('testDefinition');
$testCompilation = $this->getRequestParameter('testCompilation');

if ($checkToken) {
$csrfToken = $this->getRequestParameter('X-Auth-Token');
if (!$this->getCsrf()->checkCsrfToken($csrfToken)) {
\common_Logger::w("CSRF attempt! The token $csrfToken is no longer valid!");
throw new \common_exception_Unauthorized();
}
}

if ($this->hasRequestParameter('testServiceCallId')) {
$testExecution = $this->getRequestParameter('testServiceCallId');
} else {
Expand Down Expand Up @@ -112,6 +165,10 @@ protected function getErrorResponse($e = null) {
$response['type'] = 'FileNotFound';
$response['message'] = __('File not found');
break;

case $e instanceof \common_exception_Unauthorized:
$response['code'] = 403;
break;
}
}

Expand All @@ -136,6 +193,7 @@ protected function getErrorCode($e = null) {

case $e instanceof \common_exception_NotImplemented:
case $e instanceof \common_exception_NoImplementation:
case $e instanceof \common_exception_Unauthorized:
$code = 403;
break;

Expand All @@ -147,6 +205,26 @@ protected function getErrorCode($e = null) {
return $code;
}

/**
* Gets the state identifier for the current itemRef
* @return string
* @throws QtiRunnerClosedException
* @throws common_exception_Unauthorized
*/
protected function getStateId()
{
$serviceContext = $this->getServiceContext();
$serviceCallId = $serviceContext->getTestExecutionUri();
$testSession = $serviceContext->getTestSession();
$itemRef = $testSession->getCurrentAssessmentItemRef();

if ($itemRef instanceof \qtism\data\AssessmentItemRef) {
return $serviceCallId . $itemRef->getIdentifier();
}

throw new QtiRunnerClosedException();
}

/**
* Initializes the delivery session
*/
Expand All @@ -155,6 +233,7 @@ public function init()
$code = 200;

try {
$this->getCsrf()->revokeCsrfToken();
$serviceContext = $this->getServiceContext();
$result = $this->runnerService->init($serviceContext);

Expand Down Expand Up @@ -248,45 +327,50 @@ public function getTestMap()
}

/**
* Provides the rubrics related to the current session state
* Provides the definition data and the state for a particular item
*/
public function getRubrics()
{
// TODO: make a better implementation
// the rb are now rendererd in the output...
ob_start();
$serviceContext = $this->getServiceContext();
$this->runnerService->getRubrics($serviceContext);
$rubrics = ob_get_contents();
ob_end_clean();

$this->returnJson(array(
'success' => true,
'content' => $rubrics
));
}

/**
* Provides the definition data for a particular item
*/
public function getItemData()
public function getItem()
{
$code = 200;

$itemRef = $this->getRequestParameter('itemDefinition');

try {
$serviceContext = $this->getServiceContext();

$stateId = $this->getStateId();
$itemState = $this->runnerService->getItemState($serviceContext, $stateId);
if (!count($itemState)) {
$itemState = new StdClass();
}

// TODO: make a better implementation
// As the rubric blocs are nested at the section level, it could be interesting to send these
// rubric blocs only once per section
if (count($serviceContext->getTestSession()->getRoute()->current()->getRubricBlockRefs())) {
$rubrics = $this->runnerService->getRubrics($serviceContext);
} else {
$rubrics = null;
}

$itemData = $this->runnerService->getItemData($serviceContext, $itemRef);
$baseUrl = $this->runnerService->getItemPublicUrl($serviceContext, $itemRef);
if (is_string($itemData)) {
$response = '{"success":true,"itemData":' . $itemData . ',"baseUrl":"'.$baseUrl.'"}';
$response = '{' .
'"success":true,' .
'"token":"' . $this->getCsrf()->getCsrfToken() . '",' .
'"baseUrl":"'.$baseUrl.'",' .
'"itemData":' . $itemData . ',' .
'"itemState":' . json_encode($itemState) . ',' .
'"rubrics":' . json_encode($rubrics) .
'}';
} else {
$response = [
'itemData' => $itemData,
'success' => true,
'baseUrl' => $baseUrl
'itemData' => $itemData,
'itemState' => $itemState,
'baseUrl' => $baseUrl,
'rubrics' => $rubrics
];
}

Expand All @@ -304,81 +388,52 @@ public function getItemData()
}

/**
* Provides the state object for a particular item
* Stores the state object and the response set of a particular item
*/
public function getItemState()
public function submitItem()
{
$code = 200;

$serviceCallId = $this->getRequestParameter('testServiceCallId');

try {
$serviceContext = $this->getServiceContext();
$stateId = $serviceCallId . $serviceContext->getTestSession()->getCurrentAssessmentItemRef()->getIdentifier();


$response = [
'itemState' => $this->runnerService->getItemState($serviceContext, $stateId),
'success' => true,
];

} catch (common_Exception $e) {
$response = $this->getErrorResponse($e);
$code = $this->getErrorCode($e);
}

$this->returnJson($response, $code);
}
$itemRef = $this->getRequestParameter('itemDefinition');

/**
* Stores the state object of a particular item
*/
public function submitItemState()
{
$code = 200;
$data = \taoQtiCommon_helpers_Utils::readJsonPayload();

$state = json_decode(html_entity_decode($this->getRequestParameter('itemState')));
$serviceCallId = $this->getRequestParameter('testServiceCallId');
$state = isset($data['itemState']) ? $data['itemState'] : new StdClass();
$itemResponse = isset($data['itemResponse']) ? $data['itemResponse'] : [];

try {
$serviceContext = $this->getServiceContext(false);
$stateId = $serviceCallId . $serviceContext->getTestSession()->getCurrentAssessmentItemRef()->getIdentifier();

$response = [
'success' => $this->runnerService->setItemState($serviceContext, $stateId, $state),
];

$this->runnerService->persist($serviceContext);

} catch (common_Exception $e) {
$response = $this->getErrorResponse($e);
$code = $this->getErrorCode($e);
$stateId = $this->getStateId();
if ($serviceContext->getTestSession()->getState() == AssessmentTestSessionState::CLOSED) {
throw new QtiRunnerClosedException();
}
$storeResponse = true;

try {
// do not allow to store the response if the session is in a wrong state
$this->runnerService->check($serviceContext);
} catch (QtiRunnerPausedException $e) {
// allow to store the state but prevent to store the response
$storeResponse = false;
\common_Logger::i('Store item state after a test session pause');
}

$this->returnJson($response, $code);
}
$successState = $this->runnerService->setItemState($serviceContext, $stateId, $state);

/**
* Stores the response set of a particular item
*/
public function storeItemResponse()
{
$code = 200;

$itemRef = $this->getRequestParameter('itemDefinition');

$itemResponse = \taoQtiCommon_helpers_Utils::readJsonPayload();

try {
$serviceContext = $this->getServiceContext();

$response = array(
'success' => $this->runnerService->storeItemResponse($serviceContext, $itemRef, $itemResponse),
'displayFeedbacks' => $this->runnerService->displayFeedbacks($serviceContext)
);
if ($storeResponse) {
$successResponse = $this->runnerService->storeItemResponse($serviceContext, $itemRef, $itemResponse);
$displayFeedback = $this->runnerService->displayFeedbacks($serviceContext);
} else {
$successResponse = true;
$displayFeedback = false;
}

if($response['displayFeedbacks'] == true){
$response = [
'success' => $successState && $successResponse,
'displayFeedbacks' => $displayFeedback,
];

if ($displayFeedback == true) {
//FIXME there is here a performance issue, at the end we need the defitions only once, not at each storage
$response['feedbacks'] = $this->runnerService->getFeedbacks($serviceContext, $itemRef);
$response['itemSession'] = $this->runnerService->getItemSession($serviceContext);
Expand Down Expand Up @@ -699,7 +754,7 @@ public function time()

try {
if($this->hasRequestParameter('timerPaused')){
$duration = round($this->getRequestParameter('timerPaused'), 3);
$duration = floatval($this->getRequestParameter('timerPaused'));
if($duration > 0){
$serviceContext = $this->getServiceContext(false);
$this->runnerService->updateTimers($serviceContext, $duration);
Expand Down
2 changes: 1 addition & 1 deletion helpers/class.TestRunnerUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class taoQtiTest_helpers_TestRunnerUtils {
* temporary helper until proper servicemanager integration
* @return ExtendedStateService
*/
static protected function getExtendedStateService()
static public function getExtendedStateService()
{
if (!isset(self::$extendedStateService)) {
self::$extendedStateService = new ExtendedStateService();
Expand Down
2 changes: 1 addition & 1 deletion manifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
'label' => 'QTI test model',
'description' => 'TAO QTI test implementation',
'license' => 'GPL-2.0',
'version' => '2.20.0',
'version' => '2.20.1',
'author' => 'Open Assessment Technologies',
'requires' => array(
'taoTests' => '>=2.6',
Expand Down
Loading

0 comments on commit d013984

Please sign in to comment.