Merge pull request #4369 from christianbeeznest/MJT-20093-2

Lti provider - Add learnpath as tool provider - refs BT#20093
pull/4380/head
Nicolas Ducoulombier 3 years ago committed by GitHub
commit e367a6df78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      composer.json
  2. 9
      main/admin/statistics/index.php
  3. 6
      main/exercise/exercise_result.php
  4. 1
      main/inc/lib/api.lib.php
  5. 6
      main/inc/lib/formvalidator/FormValidator.class.php
  6. 6
      main/inc/lib/javascript/bigupload/inc/bigUpload.php
  7. 93
      main/inc/lib/statistics.lib.php
  8. 5
      main/lp/lp_ajax_initialize.php
  9. 5
      main/lp/lp_ajax_save_item.php
  10. 5
      main/lp/lp_controller.php
  11. 9
      main/lp/lp_view.php
  12. 9
      main/lp/scorm_api.php
  13. 267
      plugin/lti_provider/Entity/Result.php
  14. 228
      plugin/lti_provider/LtiProviderPlugin.php
  15. 17
      plugin/lti_provider/README.md
  16. 41
      plugin/lti_provider/db/lti13_database.php
  17. 4
      plugin/lti_provider/provider_settings.php
  18. 15
      plugin/lti_provider/src/Form/FrmAdd.php
  19. 24
      plugin/lti_provider/src/Form/FrmEdit.php
  20. 48
      plugin/lti_provider/src/LtiProvider.php
  21. 88
      plugin/lti_provider/tool/api/score.php
  22. 27
      plugin/lti_provider/tool/start.php
  23. 14
      plugin/lti_provider/view/add.tpl
  24. 2
      plugin/lti_provider/view/admin.tpl

