241 lines
7.9 KiB
PHP
241 lines
7.9 KiB
PHP
<?php
|
|
/* For licensing terms, see /license.txt */
|
|
|
|
use Chamilo\CoreBundle\Entity\Course;
|
|
use Chamilo\CoreBundle\Entity\Session;
|
|
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
|
|
use Chamilo\UserBundle\Entity\User;
|
|
|
|
/**
|
|
* Class ImsLti.
|
|
*/
|
|
class ImsLti
|
|
{
|
|
const V_1P1 = 'lti1p1';
|
|
const V_1P3 = 'lti1p3';
|
|
const LTI_RSA_KEY = 'rsa_key';
|
|
const LTI_JWK_KEYSET = 'jwk_keyset';
|
|
|
|
/**
|
|
* @param Session|null $session Optional.
|
|
* @param string $domain Optional. Institution domain.
|
|
* @param string $ltiVersion Optional. Default is lti1p1.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getSubstitutableVariables(
|
|
User $user,
|
|
Course $course,
|
|
Session $session = null,
|
|
$domain = '',
|
|
$ltiVersion = self::V_1P1,
|
|
ImsLtiTool $tool
|
|
) {
|
|
$isLti1p3 = $ltiVersion === self::V_1P3;
|
|
|
|
return [
|
|
'$User.id' => $user->getId(),
|
|
'$User.image' => $isLti1p3 ? ['claim' => 'sub'] : ['user_image'],
|
|
'$User.username' => $user->getUsername(),
|
|
'$User.org' => false,
|
|
'$User.scope.mentor' => $isLti1p3 ? ['claim' => '/claim/role_scope_mentor'] : ['role_scope_mentor'],
|
|
|
|
'$Person.sourcedId' => $isLti1p3
|
|
? self::getPersonSourcedId($domain, $user)
|
|
: "$domain:".ImsLtiPlugin::getLaunchUserIdClaim($tool, $user),
|
|
'$Person.name.full' => $user->getFullname(),
|
|
'$Person.name.family' => $user->getLastname(),
|
|
'$Person.name.given' => $user->getFirstname(),
|
|
'$Person.address.street1' => $user->getAddress(),
|
|
'$Person.phone.primary' => $user->getPhone(),
|
|
'$Person.email.primary' => $user->getEmail(),
|
|
|
|
'$CourseSection.sourcedId' => $isLti1p3
|
|
? ['claim' => '/claim/lis', 'property' => 'course_section_sourcedid']
|
|
: ['lis_course_section_sourcedid'],
|
|
'$CourseSection.label' => $course->getCode(),
|
|
'$CourseSection.title' => $course->getTitle(),
|
|
'$CourseSection.longDescription' => $session && $session->getShowDescription()
|
|
? $session->getDescription()
|
|
: false,
|
|
'$CourseSection.timeFrame.begin' => $session && $session->getDisplayStartDate()
|
|
? $session->getDisplayStartDate()->format(DateTime::ATOM)
|
|
: '$CourseSection.timeFrame.begin',
|
|
'$CourseSection.timeFrame.end' => $session && $session->getDisplayEndDate()
|
|
? $session->getDisplayEndDate()->format(DateTime::ATOM)
|
|
: '$CourseSection.timeFrame.end',
|
|
|
|
'$Membership.role' => $isLti1p3 ? ['claim' => '/claim/roles'] : ['roles'],
|
|
|
|
'$Result.sourcedGUID' => $isLti1p3 ? ['claim' => 'sub'] : ['lis_result_sourcedid'],
|
|
'$Result.sourcedId' => $isLti1p3 ? ['claim' => 'sub'] : ['lis_result_sourcedid'],
|
|
|
|
'$ResourceLink.id' => $isLti1p3
|
|
? ['claim' => '/claim/resource_link', 'property' => 'id']
|
|
: ['resource_link_id'],
|
|
'$ResourceLink.title' => $isLti1p3
|
|
? ['claim' => '/claim/resource_link', 'property' => 'title']
|
|
: ['resource_link_title'],
|
|
'$ResourceLink.description' => $isLti1p3
|
|
? ['claim' => '/claim/resource_link', 'property' => 'description']
|
|
: ['resource_link_description'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array $launchParams All params for launch.
|
|
* @param array $customParams Custom params where search variables to substitute.
|
|
* @param Session|null $session Optional.
|
|
* @param string $domain Optional. Institution domain.
|
|
* @param string $ltiVersion Optional. Default is lti1p1.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function substituteVariablesInCustomParams(
|
|
array $launchParams,
|
|
array $customParams,
|
|
User $user,
|
|
Course $course,
|
|
Session $session = null,
|
|
$domain = '',
|
|
$ltiVersion = self::V_1P1,
|
|
ImsLtiTool $tool
|
|
) {
|
|
$substitutables = self::getSubstitutableVariables($user, $course, $session, $domain, $ltiVersion, $tool);
|
|
$variables = array_keys($substitutables);
|
|
|
|
foreach ($customParams as $customKey => $customValue) {
|
|
if (!in_array($customValue, $variables)) {
|
|
continue;
|
|
}
|
|
|
|
$substitute = $substitutables[$customValue];
|
|
|
|
if (is_array($substitute)) {
|
|
if ($ltiVersion === self::V_1P1) {
|
|
$substitute = current($substitute);
|
|
|
|
$substitute = $launchParams[$substitute];
|
|
} elseif ($ltiVersion === self::V_1P3) {
|
|
$claim = array_key_exists($substitute['claim'], $launchParams)
|
|
? $substitute['claim']
|
|
: "https://purl.imsglobal.org/spec/lti{$substitute['claim']}";
|
|
|
|
$substitute = empty($substitute['property'])
|
|
? $launchParams[$claim]
|
|
: $launchParams[$claim][$substitute['property']];
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$customParams[$customKey] = $substitute;
|
|
}
|
|
|
|
array_walk_recursive(
|
|
$customParams,
|
|
function (&$value) {
|
|
if (gettype($value) !== 'array') {
|
|
$value = (string) $value;
|
|
}
|
|
}
|
|
);
|
|
|
|
return $customParams;
|
|
}
|
|
|
|
/**
|
|
* Generate a user sourced ID for LIS.
|
|
*
|
|
* @param string $domain
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getPersonSourcedId($domain, User $user)
|
|
{
|
|
$sourceId = [$domain, $user->getId()];
|
|
|
|
return implode(':', $sourceId);
|
|
}
|
|
|
|
/**
|
|
* Generate a course sourced ID for LIS.
|
|
*
|
|
* @param string $domain
|
|
* @param Session $session Optional.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getCourseSectionSourcedId($domain, Course $course, Session $session = null)
|
|
{
|
|
$sourceId = [$domain, $course->getId()];
|
|
|
|
if ($session) {
|
|
$sourceId[] = $session->getId();
|
|
}
|
|
|
|
return implode(':', $sourceId);
|
|
}
|
|
|
|
/**
|
|
* Get instances for LTI Advantage services.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getAdvantageServices(ImsLtiTool $tool)
|
|
{
|
|
return [
|
|
new LtiAssignmentGradesService($tool),
|
|
new LtiNamesRoleProvisioningService($tool),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param int $length
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function generateClientId($length = 20)
|
|
{
|
|
$hash = md5(mt_rand().time());
|
|
|
|
$clientId = '';
|
|
|
|
for ($p = 0; $p < $length; $p++) {
|
|
$op = mt_rand(1, 3);
|
|
|
|
if ($op === 1) {
|
|
$char = chr(mt_rand(97, 97 + 25));
|
|
} elseif ($op === 2) {
|
|
$char = chr(mt_rand(65, 65 + 25));
|
|
} else {
|
|
$char = substr($hash, mt_rand(0, strlen($hash) - 1), 1);
|
|
}
|
|
|
|
$clientId .= $char;
|
|
}
|
|
|
|
return $clientId;
|
|
}
|
|
|
|
/**
|
|
* Validate the format ISO 8601 for date strings coming from JSON or JavaScript.
|
|
*
|
|
* @see https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ Pattern source.
|
|
*
|
|
* @param string $strDate
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function validateFormatDateIso8601($strDate)
|
|
{
|
|
$pattern = '/^([\+-]?\d{4}(?!\d{2}\b))((-?)('
|
|
.'(0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W'
|
|
.'([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))('
|
|
.'[T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?'
|
|
.'([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/';
|
|
|
|
return preg_match($pattern, $strDate) !== false;
|
|
}
|
|
}
|