296 lines
12 KiB
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);
|
||
|
}
|
||
|
}
|