38 private static $LTI_VERSIONS = array(self::LTI_VERSION1, self::LTI_VERSION2);
42 private static $MESSAGE_TYPES = array(
'basic-lti-launch-request' =>
'onLaunch',
43 'ContentItemSelectionRequest' =>
'onContentItem',
44 'ToolProxyRegistrationRequest' =>
'register');
50 private static $METHOD_NAMES = array(
'basic-lti-launch-request' =>
'onLaunch',
51 'ContentItemSelectionRequest' =>
'onContentItem',
52 'ToolProxyRegistrationRequest' =>
'onRegister');
65 'custom_lineitems_url',
'custom_results_url',
66 'custom_context_memberships_url');
73 'ext_ims_lis_basic_outcome_url',
'ext_ims_lis_resultvalue_sourcedids',
74 'ext_ims_lis_memberships_id',
'ext_ims_lis_memberships_url',
75 'ext_ims_lti_tool_setting',
'ext_ims_lti_tool_setting_id',
'ext_ims_lti_tool_setting_url',
76 'custom_link_setting_url',
77 'custom_lineitem_url',
'custom_result_url');
84 'User.image' =>
'user_image',
85 'User.username' =>
'username',
86 'User.scope.mentor' =>
'role_scope_mentor',
87 'Membership.role' =>
'roles',
88 'Person.sourcedId' =>
'lis_person_sourcedid',
89 'Person.name.full' =>
'lis_person_name_full',
90 'Person.name.family' =>
'lis_person_name_family',
91 'Person.name.given' =>
'lis_person_name_given',
92 'Person.email.primary' =>
'lis_person_contact_email_primary',
93 'Context.id' =>
'context_id',
94 'Context.type' =>
'context_type',
95 'Context.title' =>
'context_title',
96 'Context.label' =>
'context_label',
97 'CourseOffering.sourcedId' =>
'lis_course_offering_sourcedid',
98 'CourseSection.sourcedId' =>
'lis_course_section_sourcedid',
99 'CourseSection.label' =>
'context_label',
100 'CourseSection.title' =>
'context_title',
101 'ResourceLink.id' =>
'resource_link_id',
102 'ResourceLink.title' =>
'resource_link_title',
103 'ResourceLink.description' =>
'resource_link_description',
104 'Result.sourcedId' =>
'lis_result_sourcedid',
105 'BasicOutcome.url' =>
'lis_outcome_service_url',
106 'ToolConsumerProfile.url' =>
'custom_tc_profile_url',
107 'ToolProxy.url' =>
'tool_proxy_url',
108 'ToolProxy.custom.url' =>
'custom_system_setting_url',
109 'ToolProxyBinding.custom.url' =>
'custom_context_setting_url',
110 'LtiLink.custom.url' =>
'custom_link_setting_url',
111 'LineItems.url' =>
'custom_lineitems_url',
112 'LineItem.url' =>
'custom_lineitem_url',
113 'Results.url' =>
'custom_results_url',
114 'Result.url' =>
'custom_result_url',
115 'ToolProxyBinding.memberships.url' =>
'custom_context_memberships_url');
126 $this->logger = $DIC->logger()->lti();
127 parent::__construct($dataConnector);
147 ### PROTECTED METHODS 162 if ($this->context instanceof
Context) {
163 $this->context->save();
167 $this->resourceLink->save();
219 if (is_null($callback)) {
220 $callback = self::$METHOD_NAMES[
$_POST[
'lti_message_type']];
222 if (method_exists($this, $callback)) {
224 } elseif (is_null($method) && $this->ok) {
226 $this->reason =
"Message type not supported: {$_POST['lti_message_type']}";
228 if ($this->ok && (
$_POST[
'lti_message_type'] ==
'ToolProxyRegistrationRequest')) {
229 $this->consumer->save();
249 if (!empty($this->returnUrl)) {
250 $errorUrl = $this->returnUrl;
251 if (strpos($errorUrl,
'?') ===
false) {
256 if ($this->debugMode && !is_null($this->reason)) {
257 $errorUrl .=
'lti_errormsg=' . urlencode(
"Debug error: $this->reason");
259 $errorUrl .=
'lti_errormsg=' . urlencode($this->message);
260 if (!is_null($this->reason)) {
261 $errorUrl .=
'<i_errorlog=' . urlencode(
"Debug error: $this->reason");
264 if (!is_null($this->consumer) && isset(
$_POST[
'lti_message_type']) && (
$_POST[
'lti_message_type'] ===
'ContentItemSelectionRequest')) {
265 $formParams = array();
266 if (isset(
$_POST[
'data'])) {
267 $formParams[
'data'] =
$_POST[
'data'];
270 $formParams = $this->consumer->signParameters($errorUrl,
'ContentItemSelection',
$version, $formParams);
271 $page = self::sendForm($errorUrl, $formParams);
274 header(
"Location: {$errorUrl}");
278 if (!is_null($this->errorOutput)) {
279 echo $this->errorOutput;
280 } elseif ($this->debugMode && !empty($this->reason)) {
281 echo "Debug error: {$this->reason}";
283 echo "Error: {$this->message}";
286 } elseif (!is_null($this->redirectUrl)) {
287 header(
"Location: {$this->redirectUrl}");
289 } elseif (!is_null($this->output)) {
306 $doSaveConsumer =
false;
308 $this->ok = isset(
$_POST[
'lti_message_type']) && array_key_exists(
$_POST[
'lti_message_type'], self::$MESSAGE_TYPES);
310 $this->reason =
'Invalid or missing lti_message_type parameter.';
313 $this->ok = isset(
$_POST[
'lti_version']) && in_array(
$_POST[
'lti_version'], self::$LTI_VERSIONS);
315 $this->reason =
'Invalid or missing lti_version parameter.';
319 if (
$_POST[
'lti_message_type'] ===
'basic-lti-launch-request') {
320 $this->ok = isset(
$_POST[
'resource_link_id']) && (strlen(trim(
$_POST[
'resource_link_id'])) > 0);
322 $this->reason =
'Missing resource link ID.';
324 } elseif (
$_POST[
'lti_message_type'] ===
'ContentItemSelectionRequest') {
325 if (isset(
$_POST[
'accept_media_types']) && (strlen(trim(
$_POST[
'accept_media_types'])) > 0)) {
326 $mediaTypes = array_filter(explode(
',', str_replace(
' ',
'',
$_POST[
'accept_media_types'])),
'strlen');
327 $mediaTypes = array_unique($mediaTypes);
328 $this->ok = count($mediaTypes) > 0;
330 $this->reason =
'No accept_media_types found.';
332 $this->mediaTypes = $mediaTypes;
337 if ($this->ok && isset(
$_POST[
'accept_presentation_document_targets']) && (strlen(trim(
$_POST[
'accept_presentation_document_targets'])) > 0)) {
338 $documentTargets = array_filter(explode(
',', str_replace(
' ',
'',
$_POST[
'accept_presentation_document_targets'])),
'strlen');
339 $documentTargets = array_unique($documentTargets);
340 $this->ok = count($documentTargets) > 0;
342 $this->reason =
'Missing or empty accept_presentation_document_targets parameter.';
344 foreach ($documentTargets as $documentTarget) {
347 array(
'embed',
'frame',
'iframe',
'window',
'popup',
'overlay',
'none'),
348 'Invalid value in accept_presentation_document_targets parameter: %s.' 355 $this->documentTargets = $documentTargets;
362 $this->ok = isset(
$_POST[
'content_item_return_url']) && (strlen(trim(
$_POST[
'content_item_return_url'])) > 0);
364 $this->reason =
'Missing content_item_return_url parameter.';
367 } elseif (
$_POST[
'lti_message_type'] ==
'ToolProxyRegistrationRequest') {
368 $this->ok = ((isset(
$_POST[
'reg_key']) && (strlen(trim(
$_POST[
'reg_key'])) > 0)) &&
369 (isset(
$_POST[
'reg_password']) && (strlen(trim(
$_POST[
'reg_password'])) > 0)) &&
370 (isset(
$_POST[
'tc_profile_url']) && (strlen(trim(
$_POST[
'tc_profile_url'])) > 0)) &&
371 (isset(
$_POST[
'launch_presentation_return_url']) && (strlen(trim(
$_POST[
'launch_presentation_return_url'])) > 0)));
372 if ($this->debugMode && !$this->ok) {
373 $this->reason =
'Missing message parameters.';
379 $this->logger->debug(
'Checking consumer key...');
382 if ($this->ok && (
$_POST[
'lti_message_type'] !=
'ToolProxyRegistrationRequest')) {
383 $this->ok = isset(
$_POST[
'oauth_consumer_key']);
385 $this->reason =
'Missing consumer key.';
390 $this->ok = !is_null($this->consumer->created);
392 $this->reason =
'Invalid consumer key.';
396 $today = date(
'Y-m-d', $now);
397 if (is_null($this->consumer->lastAccess)) {
398 $doSaveConsumer =
true;
400 $last = date(
'Y-m-d', $this->consumer->lastAccess);
401 $doSaveConsumer = $doSaveConsumer || ($last !== $today);
403 $this->consumer->last_access = $now;
407 $method =
new OAuth\OAuthSignatureMethod_HMAC_SHA1();
408 $server->add_signature_method($method);
409 $request = OAuth\OAuthRequest::from_request();
413 if (empty($this->reason)) {
414 if ($this->debugMode) {
415 $consumer =
new OAuth\OAuthConsumer($this->consumer->getKey(), $this->consumer->secret);
416 $signature =
$request->build_signature($method, $consumer,
false);
417 $this->reason = $e->getMessage();
418 if (empty($this->reason)) {
419 $this->reason =
'OAuth exception';
421 $this->details[] =
'Timestamp: ' . time();
422 $this->details[] =
"Signature: {$signature}";
423 $this->details[] =
"Base string: {$request->base_string}]";
425 $this->reason =
'OAuth signature check failed - perhaps an incorrect secret or timestamp.';
432 $today = date(
'Y-m-d', $now);
433 if (is_null($this->consumer->lastAccess)) {
434 $doSaveConsumer =
true;
436 $last = date(
'Y-m-d', $this->consumer->lastAccess);
437 $doSaveConsumer = $doSaveConsumer || ($last !== $today);
439 $this->consumer->last_access = $now;
440 if ($this->consumer->protected) {
441 if (!is_null($this->consumer->consumerGuid)) {
442 $this->ok = empty(
$_POST[
'tool_consumer_instance_guid']) ||
443 ($this->consumer->consumerGuid ===
$_POST[
'tool_consumer_instance_guid']);
445 $this->reason =
'Request is from an invalid tool consumer.';
448 $this->ok = isset(
$_POST[
'tool_consumer_instance_guid']);
450 $this->reason =
'A tool consumer GUID must be included in the launch request.';
455 $this->ok = $this->consumer->enabled;
457 $this->reason =
'Tool consumer has not been enabled by the tool provider.';
461 $this->ok = is_null($this->consumer->enableFrom) || ($this->consumer->enableFrom <= $now);
463 $this->ok = is_null($this->consumer->enableUntil) || ($this->consumer->enableUntil > $now);
465 $this->reason =
'Tool consumer access has expired.';
468 $this->reason =
'Tool consumer access is not yet available.';
474 if (
$_POST[
'lti_message_type'] ===
'ContentItemSelectionRequest') {
475 if (isset(
$_POST[
'accept_unsigned'])) {
476 $this->ok = $this->
checkValue(
$_POST[
'accept_unsigned'], array(
'true',
'false'),
'Invalid value for accept_unsigned parameter: %s.');
478 if ($this->ok && isset(
$_POST[
'accept_multiple'])) {
479 $this->ok = $this->
checkValue(
$_POST[
'accept_multiple'], array(
'true',
'false'),
'Invalid value for accept_multiple parameter: %s.');
481 if ($this->ok && isset(
$_POST[
'accept_copy_advice'])) {
482 $this->ok = $this->
checkValue(
$_POST[
'accept_copy_advice'], array(
'true',
'false'),
'Invalid value for accept_copy_advice parameter: %s.');
484 if ($this->ok && isset(
$_POST[
'auto_create'])) {
485 $this->ok = $this->
checkValue(
$_POST[
'auto_create'], array(
'true',
'false'),
'Invalid value for auto_create parameter: %s.');
487 if ($this->ok && isset(
$_POST[
'can_confirm'])) {
488 $this->ok = $this->
checkValue(
$_POST[
'can_confirm'], array(
'true',
'false'),
'Invalid value for can_confirm parameter: %s.');
490 } elseif (isset(
$_POST[
'launch_presentation_document_target'])) {
492 $_POST[
'launch_presentation_document_target'],
493 array(
'embed',
'frame',
'iframe',
'window',
'popup',
'overlay'),
494 'Invalid value for launch_presentation_document_target parameter: %s.' 500 if ($this->ok && (
$_POST[
'lti_message_type'] ===
'ToolProxyRegistrationRequest')) {
501 $this->ok =
$_POST[
'lti_version'] == self::LTI_VERSION2;
503 $this->reason =
'Invalid lti_version parameter';
506 $http =
new HTTPMessage(
$_POST[
'tc_profile_url'],
'GET', null,
'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
507 $this->ok =
$http->send();
509 $this->reason =
'Tool consumer profile not accessible.';
511 $tcProfile = json_decode(
$http->response);
512 $this->ok = !is_null($tcProfile);
514 $this->reason =
'Invalid JSON in tool consumer profile.';
522 $this->consumer->profile = $tcProfile;
523 $capabilities = $this->consumer->profile->capability_offered;
525 foreach ($this->resourceHandlers as $resourceHandler) {
526 foreach ($resourceHandler->requiredMessages as
$message) {
527 if (!in_array($message->type, $capabilities)) {
528 $missing[$message->type] =
true;
532 foreach ($this->constraints as
$name => $constraint) {
533 if ($constraint[
'required']) {
534 if (!in_array(
$name, $capabilities) && !in_array(
$name, array_flip($capabilities))) {
535 $missing[
$name] =
true;
539 if (!empty($missing)) {
541 $this->reason =
'Required capability not offered - \'' . implode(
'\', \
'', array_keys($missing)) .
'\'';
547 foreach ($this->requiredServices as $service) {
548 foreach ($service->formats as
$format) {
549 if (!$this->findService($format, $service->actions)) {
551 $this->reason =
'Required service(s) not offered - ';
554 $this->reason .=
', ';
556 $this->reason .=
"'{$format}' [" . implode(
', ', $service->actions) .
']';
562 if (
$_POST[
'lti_message_type'] ===
'ToolProxyRegistrationRequest') {
563 $this->consumer->profile = $tcProfile;
564 $this->consumer->secret =
$_POST[
'reg_password'];
565 $this->consumer->ltiVersion =
$_POST[
'lti_version'];
566 $this->consumer->name = $tcProfile->product_instance->service_owner->service_owner_name->default_value;
567 $this->consumer->consumerName = $this->consumer->name;
568 $this->consumer->consumerVersion =
"{$tcProfile->product_instance->product_info->product_family->code}-{$tcProfile->product_instance->product_info->product_version}";
569 $this->consumer->consumerGuid = $tcProfile->product_instance->guid;
570 $this->consumer->enabled =
true;
571 $this->consumer->protected =
true;
572 $doSaveConsumer =
true;
575 } elseif ($this->ok && !empty(
$_POST[
'custom_tc_profile_url']) && empty($this->consumer->profile)) {
576 $http =
new HTTPMessage(
$_POST[
'custom_tc_profile_url'],
'GET', null,
'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
578 $tcProfile = json_decode(
$http->response);
579 if (!is_null($tcProfile)) {
580 $this->consumer->profile = $tcProfile;
581 $doSaveConsumer =
true;
613 $this->logger->debug(
'Still ok: ' . ($this->ok ?
'1' :
'0'));
615 $this->logger->debug(
'Reason: ' . $this->reason);
621 if (isset(
$_POST[
'context_id'])) {
622 $this->context = Context::fromConsumer($this->consumer, trim(
$_POST[
'context_id']));
624 if (isset(
$_POST[
'context_title'])) {
628 $title =
"Course {$this->context->getId()}";
630 $this->context->title =
$title;
634 if (isset(
$_POST[
'resource_link_id'])) {
636 if (isset(
$_POST[
'custom_content_item_id'])) {
637 $contentItemId =
$_POST[
'custom_content_item_id'];
639 $this->resourceLink = ResourceLink::fromConsumer($this->consumer, trim(
$_POST[
'resource_link_id']), $contentItemId);
640 if (!empty($this->context)) {
641 $this->resourceLink->setContextId($this->context->getRecordId());
644 if (isset(
$_POST[
'resource_link_title'])) {
648 $title =
"Resource {$this->resourceLink->getId()}";
650 $this->resourceLink->title =
$title;
652 foreach ($this->consumer->getSettings() as
$name => $value) {
653 if (strpos($name,
'custom_') === 0) {
654 $this->consumer->setSetting($name);
655 $doSaveConsumer =
true;
658 if (!empty($this->context)) {
659 foreach ($this->context->getSettings() as $name => $value) {
660 if (strpos($name,
'custom_') === 0) {
661 $this->context->setSetting($name);
665 foreach ($this->resourceLink->getSettings() as $name => $value) {
666 if (strpos($name,
'custom_') === 0) {
667 $this->resourceLink->setSetting($name);
671 foreach (self::$LTI_CONSUMER_SETTING_NAMES as $name) {
672 if (isset(
$_POST[$name])) {
673 $this->consumer->setSetting($name,
$_POST[$name]);
675 $this->consumer->setSetting($name);
678 if (!empty($this->context)) {
679 foreach (self::$LTI_CONTEXT_SETTING_NAMES as $name) {
680 if (isset(
$_POST[$name])) {
681 $this->context->setSetting($name,
$_POST[$name]);
683 $this->context->setSetting($name);
687 foreach (self::$LTI_RESOURCE_LINK_SETTING_NAMES as $name) {
688 if (isset(
$_POST[$name])) {
689 $this->resourceLink->setSetting($name,
$_POST[$name]);
691 $this->resourceLink->setSetting($name);
695 foreach (
$_POST as $name => $value) {
696 if ((strpos($name,
'custom_') === 0) &&
697 !in_array($name, array_merge(self::$LTI_CONSUMER_SETTING_NAMES, self::$LTI_CONTEXT_SETTING_NAMES, self::$LTI_RESOURCE_LINK_SETTING_NAMES))) {
698 $this->resourceLink->setSetting($name, $value);
705 if (isset(
$_POST[
'user_id'])) {
706 $userId = trim(
$_POST[
'user_id']);
709 $this->
user = User::fromResourceLink($this->resourceLink, $userId);
712 $firstname = (isset(
$_POST[
'lis_person_name_given'])) ?
$_POST[
'lis_person_name_given'] :
'';
713 $lastname = (isset(
$_POST[
'lis_person_name_family'])) ?
$_POST[
'lis_person_name_family'] :
'';
714 $fullname = (isset(
$_POST[
'lis_person_name_full'])) ?
$_POST[
'lis_person_name_full'] :
'';
715 $this->
user->setNames($firstname, $lastname, $fullname);
718 $email = (isset(
$_POST[
'lis_person_contact_email_primary'])) ?
$_POST[
'lis_person_contact_email_primary'] :
'';
719 $this->
user->setEmail(
$email, $this->defaultEmail);
722 if (isset(
$_POST[
'user_image'])) {
727 if (isset(
$_POST[
'roles'])) {
728 $this->
user->roles = self::parseRoles(
$_POST[
'roles']);
732 $this->consumer->defaultEmail = $this->defaultEmail;
733 if ($this->consumer->ltiVersion !==
$_POST[
'lti_version']) {
734 $this->consumer->ltiVersion =
$_POST[
'lti_version'];
735 $doSaveConsumer =
true;
737 if (isset(
$_POST[
'tool_consumer_instance_name'])) {
738 if ($this->consumer->consumerName !==
$_POST[
'tool_consumer_instance_name']) {
739 $this->consumer->consumerName =
$_POST[
'tool_consumer_instance_name'];
740 $doSaveConsumer =
true;
743 if (isset(
$_POST[
'tool_consumer_info_product_family_code'])) {
745 if (isset(
$_POST[
'tool_consumer_info_version'])) {
746 $version .=
"-{$_POST['tool_consumer_info_version']}";
749 if ($this->consumer->consumerVersion !==
$version) {
750 $this->consumer->consumerVersion =
$version;
751 $doSaveConsumer =
true;
753 } elseif (isset(
$_POST[
'ext_lms']) && ($this->consumer->consumerName !==
$_POST[
'ext_lms'])) {
754 $this->consumer->consumerVersion =
$_POST[
'ext_lms'];
755 $doSaveConsumer =
true;
757 if (isset(
$_POST[
'tool_consumer_instance_guid'])) {
758 if (is_null($this->consumer->consumerGuid)) {
759 $this->consumer->consumerGuid =
$_POST[
'tool_consumer_instance_guid'];
760 $doSaveConsumer =
true;
761 } elseif (!$this->consumer->protected) {
762 $doSaveConsumer = ($this->consumer->consumerGuid !==
$_POST[
'tool_consumer_instance_guid']);
763 if ($doSaveConsumer) {
764 $this->consumer->consumerGuid =
$_POST[
'tool_consumer_instance_guid'];
768 if (isset(
$_POST[
'launch_presentation_css_url'])) {
769 if ($this->consumer->cssPath !==
$_POST[
'launch_presentation_css_url']) {
770 $this->consumer->cssPath =
$_POST[
'launch_presentation_css_url'];
771 $doSaveConsumer =
true;
773 } elseif (isset(
$_POST[
'ext_launch_presentation_css_url']) &&
774 ($this->consumer->cssPath !==
$_POST[
'ext_launch_presentation_css_url'])) {
775 $this->consumer->cssPath =
$_POST[
'ext_launch_presentation_css_url'];
776 $doSaveConsumer =
true;
777 } elseif (!empty($this->consumer->cssPath)) {
778 $this->consumer->cssPath = null;
779 $doSaveConsumer =
true;
784 if ($doSaveConsumer) {
785 $this->consumer->save();
787 if ($this->ok && isset($this->context)) {
788 $this->context->save();
794 if ($this->ok && isset($this->resourceLink)) {
799 $this->resourceLink->save();
802 if (isset(
$_POST[
'lis_result_sourcedid'])) {
803 if ($this->
user->ltiResultSourcedId !==
$_POST[
'lis_result_sourcedid']) {
804 $this->
user->ltiResultSourcedId =
$_POST[
'lis_result_sourcedid'];
807 } elseif (!empty($this->
user->ltiResultSourcedId)) {
808 $this->
user->ltiResultSourcedId =
'';
824 $doSaveResourceLink =
true;
826 $id = $this->resourceLink->primaryResourceLinkId;
828 $shareRequest = isset(
$_POST[
'custom_share_key']) && !empty(
$_POST[
'custom_share_key']);
830 if (!$this->allowSharing) {
832 $this->reason =
'Your sharing request has been refused because sharing is not being permitted.';
836 if (!is_null($shareKey->primaryConsumerKey) && !is_null($shareKey->primaryResourceLinkId)) {
838 $key = $shareKey->primaryConsumerKey;
839 $id = $shareKey->primaryResourceLinkId;
840 $ok = (
$key !== $this->consumer->getKey()) || (
$id != $this->resourceLink->getId());
842 $this->resourceLink->primaryConsumerKey =
$key;
843 $this->resourceLink->primaryResourceLinkId =
$id;
844 $this->resourceLink->shareApproved = $shareKey->autoApprove;
845 $ok = $this->resourceLink->save();
847 $doSaveResourceLink =
false;
848 $this->
user->getResourceLink()->primaryConsumerKey =
$key;
849 $this->
user->getResourceLink()->primaryResourceLinkId =
$id;
850 $this->
user->getResourceLink()->shareApproved = $shareKey->autoApprove;
851 $this->
user->getResourceLink()->updated = time();
855 $this->reason =
'An error occurred initialising your share arrangement.';
858 $this->reason =
'It is not possible to share your resource link with yourself.';
864 $this->reason =
'You have requested to share a resource link but none is available.';
866 $ok = (!is_null($this->
user->getResourceLink()->shareApproved) && $this->
user->getResourceLink()->shareApproved);
868 $this->reason =
'Your share request is waiting to be approved.';
877 $this->reason =
'You have not requested to share a resource link but an arrangement is currently in place.';
881 if (
$ok && !is_null(
$id)) {
884 $ok = !is_null($consumer->created);
886 $resourceLink = ResourceLink::fromConsumer($consumer,
$id);
887 $ok = !is_null($resourceLink->created);
890 if ($doSaveResourceLink) {
891 $this->resourceLink->save();
893 $this->resourceLink = $resourceLink;
895 $this->reason =
'Unable to load resource link being shared.';
909 if (!
$ok && !empty($reason)) {
910 $this->reason = sprintf($reason, $value);
Class to represent an OAuth Data Store.
foreach($paths as $path) $request
if(!array_key_exists('StateId', $_REQUEST)) $id
if(! $oauthconfig->getBoolean('getUserInfo.enable', FALSE)) $store
catch(Exception $e) $message
foreach($_POST as $key=> $value) $res
Class to represent a tool consumer context.
Class to represent an HTTP message.