tao-test/app/ltiDeliveryProvider/controller/DeliveryTool.php

296 lines
12 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-2021 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*/
namespace oat\ltiDeliveryProvider\controller;
use common_ext_ExtensionsManager;
use common_Logger;
use common_session_SessionManager;
use core_kernel_classes_Resource;
use oat\ltiDeliveryProvider\model\execution\implementation\Lti1p3DeliveryExecutionService;
use tao_helpers_I18n;
use function GuzzleHttp\Psr7\stream_for;
use oat\ltiDeliveryProvider\model\execution\LtiDeliveryExecutionService;
use oat\ltiDeliveryProvider\model\LtiAssignment;
use oat\ltiDeliveryProvider\model\LTIDeliveryTool;
use oat\ltiDeliveryProvider\model\LtiLaunchDataService;
use oat\tao\model\actionQueue\ActionFullException;
use oat\taoDelivery\model\execution\DeliveryExecution;
use oat\taoDelivery\model\execution\StateServiceInterface;
use oat\taoLti\controller\ToolModule;
use oat\taoLti\models\classes\LtiException;
use oat\taoLti\models\classes\LtiMessages\LtiErrorMessage;
use oat\taoLti\models\classes\LtiRoles;
use oat\taoLti\models\classes\LtiService;
use oat\taoLti\models\classes\LtiVariableMissingException;
use oat\taoQtiTest\models\QtiTestExtractionFailedException;
use tao_helpers_Uri;
use oat\ltiDeliveryProvider\model\navigation\LtiNavigationService;
class DeliveryTool extends ToolModule
{
/**
* Setting this parameter to 'true' will prevent resuming a testsession in progress
* and will start a new testsession whenever the lti tool is launched
*
* @var string
*/
const PARAM_FORCE_RESTART = 'custom_force_restart';
/**
* Setting this parameter to 'true' will prevent the thank you screen to be shown after
* the test and skip directly to the return url
*
* @var string
*/
const PARAM_SKIP_THANKYOU = 'custom_skip_thankyou';
/**
* Setting this parameter to 'true' will prevent the 'You have already taken this test'
* screen to be shown skip directly to the return url
* @var string
*/
const PARAM_SKIP_OVERVIEW = 'custom_skip_overview';
/**
* Setting this parameter to a string will show this string as the title of the thankyou
* page. (no effect if PARAM_SKIP_THANKYOU is set to 'true')
*
* @var string
*/
const PARAM_THANKYOU_MESSAGE = 'custom_message';
/**
* (non-PHPdoc)
* @see ToolModule::run()
*
* @throws LtiException
* @throws \InterruptedActionException
* @throws \ResolverException
* @throws \common_exception_Error
* @throws \common_exception_IsAjaxAction
* @throws \common_exception_NotFound
* @throws LtiVariableMissingException
*/
public function run()
{
$compiledDelivery = $this->getDelivery();
if (is_null($compiledDelivery) || !$compiledDelivery->exists()) {
if ($this->hasAccess(LinkConfiguration::class, 'configureDelivery')) {
// user authorised to select the Delivery
$this->redirect(tao_helpers_Uri::url('configureDelivery', 'LinkConfiguration', null));
} else {
// user NOT authorised to select the Delivery
throw new LtiException(
__('This tool has not yet been configured, please contact your instructor'),
LtiErrorMessage::ERROR_INVALID_PARAMETER
);
}
} else {
$user = common_session_SessionManager::getSession()->getUser();
$isLearner = !is_null($user)
&& count(array_intersect([LtiRoles::CONTEXT_LEARNER, LtiRoles::CONTEXT_LTI1P3_LEARNER], $user->getRoles())) > 0;
if ($isLearner) {
if ($this->hasAccess(DeliveryRunner::class, 'runDeliveryExecution')) {
try {
$activeExecution = $this->getActiveDeliveryExecution($compiledDelivery);
if ($activeExecution && $activeExecution->getState()->getUri() != DeliveryExecution::STATE_PAUSED) {
$deliveryExecutionStateService = $this->getServiceLocator()->get(StateServiceInterface::SERVICE_ID);
$deliveryExecutionStateService->pause($activeExecution);
}
$this->redirect($this->getLearnerUrl($compiledDelivery, $activeExecution));
} catch (QtiTestExtractionFailedException $e) {
common_Logger::i($e->getMessage());
throw new LtiException($e->getMessage());
} catch (ActionFullException $e) {
$this->redirect(_url('launchQueue', 'DeliveryTool', null, [
'position' => $e->getPosition(),
'delivery' => $compiledDelivery->getUri(),
]));
}
} else {
common_Logger::e('Lti learner has no access to delivery runner');
$this->returnError(__('Access to this functionality is restricted'), false);
}
} elseif ($this->hasAccess(LinkConfiguration::class, 'configureDelivery')) {
$this->redirect(_url('showDelivery', 'LinkConfiguration', null, ['uri' => $compiledDelivery->getUri()]));
} else {
$this->returnError(__('Access to this functionality is restricted to students'), false);
}
}
}
/**
* @throws LtiException
* @throws \common_exception_Error
* @throws \common_ext_ExtensionException
*/
public function launchQueue()
{
$this->configureI18n();
$delivery = $this->getDelivery();
if (!$delivery->exists()) {
throw new LtiException(
__('Delivery does not exist. Please contact your instructor.'),
LtiErrorMessage::ERROR_INVALID_PARAMETER
);
}
$runUrl = _url('run', 'DeliveryTool', null, ['delivery' => $delivery->getUri()]);
$config = $this->getServiceLocator()->get('ltiDeliveryProvider/LaunchQueue')->getConfig();
$config['runUrl'] = $runUrl;
$config['capacityCheckUrl'] = _url('checkCapacity', 'DeliveryTool');
$this->defaultData();
$this->setData('delivery', $delivery);
$this->setData('position', intval($this->getRequestParameter('position')));
$this->setData('client_params', $config);
$this->setView('learner/launchQueue.tpl');
}
public function checkCapacity()
{
/** @var \oat\taoDelivery\model\Capacity\CapacityInterface $capacityService */
$capacityService = $this->getServiceLocator()->get(\oat\taoDelivery\model\Capacity\CapacityInterface::SERVICE_ID);
$capacity = $capacityService->getCapacity();
$payload = [
'id' => '',
'status' => 0,
];
if ($capacity === -1 || $capacity > 0) {
$payload['status'] = 1;
}
return $this->getPsrResponse()->withBody(stream_for(json_encode($payload)))
->withHeader('Content-Type', 'application/json');
}
public function launch1p3(): void
{
$message = $this->getValidatedLtiMessagePayload();
LtiService::singleton()->startLti1p3Session($message);
$this->forward('run', null, null, $_GET);
}
/**
* @param core_kernel_classes_Resource $delivery
* @param DeliveryExecution $activeExecution
*
* @return string
* @throws LtiException
* @throws \common_exception_Error
*/
protected function getLearnerUrl(\core_kernel_classes_Resource $delivery, DeliveryExecution $activeExecution = null)
{
$currentSession = \common_session_SessionManager::getSession();
$user = $currentSession->getUser();
if ($activeExecution === null) {
$activeExecution = $this->getActiveDeliveryExecution($delivery);
}
if ($activeExecution !== null) {
return _url('runDeliveryExecution', 'DeliveryRunner', null, ['deliveryExecution' => $activeExecution->getIdentifier()]);
}
/** @var LtiAssignment $assignmentService */
$assignmentService = $this->getServiceLocator()->get(LtiAssignment::SERVICE_ID);
if (!$assignmentService->isDeliveryExecutionAllowed($delivery->getUri(), $user)) {
throw new LtiException(
__('User is not authorized to run this delivery'),
LtiErrorMessage::ERROR_LAUNCH_FORBIDDEN
);
}
if ($user->getLaunchData()->hasVariable(self::PARAM_SKIP_OVERVIEW)) {
$executionService = $this->getServiceLocator()->get(LtiDeliveryExecutionService::SERVICE_ID);
$executions = $executionService->getLinkedDeliveryExecutions($delivery, $currentSession->getLtiLinkResource(), $user->getIdentifier());
$lastDE = end($executions);
/** @var LtiNavigationService $ltiNavigationService */
$ltiNavigationService = $this->getServiceLocator()->get(LtiNavigationService::SERVICE_ID);
$url = $ltiNavigationService->getReturnUrl($user->getLaunchData(), $lastDE);
} else {
$url = _url(
'ltiOverview',
'DeliveryRunner',
null,
['delivery' => $delivery->getUri()]
);
}
return $url;
}
/**
* @param core_kernel_classes_Resource $delivery
*
* @return mixed|null|DeliveryExecution
*/
protected function getActiveDeliveryExecution(\core_kernel_classes_Resource $delivery)
{
$deliveryExecutionService = $this->getServiceLocator()->get(LtiDeliveryExecutionService::SERVICE_ID);
return $deliveryExecutionService->getActiveDeliveryExecution($delivery);
}
/**
* (non-PHPdoc)
* @see ToolModule::getTool()
*/
protected function getTool()
{
return $this->getServiceLocator()->get(LTIDeliveryTool::class);
}
/**
* Returns the delivery associated with the current link
* either from url or from the remote_link if configured
* returns null if none found
*
* @return core_kernel_classes_Resource
* @throws LtiException
* @throws \common_exception_Error
*/
protected function getDelivery()
{
//passed as a parameter
if ($this->hasRequestParameter('delivery')) {
$returnValue = new core_kernel_classes_Resource($this->getRequestParameter('delivery'));
} else {
$launchData = LtiService::singleton()->getLtiSession()->getLaunchData();
/** @var LtiLaunchDataService $launchDataService */
$launchDataService = $this->getServiceLocator()->get(LtiLaunchDataService::SERVICE_ID);
$returnValue = $launchDataService->findDeliveryFromLaunchData($launchData);
}
return $returnValue;
}
private function configureI18n(): void
{
$extension = $this->getServiceLocator()
->get(common_ext_ExtensionsManager::SERVICE_ID)
->getExtensionById('ltiDeliveryProvider');
tao_helpers_I18n::init($extension, DEFAULT_ANONYMOUS_INTERFACE_LANG);
}
}