ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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  // begin-patch ilias
286  #if (!is_null($this->context) || !is_null($this->contextId)) {
287  if($this->context || $this->contextId) {
288  $this->consumer = $this->getContext()->getConsumer();
289  } else {
290  $this->consumer = ToolConsumer::fromRecordId($this->consumerId, $this->getDataConnector());
291  }
292  }
293 
294  return $this->consumer;
295 
296  }
297 
303  public function setConsumerId($consumerId)
304  {
305 
306  $this->consumer = null;
307  $this->consumerId = $consumerId;
308 
309  }
310 
316  public function getContext()
317  {
318 
319  if (is_null($this->context) && !is_null($this->contextId)) {
320  $this->context = Context::fromRecordId($this->contextId, $this->getDataConnector());
321  }
322 
323  return $this->context;
324 
325  }
326 
332  public function getContextId()
333  {
334 
335  return $this->contextId;
336 
337  }
338 
344  public function setContextId($contextId)
345  {
346 
347  $this->context = null;
348  $this->contextId = $contextId;
349 
350  }
351 
357  public function getKey()
358  {
359 
360  return $this->getConsumer()->getKey();
361 
362  }
363 
369  public function getId()
370  {
371 
373 
374  }
375 
381  public function getRecordId()
382  {
383 
384  return $this->id;
385 
386  }
387 
393  public function setRecordId($id)
394  {
395 
396  $this->id = $id;
397 
398  }
399 
405  public function getDataConnector()
406  {
407 
408  return $this->dataConnector;
409 
410  }
411 
420  public function getSetting($name, $default = '')
421  {
422 
423  if (array_key_exists($name, $this->settings)) {
424  $value = $this->settings[$name];
425  } else {
426  $value = $default;
427  }
428 
429  return $value;
430 
431  }
432 
439  public function setSetting($name, $value = null)
440  {
441 
442  $old_value = $this->getSetting($name);
443  if ($value !== $old_value) {
444  if (!empty($value)) {
445  $this->settings[$name] = $value;
446  } else {
447  unset($this->settings[$name]);
448  }
449  $this->settingsChanged = true;
450  }
451 
452  }
453 
459  public function getSettings()
460  {
461 
462  return $this->settings;
463 
464  }
465 
471  public function setSettings($settings)
472  {
473 
474  $this->settings = $settings;
475 
476  }
477 
483  public function saveSettings()
484  {
485 
486  if ($this->settingsChanged) {
487  $ok = $this->save();
488  } else {
489  $ok = true;
490  }
491 
492  return $ok;
493 
494  }
495 
501  public function hasOutcomesService()
502  {
503 
504  $url = $this->getSetting('ext_ims_lis_basic_outcome_url') . $this->getSetting('lis_outcome_service_url');
505 
506  return !empty($url);
507 
508  }
509 
515  public function hasMembershipsService()
516  {
517 
518  $url = $this->getSetting('ext_ims_lis_memberships_url');
519 
520  return !empty($url);
521 
522  }
523 
529  public function hasSettingService()
530  {
531 
532  $url = $this->getSetting('ext_ims_lti_tool_setting_url');
533 
534  return !empty($url);
535 
536  }
537 
547  public function doOutcomesService($action, $ltiOutcome, $user)
548  {
549  global $DIC;
550  $logger = $DIC->logger()->lti();
551 
552  $response = false;
553  $this->extResponse = null;
554 
555 // Lookup service details from the source resource link appropriate to the user (in case the destination is being shared)
556  $sourceResourceLink = $user->getResourceLink();
557  $sourcedId = $user->ltiResultSourcedId;
558 
559 // Use LTI 1.1 service in preference to extension service if it is available
560  $urlLTI11 = $sourceResourceLink->getSetting('lis_outcome_service_url');
561  $urlExt = $sourceResourceLink->getSetting('ext_ims_lis_basic_outcome_url');
562  if ($urlExt || $urlLTI11) {
563  switch ($action) {
564  case self::EXT_READ:
565  if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
566  $do = 'readResult';
567  } else if ($urlExt) {
568  $urlLTI11 = null;
569  $do = 'basic-lis-readresult';
570  }
571  break;
572  case self::EXT_WRITE:
573  if ($urlLTI11 && $this->checkValueType($ltiOutcome, array(self::EXT_TYPE_DECIMAL))) {
574  $do = 'replaceResult';
575  } else if ($this->checkValueType($ltiOutcome)) {
576  $urlLTI11 = null;
577  $do = 'basic-lis-updateresult';
578  }
579  break;
580  case self::EXT_DELETE:
581  if ($urlLTI11 && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
582  $do = 'deleteResult';
583  } else if ($urlExt) {
584  $urlLTI11 = null;
585  $do = 'basic-lis-deleteresult';
586  }
587  break;
588  }
589  }
590 
591  $logger->debug('Command is: ' . $do);
592 
593  if (isset($do)) {
594  $value = $ltiOutcome->getValue();
595  if (is_null($value)) {
596  $value = '';
597  }
598  if ($urlLTI11) {
599  $xml = '';
600  if ($action === self::EXT_WRITE) {
601  $xml = <<<EOF
602 
603  <result>
604  <resultScore>
605  <language>{$ltiOutcome->language}</language>
606  <textString>{$value}</textString>
607  </resultScore>
608  </result>
609 EOF;
610  }
611  $sourcedId = htmlentities($sourcedId);
612  $xml = <<<EOF
613  <resultRecord>
614  <sourcedGUID>
615  <sourcedId>{$sourcedId}</sourcedId>
616  </sourcedGUID>{$xml}
617  </resultRecord>
618 EOF;
619 
620  $logger->debug($urlLTI11);
621  $logger->debug('xml for doLTI11Service: ' . $xml);
622 
623  if ($this->doLTI11Service($do, $urlLTI11, $xml)) {
624  switch ($action) {
625  case self::EXT_READ:
626  if (!isset($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString'])) {
627  break;
628  } else {
629  $ltiOutcome->setValue($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString']);
630  }
631  case self::EXT_WRITE:
632  case self::EXT_DELETE:
633  $response = true;
634  break;
635  }
636  }
637  } else {
638  $params = array();
639  $params['sourcedid'] = $sourcedId;
640  $params['result_resultscore_textstring'] = $value;
641  if (!empty($ltiOutcome->language)) {
642  $params['result_resultscore_language'] = $ltiOutcome->language;
643  }
644  if (!empty($ltiOutcome->status)) {
645  $params['result_statusofresult'] = $ltiOutcome->status;
646  }
647  if (!empty($ltiOutcome->date)) {
648  $params['result_date'] = $ltiOutcome->date;
649  }
650  if (!empty($ltiOutcome->type)) {
651  $params['result_resultvaluesourcedid'] = $ltiOutcome->type;
652  }
653  if (!empty($ltiOutcome->data_source)) {
654  $params['result_datasource'] = $ltiOutcome->data_source;
655  }
656 
657  $logger->debug($urlExt);
658  $logger->dump($params);
659 
660 
661  if ($this->doService($do, $urlExt, $params)) {
662  switch ($action) {
663  case self::EXT_READ:
664  if (isset($this->extNodes['result']['resultscore']['textstring'])) {
665  $response = $this->extNodes['result']['resultscore']['textstring'];
666  }
667  break;
668  case self::EXT_WRITE:
669  case self::EXT_DELETE:
670  $response = true;
671  break;
672  }
673  }
674  }
675  if (is_array($response) && (count($response) <= 0)) {
676  $response = '';
677  }
678  }
679 
680  return $response;
681 
682  }
683 
693  public function doMembershipsService($withGroups = false)
694  {
695 
696  $users = array();
698  $this->extResponse = null;
699  $url = $this->getSetting('ext_ims_lis_memberships_url');
700  $params = array();
701  $params['id'] = $this->getSetting('ext_ims_lis_memberships_id');
702  $ok = false;
703  if ($withGroups) {
704  $ok = $this->doService('basic-lis-readmembershipsforcontextwithgroups', $url, $params);
705  }
706  if ($ok) {
707  $this->groupSets = array();
708  $this->groups = array();
709  } else {
710  $ok = $this->doService('basic-lis-readmembershipsforcontext', $url, $params);
711  }
712 
713  if ($ok) {
714  if (!isset($this->extNodes['memberships']['member'])) {
715  $members = array();
716  } else if (!isset($this->extNodes['memberships']['member'][0])) {
717  $members = array();
718  $members[0] = $this->extNodes['memberships']['member'];
719  } else {
720  $members = $this->extNodes['memberships']['member'];
721  }
722 
723  for ($i = 0; $i < count($members); $i++) {
724 
725  $user = User::fromResourceLink($this, $members[$i]['user_id']);
726 
727 // Set the user name
728  $firstname = (isset($members[$i]['person_name_given'])) ? $members[$i]['person_name_given'] : '';
729  $lastname = (isset($members[$i]['person_name_family'])) ? $members[$i]['person_name_family'] : '';
730  $fullname = (isset($members[$i]['person_name_full'])) ? $members[$i]['person_name_full'] : '';
731  $user->setNames($firstname, $lastname, $fullname);
732 
733 // Set the user email
734  $email = (isset($members[$i]['person_contact_email_primary'])) ? $members[$i]['person_contact_email_primary'] : '';
735  $user->setEmail($email, $this->getConsumer()->defaultEmail);
736 
738  if (isset($members[$i]['roles'])) {
739  $user->roles = ToolProvider::parseRoles($members[$i]['roles']);
740  }
741 
742 // Set the user groups
743  if (!isset($members[$i]['groups']['group'])) {
744  $groups = array();
745  } else if (!isset($members[$i]['groups']['group'][0])) {
746  $groups = array();
747  $groups[0] = $members[$i]['groups']['group'];
748  } else {
749  $groups = $members[$i]['groups']['group'];
750  }
751  for ($j = 0; $j < count($groups); $j++) {
752  $group = $groups[$j];
753  if (isset($group['set'])) {
754  $set_id = $group['set']['id'];
755  if (!isset($this->groupSets[$set_id])) {
756  $this->groupSets[$set_id] = array('title' => $group['set']['title'], 'groups' => array(),
757  'num_members' => 0, 'num_staff' => 0, 'num_learners' => 0);
758  }
759  $this->groupSets[$set_id]['num_members']++;
760  if ($user->isStaff()) {
761  $this->groupSets[$set_id]['num_staff']++;
762  }
763  if ($user->isLearner()) {
764  $this->groupSets[$set_id]['num_learners']++;
765  }
766  if (!in_array($group['id'], $this->groupSets[$set_id]['groups'])) {
767  $this->groupSets[$set_id]['groups'][] = $group['id'];
768  }
769  $this->groups[$group['id']] = array('title' => $group['title'], 'set' => $set_id);
770  } else {
771  $this->groups[$group['id']] = array('title' => $group['title']);
772  }
773  $user->groups[] = $group['id'];
774  }
775 
776 // If a result sourcedid is provided save the user
777  if (isset($members[$i]['lis_result_sourcedid'])) {
778  $user->ltiResultSourcedId = $members[$i]['lis_result_sourcedid'];
779  $user->save();
780  }
781  $users[] = $user;
782 
783 // Remove old user (if it exists)
784  unset($oldUsers[$user->getId(ToolProvider::ID_SCOPE_RESOURCE)]);
785  }
786 
787 // Delete any old users which were not in the latest list from the tool consumer
788  foreach ($oldUsers as $id => $user) {
789  $user->delete();
790  }
791  } else {
792  $users = false;
793  }
794 
795  return $users;
796 
797  }
798 
807  public function doSettingService($action, $value = null)
808  {
809 
810  $response = false;
811  $this->extResponse = null;
812  switch ($action) {
813  case self::EXT_READ:
814  $do = 'basic-lti-loadsetting';
815  break;
816  case self::EXT_WRITE:
817  $do = 'basic-lti-savesetting';
818  break;
819  case self::EXT_DELETE:
820  $do = 'basic-lti-deletesetting';
821  break;
822  }
823  if (isset($do)) {
824 
825  $url = $this->getSetting('ext_ims_lti_tool_setting_url');
826  $params = array();
827  $params['id'] = $this->getSetting('ext_ims_lti_tool_setting_id');
828  if (is_null($value)) {
829  $value = '';
830  }
831  $params['setting'] = $value;
832 
833  if ($this->doService($do, $url, $params)) {
834  switch ($action) {
835  case self::EXT_READ:
836  if (isset($this->extNodes['setting']['value'])) {
837  $response = $this->extNodes['setting']['value'];
838  if (is_array($response)) {
839  $response = '';
840  }
841  }
842  break;
843  case self::EXT_WRITE:
844  $this->setSetting('ext_ims_lti_tool_setting', $value);
845  $this->saveSettings();
846  $response = true;
847  break;
848  case self::EXT_DELETE:
849  $response = true;
850  break;
851  }
852  }
853  }
854 
855  return $response;
856 
857  }
858 
864  public function hasToolSettingsService()
865  {
866 
867  $url = $this->getSetting('custom_link_setting_url');
868 
869  return !empty($url);
870 
871  }
872 
881  public function getToolSettings($mode = Service\ToolSettings::MODE_CURRENT_LEVEL, $simple = true)
882  {
883 
884  $url = $this->getSetting('custom_link_setting_url');
885  $service = new Service\ToolSettings($this, $url, $simple);
886  $response = $service->get($mode);
887 
888  return $response;
889 
890  }
891 
899  public function setToolSettings($settings = array())
900  {
901 
902  $url = $this->getSetting('custom_link_setting_url');
903  $service = new Service\ToolSettings($this, $url);
904  $response = $service->set($settings);
905 
906  return $response;
907 
908  }
909 
915  public function hasMembershipService()
916  {
917 
918  $has = !empty($this->contextId);
919  if ($has) {
920  $has = !empty($this->getContext()->getSetting('custom_context_memberships_url'));
921  }
922 
923  return $has;
924 
925  }
926 
932  public function getMembership()
933  {
934 
935  $response = false;
936  if (!empty($this->contextId)) {
937  $url = $this->getContext()->getSetting('custom_context_memberships_url');
938  if (!empty($url)) {
939  $service = new Service\Membership($this, $url);
940  $response = $service->get();
941  }
942  }
943 
944  return $response;
945 
946  }
947 
959  public function getUserResultSourcedIDs($localOnly = false, $idScope = null)
960  {
961 
962  return $this->getDataConnector()->getUserResultSourcedIDsResourceLink($this, $localOnly, $idScope);
963 
964  }
965 
971  public function getShares()
972  {
973 
974  return $this->getDataConnector()->getSharesResourceLink($this);
975 
976  }
977 
986  public static function fromConsumer($consumer, $ltiResourceLinkId, $tempId = null)
987  {
988 
989  $resourceLink = new ResourceLink();
990  $resourceLink->consumer = $consumer;
991  $resourceLink->dataConnector = $consumer->getDataConnector();
992  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
993  if (!empty($ltiResourceLinkId)) {
994  $resourceLink->load();
995  if (is_null($resourceLink->id) && !empty($tempId)) {
996  $resourceLink->ltiResourceLinkId = $tempId;
997  $resourceLink->load();
998  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
999  }
1000  }
1001 
1002  return $resourceLink;
1003 
1004  }
1005 
1014  public static function fromContext($context, $ltiResourceLinkId, $tempId = null)
1015  {
1016 
1017  $resourceLink = new ResourceLink();
1018  $resourceLink->setContextId($context->getRecordId());
1019  $resourceLink->context = $context;
1020  $resourceLink->dataConnector = $context->getDataConnector();
1021  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1022  if (!empty($ltiResourceLinkId)) {
1023  $resourceLink->load();
1024  if (is_null($resourceLink->id) && !empty($tempId)) {
1025  $resourceLink->ltiResourceLinkId = $tempId;
1026  $resourceLink->load();
1027  $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1028  }
1029  }
1030 
1031  return $resourceLink;
1032 
1033  }
1034 
1043  public static function fromRecordId($id, $dataConnector)
1044  {
1045  $resourceLink = new ResourceLink();
1046  $resourceLink->dataConnector = $dataConnector;
1047  $resourceLink->load($id);
1048 
1049  return $resourceLink;
1050 
1051  }
1052 
1053 ###
1054 ### PRIVATE METHODS
1055 ###
1056 
1064  private function load($id = null)
1065  {
1066 
1067  $this->initialize();
1068  $this->id = $id;
1069 
1070  return $this->getDataConnector()->loadResourceLink($this);
1071 
1072  }
1073 
1082  private function checkValueType($ltiOutcome, $supportedTypes = null)
1083  {
1084 
1085  if (empty($supportedTypes)) {
1086  $supportedTypes = explode(',', str_replace(' ', '', strtolower($this->getSetting('ext_ims_lis_resultvalue_sourcedids', self::EXT_TYPE_DECIMAL))));
1087  }
1088  $type = $ltiOutcome->type;
1089  $value = $ltiOutcome->getValue();
1090 // Check whether the type is supported or there is no value
1091  $ok = in_array($type, $supportedTypes) || (strlen($value) <= 0);
1092  if (!$ok) {
1093 // Convert numeric values to decimal
1094  if ($type === self::EXT_TYPE_PERCENTAGE) {
1095  if (substr($value, -1) === '%') {
1096  $value = substr($value, 0, -1);
1097  }
1098  $ok = is_numeric($value) && ($value >= 0) && ($value <= 100);
1099  if ($ok) {
1100  $ltiOutcome->setValue($value / 100);
1101  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1102  }
1103  } else if ($type === self::EXT_TYPE_RATIO) {
1104  $parts = explode('/', $value, 2);
1105  $ok = (count($parts) === 2) && is_numeric($parts[0]) && is_numeric($parts[1]) && ($parts[0] >= 0) && ($parts[1] > 0);
1106  if ($ok) {
1107  $ltiOutcome->setValue($parts[0] / $parts[1]);
1108  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1109  }
1110 // Convert letter_af to letter_af_plus or text
1111  } else if ($type === self::EXT_TYPE_LETTER_AF) {
1112  if (in_array(self::EXT_TYPE_LETTER_AF_PLUS, $supportedTypes)) {
1113  $ok = true;
1114  $ltiOutcome->type = self::EXT_TYPE_LETTER_AF_PLUS;
1115  } else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1116  $ok = true;
1117  $ltiOutcome->type = self::EXT_TYPE_TEXT;
1118  }
1119 // Convert letter_af_plus to letter_af or text
1120  } else if ($type === self::EXT_TYPE_LETTER_AF_PLUS) {
1121  if (in_array(self::EXT_TYPE_LETTER_AF, $supportedTypes) && (strlen($value) === 1)) {
1122  $ok = true;
1123  $ltiOutcome->type = self::EXT_TYPE_LETTER_AF;
1124  } else if (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1125  $ok = true;
1126  $ltiOutcome->type = self::EXT_TYPE_TEXT;
1127  }
1128 // Convert text to decimal
1129  } else if ($type === self::EXT_TYPE_TEXT) {
1130  $ok = is_numeric($value) && ($value >= 0) && ($value <=1);
1131  if ($ok) {
1132  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1133  } else if (substr($value, -1) === '%') {
1134  $value = substr($value, 0, -1);
1135  $ok = is_numeric($value) && ($value >= 0) && ($value <=100);
1136  if ($ok) {
1137  if (in_array(self::EXT_TYPE_PERCENTAGE, $supportedTypes)) {
1138  $ltiOutcome->type = self::EXT_TYPE_PERCENTAGE;
1139  } else {
1140  $ltiOutcome->setValue($value / 100);
1141  $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1142  }
1143  }
1144  }
1145  }
1146  }
1147 
1148  return $ok;
1149 
1150  }
1151 
1161  private function doService($type, $url, $params)
1162  {
1163 
1164  $ok = false;
1165  $this->extRequest = null;
1166  $this->extRequestHeaders = '';
1167  $this->extResponse = null;
1168  $this->extResponseHeaders = '';
1169  if (!empty($url)) {
1170  $params = $this->getConsumer()->signParameters($url, $type, $this->getConsumer()->ltiVersion, $params);
1171 // Connect to tool consumer
1172  $http = new HTTPMessage($url, 'POST', $params);
1173  \ilLoggerFactory::getLogger('lti')->debug('Sending to ' . $url);
1174  \ilLoggerFactory::getLogger('lti')->dump($params);
1175 
1176 // Parse XML response
1177  if ($http->send()) {
1178  $this->extResponse = $http->response;
1179  $this->extResponseHeaders = $http->responseHeaders;
1180 
1181  \ilLoggerFactory::getLogger('lti')->debug('Response: ' . $http->response);
1182  \ilLoggerFactory::getLogger('lti')->debug('Response: ' . $http->responseHeaders);
1183 
1184  try {
1185  $this->extDoc = new DOMDocument();
1186  $this->extDoc->loadXML($http->response);
1187  $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1188  if (isset($this->extNodes['statusinfo']['codemajor']) && ($this->extNodes['statusinfo']['codemajor'] === 'Success')) {
1189  $ok = true;
1190  }
1191  } catch (\Exception $e) {
1192  \ilLoggerFactory::getLogger('lti')->warning('Outcome failed with message: ' . $e->getMessage());
1193  }
1194  }
1195  $this->extRequest = $http->request;
1196  $this->extRequestHeaders = $http->requestHeaders;
1197 
1198  \ilLoggerFactory::getLogger('lti')->debug('Request: ' . $http->request);
1199  \ilLoggerFactory::getLogger('lti')->debug('RequestHeaders: ' . $http->requestHeaders);
1200  }
1201 
1202  return $ok;
1203 
1204  }
1205 
1215  private function doLTI11Service($type, $url, $xml)
1216  {
1217 
1218  $ok = false;
1219  $this->extRequest = null;
1220  $this->extRequestHeaders = '';
1221  $this->extResponse = null;
1222  $this->extResponseHeaders = '';
1223  if (!empty($url)) {
1224  $id = uniqid();
1225  $xmlRequest = <<< EOD
1226 <?xml version = "1.0" encoding = "UTF-8"?>
1227 <imsx_POXEnvelopeRequest xmlns = "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
1228  <imsx_POXHeader>
1229  <imsx_POXRequestHeaderInfo>
1230  <imsx_version>V1.0</imsx_version>
1231  <imsx_messageIdentifier>{$id}</imsx_messageIdentifier>
1232  </imsx_POXRequestHeaderInfo>
1233  </imsx_POXHeader>
1234  <imsx_POXBody>
1235  <{$type}Request>
1236 {$xml}
1237  </{$type}Request>
1238  </imsx_POXBody>
1239 </imsx_POXEnvelopeRequest>
1240 EOD;
1241 // Calculate body hash
1242  $hash = base64_encode(sha1($xmlRequest, true));
1243  $params = array('oauth_body_hash' => $hash);
1244 
1245 // Add OAuth signature
1246  $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
1247  $consumer = new OAuth\OAuthConsumer($this->getConsumer()->getKey(), $this->getConsumer()->secret, null);
1249  $req->sign_request($hmacMethod, $consumer, null);
1250  $params = $req->get_parameters();
1251  $header = $req->to_header();
1252  $header .= "\nContent-Type: application/xml";
1253 // Connect to tool consumer
1254  $http = new HTTPMessage($url, 'POST', $xmlRequest, $header);
1255 
1256  \ilLoggerFactory::getLogger('lti')->debug('Sending post: ' . $header);
1257  \ilLoggerFactory::getLogger('lti')->debug('Sending post: ' . $xmlRequest);
1258 
1259 // Parse XML response
1260  if ($http->send()) {
1261  $this->extResponse = $http->response;
1262  $this->extResponseHeaders = $http->responseHeaders;
1263  \ilLoggerFactory::getLogger('lti')->debug('Got response: ' . $http->response);
1264  \ilLoggerFactory::getLogger('lti')->debug('Got response: ' . $http->responseHeaders);
1265  try {
1266  $this->extDoc = new DOMDocument();
1267  $this->extDoc->loadXML($http->response);
1268  $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1269  if (isset($this->extNodes['imsx_POXHeader']['imsx_POXResponseHeaderInfo']['imsx_statusInfo']['imsx_codeMajor']) &&
1270  ($this->extNodes['imsx_POXHeader']['imsx_POXResponseHeaderInfo']['imsx_statusInfo']['imsx_codeMajor'] === 'success')) {
1271  $ok = true;
1272  }
1273  } catch (\Exception $e) {
1274  \ilLoggerFactory::getLogger('lti')->warning('lti 1.1 outcome failed with message: ' . $e->getMessage());
1275  }
1276  }
1277  else {
1278  \ilLoggerFactory::getLogger('lti')->debug('Got response: ' . $http->response);
1279  \ilLoggerFactory::getLogger('lti')->debug('Got response: ' . $http->responseHeaders);
1280  }
1281  $this->extRequest = $http->request;
1282  $this->extRequestHeaders = $http->requestHeaders;
1283  }
1284 
1285  return $ok;
1286 
1287  }
1288 
1296  private function domnodeToArray($node)
1297  {
1298 
1299  $output = '';
1300  switch ($node->nodeType) {
1301  case XML_CDATA_SECTION_NODE:
1302  case XML_TEXT_NODE:
1303  $output = trim($node->textContent);
1304  break;
1305  case XML_ELEMENT_NODE:
1306  for ($i = 0; $i < $node->childNodes->length; $i++) {
1307  $child = $node->childNodes->item($i);
1308  $v = $this->domnodeToArray($child);
1309  if (isset($child->tagName)) {
1310  $t = $child->tagName;
1311  if (!isset($output[$t])) {
1312  $output[$t] = array();
1313  }
1314  $output[$t][] = $v;
1315  } else {
1316  $s = (string) $v;
1317  if (strlen($s) > 0) {
1318  $output = $s;
1319  }
1320  }
1321  }
1322  if (is_array($output)) {
1323  if ($node->attributes->length) {
1324  $a = array();
1325  foreach ($node->attributes as $attrName => $attrNode) {
1326  $a[$attrName] = (string) $attrNode->value;
1327  }
1328  $output['@attributes'] = $a;
1329  }
1330  foreach ($output as $t => $v) {
1331  if (is_array($v) && count($v)==1 && $t!='@attributes') {
1332  $output[$t] = $v[0];
1333  }
1334  }
1335  }
1336  break;
1337  }
1338 
1339  return $output;
1340 
1341  }
1342 
1343 }
if($orgName !==null) if($spconfig->hasValue('contacts')) $email
Definition: metadata.php:201
static parseRoles($roles)
Get an array of fully qualified user roles.
static fromRecordId($id, $dataConnector)
Load the context from the database.
Definition: Context.php:407
settings()
Definition: settings.php:2
const ID_SCOPE_RESOURCE
Prefix the ID with the consumer key and resource ID.
$action
$type
global $DIC
Definition: saml.php:7
Class to implement the Membership service.
Definition: Membership.php:16
static fromRecordId($id, $dataConnector)
Load the tool consumer from the database by its record ID.
$s
Definition: pwgen.php:45
$req
Definition: getUserInfo.php:20
static fromResourceLink($resourceLink, $ltiUserId)
Class constructor from resource link.
Definition: User.php:413
static from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=null)
pretty much a helper function to set up the request
const MODE_CURRENT_LEVEL
Settings at current level mode.
Class to represent an OAuth Consumer.
$http
Definition: raiseError.php:7
EOD
Definition: example_053.php:93
$user
Definition: migrateto20.php:57
$users
Definition: authpage.php:44
$default
Definition: build.php:20
Class to implement a service.
Definition: Service.php:17
$i
Definition: disco.tpl.php:19
static getLogger($a_component_id)
Get component logger.
$url
$response
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.
const EOF
How fgetc() reports an End Of File.
Definition: JSMin_lib.php:92