LTI Tool Provider Library  3.0.2
PHP class library for building LTI Tool Providers
ResourceLink.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use DOMDocument;
6 use DOMElement;
11 
22 {
23 
27  const EXT_READ = 1;
31  const EXT_WRITE = 2;
35  const EXT_DELETE = 3;
39  const EXT_CREATE = 4;
43  const EXT_UPDATE = 5;
44 
48  const EXT_TYPE_DECIMAL = 'decimal';
52  const EXT_TYPE_PERCENTAGE = 'percentage';
56  const EXT_TYPE_RATIO = 'ratio';
60  const EXT_TYPE_LETTER_AF = 'letteraf';
64  const EXT_TYPE_LETTER_AF_PLUS = 'letterafplus';
68  const EXT_TYPE_PASS_FAIL = 'passfail';
72  const EXT_TYPE_TEXT = 'freetext';
73 
79  public $title = null;
85  public $ltiResourceLinkId = null;
91  public $groupSets = null;
97  public $groups = null;
103  public $extRequest = null;
109  public $extRequestHeaders = null;
115  public $extResponse = null;
121  public $extResponseHeaders = null;
127  public $primaryResourceLinkId = null;
133  public $shareApproved = null;
139  public $created = null;
145  public $updated = null;
146 
152  private $id = null;
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;
207 
211  public function __construct()
212  {
213 
214  $this->initialize();
215 
216  }
217 
221  public function initialize()
222  {
223 
224  $this->title = '';
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;
232 
233  }
234 
240  public function initialise()
241  {
242 
243  $this->initialize();
244 
245  }
246 
252  public function save()
253  {
254 
255  $ok = $this->getDataConnector()->saveResourceLink($this);
256  if ($ok) {
257  $this->settingsChanged = false;
258  }
259 
260  return $ok;
261 
262  }
263 
269  public function delete()
270  {
271 
272  return $this->getDataConnector()->deleteResourceLink($this);
273 
274  }
275 
281  public function getConsumer()
282  {
283 
284  if (is_null($this->consumer)) {
285  if (!is_null($this->context) || !is_null($this->contextId)) {
286  $this->consumer = $this->getContext()->getConsumer();
287  } else {
288  $this->consumer = ToolConsumer::fromRecordId($this->consumerId, $this->getDataConnector());
289  }
290  }
291 
292  return $this->consumer;
293 
294  }
295 
301  public function setConsumerId($consumerId)
302  {
303 
304  $this->consumer = null;
305  $this->consumerId = $consumerId;
306 
307  }
308 
314  public function getContext()
315  {
316 
317  if (is_null($this->context) && !is_null($this->contextId)) {
318  $this->context = Context::fromRecordId($this->contextId, $this->getDataConnector());
319  }
320 
321  return $this->context;
322 
323  }
324 
330  public function getContextId()
331  {
332 
333  return $this->contextId;
334 
335  }
336 
342  public function setContextId($contextId)
343  {
344 
345  $this->context = null;
346  $this->contextId = $contextId;
347 
348  }
349 
355  public function getKey()
356  {
357 
358  return $this->getConsumer()->getKey();
359 
360  }
361 
367  public function getId()
368  {
369 
371 
372  }
373 
379  public function getRecordId()
380  {
381 
382  return $this->id;
383 
384  }
385 
391  public function setRecordId($id)
392  {
393 
394  $this->id = $id;
395 
396  }
397 
403  public function getDataConnector()
404  {
405 
406  return $this->dataConnector;
407 
408  }
409 
418  public function getSetting($name, $default = '')
419  {
420 
421  if (array_key_exists($name, $this->settings)) {
422  $value = $this->settings[$name];
423  } else {
424  $value = $default;
425  }
426 
427  return $value;
428 
429  }
430 
437  public function setSetting($name, $value = null)
438  {
439 
440  $old_value = $this->getSetting($name);
441  if ($value !== $old_value) {
442  if (!empty($value)) {
443  $this->settings[$name] = $value;
444  } else {
445  unset($this->settings[$name]);
446  }
447  $this->settingsChanged = true;
448  }
449 
450  }
451 
457  public function getSettings()
458  {
459 
460  return $this->settings;
461 
462  }
463 
469  public function setSettings($settings)
470  {
471 
472  $this->settings = $settings;
473 
474  }
475 
481  public function saveSettings()
482  {
483 
484  if ($this->settingsChanged) {
485  $ok = $this->save();
486  } else {
487  $ok = true;
488  }
489 
490  return $ok;
491 
492  }
493 
499  public function hasOutcomesService()
500  {
501 
502  $url = $this->getSetting('ext_ims_lis_basic_outcome_url') . $this->getSetting('lis_outcome_service_url');
503 
504  return !empty($url);
505 
506  }
507 
513  public function hasMembershipsService()
514  {
515 
516  $url = $this->getSetting('ext_ims_lis_memberships_url');
517 
518  return !empty($url);
519 
520  }
521 
527  public function hasSettingService()
528  {
529 
530  $url = $this->getSetting('ext_ims_lti_tool_setting_url');
531 
532  return !empty($url);
533 
534  }
535 
545  public function doOutcomesService($action, $ltiOutcome, $user)
546  {
547 
548  $response = false;
549  $this->extResponse = null;
550 
551 // Lookup service details from the source resource link appropriate to the user (in case the destination is being shared)
552  $sourceResourceLink = $user->getResourceLink();
553  $sourcedId = $user->ltiResultSourcedId;
554 
555 // Use LTI 1.1 service in preference to extension service if it is available
556  $urlLTI11 = $sourceResourceLink->getSetting('lis_outcome_service_url');
557  $urlExt = $sourceResourceLink->getSetting('ext_ims_lis_basic_outcome_url');
558  if ($urlExt || $urlLTI11) {
559  switch ($action) {
560  case self::EXT_READ:
561  if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
562  $do = 'readResult';
563  } else if ($urlExt) {
564  $urlLTI11 = null;
565  $do = 'basic-lis-readresult';
566  }
567  break;
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)) {
572  $urlLTI11 = null;
573  $do = 'basic-lis-updateresult';
574  }
575  break;
576  case self::EXT_DELETE:
577  if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
578  $do = 'deleteResult';
579  } else if ($urlExt) {
580  $urlLTI11 = null;
581  $do = 'basic-lis-deleteresult';
582  }
583  break;
584  }
585  }
586  if (isset($do)) {
587  $value = $ltiOutcome->getValue();
588  if (is_null($value)) {
589  $value = '';
590  }
591  if ($urlLTI11) {
592  $xml = '';
593  if ($action === self::EXT_WRITE) {
594  $xml = <<<EOF
595 
596  <result>
597  <resultScore>
598  <language>{$ltiOutcome->language}</language>
599  <textString>{$value}</textString>
600  </resultScore>
601  </result>
602 EOF;
603  }
604  $sourcedId = htmlentities($sourcedId);
605  $xml = <<<EOF
606  <resultRecord>
607  <sourcedGUID>
608  <sourcedId>{$sourcedId}</sourcedId>
609  </sourcedGUID>{$xml}
610  </resultRecord>
611 EOF;
612  if ($this->doLTI11Service($do, $urlLTI11, $xml)) {
613  switch ($action) {
614  case self::EXT_READ:
615  if (!isset($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString'])) {
616  break;
617  } else {
618  $ltiOutcome->setValue($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString']);
619  }
620  case self::EXT_WRITE:
621  case self::EXT_DELETE:
622  $response = true;
623  break;
624  }
625  }
626  } else {
627  $params = array();
628  $params['sourcedid'] = $sourcedId;
629  $params['result_resultscore_textstring'] = $value;
630  if (!empty($ltiOutcome->language)) {
631  $params['result_resultscore_language'] = $ltiOutcome->language;
632  }
633  if (!empty($ltiOutcome->status)) {
634  $params['result_statusofresult'] = $ltiOutcome->status;
635  }
636  if (!empty($ltiOutcome->date)) {
637  $params['result_date'] = $ltiOutcome->date;
638  }
639  if (!empty($ltiOutcome->type)) {
640  $params['result_resultvaluesourcedid'] = $ltiOutcome->type;
641  }
642  if (!empty($ltiOutcome->data_source)) {
643  $params['result_datasource'] = $ltiOutcome->data_source;
644  }
645  if ($this->doService($do, $urlExt, $params)) {
646  switch ($action) {
647  case self::EXT_READ:
648  if (isset($this->extNodes['result']['resultscore']['textstring'])) {
649  $response = $this->extNodes['result']['resultscore']['textstring'];
650  }
651  break;
652  case self::EXT_WRITE:
653  case self::EXT_DELETE:
654  $response = true;
655  break;
656  }
657  }
658  }
659  if (is_array($response) && (count($response) <= 0)) {
660  $response = '';
661  }
662  }
663 
664  return $response;
665 
666  }
667 
677  public function doMembershipsService($withGroups = false)
678  {
679 
680  $users = array();
682  $this->extResponse = null;
683  $url = $this->getSetting('ext_ims_lis_memberships_url');
684  $params = array();
685  $params['id'] = $this->getSetting('ext_ims_lis_memberships_id');
686  $ok = false;
687  if ($withGroups) {
688  $ok = $this->doService('basic-lis-readmembershipsforcontextwithgroups', $url, $params);
689  }
690  if ($ok) {
691  $this->groupSets = array();
692  $this->groups = array();
693  } else {
694  $ok = $this->doService('basic-lis-readmembershipsforcontext', $url, $params);
695  }
696 
697  if ($ok) {
698  if (!isset($this->extNodes['memberships']['member'])) {
699  $members = array();
700  } else if (!isset($this->extNodes['memberships']['member'][0])) {
701  $members = array();
702  $members[0] = $this->extNodes['memberships']['member'];
703  } else {
704  $members = $this->extNodes['memberships']['member'];
705  }
706 
707  for ($i = 0; $i < count($members); $i++) {
708 
709  $user = User::fromResourceLink($this, $members[$i]['user_id']);
710 
711 // Set the user name
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);
716 
717 // Set the user email
718  $email = (isset($members[$i]['person_contact_email_primary'])) ? $members[$i]['person_contact_email_primary'] : '';
719  $user->setEmail($email, $this->getConsumer()->defaultEmail);
720 
722  if (isset($members[$i]['roles'])) {
723  $user->roles = ToolProvider::parseRoles($members[$i]['roles']);
724  }
725 
726 // Set the user groups
727  if (!isset($members[$i]['groups']['group'])) {
728  $groups = array();
729  } else if (!isset($members[$i]['groups']['group'][0])) {
730  $groups = array();
731  $groups[0] = $members[$i]['groups']['group'];
732  } else {
733  $groups = $members[$i]['groups']['group'];
734  }
735  for ($j = 0; $j < count($groups); $j++) {
736  $group = $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);
742  }
743  $this->groupSets[$set_id]['num_members']++;
744  if ($user->isStaff()) {
745  $this->groupSets[$set_id]['num_staff']++;
746  }
747  if ($user->isLearner()) {
748  $this->groupSets[$set_id]['num_learners']++;
749  }
750  if (!in_array($group['id'], $this->groupSets[$set_id]['groups'])) {
751  $this->groupSets[$set_id]['groups'][] = $group['id'];
752  }
753  $this->groups[$group['id']] = array('title' => $group['title'], 'set' => $set_id);
754  } else {
755  $this->groups[$group['id']] = array('title' => $group['title']);
756  }
757  $user->groups[] = $group['id'];
758  }
759 
760 // If a result sourcedid is provided save the user
761  if (isset($members[$i]['lis_result_sourcedid'])) {
762  $user->ltiResultSourcedId = $members[$i]['lis_result_sourcedid'];
763  $user->save();
764  }
765  $users[] = $user;
766 
767 // Remove old user (if it exists)
768  unset($oldUsers[$user->getId(ToolProvider::ID_SCOPE_RESOURCE)]);
769  }
770 
771 // Delete any old users which were not in the latest list from the tool consumer
772  foreach ($oldUsers as $id => $user) {
773  $user->delete();
774  }
775  } else {
776  $users = false;
777  }
778 
779  return $users;
780 
781  }
782 
791  public function doSettingService($action, $value = null)
792  {
793 
794  $response = false;
795  $this->extResponse = null;
796  switch ($action) {
797  case self::EXT_READ:
798  $do = 'basic-lti-loadsetting';
799  break;
800  case self::EXT_WRITE:
801  $do = 'basic-lti-savesetting';
802  break;
803  case self::EXT_DELETE:
804  $do = 'basic-lti-deletesetting';
805  break;
806  }
807  if (isset($do)) {
808 
809  $url = $this->getSetting('ext_ims_lti_tool_setting_url');
810  $params = array();
811  $params['id'] = $this->getSetting('ext_ims_lti_tool_setting_id');
812  if (is_null($value)) {
813  $value = '';
814  }
815  $params['setting'] = $value;
816 
817  if ($this->doService($do, $url, $params)) {
818  switch ($action) {
819  case self::EXT_READ:
820  if (isset($this->extNodes['setting']['value'])) {
821  $response = $this->extNodes['setting']['value'];
822  if (is_array($response)) {
823  $response = '';
824  }
825  }
826  break;
827  case self::EXT_WRITE:
828  $this->setSetting('ext_ims_lti_tool_setting', $value);
829  $this->saveSettings();
830  $response = true;
831  break;
832  case self::EXT_DELETE:
833  $response = true;
834  break;
835  }
836  }
837  }
838 
839  return $response;
840 
841  }
842 
848  public function hasToolSettingsService()
849  {
850 
851  $url = $this->getSetting('custom_link_setting_url');
852 
853  return !empty($url);
854 
855  }
856 
865  public function getToolSettings($mode = Service\ToolSettings::MODE_CURRENT_LEVEL, $simple = true)
866  {
867 
868  $url = $this->getSetting('custom_link_setting_url');
869  $service = new Service\ToolSettings($this, $url, $simple);
870  $response = $service->get($mode);
871 
872  return $response;
873 
874  }
875 
883  public function setToolSettings($settings = array())
884  {
885 
886  $url = $this->getSetting('custom_link_setting_url');
887  $service = new Service\ToolSettings($this, $url);
888  $response = $service->set($settings);
889 
890  return $response;
891 
892  }
893 
899  public function hasMembershipService()
900  {
901 
902  $has = !empty($this->contextId);
903  if ($has) {
904  $has = !empty($this->getContext()->getSetting('custom_context_memberships_url'));
905  }
906 
907  return $has;
908 
909  }
910 
916  public function getMembership()
917  {
918 
919  $response = false;
920  if (!empty($this->contextId)) {
921  $url = $this->getContext()->getSetting('custom_context_memberships_url');
922  if (!empty($url)) {
923  $service = new Service\Membership($this, $url);
924  $response = $service->get();
925  }
926  }
927 
928  return $response;
929 
930  }
931 
943  public function getUserResultSourcedIDs($localOnly = false, $idScope = null)
944  {
945 
946  return $this->getDataConnector()->getUserResultSourcedIDsResourceLink($this, $localOnly, $idScope);
947 
948  }
949 
955  public function getShares()
956  {
957 
958  return $this->getDataConnector()->getSharesResourceLink($this);
959 
960  }
961 
970  public static function fromConsumer($consumer, $ltiResourceLinkId, $tempId = null)
971  {
972 
973  $resourceLink = new ResourceLink();
974  $resourceLink->consumer = $consumer;
975  $resourceLink->dataConnector = $consumer->getDataConnector();
976  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
977  if (!empty($ltiResourceLinkId)) {
978  $resourceLink->load();
979  if (is_null($resourceLink->id) && !empty($tempId)) {
980  $resourceLink->ltiResourceLinkId = $tempId;
981  $resourceLink->load();
982  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
983  }
984  }
985 
986  return $resourceLink;
987 
988  }
989 
998  public static function fromContext($context, $ltiResourceLinkId, $tempId = null)
999  {
1000 
1001  $resourceLink = new ResourceLink();
1002  $resourceLink->setContextId($context->getRecordId());
1003  $resourceLink->context = $context;
1004  $resourceLink->dataConnector = $context->getDataConnector();
1005  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1006  if (!empty($ltiResourceLinkId)) {
1007  $resourceLink->load();
1008  if (is_null($resourceLink->id) && !empty($tempId)) {
1009  $resourceLink->ltiResourceLinkId = $tempId;
1010  $resourceLink->load();
1011  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1012  }
1013  }
1014 
1015  return $resourceLink;
1016 
1017  }
1018 
1027  public static function fromRecordId($id, $dataConnector)
1028  {
1029 
1030  $resourceLink = new ResourceLink();
1031  $resourceLink->dataConnector = $dataConnector;
1032  $resourceLink->load($id);
1033 
1034  return $resourceLink;
1035 
1036  }
1037 
1038 ###
1039 ### PRIVATE METHODS
1040 ###
1041 
1049  private function load($id = null)
1050  {
1051 
1052  $this->initialize();
1053  $this->id = $id;
1054 
1055  return $this->getDataConnector()->loadResourceLink($this);
1056 
1057  }
1058 
1067  private function checkValueType($ltiOutcome, $supportedTypes = null)
1068  {
1069 
1070  if (empty($supportedTypes)) {
1071  $supportedTypes = explode(',', str_replace(' ', '', strtolower($this->getSetting('ext_ims_lis_resultvalue_sourcedids', self::EXT_TYPE_DECIMAL))));
1072  }
1073  $type = $ltiOutcome->type;
1074  $value = $ltiOutcome->getValue();
1075 // Check whether the type is supported or there is no value
1076  $ok = in_array($type, $supportedTypes) || (strlen($value) <= 0);
1077  if (!$ok) {
1078 // Convert numeric values to decimal
1079  if ($type === self::EXT_TYPE_PERCENTAGE) {
1080  if (substr($value, -1) === '%') {
1081  $value = substr($value, 0, -1);
1082  }
1083  $ok = is_numeric($value) && ($value >= 0) && ($value <= 100);
1084  if ($ok) {
1085  $ltiOutcome->setValue($value / 100);
1086  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1087  }
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);
1091  if ($ok) {
1092  $ltiOutcome->setValue($parts[0] / $parts[1]);
1093  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1094  }
1095 // Convert letter_af to letter_af_plus or text
1096  } else if ($type === self::EXT_TYPE_LETTER_AF) {
1097  if (in_array(self::EXT_TYPE_LETTER_AF_PLUS, $supportedTypes)) {
1098  $ok = true;
1099  $ltiOutcome->type = self::EXT_TYPE_LETTER_AF_PLUS;
1100  } else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1101  $ok = true;
1102  $ltiOutcome->type = self::EXT_TYPE_TEXT;
1103  }
1104 // Convert letter_af_plus to letter_af or text
1105  } else if ($type === self::EXT_TYPE_LETTER_AF_PLUS) {
1106  if (in_array(self::EXT_TYPE_LETTER_AF, $supportedTypes) && (strlen($value) === 1)) {
1107  $ok = true;
1108  $ltiOutcome->type = self::EXT_TYPE_LETTER_AF;
1109  } else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1110  $ok = true;
1111  $ltiOutcome->type = self::EXT_TYPE_TEXT;
1112  }
1113 // Convert text to decimal
1114  } else if ($type === self::EXT_TYPE_TEXT) {
1115  $ok = is_numeric($value) && ($value >= 0) && ($value <=1);
1116  if ($ok) {
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);
1121  if ($ok) {
1122  if (in_array(self::EXT_TYPE_PERCENTAGE, $supportedTypes)) {
1123  $ltiOutcome->type = self::EXT_TYPE_PERCENTAGE;
1124  } else {
1125  $ltiOutcome->setValue($value / 100);
1126  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1127  }
1128  }
1129  }
1130  }
1131  }
1132 
1133  return $ok;
1134 
1135  }
1136 
1146  private function doService($type, $url, $params)
1147  {
1148 
1149  $ok = false;
1150  $this->extRequest = null;
1151  $this->extRequestHeaders = '';
1152  $this->extResponse = null;
1153  $this->extResponseHeaders = '';
1154  if (!empty($url)) {
1155  $params = $this->getConsumer()->signParameters($url, $type, $this->getConsumer()->ltiVersion, $params);
1156 // Connect to tool consumer
1157  $http = new HTTPMessage($url, 'POST', $params);
1158 // Parse XML response
1159  if ($http->send()) {
1160  $this->extResponse = $http->response;
1161  $this->extResponseHeaders = $http->responseHeaders;
1162  try {
1163  $this->extDoc = new DOMDocument();
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')) {
1167  $ok = true;
1168  }
1169  } catch (\Exception $e) {
1170  }
1171  }
1172  $this->extRequest = $http->request;
1173  $this->extRequestHeaders = $http->requestHeaders;
1174  }
1175 
1176  return $ok;
1177 
1178  }
1179 
1189  private function doLTI11Service($type, $url, $xml)
1190  {
1191 
1192  $ok = false;
1193  $this->extRequest = null;
1194  $this->extRequestHeaders = '';
1195  $this->extResponse = null;
1196  $this->extResponseHeaders = '';
1197  if (!empty($url)) {
1198  $id = uniqid();
1199  $xmlRequest = <<< EOD
1200 <?xml version = "1.0" encoding = "UTF-8"?>
1201 <imsx_POXEnvelopeRequest xmlns = "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
1202  <imsx_POXHeader>
1203  <imsx_POXRequestHeaderInfo>
1204  <imsx_version>V1.0</imsx_version>
1205  <imsx_messageIdentifier>{$id}</imsx_messageIdentifier>
1206  </imsx_POXRequestHeaderInfo>
1207  </imsx_POXHeader>
1208  <imsx_POXBody>
1209  <{$type}Request>
1210 {$xml}
1211  </{$type}Request>
1212  </imsx_POXBody>
1213 </imsx_POXEnvelopeRequest>
1214 EOD;
1215 // Calculate body hash
1216  $hash = base64_encode(sha1($xmlRequest, true));
1217  $params = array('oauth_body_hash' => $hash);
1218 
1219 // Add OAuth signature
1220  $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
1221  $consumer = new OAuth\OAuthConsumer($this->getConsumer()->getKey(), $this->getConsumer()->secret, null);
1222  $req = OAuth\OAuthRequest::from_consumer_and_token($consumer, null, 'POST', $url, $params);
1223  $req->sign_request($hmacMethod, $consumer, null);
1224  $params = $req->get_parameters();
1225  $header = $req->to_header();
1226  $header .= "\nContent-Type: application/xml";
1227 // Connect to tool consumer
1228  $http = new HTTPMessage($url, 'POST', $xmlRequest, $header);
1229 // Parse XML response
1230  if ($http->send()) {
1231  $this->extResponse = $http->response;
1232  $this->extResponseHeaders = $http->responseHeaders;
1233  try {
1234  $this->extDoc = new DOMDocument();
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')) {
1239  $ok = true;
1240  }
1241  } catch (\Exception $e) {
1242  }
1243  }
1244  $this->extRequest = $http->request;
1245  $this->extRequestHeaders = $http->requestHeaders;
1246  }
1247 
1248  return $ok;
1249 
1250  }
1251 
1259  private function domnodeToArray($node)
1260  {
1261 
1262  $output = '';
1263  switch ($node->nodeType) {
1264  case XML_CDATA_SECTION_NODE:
1265  case XML_TEXT_NODE:
1266  $output = trim($node->textContent);
1267  break;
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();
1276  }
1277  $output[$t][] = $v;
1278  } else {
1279  $s = (string) $v;
1280  if (strlen($s) > 0) {
1281  $output = $s;
1282  }
1283  }
1284  }
1285  if (is_array($output)) {
1286  if ($node->attributes->length) {
1287  $a = array();
1288  foreach ($node->attributes as $attrName => $attrNode) {
1289  $a[$attrName] = (string) $attrNode->value;
1290  }
1291  $output['@attributes'] = $a;
1292  }
1293  foreach ($output as $t => $v) {
1294  if (is_array($v) && count($v)==1 && $t!='@attributes') {
1295  $output[$t] = $v[0];
1296  }
1297  }
1298  }
1299  break;
1300  }
1301 
1302  return $output;
1303 
1304  }
1305 
1306 }
static fromRecordId($id, $dataConnector)
Load the tool consumer from the database by its record ID.
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 implement the Membership service.
Definition: Membership.php:16
static fromResourceLink($resourceLink, $ltiUserId)
Class constructor from resource link.
Definition: User.php:413
Class to represent an OAuth Consumer.
static parseRoles($roles)
Get an array of fully qualified user roles.
const MODE_CURRENT_LEVEL
Settings at current level mode.
Class to implement a service.
Definition: Service.php:17
const ID_SCOPE_RESOURCE
Prefix the ID with the consumer key and resource ID.
static fromRecordId($id, $dataConnector)
Load the context from the database.
Definition: Context.php:407
Class to implement the Tool Settings service.
Class to represent an HTTP message.
Definition: HTTPMessage.php:14
Class to represent an OAuth HMAC_SHA1 signature method.