@ -83,7 +83,6 @@
"mpdf/mpdf": "^8.0",
"ocramius/proxy-manager": "~1.0|2.0.*",
"onelogin/php-saml": "^3.0",
"packbackbooks/lti-1p3-tool": "^1.1",
"paragonie/random-lib": "2.0.0",
"patchwork/utf8": "~1.2",
"php-ffmpeg/php-ffmpeg": "0.5.1",
@ -121,6 +120,7 @@
"twig/twig": "1.*",
"webit/eval-math": "1.0.1",
"yuloh/bccomp-polyfill": "dev-master",
"packbackbooks/lti-1p3-tool": "1.1.1.x-dev",
"zendframework/zend-config": "~3.0",
"zendframework/zend-feed": "~2.6|^3.0",
"zendframework/zend-http": "~2.6|^3.0",
@ -185,6 +185,11 @@
"type": "github",
"url": "https://github.com/AngelFQC/xapi-symfony-serializer.git",
"no-api": true
},
{
"type": "github",
"url": "https://github.com/chamilo/lti-1-3-php-library.git",
"no-api": true
}
],
"config": {

@ -372,6 +372,10 @@ $tools = [
],
];
if ('true' === api_get_plugin_setting('lti_provider', 'enabled')) {
$tools[get_lang('Users')]['report=lti_tool_lp'] = get_lang('LearningPathLTI');
}
$course_categories = Statistics::getCourseCategories();
$content = '';
@ -1015,7 +1019,7 @@ switch ($report) {
//$contract = isset($extraFields['termactivated']) ? $extraFields['termactivated'] : '';
$contract = false;
$legalAccept = $extraFieldValueUser->get_values_by_handler_and_field_variable($userId, 'legal_accept');
if ($legalAccept && isset($legalAccept['value'])) {
if ($legalAccept && !empty($legalAccept['value'])) {
list($legalId, $legalLanguageId, $legalTime) = explode(':', $legalAccept['value']);
if ($legalId) {
$contract = true;
@ -1700,6 +1704,9 @@ switch ($report) {
case 'logins_by_date':
$content .= Statistics::printLoginsByDate();
break;
case 'lti_tool_lp':
$content .= Statistics::printLtiLearningPath();
break;
}
Display::display_header($tool_name);

@ -341,7 +341,9 @@ if (!in_array($origin, ['learnpath', 'embeddable', 'mobileapp', 'iframe'])) {
// Record the results in the learning path, using the SCORM interface (API)
$pageBottom .= "<script>window.parent.API.void_save_asset('$total_score', '$max_score', 0, 'completed');</script>";
$pageBottom .= '<script type="text/javascript">'.$href.'</script>';
if (empty($_SESSION['oLP']->lti_launch_id)) {
$pageBottom .= '<script type="text/javascript">'.$href.'</script>';
}
$showFooter = false;
}
@ -384,7 +386,7 @@ function showEmbeddableFinishButton()
global $exeId;
$js .= '<script>
$(function () {
var url = "'.api_get_path(WEB_PLUGIN_PATH).'lti_provider/tool/api/score.php?'.api_get_cidreq().'&launch_id='.$ltiLaunchId.'&exeId='.$exeId.'";
var url = "'.api_get_path(WEB_PLUGIN_PATH).'lti_provider/tool/api/score.php?'.api_get_cidreq().'&lti_tool=quiz&launch_id='.$ltiLaunchId.'&lti_result_id='.$exeId.'";
$.get(url);
});
</script>';

@ -171,6 +171,7 @@ define('SECTION_CUSTOMPAGE', 'custompage');
define('PLATFORM_AUTH_SOURCE', 'platform');
define('CAS_AUTH_SOURCE', 'cas');
define('LDAP_AUTH_SOURCE', 'extldap');
define('IMS_LTI_SOURCE', 'ims_lti');
// CONSTANT defining the default HotPotatoes files directory
define('DIR_HOTPOTATOES', '/HotPotatoes_files');

@ -1811,6 +1811,10 @@ EOT;
*/
private function addMultipleUploadJavascript($url, $inputName, $urlToRedirect = '')
{
$target = '_blank';
if (!empty($_SESSION['oLP']->lti_launch_id)) {
$target = '_self';
}
$redirectCondition = '';
if (!empty($urlToRedirect)) {
$redirectCondition = "window.location.replace('$urlToRedirect'); ";
@ -1927,7 +1931,7 @@ EOT;
}
if (file.url) {
var link = $('<a>')
.attr({target: '_blank', class : 'panel-image'})
.attr({target: '".$target."', class : 'panel-image'})
.prop('href', file.url);
$(data.context.children()[index]).parent().wrap(link);
}

@ -250,7 +250,11 @@ class BigUploadResponse
$file,
api_get_configuration_value('assignment_prevent_duplicate_upload')
);
$redirectUrl = api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq();
$extraParams = '';
if (!empty($_SESSION['oLP'])) {
$extraParams .= '&origin=learnpath';
}
$redirectUrl = api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().$extraParams;
return json_encode(['errorStatus' => 0, 'redirect' => $redirectUrl]);
}

@ -1428,6 +1428,71 @@ class Statistics
return ['chart' => $list, 'table' => $table];
}
/**
* It displays learnpath results from lti provider.
*
* @return false|string
*/
public static function printLtiLearningPath()
{
$pluginLtiProvider = ('true' === api_get_plugin_setting('lti_provider', 'enabled'));
if (!$pluginLtiProvider) {
return false;
}
$content = Display::page_header(get_lang('LearningPathLTI'));
$actions = '';
$form = new FormValidator('frm_lti_tool_lp', 'get');
$form->addDateRangePicker(
'daterange',
get_lang('DateRange'),
true,
['format' => 'YYYY-MM-DD', 'timePicker' => 'false', 'validate_format' => 'Y-m-d']
);
$form->addHidden('report', 'lti_tool_lp');
$form->addButtonFilter(get_lang('Search'));
if ($form->validate()) {
$values = $form->exportValues();
$result = self::getLtiLeaningPathByDate($values['daterange_start'], $values['daterange_end']);
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$table->setHeaderContents(0, 0, get_lang('URL'));
$table->setHeaderContents(0, 1, get_lang('Users'));
$i = 1;
foreach ($result as $item) {
$table->setCellContents($i, 0, $item['issuer']);
$table->setCellContents($i, 1, $item['count_iss_users']);
if (!empty($item['learnpaths'])) {
foreach ($item['learnpaths'] as $lpId => $lpValues) {
$i++;
$lpName = learnpath::getLpNameById($lpId);
$table->setHeaderContents($i, 0, '&dash;&dash;&nbsp;'.get_lang('ToolLp').': '.$lpName);
$table->setHeaderContents($i, 1, count($lpValues['users']));
if (count($lpValues['users']) > 0) {
foreach ($lpValues['users'] as $user) {
$i++;
$fullname = $user['firstname'].' '.$user['lastname'];
$table->setCellContents($i, 0, '&nbsp;&nbsp;&nbsp;'.$fullname);
}
}
}
}
$i++;
}
$content = $table->toHtml();
}
$content .= $form->returnForm();
if (!empty($actions)) {
$content .= Display::toolbarAction('lti_tool_lp_toolbar', [$actions]);
}
return $content;
}
/**
* Display the Logins By Date report and allow export its result to XLS.
*/
@ -1544,6 +1609,34 @@ class Statistics
return '<table id="table_'.$bossId.'"></table>';
}
/**
* It gets lti learnpath results by date.
*
* @param $startDate
* @param $endDate
*
* @return array
*/
private static function getLtiLeaningPathByDate($startDate, $endDate)
{
/** @var DateTime $startDate */
$startDate = api_get_utc_datetime("$startDate 00:00:00");
/** @var DateTime $endDate */
$endDate = api_get_utc_datetime("$endDate 23:59:59");
if (empty($startDate) || empty($endDate)) {
return [];
}
require_once api_get_path(SYS_PLUGIN_PATH).'lti_provider/LtiProviderPlugin.php';
$plugin = LtiProviderPlugin::create();
$result = $plugin->getToolLearnPathResult($startDate, $endDate);
return $result;
}
/**
* @param string $startDate
* @param string $endDate

@ -176,6 +176,11 @@ function initialize_item($lp_id, $user_id, $view_id, $next_item)
error_log("mylp->lp_view_session_id: ".$mylp->lp_view_session_id);
}
if (isset($mylp->lti_launch_id)) {
$ltiLaunchId = $mylp->lti_launch_id;
$return .= "sendLtiLaunch('$ltiLaunchId', '$lp_id');";
}
return $return;
}

@ -538,6 +538,11 @@ function save_item(
if (!$progressBarSpecial) {
$return .= "update_progress_bar('$myComplete', '$myTotal', '$myProgressMode');";
}
if (isset($myLP->lti_launch_id)) {
$ltiLaunchId = $myLP->lti_launch_id;
$return .= "sendLtiLaunch('$ltiLaunchId', '$lp_id');";
}
}
if (!Session::read('login_as')) {

@ -428,6 +428,11 @@ $is_allowed_to_edit = api_is_allowed_to_edit(false, true, false, false);
if (isset($_SESSION['oLP'])) {
// Reinitialises array used by javascript to update items in the TOC.
$_SESSION['oLP']->update_queue = [];
// We check if a tool provider
if (isset($_REQUEST['lti_launch_id'])) {
$ltiLaunchId = Security::remove_XSS($_REQUEST['lti_launch_id']);
$_SESSION['oLP']->lti_launch_id = $ltiLaunchId;
}
}
$action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';

@ -130,6 +130,15 @@ $(function() {
});
var chamilo_xajax_handler = window.oxajax;
</script>';
if (!empty($lp->lti_launch_id)) {
$htmlHeadXtra[] = '<script>
$(function() {
if ($("#btn-menu-float").length > 0) {
$("#btn-menu-float").find("#home-course").hide();
}
});
</script>';
}
$zoomOptions = api_get_configuration_value('quiz_image_zoom');
if (isset($zoomOptions['options']) && !in_array($origin, ['embeddable', 'noheader'])) {

@ -2626,3 +2626,12 @@ function save_suspend_data_in_local()
}
}
/**
* It launchs results for lti provider
*/
function sendLtiLaunch(ltiLaunchId, lpId)
{
var url = "<?php echo api_get_path(WEB_PLUGIN_PATH).'lti_provider/tool/api/score.php?'.api_get_cidreq(); ?>&lti_tool=lp&launch_id="+ltiLaunchId+"&lti_result_id="+lpId;
$.get(url);
}

@ -0,0 +1,267 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Entity\LtiProvider;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Result.
*
* @ORM\Table(name="plugin_lti_provider_result")
* @ORM\Entity()
*/
class Result
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id()
* @ORM\GeneratedValue()
*/
protected $id;
/**
* @var string
*
* @ORM\Column(name="issuer", type="text")
*/
protected $issuer;
/**
* @var int
*
* @ORM\Column(name="user_id", type="integer", nullable=false)
*/
protected $userId;
/**
* @var int
*
* @ORM\Column(name="client_uid", type="integer", nullable=false)
*/
protected $clientUId;
/**
* @var string
*
* @ORM\Column(name="course_code", type="string", length=40, nullable=true)
*/
protected $courseCode;
/**
* @var int
*
* @ORM\Column(name="tool_id", type="integer", nullable=false)
*/
protected $toolId;
/**
* @var string
*
* @ORM\Column(name="tool_name", type="string")
*/
protected $toolName;
/**
* @var float
*
* @ORM\Column(name="score", type="float", precision=6, scale=2, nullable=false)
*/
protected $score;
/**
* @var int
*
* @ORM\Column(name="progress", type="integer", nullable=false)
*/
protected $progress;
/**
* @var int
*
* @ORM\Column(name="duration", type="integer", nullable=false)
*/
protected $duration;
/**
* @var \DateTime
*
* @ORM\Column(name="start_date", type="datetime", nullable=false)
*/
protected $startDate;
/**
* @var string
*
* @ORM\Column(name="user_ip", type="string")
*/
protected $userIp;
/**
* @var string
*
* @ORM\Column(name="lti_launch_id", type="string")
*/
protected $ltiLaunchId;
public function getId(): int
{
return $this->id;
}
public function setId(int $id): Result
{
$this->id = $id;
return $this;
}
public function getIssuer(): string
{
return $this->issuer;
}
public function setIssuer(string $issuer): Result
{
$this->issuer = $issuer;
return $this;
}
public function getUserId(): int
{
return $this->userId;
}
public function setUserId(int $userId): Result
{
$this->userId = $userId;
return $this;
}
public function getClientUId(): int
{
return $this->clientUId;
}
public function setClientUId(int $clientUId): Result
{
$this->clientUId = $clientUId;
return $this;
}
public function getCourseCode(): string
{
return $this->courseCode;
}
/**
* @param string $tool
*/
public function setCourseCode(string $courseCode): Result
{
$this->courseCode = $courseCode;
return $this;
}
public function getToolId(): int
{
return $this->toolId;
}
public function setToolId(int $toolId): Result
{
$this->toolId = $toolId;
return $this;
}
public function getToolName(): string
{
return $this->toolName;
}
public function setToolName(string $toolName): Result
{
$this->toolName = $toolName;
return $this;
}
public function getScore(): float
{
return $this->score;
}
public function setScore(float $score): Result
{
$this->score = $score;
return $this;
}
public function getProgress(): int
{
return $this->progress;
}
public function setProgress(int $progress): Result
{
$this->progress = $progress;
return $this;
}
public function getDuration(): int
{
return $this->duration;
}
public function setDuration(int $duration): Result
{
$this->duration = $duration;
return $this;
}
public function getStartDate(): \DateTime
{
return $this->startDate;
}
public function setStartDate(\DateTime $startDate): Result
{
$this->startDate = $startDate;
return $this;
}
public function getUserIp(): string
{
return $this->userIp;
}
public function setUserIp(string $userIp): Result
{
$this->userIp = $userIp;
return $this;
}
public function getLtiLaunchId(): string
{
return $this->ltiLaunchId;
}
public function setLtiLaunchId(string $ltiLaunchId): Result
{
$this->ltiLaunchId = $ltiLaunchId;
return $this;
}
}

