272 lines
9.5 KiB
PHP
272 lines
9.5 KiB
PHP
|
<?php
|
||
|
|
||
|
/**
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; under version 2
|
||
|
* of the License (non-upgradable).
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* 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);
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
namespace oat\taoQtiItem\controller;
|
||
|
|
||
|
//use oat\taoQtiItem\controller\QtiPreview;
|
||
|
use oat\oatbox\service\ServiceManager;
|
||
|
use oat\tao\model\asset\AssetService;
|
||
|
use oat\taoQtiItem\helpers\QtiFile;
|
||
|
use oat\taoQtiItem\model\qti\Service;
|
||
|
use oat\taoQtiItem\model\qti\Item;
|
||
|
use OutOfBoundsException;
|
||
|
use OutOfRangeException;
|
||
|
use \taoItems_actions_ItemPreview;
|
||
|
use \tao_helpers_Uri;
|
||
|
use \core_kernel_classes_Resource;
|
||
|
use \common_Exception;
|
||
|
use \taoQtiCommon_helpers_PciVariableFiller;
|
||
|
use \common_Logger;
|
||
|
use \taoQtiCommon_helpers_ResultTransmissionException;
|
||
|
use \taoQtiCommon_helpers_PciStateOutput;
|
||
|
use \taoQtiCommon_helpers_Utils;
|
||
|
use \common_ext_ExtensionsManager;
|
||
|
use qtism\common\datatypes\files\FileSystemFileManager;
|
||
|
use qtism\runtime\common\State;
|
||
|
use qtism\runtime\tests\SessionManager;
|
||
|
use qtism\runtime\tests\AssessmentItemSession;
|
||
|
use qtism\runtime\tests\AssessmentItemSessionException;
|
||
|
use qtism\data\storage\xml\XmlDocument;
|
||
|
use qtism\data\storage\StorageException;
|
||
|
|
||
|
/**
|
||
|
* Qti Item Runner Controller
|
||
|
*
|
||
|
* @author CRP Henri Tudor - TAO Team - {@link http://www.tao.lu}
|
||
|
* @package taoQTI
|
||
|
|
||
|
* @license GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
|
||
|
*/
|
||
|
class QtiPreview extends taoItems_actions_ItemPreview
|
||
|
{
|
||
|
|
||
|
public function getPreviewUrl($item, $options = [])
|
||
|
{
|
||
|
$code = base64_encode($item->getUri());
|
||
|
return _url('render/' . $code . '/index', 'QtiPreview', 'taoQtiItem', $options);
|
||
|
}
|
||
|
|
||
|
public function submitResponses()
|
||
|
{
|
||
|
|
||
|
$itemUri = tao_helpers_Uri::decode($this->getRequestParameter('itemUri'));
|
||
|
|
||
|
if (!empty($itemUri)) {
|
||
|
$this->processResponses(new core_kernel_classes_Resource($itemUri));
|
||
|
} else {
|
||
|
throw new common_Exception('missing required itemUri');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Item's ResponseProcessing.
|
||
|
*
|
||
|
* @param core_kernel_classes_Resource $item The Item you want to apply ResponseProcessing.
|
||
|
* @throws \RuntimeException If an error occurs while processing responses or transmitting results
|
||
|
*/
|
||
|
protected function processResponses(core_kernel_classes_Resource $item)
|
||
|
{
|
||
|
|
||
|
$jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
|
||
|
|
||
|
try {
|
||
|
$qtiXmlFileContent = QtiFile::getQtiFileContent($item);
|
||
|
$qtiXmlDoc = new XmlDocument();
|
||
|
$qtiXmlDoc->loadFromString($qtiXmlFileContent);
|
||
|
} catch (StorageException $e) {
|
||
|
$msg = "An error occurred while loading QTI-XML file at expected location '${qtiXmlFilePath}'.";
|
||
|
common_Logger::e(($e->getPrevious() !== null) ? $e->getPrevious()->getMessage() : $e->getMessage());
|
||
|
throw new \RuntimeException($msg, 0, $e);
|
||
|
}
|
||
|
|
||
|
$itemSession = new AssessmentItemSession($qtiXmlDoc->getDocumentComponent(), new SessionManager());
|
||
|
$itemSession->beginItemSession();
|
||
|
|
||
|
$variables = [];
|
||
|
$filler = new taoQtiCommon_helpers_PciVariableFiller($qtiXmlDoc->getDocumentComponent());
|
||
|
|
||
|
// Convert client-side data as QtiSm Runtime Variables.
|
||
|
foreach ($jsonPayload as $id => $response) {
|
||
|
try {
|
||
|
$var = $filler->fill($id, $response);
|
||
|
// Do not take into account QTI Files at preview time.
|
||
|
// Simply delete the created file.
|
||
|
if (taoQtiCommon_helpers_Utils::isQtiFile($var, false) === true) {
|
||
|
$fileManager = new FileSystemFileManager();
|
||
|
$fileManager->delete($var->getValue());
|
||
|
} else {
|
||
|
$variables[] = $var;
|
||
|
}
|
||
|
} catch (OutOfRangeException $e) {
|
||
|
// A variable value could not be converted, ignore it.
|
||
|
// Developer's note: QTI Pairs with a single identifier (missing second identifier of the pair) are transmitted as an array of length 1,
|
||
|
// this might cause problem. Such "broken" pairs are simply ignored.
|
||
|
common_Logger::d("Client-side value for variable '${id}' is ignored due to data malformation.");
|
||
|
} catch (OutOfBoundsException $e) {
|
||
|
// No such identifier found in item.
|
||
|
common_Logger::d("The variable with identifier '${id}' is not declared in the item definition.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$itemSession->beginAttempt();
|
||
|
$itemSession->endAttempt(new State($variables));
|
||
|
|
||
|
// Return the item session state to the client-side.
|
||
|
$this->returnJson(
|
||
|
[
|
||
|
'success' => true,
|
||
|
'displayFeedback' => true,
|
||
|
'itemSession' => self::buildOutcomeResponse($itemSession)
|
||
|
]
|
||
|
);
|
||
|
} catch (AssessmentItemSessionException $e) {
|
||
|
$msg = "An error occurred while processing the responses.";
|
||
|
throw new \RuntimeException($msg, 0, $e);
|
||
|
} catch (taoQtiCommon_helpers_ResultTransmissionException $e) {
|
||
|
$msg = "An error occurred while transmitting a result to the target Result Server.";
|
||
|
throw new \RuntimeException($msg, 0, $e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the ResultServer API call to be used by the item.
|
||
|
*
|
||
|
* @return string A string representing JavaScript instructions.
|
||
|
*/
|
||
|
protected function getResultServer()
|
||
|
{
|
||
|
$itemUri = tao_helpers_Uri::decode($this->getRequestParameter('uri'));
|
||
|
return [
|
||
|
'module' => 'taoQtiItem/QtiPreviewResultServerApi',
|
||
|
'endpoint' => ROOT_URL . 'taoQtiItem/QtiPreview/',
|
||
|
'params' => $itemUri
|
||
|
];
|
||
|
}
|
||
|
|
||
|
|
||
|
protected static function buildOutcomeResponse(AssessmentItemSession $itemSession)
|
||
|
{
|
||
|
$stateOutput = new taoQtiCommon_helpers_PciStateOutput();
|
||
|
|
||
|
foreach ($itemSession->getOutcomeVariables(false) as $var) {
|
||
|
$stateOutput->addVariable($var);
|
||
|
}
|
||
|
|
||
|
$output = $stateOutput->getOutput();
|
||
|
return $output;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* (non-PHPdoc)
|
||
|
* @see taoItems_actions_ItemPreview::getRenderedItem()
|
||
|
*/
|
||
|
protected function getRenderedItem($item)
|
||
|
{
|
||
|
|
||
|
//@todo make getRenderedItem language dependent
|
||
|
$lang = \common_session_SessionManager::getSession()->getDataLanguage();
|
||
|
$qtiItem = Service::singleton()->getDataItemByRdfItem($item, $lang, true);
|
||
|
if ($qtiItem) {
|
||
|
$contentVariableElements = array_merge($this->getModalFeedbacks($qtiItem), $this->getRubricBlocks($qtiItem));
|
||
|
|
||
|
/** @var AssetService $assetService */
|
||
|
$assetService = ServiceManager::getServiceManager()->get(AssetService::SERVICE_ID);
|
||
|
|
||
|
$taoBaseUrl = $assetService->getJsBaseWww('tao');
|
||
|
$qtiBaseUrl = $assetService->getJsBaseWww('taoQtiItem');
|
||
|
|
||
|
$taoLibUrl = $taoBaseUrl . 'js/';
|
||
|
$taoQtiItemLibUrl = $qtiBaseUrl . 'js/';
|
||
|
|
||
|
$xhtml = $qtiItem->toXHTML([
|
||
|
'contentVariableElements' => $contentVariableElements,
|
||
|
// 'js' => array($qtiBaseUrl.'js/preview/qtiViewSelector.js'),
|
||
|
'js_var' => ['view' => $this->getRequestView()],
|
||
|
// 'css' => array($qtiBaseUrl.'css/preview/qtiViewSelector.css'),
|
||
|
'path' => [
|
||
|
'tao' => $taoLibUrl,
|
||
|
'taoQtiItem' => $taoQtiItemLibUrl
|
||
|
],
|
||
|
'client_config_url' => $this->getClientConfigUrl()
|
||
|
|
||
|
]);
|
||
|
} else {
|
||
|
\common_Logger::i('Try to preview an empty item', [$item->getUri()]);
|
||
|
$xhtml = '';
|
||
|
}
|
||
|
|
||
|
return $xhtml;
|
||
|
}
|
||
|
|
||
|
protected function getRequestView()
|
||
|
{
|
||
|
$returnValue = 'candidate';
|
||
|
|
||
|
if ($this->hasRequestParameter('view')) {
|
||
|
$returnValue = tao_helpers_Uri::decode($this->getRequestParameter('view'));
|
||
|
}
|
||
|
|
||
|
return $returnValue;
|
||
|
}
|
||
|
|
||
|
protected function getRubricBlocks(Item $qtiItem)
|
||
|
{
|
||
|
|
||
|
$returnValue = [];
|
||
|
|
||
|
$currentView = $this->getRequestView();
|
||
|
$rubricBlocks = $qtiItem->getRubricBlocks();
|
||
|
|
||
|
foreach ($rubricBlocks as $rubricBlock) {
|
||
|
$view = $rubricBlock->attr('view');
|
||
|
|
||
|
if (!empty($view) && in_array($currentView, $view)) {
|
||
|
$returnValue[$rubricBlock->getSerial()] = $rubricBlock->toArray();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $returnValue;
|
||
|
}
|
||
|
|
||
|
protected function getModalFeedbacks(Item $qtiItem)
|
||
|
{
|
||
|
|
||
|
$returnValue = [];
|
||
|
|
||
|
$feedbacks = $qtiItem->getModalFeedbacks();
|
||
|
foreach ($feedbacks as $feedback) {
|
||
|
$returnValue[$feedback->getSerial()] = $feedback->toArray();
|
||
|
}
|
||
|
|
||
|
return $returnValue;
|
||
|
}
|
||
|
|
||
|
public function getTemplateElements(Item $qtiItem)
|
||
|
{
|
||
|
|
||
|
throw new common_Exception('qti template elments, to be implemented');
|
||
|
//1. process templateRules
|
||
|
//2. return the template values
|
||
|
}
|
||
|
}
|