158 private $consumer = null;
164 private $consumerId = null;
170 private $context = null;
176 private $contextId = null;
182 private $settings = null;
188 private $settingsChanged =
false;
194 private $extDoc = null;
200 private $extNodes = null;
206 private $dataConnector = null;
225 $this->settings = array();
226 $this->groupSets = null;
227 $this->groups = null;
228 $this->primaryResourceLinkId = null;
229 $this->shareApproved = null;
230 $this->created = null;
231 $this->updated = null;
257 $this->settingsChanged =
false;
269 public function delete()
284 if (is_null($this->consumer)) {
285 if (!is_null($this->context) || !is_null($this->contextId)) {
286 $this->consumer = $this->
getContext()->getConsumer();
292 return $this->consumer;
304 $this->consumer = null;
305 $this->consumerId = $consumerId;
317 if (is_null($this->context) && !is_null($this->contextId)) {
321 return $this->context;
333 return $this->contextId;
345 $this->context = null;
346 $this->contextId = $contextId;
406 return $this->dataConnector;
421 if (array_key_exists($name, $this->settings)) {
422 $value = $this->settings[$name];
441 if ($value !== $old_value) {
442 if (!empty($value)) {
443 $this->settings[$name] = $value;
445 unset($this->settings[$name]);
447 $this->settingsChanged =
true;
460 return $this->settings;
472 $this->settings = $settings;
484 if ($this->settingsChanged) {
502 $url = $this->
getSetting(
'ext_ims_lis_basic_outcome_url') . $this->
getSetting(
'lis_outcome_service_url');
516 $url = $this->
getSetting(
'ext_ims_lis_memberships_url');
530 $url = $this->
getSetting(
'ext_ims_lti_tool_setting_url');
549 $this->extResponse = null;
552 $sourceResourceLink = $user->getResourceLink();
553 $sourcedId = $user->ltiResultSourcedId;
556 $urlLTI11 = $sourceResourceLink->getSetting(
'lis_outcome_service_url');
557 $urlExt = $sourceResourceLink->getSetting(
'ext_ims_lis_basic_outcome_url');
558 if ($urlExt || $urlLTI11) {
561 if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
563 }
else if ($urlExt) {
565 $do =
'basic-lis-readresult';
568 case self::EXT_WRITE:
569 if ($urlLTI11 && $this->checkValueType($ltiOutcome, array(self::EXT_TYPE_DECIMAL))) {
570 $do =
'replaceResult';
571 }
else if ($this->checkValueType($ltiOutcome)) {
573 $do =
'basic-lis-updateresult';
576 case self::EXT_DELETE:
577 if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
578 $do =
'deleteResult';
579 }
else if ($urlExt) {
581 $do =
'basic-lis-deleteresult';
587 $value = $ltiOutcome->getValue();
588 if (is_null($value)) {
593 if ($action === self::EXT_WRITE) {
598 <language>{$ltiOutcome->language}</language>
599 <textString>{$value}</textString>
604 $sourcedId = htmlentities($sourcedId);
608 <sourcedId>{$sourcedId}</sourcedId>
612 if ($this->doLTI11Service($do, $urlLTI11, $xml)) {
615 if (!isset($this->extNodes[
'imsx_POXBody'][
"{$do}Response"][
'result'][
'resultScore'][
'textString'])) {
618 $ltiOutcome->setValue($this->extNodes[
'imsx_POXBody'][
"{$do}Response"][
'result'][
'resultScore'][
'textString']);
620 case self::EXT_WRITE:
621 case self::EXT_DELETE:
628 $params[
'sourcedid'] = $sourcedId;
629 $params[
'result_resultscore_textstring'] = $value;
630 if (!empty($ltiOutcome->language)) {
631 $params[
'result_resultscore_language'] = $ltiOutcome->language;
633 if (!empty($ltiOutcome->status)) {
634 $params[
'result_statusofresult'] = $ltiOutcome->status;
636 if (!empty($ltiOutcome->date)) {
637 $params[
'result_date'] = $ltiOutcome->date;
639 if (!empty($ltiOutcome->type)) {
640 $params[
'result_resultvaluesourcedid'] = $ltiOutcome->type;
642 if (!empty($ltiOutcome->data_source)) {
643 $params[
'result_datasource'] = $ltiOutcome->data_source;
645 if ($this->doService($do, $urlExt, $params)) {
648 if (isset($this->extNodes[
'result'][
'resultscore'][
'textstring'])) {
649 $response = $this->extNodes[
'result'][
'resultscore'][
'textstring'];
652 case self::EXT_WRITE:
653 case self::EXT_DELETE:
659 if (is_array($response) && (count($response) <= 0)) {
682 $this->extResponse = null;
683 $url = $this->
getSetting(
'ext_ims_lis_memberships_url');
685 $params[
'id'] = $this->
getSetting(
'ext_ims_lis_memberships_id');
688 $ok = $this->doService(
'basic-lis-readmembershipsforcontextwithgroups', $url, $params);
691 $this->groupSets = array();
692 $this->groups = array();
694 $ok = $this->doService(
'basic-lis-readmembershipsforcontext', $url, $params);
698 if (!isset($this->extNodes[
'memberships'][
'member'])) {
700 }
else if (!isset($this->extNodes[
'memberships'][
'member'][0])) {
702 $members[0] = $this->extNodes[
'memberships'][
'member'];
704 $members = $this->extNodes[
'memberships'][
'member'];
707 for ($i = 0; $i < count($members); $i++) {
712 $firstname = (isset($members[$i][
'person_name_given'])) ? $members[$i][
'person_name_given'] :
'';
713 $lastname = (isset($members[$i][
'person_name_family'])) ? $members[$i][
'person_name_family'] :
'';
714 $fullname = (isset($members[$i][
'person_name_full'])) ? $members[$i][
'person_name_full'] :
'';
715 $user->setNames($firstname, $lastname, $fullname);
718 $email = (isset($members[$i][
'person_contact_email_primary'])) ? $members[$i][
'person_contact_email_primary'] :
'';
719 $user->setEmail($email, $this->
getConsumer()->defaultEmail);
722 if (isset($members[$i][
'roles'])) {
727 if (!isset($members[$i][
'groups'][
'group'])) {
729 }
else if (!isset($members[$i][
'groups'][
'group'][0])) {
731 $groups[0] = $members[$i][
'groups'][
'group'];
733 $groups = $members[$i][
'groups'][
'group'];
735 for ($j = 0; $j < count(
$groups); $j++) {
737 if (isset($group[
'set'])) {
738 $set_id = $group[
'set'][
'id'];
739 if (!isset($this->groupSets[$set_id])) {
740 $this->groupSets[$set_id] = array(
'title' => $group[
'set'][
'title'],
'groups' => array(),
741 'num_members' => 0,
'num_staff' => 0,
'num_learners' => 0);
743 $this->groupSets[$set_id][
'num_members']++;
744 if ($user->isStaff()) {
745 $this->groupSets[$set_id][
'num_staff']++;
747 if ($user->isLearner()) {
748 $this->groupSets[$set_id][
'num_learners']++;
750 if (!in_array($group[
'id'], $this->groupSets[$set_id][
'groups'])) {
751 $this->groupSets[$set_id][
'groups'][] = $group[
'id'];
753 $this->groups[$group[
'id']] = array(
'title' => $group[
'title'],
'set' => $set_id);
755 $this->groups[$group[
'id']] = array(
'title' => $group[
'title']);
757 $user->groups[] = $group[
'id'];
761 if (isset($members[$i][
'lis_result_sourcedid'])) {
762 $user->ltiResultSourcedId = $members[$i][
'lis_result_sourcedid'];
772 foreach ($oldUsers as $id => $user) {
795 $this->extResponse = null;
798 $do =
'basic-lti-loadsetting';
800 case self::EXT_WRITE:
801 $do =
'basic-lti-savesetting';
803 case self::EXT_DELETE:
804 $do =
'basic-lti-deletesetting';
809 $url = $this->
getSetting(
'ext_ims_lti_tool_setting_url');
811 $params[
'id'] = $this->
getSetting(
'ext_ims_lti_tool_setting_id');
812 if (is_null($value)) {
815 $params[
'setting'] = $value;
817 if ($this->doService($do, $url, $params)) {
820 if (isset($this->extNodes[
'setting'][
'value'])) {
821 $response = $this->extNodes[
'setting'][
'value'];
822 if (is_array($response)) {
827 case self::EXT_WRITE:
828 $this->
setSetting(
'ext_ims_lti_tool_setting', $value);
832 case self::EXT_DELETE:
851 $url = $this->
getSetting(
'custom_link_setting_url');
868 $url = $this->
getSetting(
'custom_link_setting_url');
870 $response = $service->get($mode);
886 $url = $this->
getSetting(
'custom_link_setting_url');
888 $response = $service->set($settings);
902 $has = !empty($this->contextId);
920 if (!empty($this->contextId)) {
921 $url = $this->
getContext()->getSetting(
'custom_context_memberships_url');
924 $response = $service->get();
946 return $this->
getDataConnector()->getUserResultSourcedIDsResourceLink($this, $localOnly, $idScope);
974 $resourceLink->consumer = $consumer;
975 $resourceLink->dataConnector = $consumer->getDataConnector();
978 $resourceLink->load();
979 if (is_null($resourceLink->id) && !empty($tempId)) {
980 $resourceLink->ltiResourceLinkId = $tempId;
981 $resourceLink->load();
986 return $resourceLink;
1002 $resourceLink->setContextId($context->getRecordId());
1003 $resourceLink->context = $context;
1004 $resourceLink->dataConnector = $context->getDataConnector();
1007 $resourceLink->load();
1008 if (is_null($resourceLink->id) && !empty($tempId)) {
1009 $resourceLink->ltiResourceLinkId = $tempId;
1010 $resourceLink->load();
1015 return $resourceLink;
1031 $resourceLink->dataConnector = $dataConnector;
1032 $resourceLink->load($id);
1034 return $resourceLink;
1049 private function load($id = null)
1067 private function checkValueType($ltiOutcome, $supportedTypes = null)
1070 if (empty($supportedTypes)) {
1071 $supportedTypes = explode(
',', str_replace(
' ',
'', strtolower($this->
getSetting(
'ext_ims_lis_resultvalue_sourcedids', self::EXT_TYPE_DECIMAL))));
1073 $type = $ltiOutcome->type;
1074 $value = $ltiOutcome->getValue();
1076 $ok = in_array($type, $supportedTypes) || (strlen($value) <= 0);
1079 if ($type === self::EXT_TYPE_PERCENTAGE) {
1080 if (substr($value, -1) ===
'%') {
1081 $value = substr($value, 0, -1);
1083 $ok = is_numeric($value) && ($value >= 0) && ($value <= 100);
1085 $ltiOutcome->setValue($value / 100);
1086 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1088 }
else if ($type === self::EXT_TYPE_RATIO) {
1089 $parts = explode(
'/', $value, 2);
1090 $ok = (count($parts) === 2) && is_numeric($parts[0]) && is_numeric($parts[1]) && ($parts[0] >= 0) && ($parts[1] > 0);
1092 $ltiOutcome->setValue($parts[0] / $parts[1]);
1093 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1096 }
else if ($type === self::EXT_TYPE_LETTER_AF) {
1097 if (in_array(self::EXT_TYPE_LETTER_AF_PLUS, $supportedTypes)) {
1099 $ltiOutcome->type = self::EXT_TYPE_LETTER_AF_PLUS;
1100 }
else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1102 $ltiOutcome->type = self::EXT_TYPE_TEXT;
1105 }
else if ($type === self::EXT_TYPE_LETTER_AF_PLUS) {
1106 if (in_array(self::EXT_TYPE_LETTER_AF, $supportedTypes) && (strlen($value) === 1)) {
1108 $ltiOutcome->type = self::EXT_TYPE_LETTER_AF;
1109 }
else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1111 $ltiOutcome->type = self::EXT_TYPE_TEXT;
1114 }
else if ($type === self::EXT_TYPE_TEXT) {
1115 $ok = is_numeric($value) && ($value >= 0) && ($value <=1);
1117 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1118 }
else if (substr($value, -1) ===
'%') {
1119 $value = substr($value, 0, -1);
1120 $ok = is_numeric($value) && ($value >= 0) && ($value <=100);
1122 if (in_array(self::EXT_TYPE_PERCENTAGE, $supportedTypes)) {
1123 $ltiOutcome->type = self::EXT_TYPE_PERCENTAGE;
1125 $ltiOutcome->setValue($value / 100);
1126 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1146 private function doService($type, $url, $params)
1150 $this->extRequest = null;
1151 $this->extRequestHeaders =
'';
1152 $this->extResponse = null;
1153 $this->extResponseHeaders =
'';
1159 if ($http->send()) {
1160 $this->extResponse = $http->response;
1161 $this->extResponseHeaders = $http->responseHeaders;
1164 $this->extDoc->loadXML($http->response);
1165 $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1166 if (isset($this->extNodes[
'statusinfo'][
'codemajor']) && ($this->extNodes[
'statusinfo'][
'codemajor'] ===
'Success')) {
1169 }
catch (\Exception $e) {
1172 $this->extRequest = $http->request;
1173 $this->extRequestHeaders = $http->requestHeaders;
1189 private function doLTI11Service($type, $url, $xml)
1193 $this->extRequest = null;
1194 $this->extRequestHeaders =
'';
1195 $this->extResponse = null;
1196 $this->extResponseHeaders =
'';
1199 $xmlRequest = <<< EOD
1200 <?xml version =
"1.0" encoding =
"UTF-8"?>
1201 <imsx_POXEnvelopeRequest xmlns =
"http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
1203 <imsx_POXRequestHeaderInfo>
1204 <imsx_version>V1.0</imsx_version>
1205 <imsx_messageIdentifier>{$id}</imsx_messageIdentifier>
1206 </imsx_POXRequestHeaderInfo>
1213 </imsx_POXEnvelopeRequest>
1216 $hash = base64_encode(sha1($xmlRequest,
true));
1217 $params = array(
'oauth_body_hash' => $hash);
1223 $req->sign_request($hmacMethod, $consumer, null);
1224 $params = $req->get_parameters();
1225 $header = $req->to_header();
1226 $header .=
"\nContent-Type: application/xml";
1228 $http =
new HTTPMessage($url,
'POST', $xmlRequest, $header);
1230 if ($http->send()) {
1231 $this->extResponse = $http->response;
1232 $this->extResponseHeaders = $http->responseHeaders;
1235 $this->extDoc->loadXML($http->response);
1236 $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1237 if (isset($this->extNodes[
'imsx_POXHeader'][
'imsx_POXResponseHeaderInfo'][
'imsx_statusInfo'][
'imsx_codeMajor']) &&
1238 ($this->extNodes[
'imsx_POXHeader'][
'imsx_POXResponseHeaderInfo'][
'imsx_statusInfo'][
'imsx_codeMajor'] ===
'success')) {
1241 }
catch (\Exception $e) {
1244 $this->extRequest = $http->request;
1245 $this->extRequestHeaders = $http->requestHeaders;
1259 private function domnodeToArray($node)
1263 switch ($node->nodeType) {
1264 case XML_CDATA_SECTION_NODE:
1266 $output = trim($node->textContent);
1268 case XML_ELEMENT_NODE:
1269 for ($i = 0; $i < $node->childNodes->length; $i++) {
1270 $child = $node->childNodes->item($i);
1271 $v = $this->domnodeToArray($child);
1272 if (isset($child->tagName)) {
1273 $t = $child->tagName;
1274 if (!isset($output[$t])) {
1275 $output[$t] = array();
1280 if (strlen($s) > 0) {
1285 if (is_array($output)) {
1286 if ($node->attributes->length) {
1288 foreach ($node->attributes as $attrName => $attrNode) {
1289 $a[$attrName] = (string) $attrNode->value;
1291 $output[
'@attributes'] = $a;
1293 foreach ($output as $t => $v) {
1294 if (is_array($v) && count($v)==1 && $t!=
'@attributes') {
1295 $output[$t] = $v[0];
static from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=null)
pretty much a helper function to set up the request
Class to represent an OAuth Consumer.
static fromRecordId($id, $dataConnector)
Load the context from the database.
Class to represent an HTTP message.
Class to represent an OAuth HMAC_SHA1 signature method.