@ -3,6 +3,7 @@
use Chamilo\PluginBundle\Entity\LtiProvider\Platform;
use Chamilo\PluginBundle\Entity\LtiProvider\PlatformKey;
use Chamilo\PluginBundle\Entity\LtiProvider\Result;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\Tools\SchemaTool;
@ -14,6 +15,9 @@ use Doctrine\ORM\Tools\SchemaTool;
class LtiProviderPlugin extends Plugin
{
public const TABLE_PLATFORM = 'plugin_lti_provider_platform';
public const LAUNCH_PATH = 'lti_provider/tool/start.php';
public const LOGIN_PATH = 'lti_provider/tool/login.php';
public const REDIRECT_PATH = 'lti_provider/tool/start.php';
public $isAdminPlugin = true;
@ -24,48 +28,87 @@ class LtiProviderPlugin extends Plugin
$message = Display::return_message($this->get_lang('Description'));
$launchUrlHtml = '';
$loginUrlHtml = '';
$redirectUrlHtml = '';
if ($this->areTablesCreated()) {
$publicKey = $this->getPublicKey();
$pkHtml = '<div class="form-group">
<label for="lti_provider_public_key" class="col-sm-2 control-label">'
.$this->get_lang('PublicKey').'</label>
<div class="col-sm-8">
<pre>'.$publicKey.'</pre>
</div>
<div class="col-sm-2"></div>
</div>';
$pkHtml = $this->getSettingHtmlReadOnly(
$this->get_lang('PublicKey'),
'public_key',
$publicKey
);
$launchUrlHtml = $this->getSettingHtmlReadOnly(
$this->get_lang('LaunchUrl'),
'launch_url',
api_get_path(WEB_PLUGIN_PATH).self::LAUNCH_PATH
);
$loginUrlHtml = $this->getSettingHtmlReadOnly(
$this->get_lang('LoginUrl'),
'login_url',
api_get_path(WEB_PLUGIN_PATH).self::LOGIN_PATH
);
$redirectUrlHtml = $this->getSettingHtmlReadOnly(
$this->get_lang('RedirectUrl'),
'redirect_url',
api_get_path(WEB_PLUGIN_PATH).self::REDIRECT_PATH
);
} else {
$pkHtml = $this->get_lang('GenerateKeyPairInfo');
}
$settings = [
$message => 'html',
'name' => 'text',
'launch_url' => 'text',
'login_url' => 'text',
'redirect_url' => 'text',
'name' => 'hidden',
$launchUrlHtml => 'html',
$loginUrlHtml => 'html',
$redirectUrlHtml => 'html',
$pkHtml => 'html',
'enabled' => 'boolean',
];
parent::__construct($version, $author, $settings);
}
/**
* Get the value by default and readonly for the configuration html form.
*
* @param $label
* @param $id
* @param $value
*
* @return string
*/
public function getSettingHtmlReadOnly($label, $id, $value)
{
$html = '<div class="form-group">
<label for="lti_provider_'.$id.'" class="col-sm-2 control-label">'
.$label.'</label>
<div class="col-sm-8">
<pre>'.$value.'</pre>
</div>
<div class="col-sm-2"></div>
<input type="hidden" name="'.$id.'" value="'.$value.'" />
</div>';
return $html;
}
/**
* Get a selectbox with quizzes in courses , used for a tool provider.
*
* @param null $issuer
* @param null $clientId
*
* @return string
*/
public function getQuizzesSelect($issuer = null)
public function getQuizzesSelect($clientId = null)
{
$courses = CourseManager::get_courses_list();
$toolProvider = $this->getToolProvider($issuer);
$htmlcontent = '<div class="form-group">
$toolProvider = $this->getToolProvider($clientId);
$htmlcontent = '<div class="form-group select-tool" id="select-quiz">
<label for="lti_provider_create_platform_kid" class="col-sm-2 control-label">'.$this->get_lang('ToolProvider').'</label>
<div class="col-sm-8">
<select name="tool_provider">';
<select name="tool_provider" class="sbox-tool" id="sbox-tool-quiz" disabled="disabled">';
$htmlcontent .= '<option value="">-- '.$this->get_lang('SelectOneActivity').' --</option>';
foreach ($courses as $course) {
$courseInfo = api_get_course_info($course['code']);
@ -91,6 +134,47 @@ class LtiProviderPlugin extends Plugin
return $htmlcontent;
}
/**
* Get a selectbox with quizzes in courses , used for a tool provider.
*
* @param null $clientId
*
* @return string
*/
public function getLearnPathsSelect($clientId = null)
{
$courses = CourseManager::get_courses_list();
$toolProvider = $this->getToolProvider($clientId);
$htmlcontent = '<div class="form-group select-tool" id="select-lp" style="display:none">
<label for="lti_provider_create_platform_kid" class="col-sm-2 control-label">'.$this->get_lang('ToolProvider').'</label>
<div class="col-sm-8">
<select name="tool_provider" class="sbox-tool" id="sbox-tool-lp" disabled="disabled">';
$htmlcontent .= '<option value="">-- '.$this->get_lang('SelectOneActivity').' --</option>';
foreach ($courses as $course) {
$courseInfo = api_get_course_info($course['code']);
$optgroupLabel = "{$course['title']} : ".get_lang('Learnpath');
$htmlcontent .= '<optgroup label="'.$optgroupLabel.'">';
$list = new LearnpathList(
api_get_user_id(),
$courseInfo
);
$flatList = $list->get_flat_list();
foreach ($flatList as $id => $details) {
$selectValue = "{$course['code']}@@lp-{$id}";
$htmlcontent .= '<option value="'.$selectValue.'" '.($toolProvider == $selectValue ? ' selected="selected"' : '').'>'.Security::remove_XSS($details['lp_name']).'</option>';
}
$htmlcontent .= '</optgroup>';
}
$htmlcontent .= "</select>";
$htmlcontent .= ' </div>
<div class="col-sm-2"></div>
</div>';
return $htmlcontent;
}
/**
* Get the public key.
*/
@ -108,15 +192,65 @@ class LtiProviderPlugin extends Plugin
return $publicKey;
}
public function getToolLearnPathResult($startDate, $endDate)
{
$dql = "SELECT
a.issuer,
count(DISTINCT(a.userId)) as cnt
FROM
ChamiloPluginBundle:LtiProvider\Result a
WHERE
a.toolName = 'lp' AND
a.startDate BETWEEN '$startDate' AND '$endDate'
GROUP BY a.issuer";
$qb = Database::getManager()->createQuery($dql);
$issuersValues = $qb->getResult();
$result = [];
if (!empty($issuersValues)) {
foreach ($issuersValues as $issuerValue) {
$issuer = $issuerValue['issuer'];
$dqlLp = "SELECT
a.toolId,
a.userId
FROM
ChamiloPluginBundle:LtiProvider\Result a
WHERE
a.toolName = 'lp' AND
a.startDate BETWEEN '$startDate' AND '$endDate' AND
a.issuer = '".$issuer."'
GROUP BY a.toolId, a.userId";
$qbLp = Database::getManager()->createQuery($dqlLp);
$lpValues = $qbLp->getResult();
$lps = [];
foreach ($lpValues as $lp) {
$uinfo = api_get_user_info($lp['userId']);
$lps[$lp['toolId']]['users'][$lp['userId']] = [
'firstname' => $uinfo['firstname'],
'lastname' => $uinfo['lastname'],
];
}
$result[] = [
'issuer' => $issuer,
'count_iss_users' => $issuerValue['cnt'],
'learnpaths' => $lps,
];
}
}
return $result;
}
/**
* Get the tool provider.
*/
public function getToolProvider($issuer): string
public function getToolProvider($clientId): string
{
$toolProvider = '';
$platform = Database::getManager()
->getRepository('ChamiloPluginBundle:LtiProvider\Platform')
->findOneBy(['issuer' => $issuer]);
->findOneBy(['clientId' => $clientId]);
if ($platform) {
$toolProvider = $platform->getToolProvider();
@ -125,9 +259,9 @@ class LtiProviderPlugin extends Plugin
return $toolProvider;
}
public function getToolProviderVars($issuer): array
public function getToolProviderVars($clientId): array
{
$toolProvider = $this->getToolProvider($issuer);
$toolProvider = $this->getToolProvider($clientId);
list($courseCode, $tool) = explode('@@', $toolProvider);
list($toolName, $toolId) = explode('-', $tool);
$vars = ['courseCode' => $courseCode, 'toolName' => $toolName, 'toolId' => $toolId];
@ -172,7 +306,7 @@ class LtiProviderPlugin extends Plugin
{
$em = Database::getManager();
if ($em->getConnection()->getSchemaManager()->tablesExist(['sfu_post'])) {
if ($em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_PLATFORM])) {
return;
}
@ -181,6 +315,7 @@ class LtiProviderPlugin extends Plugin
[
$em->getClassMetadata(Platform::class),
$em->getClassMetadata(PlatformKey::class),
$em->getClassMetadata(Result::class),
]
);
}
@ -234,7 +369,7 @@ class LtiProviderPlugin extends Plugin
{
$em = Database::getManager();
if (!$em->getConnection()->getSchemaManager()->tablesExist(['sfu_post'])) {
if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_PLATFORM])) {
return;
}
@ -243,6 +378,7 @@ class LtiProviderPlugin extends Plugin
[
$em->getClassMetadata(Platform::class),
$em->getClassMetadata(PlatformKey::class),
$em->getClassMetadata(Result::class),
]
);
}
@ -255,6 +391,52 @@ class LtiProviderPlugin extends Plugin
}
}
public function saveResult($values, $ltiLaunchId = null)
{
$em = Database::getManager();
if (!empty($ltiLaunchId)) {
$repo = $em->getRepository(Result::class);
/** @var Result $objResult */
$objResult = $repo->findOneBy(
[
'ltiLaunchId' => $ltiLaunchId,
]
);
if ($objResult) {
$objResult->setScore($values['score']);
$objResult->setProgress($values['progress']);
$objResult->setDuration($values['duration']);
$em->persist($objResult);
$em->flush();
return $objResult->getId();
}
} else {
$objResult = new Result();
$objResult
->setIssuer($values['issuer'])
->setUserId($values['user_id'])
->setClientUId($values['client_uid'])
->setCourseCode($values['course_code'])
->setToolId($values['tool_id'])
->setToolName($values['tool_name'])
->setScore(0)
->setProgress(0)
->setDuration(0)
->setStartDate(new DateTime())
->setUserIp(api_get_real_ip())
->setLtiLaunchId($values['lti_launch_id'])
;
$em->persist($objResult);
$em->flush();
return $objResult->getId();
}
return false;
}
private function areTablesCreated(): bool
{
$entityManager = Database::getManager();

@ -47,4 +47,21 @@ CREATE TABLE plugin_lti_provider_platform_key (
private_key LONGTEXT NOT NULL,
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB;
CREATE TABLE plugin_lti_provider_result (
id int(11) NOT NULL AUTO_INCREMENT,
issuer longtext NOT NULL,
user_id int(11) NOT NULL,
client_uid int(11) NOT NULL,
course_code varchar(40) NOT NULL,
tool_id int(11) NOT NULL,
tool_name varchar(255) NOT NULL,
score double NOT NULL,
progress int(11) NOT NULL,
duration int(11) NOT NULL,
start_date datetime NOT NULL,
user_ip varchar(255) NOT NULL,
lti_launch_id varchar(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
```

@ -10,21 +10,21 @@ class Lti13Database implements Interfaces\Database
{
public function findRegistrationByIssuer($iss, $clientId = null)
{
$ltiCustomers = $this->getLtiConnection();
if (empty($ltiCustomers[$iss])) {
return false;
if (!isset($clientId)) {
$clientId = $this->getClientIdByIssuer($iss);
}
if (!isset($clientId)) {
$clientId = $ltiCustomers[$iss]['client_id'];
$ltiCustomers = $this->getLtiConnection();
if (empty($ltiCustomers[$clientId])) {
return false;
}
return LtiRegistration::new()
->setAuthLoginUrl($ltiCustomers[$iss]['auth_login_url'])
->setAuthTokenUrl($ltiCustomers[$iss]['auth_token_url'])
->setAuthLoginUrl($ltiCustomers[$clientId]['auth_login_url'])
->setAuthTokenUrl($ltiCustomers[$clientId]['auth_token_url'])
->setClientId($clientId)
->setKeySetUrl($ltiCustomers[$iss]['key_set_url'])
->setKid($ltiCustomers[$iss]['kid'])
->setKeySetUrl($ltiCustomers[$clientId]['key_set_url'])
->setKid($ltiCustomers[$clientId]['kid'])
->setIssuer($iss)
->setToolPrivateKey($this->getPrivateKey());
}
@ -32,7 +32,7 @@ class Lti13Database implements Interfaces\Database
public function findDeployment($iss, $deploymentId, $clientId = null)
{
$issSession = Session::read('iss');
if (!in_array($deploymentId, $issSession[$iss]['deployment'])) {
if (!in_array($deploymentId, $issSession[$clientId]['deployment'])) {
return false;
}
@ -46,9 +46,10 @@ class Lti13Database implements Interfaces\Database
$ltiCustomers = [];
foreach ($platforms as $platform) {
$issuer = $platform->getIssuer();
$ltiCustomers[$issuer] = [
'client_id' => $platform->getClientId(),
$clientId = $platform->getClientId();
$ltiCustomers[$clientId] = [
'client_id' => $clientId,
'issuer' => $platform->getIssuer(),
'auth_login_url' => $platform->getAuthLoginUrl(),
'auth_token_url' => $platform->getAuthTokenUrl(),
'key_set_url' => $platform->getKeySetUrl(),
@ -61,6 +62,20 @@ class Lti13Database implements Interfaces\Database
return $ltiCustomers;
}
private function getClientIdByIssuer($issuer)
{
$clientId = '';
$platform = Database::getManager()
->getRepository('ChamiloPluginBundle:LtiProvider\Platform')
->findOneBy(['issuer' => $issuer]);
if ($platform) {
$clientId = $platform->getClientId();
}
return $clientId;
}
private function getPrivateKey()
{
$privateKey = '';

@ -32,10 +32,6 @@ try {
}
$html = '<div class="row">'
.'<div class="col-xs-2 text-right"><strong>'.$plugin->get_lang('Name').'</strong></div>'
.'<div class="col-xs-10">'.$name.'</div>'
.'</div>'
.'<div class="row">'
.'<div class="col-xs-2 text-right"><strong>'.$plugin->get_lang('LaunchUrl').'</strong></div>'
.'<div class="col-xs-10">'.$launchUrl.'</div>'
.'</div>'

@ -46,6 +46,20 @@ class FrmAdd extends FormValidator
$this->addText('client_id', $plugin->get_lang('ClientId'));
$this->addText('deployment_id', $plugin->get_lang('DeploymentId'));
$this->addText('kid', $plugin->get_lang('KeyId'), false);
$this->addRadio(
'tool_type',
get_lang('ToolProvider'),
[
'quiz' => $plugin->get_lang('Quizzes'),
'lp' => $plugin->get_lang('Learnpaths'),
],
[
'onclick' => 'selectToolProvider(this.value)',
]
);
$this->addElement('html', $plugin->getLearnPathsSelect());
$this->addElement('html', $plugin->getQuizzesSelect());
$this->addButtonCreate($plugin->get_lang('AddPlatform'));
@ -67,6 +81,7 @@ class FrmAdd extends FormValidator
$defaults['client_id'] = $this->platform->getClientId();
$defaults['deployment_id'] = $this->platform->getDeploymentId();
$defaults['kid'] = $this->platform->getKid();
$defaults['tool_type'] = 'quiz';
$this->setDefaults($defaults);
}

@ -46,7 +46,21 @@ class FrmEdit extends FormValidator
$this->addText('client_id', $plugin->get_lang('ClientId'));
$this->addText('deployment_id', $plugin->get_lang('DeploymentId'));
$this->addText('kid', $plugin->get_lang('KeyId'), false);
$this->addElement('html', $plugin->getQuizzesSelect($this->platform->getIssuer()));
$this->addRadio(
'tool_type',
get_lang('ToolProvider'),
[
'quiz' => $plugin->get_lang('Quizzes'),
'lp' => $plugin->get_lang('Learnpaths'),
],
[
'onclick' => 'selectToolProvider(this.value)',
]
);
$this->addElement('html', $plugin->getLearnPathsSelect($this->platform->getClientId()));
$this->addElement('html', $plugin->getQuizzesSelect($this->platform->getClientId()));
$this->addButtonCreate($plugin->get_lang('EditPlatform'));
$this->addHidden('id', $this->platform->getId());
@ -67,7 +81,13 @@ class FrmEdit extends FormValidator
$defaults['client_id'] = $this->platform->getClientId();
$defaults['deployment_id'] = $this->platform->getDeploymentId();
$defaults['kid'] = $this->platform->getKid();
$defaults['tool_provider'] = $this->platform->getToolProvider();
$toolProvider = $this->platform->getToolProvider();
list($courseCode, $tool) = explode('@@', $toolProvider);
list($toolName, $toolId) = explode('-', $tool);
$defaults['tool_type'] = $toolName;
$defaults['tool_provider'] = $toolProvider;
$this->setDefaults($defaults);
}

@ -1,6 +1,7 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
use Packback\Lti1p3;
use Packback\Lti1p3\LtiMessageLaunch;
use Packback\Lti1p3\LtiOidcLogin;
@ -41,6 +42,29 @@ class LtiProvider
->doRedirect();
}
/**
* It removes user and oLP session.
*
* @param string $toolName
*/
public function logout(string $toolName = '')
{
Session::erase('_user');
Session::erase('is_platformAdmin');
Session::erase('is_allowedCreateCourse');
Session::erase('_uid');
if ('lp' == $toolName) {
// Deleting the objects
Session::erase('oLP');
Session::erase('lpobject');
Session::erase('scorm_view_id');
Session::erase('scorm_item_id');
Session::erase('exerciseResult');
Session::erase('objExercise');
Session::erase('questionList');
}
}
/**
* Lti Message Launch.
*/
@ -58,7 +82,7 @@ class LtiProvider
/**
* Verify if email user is in the platform to create it and login (true) or not (false).
*/
public function validateUser(array $launchData, string $courseCode): bool
public function validateUser(array $launchData, string $courseCode, string $toolName): bool
{
if (empty($launchData)) {
return false;
@ -73,6 +97,12 @@ class LtiProvider
if (empty($userInfo)) {
// We create the user
$username = $launchData['https://purl.imsglobal.org/spec/lti/claim/ext']['user_username'];
if (!UserManager::is_username_available($username)) {
$username = UserManager::create_unique_username(
$firstName,
$lastName
);
}
$password = api_generate_password();
$userId = UserManager::create_user(
$firstName,
@ -80,7 +110,12 @@ class LtiProvider
$status,
$email,
$username,
$password
$password,
'',
'',
'',
'',
IMS_LTI_SOURCE
);
} else {
$userId = $userInfo['user_id'];
@ -90,7 +125,16 @@ class LtiProvider
CourseManager::subscribeUser($userId, $courseCode, $status);
}
$this->logout($toolName);
$login = UserManager::loginAsUser($userId, false);
if ($login && CourseManager::is_user_subscribed_in_course($userId, $courseCode)) {
$_course = api_get_course_info($courseCode);
Session::write('is_allowed_in_course', true);
Session::write('_real_cid', $_course['real_id']);
Session::write('_cid', $_course['code']);
Session::write('_course', $_course);
}
return $login;
}

@ -10,50 +10,52 @@ if (!$launch->hasAgs()) {
throw new Exception("Don't have grades!");
}
if (!isset($_REQUEST['exeId'])) {
throw new Exception("Any Exercise result");
if (!isset($_REQUEST['lti_result_id'])) {
throw new Exception("Any tool result");
}
$launchData = $launch->getLaunchData();
$exeId = (int) $_REQUEST['exeId'];
$trackInfo = Exercise::get_stat_track_exercise_info_by_exe_id($exeId);
$score = $trackInfo['exe_result'];
$weight = $trackInfo['exe_weighting'];
$duration = $trackInfo['duration'];
$timestamp = date(DateTime::ISO8601);
$grades = $launch->getAgs();
$score = Packback\Lti1p3\LtiGrade::new()
->setScoreGiven($score)
->setScoreMaximum($weight)
->setTimestamp($timestamp)
->setActivityProgress('Completed')
->setGradingProgress('FullyGraded')
->setUserId($launch->getLaunchData()['sub']);
$scoreLineitem = Packback\Lti1p3\LtiLineitem::new()
->setTag('score')
->setScoreMaximum($weight)
->setLabel('Score')
->setResourceId($launch->getLaunchData()['https://purl.imsglobal.org/spec/lti/claim/resource_link']['id']);
$grades->putGrade($score, $scoreLineitem);
$time = Packback\Lti1p3\LtiGrade::new()
->setScoreGiven($duration)
->setScoreMaximum(999)
->setTimestamp($timestamp)
->setActivityProgress('Completed')
->setGradingProgress('FullyGraded')
->setUserId($launch->getLaunchData()['sub']);
$timeLineitem = Packback\Lti1p3\LtiLineitem::new()
->setTag('time')
->setScoreMaximum(999)
->setLabel('Time Taken')
->setResourceId('time'.$launch->getLaunchData()['https://purl.imsglobal.org/spec/lti/claim/resource_link']['id']);
$grades->putGrade($time, $timeLineitem);
echo '{"success" : true}';
$courseCode = $_REQUEST['cidReq'];
$courseId = api_get_course_int_id($courseCode);
$toolName = $_REQUEST['lti_tool'];
if (in_array($toolName, ['quiz', 'lp'])) {
if ('quiz' == $toolName) {
$objExercise = new Exercise($courseId);
$trackInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($_REQUEST['lti_result_id']);
$score = $trackInfo['exe_result'];
$weight = $trackInfo['exe_weighting'];
$timestamp = date(DATE_ISO8601);
} else {
$lpId = (int) $_REQUEST['lti_result_id'];
$lpProgress = learnpath::getProgress(
$lpId,
api_get_user_id(),
api_get_course_int_id(),
api_get_session_id()
);
$score = $lpProgress;
$weight = 100;
$timestamp = date(DATE_ISO8601);
}
$grades = $launch->getAgs();
$scoreGrade = Packback\Lti1p3\LtiGrade::new()
->setScoreGiven($score)
->setScoreMaximum($weight)
->setTimestamp($timestamp)
->setActivityProgress('Completed')
->setGradingProgress('FullyGraded')
->setUserId($launchData['sub']);
$grades->putGrade($scoreGrade);
$plugin = LtiProviderPlugin::create();
$values = [];
$values['score'] = $score;
$values['progress'] = 0;
$values['duration'] = 0;
$plugin->saveResult($values, $_REQUEST['launch_id']);
echo '{"success" : true}';
}

@ -7,17 +7,34 @@ require_once __DIR__.'/../LtiProviderPlugin.php';
$launch = LtiProvider::create()->launch();
if (!$launch->hasNrps()) {
throw new Exception("Don't have names and roles!");
// throw new Exception("Don't have names and roles!");
}
$launchData = $launch->getLaunchData();
$plugin = LtiProviderPlugin::create();
$toolVars = $plugin->getToolProviderVars($launchData['iss']);
$toolVars = $plugin->getToolProviderVars($launchData['aud']);
$login = LtiProvider::create()->validateUser($launchData, $toolVars['courseCode'], $toolVars['toolName']);
if ($login) {
$values = [];
$values['issuer'] = $launchData['iss'];
$values['user_id'] = api_get_user_id();
$values['client_uid'] = $launchData['sub'];
$values['course_code'] = $toolVars['courseCode'];
$values['tool_id'] = $toolVars['toolId'];
$values['tool_name'] = $toolVars['toolName'];
$values['lti_launch_id'] = $launch->getLaunchId();
$plugin->saveResult($values);
}
$login = LtiProvider::create()->validateUser($launchData, $toolVars['courseCode']);
$cidReq = 'cidReq='.$toolVars['courseCode'].'&id_session=0&gidReq=0&gradebook=0';
$launchUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.$cidReq.'&origin=embeddable&exerciseId='.$toolVars['toolId'].'&lti_launch_id='.$launch->getLaunchId();
header('Location: '.$launchUrl);
if ('lp' == $toolVars['toolName']) {
$launchUrl = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.$cidReq.'&action=view&lp_id='.$toolVars['toolId'].'&isStudentView=true&lti_launch_id='.$launch->getLaunchId();
header('Location: '.$launchUrl);
} else {
$launchUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.$cidReq.'&origin=embeddable&exerciseId='.$toolVars['toolId'].'&lti_launch_id='.$launch->getLaunchId();
header('Location: '.$launchUrl);
}
exit;

@ -3,3 +3,17 @@
{{ form }}
</div>
</div>
<script>
$(function() {
if ($("input[name='tool_type']").length > 0) {
var toolType = $("input[name='tool_type']:checked").val();
selectToolProvider(toolType)
}
});
function selectToolProvider(tool) {
$(".sbox-tool").attr('disabled', 'disabled');
$(".select-tool").hide();
$("#select-"+tool).show();
$("#sbox-tool-"+tool).removeAttr('disabled');
}
</script>

@ -18,6 +18,7 @@
<th>{{ 'ClientId'|get_plugin_lang('LtiProviderPlugin') }}</th>
<th class="text-center">{{ 'DeploymentId'|get_plugin_lang('LtiProviderPlugin') }}</th>
<th class="text-center">{{ 'URLs'|get_plugin_lang('LtiProviderPlugin') }}</th>
<th class="text-center">{{ 'ToolProvider'|get_plugin_lang('LtiProviderPlugin') }}</th>
<th class="text-right">{{ 'Actions'|get_lang }}</th>
</tr>
</thead>
@ -33,6 +34,7 @@
<p><strong>{{ 'AuthTokenUrl'|get_plugin_lang('LtiProviderPlugin') }}:</strong><br> {{ platform.getAuthTokenUrl }}</p>
<p><strong>{{ 'KeySetUrl'|get_plugin_lang('LtiProviderPlugin') }}:</strong><br> {{ platform.getKeySetUrl }}</p>
</td>
<td>{{ platform.getToolProvider }}</td>
<td>
<a href="{{ _p.web_plugin }}lti_provider/edit.php?{{ url_params }}">
{{ 'edit.png'|img(22, 'Edit'|get_lang) }}

Loading…
Cancel
Save