67 private static $LTI_VERSIONS = array(self::LTI_VERSION1, self::LTI_VERSION2);
72 private static $METHOD_NAMES = array(
'basic-lti-launch-request' =>
'onLaunch',
73 'ConfigureLaunchRequest' =>
'onConfigure',
74 'DashboardRequest' =>
'onDashboard',
75 'ContentItemSelectionRequest' =>
'onContentItem',
76 'ToolProxyRegistrationRequest' =>
'onRegister' 82 private static $LTI_CONSUMER_SETTING_NAMES = array(
'custom_tc_profile_url',
'custom_system_setting_url',
'custom_oauth2_access_token_url');
87 private static $LTI_CONTEXT_SETTING_NAMES = array(
'custom_context_setting_url',
88 'custom_lineitems_url',
'custom_results_url',
89 'custom_context_memberships_url');
94 private static $LTI_RESOURCE_LINK_SETTING_NAMES = array(
'lis_result_sourcedid',
'lis_outcome_service_url',
95 'ext_ims_lis_basic_outcome_url',
'ext_ims_lis_resultvalue_sourcedids',
96 'ext_ims_lis_memberships_id',
'ext_ims_lis_memberships_url',
97 'ext_ims_lti_tool_setting',
'ext_ims_lti_tool_setting_id',
'ext_ims_lti_tool_setting_url',
98 'custom_link_setting_url',
99 'custom_lineitem_url',
'custom_result_url');
104 private static $CUSTOM_SUBSTITUTION_VARIABLES = array(
'User.id' =>
'user_id',
105 'User.image' =>
'user_image',
106 'User.username' =>
'username',
107 'User.scope.mentor' =>
'role_scope_mentor',
108 'Membership.role' =>
'roles',
109 'Person.sourcedId' =>
'lis_person_sourcedid',
110 'Person.name.full' =>
'lis_person_name_full',
111 'Person.name.family' =>
'lis_person_name_family',
112 'Person.name.given' =>
'lis_person_name_given',
113 'Person.email.primary' =>
'lis_person_contact_email_primary',
114 'Context.id' =>
'context_id',
115 'Context.type' =>
'context_type',
116 'Context.title' =>
'context_title',
117 'Context.label' =>
'context_label',
118 'CourseOffering.sourcedId' =>
'lis_course_offering_sourcedid',
119 'CourseSection.sourcedId' =>
'lis_course_section_sourcedid',
120 'CourseSection.label' =>
'context_label',
121 'CourseSection.title' =>
'context_title',
122 'ResourceLink.id' =>
'resource_link_id',
123 'ResourceLink.title' =>
'resource_link_title',
124 'ResourceLink.description' =>
'resource_link_description',
125 'Result.sourcedId' =>
'lis_result_sourcedid',
126 'BasicOutcome.url' =>
'lis_outcome_service_url',
127 'ToolConsumerProfile.url' =>
'custom_tc_profile_url',
128 'ToolProxy.url' =>
'tool_proxy_url',
129 'ToolProxy.custom.url' =>
'custom_system_setting_url',
130 'ToolProxyBinding.custom.url' =>
'custom_context_setting_url',
131 'LtiLink.custom.url' =>
'custom_link_setting_url',
132 'LineItems.url' =>
'custom_lineitems_url',
133 'LineItem.url' =>
'custom_lineitem_url',
134 'Results.url' =>
'custom_results_url',
135 'Result.url' =>
'custom_result_url',
136 'ToolProxyBinding.memberships.url' =>
'custom_context_memberships_url',
137 'LtiLink.memberships.url' =>
'custom_link_memberships_url');
326 private $constraints =
null;
335 $this->constraints = array();
337 $this->ok = !is_null($this->dataConnector);
340 $this->requiredServices = array();
341 $this->optionalServices = array();
342 $this->resourceHandlers = array();
352 if ($this->authenticate()) {
370 if (strlen($name) > 0) {
371 $this->constraints[$name] = array(
'required' => $required,
'max_length' => $maxLength,
'messages' => $messageTypes);
382 return $this->dataConnector->getToolConsumers();
396 $services = $this->consumer->profile->service_offered;
397 if (is_array($services)) {
399 foreach ($services as $service) {
401 if (!is_array($service->format) || !in_array($format, $service->format)) {
405 foreach ($methods as $method) {
406 if (!is_array($service->action) || !in_array($method, $service->action)) {
407 $missing[] = $method;
411 if (count($methods) <= 0) {
429 $toolProxyService = $this->
findService(
'application/vnd.ims.lti.v2.toolproxy+json', array(
'POST'));
432 $http = $this->consumer->doServiceRequest($toolProxyService,
'POST',
'application/vnd.ims.lti.v2.toolproxy+json',
433 json_encode($toolProxy));
434 $ok = $http->ok && ($http->status == 201) && isset($http->responseJson->tool_proxy_guid) && (strlen($http->responseJson->tool_proxy_guid) > 0);
436 $this->consumer->setKey($http->responseJson->tool_proxy_guid);
437 $this->consumer->secret = $toolProxy->security_contract->shared_secret;
438 $this->consumer->toolProxy = json_encode($toolProxy);
439 $this->consumer->save();
452 if ($this->ok && is_null($this->messageParameters)) {
454 if (!empty($this->messageParameters[
'oauth_consumer_key'])) {
455 $this->consumer =
new ToolConsumer($this->messageParameters[
'oauth_consumer_key'], $this->dataConnector);
459 $this->debugMode = isset($this->messageParameters[
'custom_debug']) && (strtolower($this->messageParameters[
'custom_debug']) ===
'true');
461 if (isset($this->messageParameters[
'launch_presentation_return_url'])) {
462 $this->returnUrl = $this->messageParameters[
'launch_presentation_return_url'];
463 } elseif (isset($this->messageParameters[
'content_item_return_url'])) {
464 $this->returnUrl = $this->messageParameters[
'content_item_return_url'];
479 public static function parseRoles($roles, $ltiVersion = self::LTI_VERSION1)
481 if (!is_array($roles)) {
482 $roles = explode(
',', $roles);
484 $parsedRoles = array();
485 foreach ($roles as $role) {
488 if ($ltiVersion === self::LTI_VERSION1) {
489 if (substr($role, 0, 4) !==
'urn:') {
490 $role =
'urn:lti:role:ims/lis/' . $role;
492 } elseif ((substr($role, 0, 7) !==
'http://') && (substr($role, 0, 8) !==
'https://')) {
493 $role =
'http://purl.imsglobal.org/vocab/lis/v2/membership#' . $role;
495 $parsedRoles[] = $role;
511 public static function sendForm($url, $params, $target =
'')
516 <title>IMS LTI message</title>
517 <script type=
"text/javascript">
519 function doOnLoad() {
520 document.forms[0].submit();
523 window.onload=doOnLoad;
528 <form action=
"{$url}" method=
"post" target=
"" encType=
"application/x-www-form-urlencoded">
531 foreach ($params as $key => $value) {
532 $key = htmlentities($key, ENT_COMPAT | ENT_HTML401,
'UTF-8');
533 if (!is_array($value)) {
534 $value = htmlentities($value, ENT_COMPAT | ENT_HTML401,
'UTF-8');
536 <input type=
"hidden" name=
"{$key}" value=
"{$value}" />
540 foreach ($value as $element) {
541 $element = htmlentities($element, ENT_COMPAT | ENT_HTML401,
'UTF-8');
543 <input type=
"hidden" name=
"{$key}" value=
"{$element}" />
560 ### PROTECTED METHODS 634 private function doCallback($method =
null)
637 if (is_null($callback)) {
638 $callback = self::$METHOD_NAMES[$this->messageParameters[
'lti_message_type']];
640 if (method_exists($this, $callback)) {
642 } elseif (is_null($method) && $this->ok) {
644 $this->reason =
"Message type not supported: {$this->messageParameters['lti_message_type']}";
646 if ($this->ok && ($this->messageParameters[
'lti_message_type'] ==
'ToolProxyRegistrationRequest')) {
647 $this->consumer->save();
658 private function result()
668 if (!empty($this->returnUrl)) {
670 if (strpos($errorUrl,
'?') ===
false) {
675 if ($this->debugMode && !is_null($this->reason)) {
676 $errorUrl .=
'lti_errormsg=' . urlencode(
"Debug error: $this->reason");
678 $errorUrl .=
'lti_errormsg=' . urlencode($this->message);
679 if (!is_null($this->reason)) {
680 $errorUrl .=
'<i_errorlog=' . urlencode(
"Debug error: $this->reason");
683 if (!is_null($this->consumer) && isset($this->messageParameters[
'lti_message_type']) && (($this->messageParameters[
'lti_message_type'] ===
'ContentItemSelectionRequest') ||
684 ($this->messageParameters[
'lti_message_type'] ===
'LtiDeepLinkingRequest'))) {
685 $formParams = array();
686 if (isset($this->messageParameters[
'data'])) {
687 $formParams[
'data'] = $this->messageParameters[
'data'];
689 $version = (isset($this->messageParameters[
'lti_version'])) ? $this->messageParameters[
'lti_version'] : self::LTI_VERSION1;
690 $formParams = $this->consumer->signParameters($errorUrl,
'ContentItemSelection', $version, $formParams);
694 header(
"Location: {$errorUrl}");
698 if (!is_null($this->errorOutput)) {
700 } elseif ($this->debugMode && !empty($this->reason)) {
701 echo
"Debug error: {$this->reason}";
703 echo
"Error: {$this->message}";
706 } elseif (!is_null($this->redirectUrl)) {
707 header(
"Location: {$this->redirectUrl}");
709 } elseif (!is_null($this->output)) {
722 private function authenticate()
725 $doSaveConsumer =
false;
726 $this->ok = $_SERVER[
'REQUEST_METHOD'] ===
'POST';
728 $this->reason =
'Message should be an HTTP POST request';
732 $this->ok = isset($this->messageParameters[
'lti_message_type']) && array_key_exists($this->messageParameters[
'lti_message_type'],
733 self::$METHOD_NAMES);
735 $this->reason =
'Invalid or missing lti_message_type parameter.';
739 $this->ok = isset($this->messageParameters[
'lti_version']) && in_array($this->messageParameters[
'lti_version'],
740 self::$LTI_VERSIONS);
742 $this->reason =
'Invalid or missing lti_version parameter.';
746 if (($this->messageParameters[
'lti_message_type'] ===
'basic-lti-launch-request') || ($this->messageParameters[
'lti_message_type'] ===
'LtiResourceLinkRequest') || ($this->messageParameters[
'lti_message_type'] ===
'DashboardRequest')) {
747 $this->ok = isset($this->messageParameters[
'resource_link_id']) && (strlen(trim($this->messageParameters[
'resource_link_id'])) > 0);
749 $this->reason =
'Missing resource link ID.';
751 } elseif (($this->messageParameters[
'lti_message_type'] ===
'ContentItemSelectionRequest') || ($this->messageParameters[
'lti_message_type'] ===
'LtiDeepLinkingRequest')) {
752 if (isset($this->messageParameters[
'accept_media_types']) && (strlen(trim($this->messageParameters[
'accept_media_types'])) > 0)) {
753 $mediaTypes = array_filter(explode(
',', str_replace(
' ',
'', $this->messageParameters[
'accept_media_types'])),
758 $this->reason =
'No accept_media_types found.';
765 if ($this->ok && isset($this->messageParameters[
'accept_presentation_document_targets']) && (strlen(trim($this->messageParameters[
'accept_presentation_document_targets'])) > 0)) {
767 str_replace(
' ',
'', $this->messageParameters[
'accept_presentation_document_targets'])),
'strlen');
771 $this->reason =
'Missing or empty accept_presentation_document_targets parameter.';
774 $this->ok = $this->checkValue($documentTarget,
775 array(
'embed',
'frame',
'iframe',
'window',
'popup',
'overlay',
'none'),
776 'Invalid value in accept_presentation_document_targets parameter: %s.');
789 $this->ok = isset($this->messageParameters[
'content_item_return_url']) && (strlen(trim($this->messageParameters[
'content_item_return_url'])) > 0);
791 $this->reason =
'Missing content_item_return_url parameter.';
794 } elseif ($this->messageParameters[
'lti_message_type'] ==
'ToolProxyRegistrationRequest') {
795 $this->ok = ((isset($this->messageParameters[
'reg_key']) && (strlen(trim($this->messageParameters[
'reg_key'])) > 0)) &&
796 (isset($this->messageParameters[
'reg_password']) && (strlen(trim($this->messageParameters[
'reg_password'])) > 0)) &&
797 (isset($this->messageParameters[
'tc_profile_url']) && (strlen(trim($this->messageParameters[
'tc_profile_url'])) > 0)) &&
798 (isset($this->messageParameters[
'launch_presentation_return_url']) && (strlen(trim($this->messageParameters[
'launch_presentation_return_url'])) > 0)));
799 if ($this->debugMode && !$this->ok) {
800 $this->reason =
'Missing message parameters.';
806 if ($this->ok && ($this->messageParameters[
'lti_message_type'] !=
'ToolProxyRegistrationRequest')) {
807 $this->ok = isset($this->messageParameters[
'oauth_consumer_key']);
809 $this->reason =
'Missing consumer key.';
812 $this->ok = !is_null($this->consumer->created);
814 $this->reason =
'Invalid consumer key: ' . $this->messageParameters[
'oauth_consumer_key'];
818 $today = date(
'Y-m-d', $now);
819 if (is_null($this->consumer->lastAccess)) {
820 $doSaveConsumer =
true;
822 $last = date(
'Y-m-d', $this->consumer->lastAccess);
823 $doSaveConsumer = $doSaveConsumer || ($last !== $today);
825 $this->consumer->lastAccess = $now;
826 $this->consumer->signatureMethod = isset($this->messageParameters[
'oauth_signature_method']) ? $this->messageParameters[
'oauth_signature_method'] :
827 $this->consumer->signatureMethod;
832 $server->add_signature_method($method);
834 $server->add_signature_method($method);
836 $server->add_signature_method($method);
838 $server->add_signature_method($method);
840 $server->add_signature_method($method);
842 $res = $server->verify_request($request);
843 }
catch (\Exception $e) {
845 if (empty($this->reason)) {
847 $signature = $request->build_signature($method,
$consumer,
false);
848 if ($this->debugMode) {
849 $this->reason = $e->getMessage();
851 if (empty($this->reason)) {
852 $this->reason =
'OAuth signature check failed - perhaps an incorrect secret or timestamp.';
854 $this->details[] =
'Current timestamp: ' . time();
855 $this->details[] =
"Expected signature: {$signature}";
856 $this->details[] =
"Base string: {$request->base_string}";
861 if ($this->consumer->protected) {
862 if (!is_null($this->consumer->consumerGuid)) {
863 $this->ok = empty($this->messageParameters[
'tool_consumer_instance_guid']) ||
864 ($this->consumer->consumerGuid === $this->messageParameters[
'tool_consumer_instance_guid']);
866 $this->reason =
'Request is from an invalid tool consumer.';
869 $this->ok = isset($this->messageParameters[
'tool_consumer_instance_guid']);
871 $this->reason =
'A tool consumer GUID must be included in the launch request.';
876 $this->ok = $this->consumer->enabled;
878 $this->reason =
'Tool consumer has not been enabled by the tool provider.';
882 $this->ok = is_null($this->consumer->enableFrom) || ($this->consumer->enableFrom <= $now);
884 $this->ok = is_null($this->consumer->enableUntil) || ($this->consumer->enableUntil > $now);
886 $this->reason =
'Tool consumer access has expired.';
889 $this->reason =
'Tool consumer access is not yet available.';
896 if (($this->messageParameters[
'lti_message_type'] ===
'ContentItemSelectionRequest') || ($this->messageParameters[
'lti_message_type'] ===
'LtiDeepLinkingRequest')) {
897 if (isset($this->messageParameters[
'accept_unsigned'])) {
898 $this->ok = $this->checkValue($this->messageParameters[
'accept_unsigned'], array(
'true',
'false'),
899 'Invalid value for accept_unsigned parameter: %s.');
901 if ($this->ok && isset($this->messageParameters[
'accept_multiple'])) {
902 $this->ok = $this->checkValue($this->messageParameters[
'accept_multiple'], array(
'true',
'false'),
903 'Invalid value for accept_multiple parameter: %s.');
905 if ($this->ok && isset($this->messageParameters[
'accept_copy_advice'])) {
906 $this->ok = $this->checkValue($this->messageParameters[
'accept_copy_advice'], array(
'true',
'false'),
907 'Invalid value for accept_copy_advice parameter: %s.');
909 if ($this->ok && isset($this->messageParameters[
'auto_create'])) {
910 $this->ok = $this->checkValue($this->messageParameters[
'auto_create'], array(
'true',
'false'),
911 'Invalid value for auto_create parameter: %s.');
913 if ($this->ok && isset($this->messageParameters[
'can_confirm'])) {
914 $this->ok = $this->checkValue($this->messageParameters[
'can_confirm'], array(
'true',
'false'),
915 'Invalid value for can_confirm parameter: %s.');
917 } elseif (isset($this->messageParameters[
'launch_presentation_document_target'])) {
918 $this->ok = $this->checkValue($this->messageParameters[
'launch_presentation_document_target'],
919 array(
'embed',
'frame',
'iframe',
'window',
'popup',
'overlay'),
920 'Invalid value for launch_presentation_document_target parameter: %s.');
925 if ($this->ok && ($this->messageParameters[
'lti_message_type'] ===
'ToolProxyRegistrationRequest')) {
928 $this->reason =
'Invalid lti_version parameter';
931 $url = $this->messageParameters[
'tc_profile_url'];
932 if (strpos($url,
'?') === FALSE) {
938 $http =
new HTTPMessage($url,
'GET',
null,
'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
939 $this->ok = $http->send();
941 $this->reason =
'Tool consumer profile not accessible.';
943 $tcProfile = json_decode($http->response);
944 $this->ok = !is_null($tcProfile);
946 $this->reason =
'Invalid JSON in tool consumer profile.';
952 $this->consumer =
new ToolConsumer($this->messageParameters[
'reg_key'], $this->dataConnector);
953 $this->consumer->profile = $tcProfile;
954 $capabilities = $this->consumer->profile->capability_offered;
956 foreach ($this->resourceHandlers as $resourceHandler) {
957 foreach ($resourceHandler->requiredMessages as
$message) {
958 if (!in_array(
$message->type, $capabilities)) {
963 foreach ($this->constraints as $name => $constraint) {
964 if ($constraint[
'required']) {
965 if (!in_array($name, $capabilities)) {
966 $missing[$name] =
true;
970 if (!empty($missing)) {
972 $this->reason =
'Required capability not offered - \'' . implode(
'\', \
'', array_keys($missing)) .
'\'';
978 foreach ($this->requiredServices as $service) {
979 foreach ($service->formats as $format) {
980 if (!$this->
findService($format, $service->actions)) {
982 $this->reason =
'Required service(s) not offered - ';
985 $this->reason .=
', ';
987 $this->reason .=
"'{$format}' [" . implode(
', ', $service->actions) .
']';
993 if ($this->messageParameters[
'lti_message_type'] ===
'ToolProxyRegistrationRequest') {
994 $this->consumer->profile = $tcProfile;
995 $this->consumer->secret = $this->messageParameters[
'reg_password'];
996 $this->consumer->ltiVersion = $this->messageParameters[
'lti_version'];
997 $this->consumer->name = $tcProfile->product_instance->service_owner->service_owner_name->default_value;
998 $this->consumer->consumerName = $this->consumer->name;
999 $this->consumer->consumerVersion =
"{$tcProfile->product_instance->product_info->product_family->code}-{$tcProfile->product_instance->product_info->product_version}";
1000 $this->consumer->consumerGuid = $tcProfile->product_instance->guid;
1001 $this->consumer->enabled =
true;
1002 $this->consumer->protected =
true;
1003 $doSaveConsumer =
true;
1006 } elseif ($this->ok && !empty($this->messageParameters[
'custom_tc_profile_url']) && empty($this->consumer->profile)) {
1007 $url = $this->messageParameters[
'custom_tc_profile_url'];
1008 if (strpos($url,
'?') === FALSE) {
1013 $url .=
'lti_version=' . $this->consumer->ltiVersion;
1014 $http =
new HTTPMessage($url,
'GET',
null,
'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
1015 if ($http->send()) {
1016 $tcProfile = json_decode($http->response);
1017 if (!is_null($tcProfile)) {
1018 $this->consumer->profile = $tcProfile;
1019 $doSaveConsumer =
true;
1026 $invalidParameters = array();
1027 foreach ($this->constraints as $name => $constraint) {
1028 if (empty($constraint[
'messages']) || in_array($this->messageParameters[
'lti_message_type'], $constraint[
'messages'])) {
1030 if ($constraint[
'required']) {
1031 if (!isset($this->messageParameters[$name]) || (strlen(trim($this->messageParameters[$name])) <= 0)) {
1032 $invalidParameters[] =
"{$name} (missing)";
1036 if (
$ok && !is_null($constraint[
'max_length']) && isset($this->messageParameters[$name])) {
1037 if (strlen(trim($this->messageParameters[$name])) > $constraint[
'max_length']) {
1038 $invalidParameters[] =
"{$name} (too long)";
1043 if (count($invalidParameters) > 0) {
1045 if (empty($this->reason)) {
1046 $this->reason =
'Invalid parameter(s): ' . implode(
', ', $invalidParameters) .
'.';
1055 if ($this->hasApiHook(self::$CONTEXT_ID_HOOK, $this->consumer->getFamilyCode())) {
1056 $className = $this->getApiHook(self::$CONTEXT_ID_HOOK, $this->consumer->getFamilyCode());
1057 $tpHook =
new $className($this);
1058 $contextId = $tpHook->getContextId();
1060 if (empty($contextId) && isset($this->messageParameters[
'context_id'])) {
1061 $contextId = trim($this->messageParameters[
'context_id']);
1063 if (!empty($contextId)) {
1066 if (isset($this->messageParameters[
'context_title'])) {
1067 $title = trim($this->messageParameters[
'context_title']);
1069 if (empty($title)) {
1070 $title =
"Course {$this->context->getId()}";
1072 $this->context->title = $title;
1073 if (isset($this->messageParameters[
'context_type'])) {
1074 $this->context->type = trim($this->messageParameters[
'context_type']);
1079 if (isset($this->messageParameters[
'resource_link_id'])) {
1080 $contentItemId =
'';
1081 if (isset($this->messageParameters[
'custom_content_item_id'])) {
1082 $contentItemId = $this->messageParameters[
'custom_content_item_id'];
1085 trim($this->messageParameters[
'resource_link_id']), $contentItemId);
1086 if (!empty($this->context)) {
1087 $this->resourceLink->setContextId($this->context->getRecordId());
1090 if (isset($this->messageParameters[
'resource_link_title'])) {
1091 $title = trim($this->messageParameters[
'resource_link_title']);
1093 if (empty($title)) {
1094 $title =
"Resource {$this->resourceLink->getId()}";
1096 $this->resourceLink->title = $title;
1098 foreach ($this->consumer->getSettings() as $name => $value) {
1099 if (strpos($name,
'custom_') === 0) {
1100 $this->consumer->setSetting($name);
1101 $doSaveConsumer =
true;
1104 if (!empty($this->context)) {
1105 foreach ($this->context->getSettings() as $name => $value) {
1106 if (strpos($name,
'custom_') === 0) {
1107 $this->context->setSetting($name);
1111 foreach ($this->resourceLink->getSettings() as $name => $value) {
1112 if (strpos($name,
'custom_') === 0) {
1113 $this->resourceLink->setSetting($name);
1117 foreach (self::$LTI_CONSUMER_SETTING_NAMES as $name) {
1118 if (isset($this->messageParameters[$name])) {
1119 $this->consumer->setSetting($name, $this->messageParameters[$name]);
1121 $this->consumer->setSetting($name);
1124 if (!empty($this->context)) {
1125 foreach (self::$LTI_CONTEXT_SETTING_NAMES as $name) {
1126 if (isset($this->messageParameters[$name])) {
1127 $this->context->setSetting($name, $this->messageParameters[$name]);
1129 $this->context->setSetting($name);
1133 foreach (self::$LTI_RESOURCE_LINK_SETTING_NAMES as $name) {
1134 if (isset($this->messageParameters[$name])) {
1135 $this->resourceLink->setSetting($name, $this->messageParameters[$name]);
1137 $this->resourceLink->setSetting($name);
1141 foreach ($this->messageParameters as $name => $value) {
1142 if ((strpos($name,
'custom_') === 0) &&
1144 array_merge(self::$LTI_CONSUMER_SETTING_NAMES, self::$LTI_CONTEXT_SETTING_NAMES,
1145 self::$LTI_RESOURCE_LINK_SETTING_NAMES))) {
1146 $this->consumer->setSetting($name, $value);
1147 if (!empty($this->context)) {
1148 $this->context->setSetting($name, $value);
1150 $this->resourceLink->setSetting($name, $value);
1157 if ($this->hasApiHook(self::$USER_ID_HOOK, $this->consumer->getFamilyCode())) {
1158 $className = $this->getApiHook(self::$USER_ID_HOOK, $this->consumer->getFamilyCode());
1159 $tpHook =
new $className($this);
1160 $userId = $tpHook->getUserId();
1162 if (empty($userId) && isset($this->messageParameters[
'user_id'])) {
1163 $userId = trim($this->messageParameters[
'user_id']);
1169 $firstname = (isset($this->messageParameters[
'lis_person_name_given'])) ? $this->messageParameters[
'lis_person_name_given'] :
'';
1170 $lastname = (isset($this->messageParameters[
'lis_person_name_family'])) ? $this->messageParameters[
'lis_person_name_family'] :
'';
1171 $fullname = (isset($this->messageParameters[
'lis_person_name_full'])) ? $this->messageParameters[
'lis_person_name_full'] :
'';
1172 $this->userResult->setNames($firstname, $lastname, $fullname);
1175 $email = (isset($this->messageParameters[
'lis_person_contact_email_primary'])) ? $this->messageParameters[
'lis_person_contact_email_primary'] :
'';
1176 $this->userResult->setEmail($email, $this->defaultEmail);
1179 if (isset($this->messageParameters[
'user_image'])) {
1180 $this->userResult->image = $this->messageParameters[
'user_image'];
1184 if (isset($this->messageParameters[
'roles'])) {
1185 $this->userResult->roles =
self::parseRoles($this->messageParameters[
'roles'], $this->consumer->ltiVersion);
1190 if ($this->consumer->ltiVersion !== $this->messageParameters[
'lti_version']) {
1191 $this->consumer->ltiVersion = $this->messageParameters[
'lti_version'];
1192 $doSaveConsumer =
true;
1194 if (isset($this->messageParameters[
'tool_consumer_instance_name'])) {
1195 if ($this->consumer->consumerName !== $this->messageParameters[
'tool_consumer_instance_name']) {
1196 $this->consumer->consumerName = $this->messageParameters[
'tool_consumer_instance_name'];
1197 $doSaveConsumer =
true;
1200 if (isset($this->messageParameters[
'tool_consumer_info_product_family_code'])) {
1201 $version = $this->messageParameters[
'tool_consumer_info_product_family_code'];
1202 if (isset($this->messageParameters[
'tool_consumer_info_version'])) {
1203 $version .=
"-{$this->messageParameters['tool_consumer_info_version'] 1207 if ($this->consumer->consumerVersion !== $version) {
1208 $this->consumer->consumerVersion = $version;
1209 $doSaveConsumer =
true;
1211 } elseif (isset($this->messageParameters[
'ext_lms']) && ($this->consumer->consumerName !== $this->messageParameters[
'ext_lms'])) {
1212 $this->consumer->consumerVersion = $this->messageParameters[
'ext_lms'];
1213 $doSaveConsumer =
true;
1215 if (isset($this->messageParameters[
'tool_consumer_instance_guid'])) {
1216 if (is_null($this->consumer->consumerGuid)) {
1217 $this->consumer->consumerGuid = $this->messageParameters[
'tool_consumer_instance_guid'];
1218 $doSaveConsumer =
true;
1219 } elseif (!$this->consumer->protected) {
1220 $doSaveConsumer = ($this->consumer->consumerGuid !== $this->messageParameters[
'tool_consumer_instance_guid']);
1221 if ($doSaveConsumer) {
1222 $this->consumer->consumerGuid = $this->messageParameters[
'tool_consumer_instance_guid'];
1226 if (isset($this->messageParameters[
'launch_presentation_css_url'])) {
1227 if ($this->consumer->cssPath !== $this->messageParameters[
'launch_presentation_css_url']) {
1228 $this->consumer->cssPath = $this->messageParameters[
'launch_presentation_css_url'];
1229 $doSaveConsumer =
true;
1231 } elseif (isset($this->messageParameters[
'ext_launch_presentation_css_url']) &&
1232 ($this->consumer->cssPath !== $this->messageParameters[
'ext_launch_presentation_css_url'])) {
1233 $this->consumer->cssPath = $this->messageParameters[
'ext_launch_presentation_css_url'];
1234 $doSaveConsumer =
true;
1235 } elseif (!empty($this->consumer->cssPath)) {
1236 $this->consumer->cssPath =
null;
1237 $doSaveConsumer =
true;
1242 if ($doSaveConsumer) {
1243 $this->consumer->save();
1245 if ($this->ok && isset($this->context)) {
1246 $this->context->save();
1248 if ($this->ok && isset($this->resourceLink)) {
1251 $this->ok = $this->checkForShare();
1254 $this->resourceLink->save();
1257 $this->userResult->setResourceLinkId($this->resourceLink->getRecordId());
1258 if (isset($this->messageParameters[
'lis_result_sourcedid'])) {
1259 if ($this->userResult->ltiResultSourcedId !== $this->messageParameters[
'lis_result_sourcedid']) {
1260 $this->userResult->ltiResultSourcedId = $this->messageParameters[
'lis_result_sourcedid'];
1261 $this->userResult->save();
1263 } elseif (!empty($this->userResult->ltiResultSourcedId)) {
1264 $this->userResult->ltiResultSourcedId =
'';
1265 $this->userResult->save();
1277 private function checkForShare()
1280 $doSaveResourceLink =
true;
1282 $id = $this->resourceLink->primaryResourceLinkId;
1284 $shareRequest = isset($this->messageParameters[
'custom_share_key']) && !empty($this->messageParameters[
'custom_share_key']);
1285 if ($shareRequest) {
1286 if (!$this->allowSharing) {
1288 $this->reason =
'Your sharing request has been refused because sharing is not being permitted.';
1291 $shareKey =
new ResourceLinkShareKey($this->resourceLink, $this->messageParameters[
'custom_share_key']);
1292 if (!is_null($shareKey->primaryConsumerKey) && !is_null($shareKey->primaryResourceLinkId)) {
1294 $key = $shareKey->primaryConsumerKey;
1295 $id = $shareKey->primaryResourceLinkId;
1296 $ok = ($key !== $this->consumer->getKey()) || ($id != $this->resourceLink->getId());
1298 $this->resourceLink->primaryConsumerKey = $key;
1299 $this->resourceLink->primaryResourceLinkId = $id;
1300 $this->resourceLink->shareApproved = $shareKey->autoApprove;
1301 $ok = $this->resourceLink->save();
1303 $doSaveResourceLink =
false;
1304 $this->userResult->getResourceLink()->primaryConsumerKey = $key;
1305 $this->userResult->getResourceLink()->primaryResourceLinkId = $id;
1306 $this->userResult->getResourceLink()->shareApproved = $shareKey->autoApprove;
1307 $this->userResult->getResourceLink()->updated = time();
1309 $shareKey->delete();
1311 $this->reason =
'An error occurred initialising your share arrangement.';
1314 $this->reason =
'It is not possible to share your resource link with yourself.';
1318 $ok = !is_null($key);
1320 $this->reason =
'You have requested to share a resource link but none is available.';
1322 $ok = (!is_null($this->userResult->getResourceLink()->shareApproved) && $this->userResult->getResourceLink()->shareApproved);
1324 $this->reason =
'Your share request is waiting to be approved.';
1333 $this->reason =
'You have not requested to share a resource link but an arrangement is currently in place.';
1338 if (
$ok && !is_null($id)) {
1346 if ($doSaveResourceLink) {
1347 $this->resourceLink->save();
1351 $this->reason =
'Unable to load resource link being shared.';
1367 private function checkValue($value, $values,
$reason)
1369 $ok = in_array($value, $values);
1371 $this->reason = sprintf(
$reason, $value);
static fromResourceLink($resourceLink, $ltiUserId)
Class constructor from resource link.
Class to represent an OAuth Consumer.
Class to represent a generic item object.
Class to represent an OAuth HMAC_SHA256 signature method.
Class to represent an OAuth HMAC_SHA384 signature method.
static fromConsumer($consumer, $ltiResourceLinkId, $tempId=null)
Class constructor from consumer.
Class to represent an OAuth HMAC_SHA224 signature method.
Class to represent an OAuth HMAC_SHA1 signature method.
Class to represent an HTTP message request.
static from_request($http_method=null, $http_url=null, $parameters=null)
attempt to build up a request from what was passed to the server
Trait to handle API hook registrations.
Class to represent a tool consumer resource link share key.
static parse_parameters($input)
Class to represent an OAuth Server.
Class to represent an OAuth Data Store.
static getRandomString($length=8)
Generate a random string.
static fromConsumer($consumer, $ltiContextId)
Class constructor from consumer.
Class to represent an OAuth HMAC_SHA512 signature method.