<?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 } }