ILIAS  release_8 Revision v8.24
ResourceLink.php
Go to the documentation of this file.
1<?php
2
20
23use ILIAS\LTI\ToolProvider\Http\HTTPMessage;
25use DOMDocument;
26use DOMElement;
27
36{
37 use ApiHook;
38
42 public const EXT_READ = 1;
43
47 public const EXT_WRITE = 2;
48
52 public const EXT_DELETE = 3;
53
57 public const EXT_CREATE = 4;
58
62 public const EXT_UPDATE = 5;
63
67 public const EXT_TYPE_DECIMAL = 'decimal';
68
72 public const EXT_TYPE_PERCENTAGE = 'percentage';
73
77 public const EXT_TYPE_RATIO = 'ratio';
78
82 public const EXT_TYPE_LETTER_AF = 'letteraf';
83
87 public const EXT_TYPE_LETTER_AF_PLUS = 'letterafplus';
88
92 public const EXT_TYPE_PASS_FAIL = 'passfail';
93
97 public const EXT_TYPE_TEXT = 'freetext';
98
104 public ?string $title = null;
105
111 public ?string $ltiResourceLinkId = null;
112
126 public ?array $groupSets = null;
127
138 public ?array $groups = null;
139
145 public ?HTTPMessage $lastServiceRequest = null;
146
152 public ?string $extRequest = null;
153
159 public $extRequestHeaders = null;
160
166 public ?string $extResponse = null;
167
173 public $extResponseHeaders = null;
174
180 public ?string $primaryResourceLinkId = null;
181
187 public ?bool $shareApproved = null;
188
194 public ?int $created = null;
195
201 public ?int $updated = null;
202
208 private ?int $id = null;
209
215 private ?Platform $platform = null;
216
222 private ?int $platformId = null;
223
229 private ?Context $context = null;
230
236 private ?int $contextId = null;
237
243 private ?array $settings = null;
244
250 private bool $settingsChanged = false;
251
257 private ?DOMDocument $extDoc = null;
258
264 private ?array $extNodes = null;
265
272
276 public function __construct()
277 {
278 $this->initialize();
279 }
280
284 public function initialize()
285 {
286 $this->title = '';
287 $this->settings = array();
288 $this->groupSets = null;
289 $this->groups = null;
290 $this->primaryResourceLinkId = null;
291 $this->shareApproved = null;
292 $this->created = null;
293 $this->updated = null;
294 }
295
301 public function initialise()
302 {
303 $this->initialize();
304 }
305
311 public function save(): bool
312 {
313 $ok = $this->getDataConnector()->saveResourceLink($this);
314 if ($ok) {
315 $this->settingsChanged = false;
316 }
317
318 return $ok;
319 }
320
326 public function delete(): bool
327 {
328 return $this->getDataConnector()->deleteResourceLink($this);
329 }
330
331// /**
332// * Get tool consumer.
333// *
334// * @deprecated Use getPlatform() instead
335// * @see Context::getPlatform()
336// *
337// * @return ToolConsumer Tool consumer object for this resource link.
338// */
339// public function getConsumer()
340// {
341// Util::logDebug('Method ceLTIc\LTI\ResourceLink::getConsumer() has been deprecated; please use ceLTIc\LTI\ResourceLink::getPlatform() instead.',
342// true);
343// return $this->getPlatform();
344// }
345
346// /**
347// * Get tool consumer ID.
348// *
349// * @deprecated Use getPlatformId() instead
350// * @see Context::getPlatformId()
351// *
352// * @return int|null Tool Consumer ID for this resource link.
353// */
354// public function getConsumerId()
355// {
356// Util::logDebug('Method ceLTIc\LTI\ResourceLink::getConsumerId() has been deprecated; please use ceLTIc\LTI\ResourceLink::getPlatformId() instead.',
357// true);
358// return $this->getPlatformId();
359// }
360
361// /**
362// * Set tool consumer ID.
363// *
364// * @deprecated Use setPlatformId() instead
365// * @see Context::setPlatformId()
366// *
367// * @param int $consumerId Tool Consumer ID for this resource link.
368// */
369// public function setConsumerId($consumerId)
370// {
371// Util::logDebug('Method ceLTIc\LTI\ResourceLink::setConsumerId() has been deprecated; please use ceLTIc\LTI\ResourceLink::setPlatformId() instead.',
372// true);
373// $this->setPlatformId($consumerId);
374// }
375
381 public function getPlatform(): ?Platform
382 {
383 if (is_null($this->platform)) {
384 if (!is_null($this->context) || !is_null($this->contextId)) {
385 $this->platform = $this->getContext()->getPlatform();
386 } else {
387 $this->platform = Platform::fromRecordId($this->platformId, $this->getDataConnector());
388 }
389 }
390
391 return $this->platform;
392 }
393
399 public function getPlatformId(): ?int
400 {
401 return $this->platformId;
402 }
403
408 public function setPlatformId(?int $platformId)
409 {
410 $this->platform = null;
411 $this->platformId = $platformId;
412 }
413
419 public function getContext(): ?Context
420 {
421 if (is_null($this->context) && !is_null($this->contextId)) {
422 $this->context = Context::fromRecordId($this->contextId, $this->getDataConnector());
423 }
424
425 return $this->context;
426 }
427
433 public function getContextId(): ?int
434 {
435 if (is_null($this->contextId) && !is_null($this->context)) {
436 $this->contextId = $this->context->getRecordId();
437 }
438
439 return $this->contextId;
440 }
441
446 public function setContext(Context $context)
447 {
448 $this->context = $context;
449 $this->contextId = $context->getRecordId();
450 }
451
456 public function setContextId(?int $contextId)
457 {
458 if ($this->contextId !== $contextId) {
459 $this->context = null;
460 $this->contextId = $contextId;
461 }
462 }
463
469 public function getKey(): string
470 {
471 return $this->getPlatform()->getKey();
472 }
473
479 public function getId(): ?string
480 {
482 }
483
489 public function getRecordId(): ?int
490 {
491 return $this->id;
492 }
493
498 public function setRecordId(int $id)
499 {
500 $this->id = $id;
501 }
502
508 public function getDataConnector(): ?DataConnector
509 {
510 if (empty($this->dataConnector)) {
511 $this->getPlatform();
512 if (!empty($this->platform)) {
513 $this->dataConnector = $this->platform->getDataConnector();
514 }
515 }
516
518 }
519
526 public function getSetting(string $name, string $default = ''): string
527 {
528 if (array_key_exists($name, $this->settings)) {
529 $value = $this->settings[$name];
530 } else {
531 $value = $default;
532 }
533
534 return $value;
535 }
536
542 public function setSetting(string $name, string $value = null)
543 {
544 $old_value = $this->getSetting($name);
545 if ($value !== $old_value) {
546 if (!empty($value)) {
547 $this->settings[$name] = $value;
548 } else {
549 unset($this->settings[$name]);
550 }
551 $this->settingsChanged = true;
552 }
553 }
554
560 public function getSettings(): ?array
561 {
562 return $this->settings;
563 }
564
569 public function setSettings(array $settings)
570 {
571 $this->settings = $settings;
572 }
573
579 public function saveSettings(): bool
580 {
581 if ($this->settingsChanged) {
582 $ok = $this->save();
583 } else {
584 $ok = true;
585 }
586
587 return $ok;
588 }
589
595 public function hasOutcomesService(): bool
596 {
597 $has = !empty($this->getSetting('ext_ims_lis_basic_outcome_url')) || !empty($this->getSetting('lis_outcome_service_url'));
598 if (!$has && !empty($this->getSetting('custom_lineitem_url')) && !empty($this->getSetting('custom_ags_scopes'))) {
599 $scopes = explode(',', $this->getSetting('custom_ags_scopes'));
600 $has = in_array(Service\Score::$SCOPE, $scopes) && in_array(Service\Result::$SCOPE, $scopes);
601 }
602 if (!$has) {
603 $has = self::hasConfiguredApiHook(self::$OUTCOMES_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this);
604 }
605 return $has;
606 }
607
613 public function hasMembershipsService(): bool
614 {
615 $has = false;
616 if (!empty($this->getContextId())) {
617 $has = !empty($this->getContext()->getSetting('custom_context_memberships_url')) || !empty($this->getContext()->getSetting('custom_context_memberships_v2_url'));
618 }
619 if (!$has) {
620 $has = !empty($this->getSetting('custom_link_memberships_url'));
621 }
622 if (!$has) {
623 $has = !empty($this->getSetting('ext_ims_lis_memberships_url'));
624 }
625 if (!$has) {
626 $has = self::hasConfiguredApiHook(self::$MEMBERSHIPS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this);
627 }
628
629 return $has;
630 }
631
637 public function hasSettingService(): bool
638 {
639 $url = $this->getSetting('ext_ims_lti_tool_setting_url');
640
641 return !empty($url);
642 }
643
649 public function hasLineItemService(): bool
650 {
651 $has = false;
652 if (!empty($this->getSetting('custom_ags_scopes'))) {
653 $scopes = explode(',', $this->getSetting('custom_ags_scopes'));
654 if (in_array(Service\LineItem::$SCOPE, $scopes) || in_array(Service\LineItem::$SCOPE_READONLY, $scopes)) {
655 $has = !empty($this->getSetting('custom_lineitems_url'));
656 }
657 }
658
659 return $has;
660 }
661
667 public function hasScoreService(): bool
668 {
669 $has = false;
670 if (!empty($this->getSetting('custom_ags_scopes'))) {
671 $scopes = explode(',', $this->getSetting('custom_ags_scopes'));
672 if (in_array(Service\Score::$SCOPE, $scopes)) {
673 $has = !empty($this->getSetting('custom_lineitem_url'));
674 }
675 }
676
677 return $has;
678 }
679
685 public function hasResultService(): bool
686 {
687 $has = false;
688 if (!empty($this->getSetting('custom_ags_scopes'))) {
689 $scopes = explode(',', $this->getSetting('custom_ags_scopes'));
690 if (in_array(Service\Result::$SCOPE, $scopes)) {
691 $has = !empty($this->getSetting('custom_lineitem_url'));
692 }
693 }
694
695 return $has;
696 }
697
703 public function hasAssessmentControlService(): bool
704 {
705 $url = $this->getSetting('custom_ap_acs_url');
706
707 return !empty($url);
708 }
709
717 public function doOutcomesService(int $action, Outcome $ltiOutcome, UserResult $userResult): bool
718 {
719 $ok = false;
720 $this->extResponse = '';
721 // Lookup service details from the source resource link appropriate to the user (in case the destination is being shared)
722 $sourceResourceLink = $userResult->getResourceLink();
723 $sourcedId = $userResult->ltiResultSourcedId;
724
725 // Use LTI 1.1 service in preference to extension service if it is available
726 $urlAGS = $sourceResourceLink->getSetting('custom_lineitem_url');
727 $urlLTI11 = $sourceResourceLink->getSetting('lis_outcome_service_url');
728 $urlExt = $sourceResourceLink->getSetting('ext_ims_lis_basic_outcome_url');
729
730 if (!empty($urlAGS)) {
731 if (($action === self::EXT_READ) && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL) && $sourceResourceLink->hasResultService()) {
732 $ltiOutcome = $this->doResultService($userResult, $urlAGS);
733 $ok = !empty($ltiOutcome);
734 } elseif ((($action === self::EXT_WRITE) && $this->checkValueType($ltiOutcome, array(self::EXT_TYPE_DECIMAL)) && $sourceResourceLink->hasScoreService()) ||
735 ($action === self::EXT_DELETE)) {
736 if ($action === self::EXT_DELETE) {
737 $ltiOutcome->setValue(null);
738 $ltiOutcome->activityProgress = 'Initialized';
739 $ltiOutcome->gradingProgress = 'NotReady';
740 }
741 $ok = $this->doScoreService($ltiOutcome, $userResult, $urlAGS);
742 }
743 }
744 if (!$ok && is_null($ltiOutcome->getValue())) {
745 $ltiOutcome->setValue('');
746 }
747 if (!$ok && !empty($urlLTI11)) {
748 $do = '';
749 $outcome = $ltiOutcome->getValue();
750 if (($action === self::EXT_READ) && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
751 $do = 'readResult';
752 } elseif (($action === self::EXT_WRITE) && $this->checkValueType($ltiOutcome, array(self::EXT_TYPE_DECIMAL))) {
753 $do = 'replaceResult';
754 if (($ltiOutcome->getPointsPossible() <> 1) && ($ltiOutcome->getPointsPossible() > 0)) {
755 $outcome = $outcome / $ltiOutcome->getPointsPossible();
756 }
757 } elseif ($action === self::EXT_DELETE) {
758 $do = 'deleteResult';
759 }
760 if (!empty($do)) {
761 $xml = '';
762 if ($action === self::EXT_WRITE) {
763 $comment = (empty($ltiOutcome->comment)) ? '' : trim($ltiOutcome->comment);
764 if (!empty($comment) && !empty($sourceResourceLink->getSetting('ext_outcome_data_values_accepted'))) {
765 $resultDataTypes = explode(',', $sourceResourceLink->getSetting('ext_outcome_data_values_accepted'));
766 $resultDataType = '';
767 if (count($resultDataTypes) === 1) {
768 $resultDataType = $resultDataTypes[0];
769 } elseif (count($resultDataTypes) > 1) {
770 $isUrl = (strpos($comment, 'http://') === 0) || (strpos($comment, 'https://') === 0);
771 if ($isUrl && in_array('ltiLaunchUrl', $resultDataTypes)) {
772 $resultDataType = 'ltiLaunchUrl';
773 } elseif ($isUrl && in_array('url', $resultDataTypes)) {
774 $resultDataType = 'url';
775 } elseif (in_array('text', $resultDataTypes)) {
776 $resultDataType = 'text';
777 }
778 }
779 if (!empty($resultDataType)) {
780 $xml = <<< EOF
781
782 <resultData>
783 <{$resultDataType}>{$comment}</{$resultDataType}>
784 </resultData>
785EOF;
786 }
787 }
788 $xml = <<< EOF
789
790 <result>
791 <resultScore>
792 <language>{$ltiOutcome->language}</language>
793 <textString>{$outcome}</textString>
794 </resultScore>{$xml}
795 </result>
796EOF;
797 }
798 $sourcedId = htmlentities($sourcedId);
799 $xml = <<<EOF
800 <resultRecord>
801 <sourcedGUID>
802 <sourcedId>{$sourcedId}</sourcedId>
803 </sourcedGUID>{$xml}
804 </resultRecord>
805EOF;
806 if ($this->doLTI11Service($do, $urlLTI11, $xml)) {
807 switch ($action) {
808 case self::EXT_READ:
809 if (!isset($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString'])) {
810 break;
811 } else {
812 $ltiOutcome->setValue($this->extNodes['imsx_POXBody']["{$do}Response"]['result']['resultScore']['textString']);
813 }
814 // no break
815 case self::EXT_WRITE:
816 case self::EXT_DELETE:
817 $ok = true;
818 break;
819 }
820 }
821 }
822 }
823 if (!$ok && !empty($urlExt)) {
824 $do = '';
825 $outcome = $ltiOutcome->getValue();
826 if (($action === self::EXT_READ) && ($ltiOutcome->type === self::EXT_TYPE_DECIMAL)) {
827 $do = 'basic-lis-readresult';
828 } elseif (($action === self::EXT_WRITE) && $this->checkValueType($ltiOutcome, array(self::EXT_TYPE_DECIMAL))) {
829 $do = 'basic-lis-updateresult';
830 if (($ltiOutcome->getPointsPossible() <> 1) && ($ltiOutcome->getPointsPossible() > 0)) {
831 $outcome = $outcome / $ltiOutcome->getPointsPossible();
832 }
833 } elseif ($action === self::EXT_DELETE) {
834 $do = 'basic-lis-deleteresult';
835 }
836 if (!empty($do)) {
837 $params = array();
838 $params['sourcedid'] = $sourcedId;
839 $params['result_resultscore_textstring'] = $outcome;
840 if (!empty($ltiOutcome->language)) {
841 $params['result_resultscore_language'] = $ltiOutcome->language;
842 }
843 if (!empty($ltiOutcome->status)) {
844 $params['result_statusofresult'] = $ltiOutcome->status;
845 }
846 if (!empty($ltiOutcome->date)) {
847 $params['result_date'] = $ltiOutcome->date;
848 }
849 if (!empty($ltiOutcome->type)) {
850 $params['result_resultvaluesourcedid'] = $ltiOutcome->type;
851 }
852 if (!empty($ltiOutcome->dataSource)) {
853 $params['result_datasource'] = $ltiOutcome->dataSource;
854 }
855 if ($this->doService($do, $urlExt, $params, 'https://purl.imsglobal.org/spec/lti-ext/scope/outcomes')) {
856 switch ($action) {
857 case self::EXT_READ:
858 if (isset($this->extNodes['result']['resultscore']['textstring'])) {
859 $ltiOutcome->setValue($this->extNodes['result']['resultscore']['textstring']);
860 }
861 // no break
862 case self::EXT_WRITE:
863 case self::EXT_DELETE:
864 $ok = true;
865 break;
866 }
867 }
868 }
869 }
870 if ((!$ok) && $this->hasConfiguredApiHook(self::$OUTCOMES_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this)) {
871 $className = $this->getApiHook(self::$OUTCOMES_SERVICE_HOOK, $this->getPlatform()->getFamilyCode());
872 $hook = new $className($this);
873 $response = $hook->doOutcomesService($action, $ltiOutcome, $userResult);
874 if ($response !== false) {
875 $ok = true;
876 if ($action === self::EXT_READ) {
877 $ltiOutcome->setValue($response);
878 }
879 }
880 }
881
882 return $ok;
883 }
884
885// /**
886// * Perform a Memberships extension service request.
887// * The userResult table is updated with any user objects with lis_result_sourcedid values.
888// * @param bool $withGroups True is group information is to be requested as well
889// * @return mixed Array of UserResult objects or False if the request was not successful
890// *@deprecated Use getMemberships() instead
891// * @see ResourceLink::getMemberships()
892// */
893// public function doMembershipsService(bool $withGroups = false)
894// {
895// Util::logDebug('Method ceLTIc\LTI\ResourceLink::doMembershipsService() has been deprecated; please use ceLTIc\LTI\ResourceLink::getMemberships() instead.',
896// true);
897// return $this->getMemberships($withGroups);
898// }
899
906 public function doSettingService(int $action, string $value = null)
907 {
908 $response = false;
909 $this->extResponse = '';
910 switch ($action) {
911 case self::EXT_READ:
912 $do = 'basic-lti-loadsetting';
913 break;
914 case self::EXT_WRITE:
915 $do = 'basic-lti-savesetting';
916 break;
917 case self::EXT_DELETE:
918 $do = 'basic-lti-deletesetting';
919 break;
920 }
921 if (isset($do)) {
922 $url = $this->getSetting('ext_ims_lti_tool_setting_url');
923 $params = array();
924 $params['id'] = $this->getSetting('ext_ims_lti_tool_setting_id');
925 if (is_null($value)) {
926 $value = '';
927 }
928 $params['setting'] = $value;
929
930 if ($this->doService($do, $url, $params, 'https://purl.imsglobal.org/spec/lti-ext/scope/setting')) {
931 switch ($action) {
932 case self::EXT_READ:
933 if (isset($this->extNodes['setting']['value'])) {
934 $response = $this->extNodes['setting']['value'];
935 if (is_array($response)) {
936 $response = '';
937 }
938 }
939 break;
940 case self::EXT_WRITE:
941 $this->setSetting('ext_ims_lti_tool_setting', $value);
942 $this->saveSettings();
943 $response = true;
944 break;
945 case self::EXT_DELETE:
946 $response = true;
947 break;
948 }
949 }
950 }
951
952 return $response;
953 }
954
960 public function hasToolSettingsService(): bool
961 {
962 $has = !empty($this->getSetting('custom_link_setting_url'));
963 if (!$has) {
964 $has = self::hasConfiguredApiHook(self::$TOOL_SETTINGS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this);
965 }
966 return $has;
967 }
968
975 public function getToolSettings(int $mode = Service\ToolSettings::MODE_CURRENT_LEVEL, bool $simple = true)
976 {
977 $ok = false;
978 $settings = array();
979 if (!empty($this->getSetting('custom_link_setting_url'))) {
980 $url = $this->getSetting('custom_link_setting_url');
981 $service = new Service\ToolSettings($this, $url, $simple);
982 $settings = $service->get($mode);
983 $this->lastServiceRequest = $service->getHttpMessage();
984 $ok = $settings !== false;
985 }
986 if (!$ok && $this->hasConfiguredApiHook(self::$TOOL_SETTINGS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this)) {
987 $className = $this->getApiHook(self::$TOOL_SETTINGS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode());
988 $hook = new $className($this);
989 $settings = $hook->getToolSettings($mode, $simple);
990 }
991
992 return $settings;
993 }
994
1000 public function setToolSettings(array $settings = array()): bool
1001 {
1002 $ok = false;
1003 if (!empty($this->getSetting('custom_link_setting_url'))) {
1004 $url = $this->getSetting('custom_link_setting_url');
1005 $service = new Service\ToolSettings($this, $url);
1006 $ok = $service->set($settings);
1007 $this->lastServiceRequest = $service->getHttpMessage();
1008 }
1009 if (!$ok && $this->hasConfiguredApiHook(self::$TOOL_SETTINGS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this)) {
1010 $className = $this->getApiHook(self::$TOOL_SETTINGS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode());
1011 $hook = new $className($this);
1012 $ok = $hook->setToolSettings($settings);
1013 }
1014
1015 return $ok;
1016 }
1017
1026 public function hasMembershipService(): bool
1027 {
1029 'Method ceLTIc\LTI\ResourceLink::hasMembershipService() has been deprecated; please use ceLTIc\LTI\ResourceLink::hasMembershipsService() instead.',
1030 true
1031 );
1032 return $this->hasMembershipsService();
1033 }
1034
1043 public function getMembership()
1044 {
1046 'Method ceLTIc\LTI\ResourceLink::getMembership() has been deprecated; please use ceLTIc\LTI\ResourceLink::getMemberships() instead.',
1047 true
1048 );
1049 return $this->getMemberships();
1050 }
1051
1057 public function getMemberships(bool $withGroups = false)
1058 {
1059 $ok = false;
1060 $userResults = array();
1061 $hasLtiLinkService = !empty($this->getSetting('custom_link_memberships_url'));
1062 $hasLtiContextService = !empty($this->getContextId()) &&
1063 (!empty($this->getContext()->getSetting('custom_context_memberships_url')) || !empty($this->getContext()->getSetting('custom_context_memberships_v2_url')));
1064 $hasGroupsService = !empty($this->getContextId()) && !empty($this->getContext()->getSetting('custom_context_groups_url'));
1065 $hasExtService = !empty($this->getSetting('ext_ims_lis_memberships_url'));
1066 $hasApiHook = $this->hasConfiguredApiHook(self::$MEMBERSHIPS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode(), $this);
1067 if (($hasLtiContextService && (!$withGroups || $hasGroupsService)) || (!$hasExtService && !$hasApiHook)) {
1068 if (!empty($this->getContextId()) && !empty($this->getContext()->getSetting('custom_context_memberships_v2_url'))) {
1069 $url = $this->getContext()->getSetting('custom_context_memberships_v2_url');
1071 } else {
1072 $url = $this->getContext()->getSetting('custom_context_memberships_url');
1074 }
1075 $service = new Service\Membership($this, $url, $format);
1076 if (!$withGroups) {
1077 $userResults = $service->get();
1078 } else {
1079 $userResults = $service->getWithGroups();
1080 }
1081 $this->lastServiceRequest = $service->getHttpMessage();
1082 $ok = $userResults !== false;
1083 } elseif ($hasLtiLinkService) {
1084 $id = $this->id;
1085 $this->id = null;
1086 $url = $this->getSetting('custom_link_memberships_url');
1088 $service = new Service\Membership($this, $url, $format);
1089 if (!$withGroups) {
1090 $userResults = $service->get();
1091 } else {
1092 $userResults = $service->getWithGroups();
1093 }
1094 $this->lastServiceRequest = $service->getHttpMessage();
1095 $this->id = $id;
1096 $ok = $userResults !== false;
1097 }
1098 if (!$ok && $hasExtService) {
1099 $this->extResponse = '';
1100 $url = $this->getSetting('ext_ims_lis_memberships_url');
1101 $params = array();
1102 $params['id'] = $this->getSetting('ext_ims_lis_memberships_id');
1103 if ($withGroups) {
1104 $ok = $this->doService(
1105 'basic-lis-readmembershipsforcontextwithgroups',
1106 $url,
1107 $params,
1108 'https://purl.imsglobal.org/spec/lti-ext/scope/memberships'
1109 );
1110 }
1111 if (!$ok) {
1112 $ok = $this->doService(
1113 'basic-lis-readmembershipsforcontext',
1114 $url,
1115 $params,
1116 'https://purl.imsglobal.org/spec/lti-ext/scope/memberships'
1117 );
1118 }
1119 if ($ok) {
1120 $this->groupSets = array();
1121 $this->groups = array();
1122 if (isset($this->extNodes['memberships'])) {
1123 $memberships = $this->extNodes['memberships'];
1124 } elseif (isset($this->extNodes['members'])) {
1125 $memberships = $this->extNodes['members'];
1126 } else {
1127 $ok = false;
1128 }
1129 }
1130 if ($ok) {
1131 if (!isset($memberships['member'])) {
1132 $members = array();
1133 } elseif (!isset($memberships['member'][0])) {
1134 $members = array();
1135 $members[0] = $memberships['member'];
1136 } else {
1137 $members = $memberships['member'];
1138 }
1139
1140 for ($i = 0; $i < count($members); $i++) {
1141 $userresult = UserResult::fromResourceLink($this, $members[$i]['user_id']);
1142
1143 // Set the user name
1144 $firstname = (isset($members[$i]['person_name_given'])) ? $members[$i]['person_name_given'] : '';
1145 $lastname = (isset($members[$i]['person_name_family'])) ? $members[$i]['person_name_family'] : '';
1146 $fullname = (isset($members[$i]['person_name_full'])) ? $members[$i]['person_name_full'] : '';
1147 $userresult->setNames($firstname, $lastname, $fullname);
1148
1149 // Set the sourcedId
1150 if (isset($members[$i]['person_sourcedid'])) {
1151 $userresult->sourcedId = $members[$i]['person_sourcedid'];
1152 }
1153
1154 // Set the user email
1155 $email = (isset($members[$i]['person_contact_email_primary'])) ? $members[$i]['person_contact_email_primary'] : '';
1156 $userresult->setEmail($email, $this->getPlatform()->defaultEmail);
1157
1158 // Set the user roles
1159 if (isset($members[$i]['roles'])) {
1160 $userresult->roles = Tool::parseRoles($members[$i]['roles']);
1161 }
1162
1163 // Set the user groups
1164 if (!isset($members[$i]['groups']['group'])) {
1165 $groups = array();
1166 } elseif (!isset($members[$i]['groups']['group'][0])) {
1167 $groups = array();
1168 $groups[0] = $members[$i]['groups']['group'];
1169 } else {
1170 $groups = $members[$i]['groups']['group'];
1171 }
1172 for ($j = 0; $j < count($groups); $j++) {
1173 $group = $groups[$j];
1174 if (isset($group['set'])) {
1175 $set_id = $group['set']['id'];
1176 if (!isset($this->groupSets[$set_id])) {
1177 $this->groupSets[$set_id] = array('title' => $group['set']['title'], 'groups' => array(),
1178 'num_members' => 0, 'num_staff' => 0, 'num_learners' => 0);
1179 }
1180 $this->groupSets[$set_id]['num_members']++;
1181 if ($userresult->isStaff()) {
1182 $this->groupSets[$set_id]['num_staff']++;
1183 }
1184 if ($userresult->isLearner()) {
1185 $this->groupSets[$set_id]['num_learners']++;
1186 }
1187 if (!in_array($group['id'], $this->groupSets[$set_id]['groups'])) {
1188 $this->groupSets[$set_id]['groups'][] = $group['id'];
1189 }
1190 $this->groups[$group['id']] = array('title' => $group['title'], 'set' => $set_id);
1191 } else {
1192 $this->groups[$group['id']] = array('title' => $group['title']);
1193 }
1194 $userresult->groups[] = $group['id'];
1195 }
1196 if (isset($members[$i]['lis_result_sourcedid'])) {
1197 $userresult->ltiResultSourcedId = $members[$i]['lis_result_sourcedid'];
1198 }
1199 $userResults[] = $userresult;
1200 }
1201 } else {
1202 $userResults = false;
1203 }
1204 $ok = $userResults !== false;
1205 }
1206 if (!$ok && $hasApiHook) {
1207 $className = $this->getApiHook(self::$MEMBERSHIPS_SERVICE_HOOK, $this->getPlatform()->getFamilyCode());
1208 $hook = new $className($this);
1209 $userResults = $hook->getMemberships($withGroups);
1210 $ok = $userResults !== false;
1211 }
1212 if ($ok) {
1213 $oldUsers = $this->getUserResultSourcedIDs(true, Tool::ID_SCOPE_RESOURCE);
1214 foreach ($userResults as $userresult) {
1215 // If a result sourcedid is provided save the user
1216 if (!empty($userresult->ltiResultSourcedId)) {
1217 $userresult->save();
1218 }
1219 // Remove old user (if it exists)
1220 unset($oldUsers[$userresult->getId(Tool::ID_SCOPE_RESOURCE)]);
1221 }
1222 // Delete any old users which were not in the latest list from the platform
1223 foreach ($oldUsers as $id => $userresult) {
1224 $userresult->delete();
1225 }
1226 }
1227
1228 return $userResults;
1229 }
1230
1239 public function getUserResultSourcedIDs(bool $localOnly = false, int $idScope = null): array
1240 {
1241 return $this->getDataConnector()->getUserResultSourcedIDsResourceLink($this, $localOnly, $idScope);
1242 }
1243
1249 public function getShares(): array
1250 {
1251 return $this->getDataConnector()->getSharesResourceLink($this);
1252 }
1253
1261 public function getLineItems(string $resourceId = null, string $tag = null, int $limit = null)
1262 {
1263 $lineItems = false;
1264 $this->extRequest = '';
1265 $this->extRequestHeaders = '';
1266 $this->extResponse = '';
1267 $this->extResponseHeaders = '';
1268 $this->lastServiceRequest = null;
1269 $lineItemService = $this->getLineItemService();
1270 if (!empty($lineItemService)) {
1271 $lineItems = $lineItemService->getAll($this->ltiResourceLinkId, $resourceId, $tag, $limit);
1272 $http = $lineItemService->getHttpMessage();
1273 $this->extResponse = $http->response;
1274 $this->extResponseHeaders = $http->responseHeaders;
1275 $this->extRequest = $http->request;
1276 $this->extRequestHeaders = $http->requestHeaders;
1277 $this->lastServiceRequest = $http;
1278 }
1279
1280 return $lineItems;
1281 }
1282
1288 public function createLineItem(LineItem $lineItem): bool
1289 {
1290 $ok = false;
1291 $lineItemService = $this->getLineItemService();
1292 if (!empty($lineItemService)) {
1293 $lineItem->ltiResourceLinkId = $this->ltiResourceLinkId;
1294 $ok = $lineItemService->createLineItem($lineItem);
1295 }
1296
1297 return $ok;
1298 }
1299
1305 public function getOutcomes(int $limit = null)
1306 {
1307 $outcomes = false;
1308 $this->extRequest = '';
1309 $this->extRequestHeaders = '';
1310 $this->extResponse = '';
1311 $this->extResponseHeaders = '';
1312 $this->lastServiceRequest = null;
1313 $url = $this->getSetting('custom_lineitem_url');
1314 if (!empty($url)) {
1315 $resultService = new Service\Result($this->getPlatform(), $url);
1316 $outcomes = $resultService->getAll($limit);
1317 $http = $resultService->getHttpMessage();
1318 $this->extResponse = $http->response;
1319 $this->extResponseHeaders = $http->responseHeaders;
1320 $this->extRequest = $http->request;
1321 $this->extRequestHeaders = $http->requestHeaders;
1322 $this->lastServiceRequest = $http;
1323 }
1324
1325 return $outcomes;
1326 }
1327
1335 public function doAssessmentControlAction(AssessmentControlAction $assessmentControlAction, User $user, int $attemptNumber)
1336 {
1337 $status = false;
1338 $this->extRequest = '';
1339 $this->extRequestHeaders = '';
1340 $this->extResponse = '';
1341 $this->extResponseHeaders = '';
1342 $this->lastServiceRequest = null;
1343 $url = $this->getSetting('custom_ap_acs_url');
1344 if (!empty($url)) {
1345 $assessmentControlService = new Service\AssessmentControl($this, $url);
1346 $status = $assessmentControlService->submitAction($assessmentControlAction, $user, $attemptNumber);
1347 $http = $assessmentControlService->getHttpMessage();
1348 $this->extResponse = $http->response;
1349 $this->extResponseHeaders = $http->responseHeaders;
1350 $this->extRequest = $http->request;
1351 $this->extRequestHeaders = $http->requestHeaders;
1352 $this->lastServiceRequest = $http;
1353 }
1354
1355 return $status;
1356 }
1357
1358// /**
1359// * Class constructor from consumer.
1360// *
1361// * @deprecated Use fromPlatform() instead
1362// * @see ResourceLink::fromPlatform()
1363// *
1364// * @param ToolConsumer $consumer Consumer object
1365// * @param string $ltiResourceLinkId Resource link ID value
1366// * @param string $tempId Temporary Resource link ID value (optional, default is null)
1367// *
1368// * @return ResourceLink
1369// */
1370// public static function fromConsumer($consumer, $ltiResourceLinkId, $tempId = null)
1371// {
1372// Util::logDebug('Method ceLTIc\LTI\ResourceLink::fromConsumer() has been deprecated; please use ceLTIc\LTI\ResourceLink::fromPlatform() instead.',
1373// true);
1374// return self::fromPlatform($consumer, $ltiResourceLinkId, $tempId);
1375// }
1376
1384 public static function fromPlatform(Platform $platform, string $ltiResourceLinkId, string $tempId = null): ResourceLink
1385 {
1386 $resourceLink = new ResourceLink();
1387 $resourceLink->platform = $platform;
1388 $resourceLink->dataConnector = $platform->getDataConnector();
1389 $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1390 if (!empty($ltiResourceLinkId)) {
1391 $resourceLink->load();
1392 if (is_null($resourceLink->id) && !empty($tempId)) {
1393 $resourceLink->ltiResourceLinkId = $tempId;
1394 $resourceLink->load();
1395 $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1396 }
1397 }
1398
1399 return $resourceLink;
1400 }
1401
1409 public static function fromContext(Context $context, string $ltiResourceLinkId, string $tempId = null): ResourceLink
1410 {
1411 $resourceLink = new ResourceLink();
1412 $resourceLink->setContext($context);
1413 $resourceLink->dataConnector = $context->getDataConnector();
1414 $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1415 if (!empty($ltiResourceLinkId)) {
1416 $resourceLink->load();
1417 if (is_null($resourceLink->id) && !empty($tempId)) {
1418 $resourceLink->ltiResourceLinkId = $tempId;
1419 $resourceLink->load();
1420 $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
1421 }
1422 $resourceLink->setContext($context); // Ensure context remains set
1423 }
1424
1425 return $resourceLink;
1426 }
1427
1435 {
1436 $resourceLink = new ResourceLink();
1437 $resourceLink->dataConnector = $dataConnector;
1438 $resourceLink->load($id);
1439
1440 return $resourceLink;
1441 }
1442
1443 ###
1444 ### PRIVATE METHODS
1445 ###
1446
1452 private function load(int $id = null): bool
1453 {
1454 $this->initialize();
1455 $this->id = $id;
1456
1457 return $this->getDataConnector()->loadResourceLink($this);
1458 }
1459
1466 private function checkValueType(Outcome $ltiOutcome, array $supportedTypes = null): bool
1467 {
1468 if (empty($supportedTypes)) {
1469 $supportedTypes = explode(
1470 ',',
1471 str_replace(' ', '', strtolower($this->getSetting('ext_ims_lis_resultvalue_sourcedids', self::EXT_TYPE_DECIMAL)))
1472 );
1473 }
1474 $type = $ltiOutcome->type;
1475 $value = $ltiOutcome->getValue();
1476 // Check whether the type is supported or there is no value
1477 $ok = in_array($type, $supportedTypes) || empty($value);
1478 if (!$ok) {
1479 // Convert numeric values to decimal
1480 if ($type === self::EXT_TYPE_PERCENTAGE) {
1481 if (substr($value, -1) === '%') {
1482 $value = substr($value, 0, -1);
1483 }
1484 $ok = is_numeric($value) && ($value >= 0) && ($value <= 100);
1485 if ($ok) {
1486 $ltiOutcome->setValue($value / 100);
1487 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1488 }
1489 } elseif ($type === self::EXT_TYPE_RATIO) {
1490 $parts = explode('/', $value, 2);
1491 $ok = (count($parts) === 2) && is_numeric($parts[0]) && is_numeric($parts[1]) && ($parts[0] >= 0) && ($parts[1] > 0);
1492 if ($ok) {
1493 $ltiOutcome->setValue($parts[0] / $parts[1]);
1494 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1495 }
1496 // Convert letter_af to letter_af_plus or text
1497 } elseif ($type === self::EXT_TYPE_LETTER_AF) {
1498 if (in_array(self::EXT_TYPE_LETTER_AF_PLUS, $supportedTypes)) {
1499 $ok = true;
1500 $ltiOutcome->type = self::EXT_TYPE_LETTER_AF_PLUS;
1501 } elseif (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1502 $ok = true;
1503 $ltiOutcome->type = self::EXT_TYPE_TEXT;
1504 }
1505 // Convert letter_af_plus to letter_af or text
1506 } elseif ($type === self::EXT_TYPE_LETTER_AF_PLUS) {
1507 if (in_array(self::EXT_TYPE_LETTER_AF, $supportedTypes) && (strlen($value) === 1)) {
1508 $ok = true;
1509 $ltiOutcome->type = self::EXT_TYPE_LETTER_AF;
1510 } elseif (in_array(self::EXT_TYPE_TEXT, $supportedTypes)) {
1511 $ok = true;
1512 $ltiOutcome->type = self::EXT_TYPE_TEXT;
1513 }
1514 // Convert text to decimal
1515 } elseif ($type === self::EXT_TYPE_TEXT) {
1516 $ok = is_numeric($value) && ($value >= 0) && ($value <= 1);
1517 if ($ok) {
1518 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1519 } elseif (substr($value, -1) === '%') {
1520 $value = substr($value, 0, -1);
1521 $ok = is_numeric($value) && ($value >= 0) && ($value <= 100);
1522 if ($ok) {
1523 if (in_array(self::EXT_TYPE_PERCENTAGE, $supportedTypes)) {
1524 $ltiOutcome->type = self::EXT_TYPE_PERCENTAGE;
1525 } else {
1526 $ltiOutcome->setValue($value / 100);
1527 $ltiOutcome->type = self::EXT_TYPE_DECIMAL;
1528 }
1529 }
1530 }
1531 }
1532 }
1533
1534 return $ok;
1535 }
1536
1545 private function doService(string $type, string $url, array $params, string $scope): bool
1546 {
1547 $ok = false;
1548 $this->extRequest = '';
1549 $this->extRequestHeaders = '';
1550 $this->extResponse = '';
1551 $this->extResponseHeaders = '';
1552 $this->lastServiceRequest = null;
1553 if (!empty($url)) {
1554 $params['lti_version'] = $this->getPlatform()->ltiVersion;
1555 $params['lti_message_type'] = $type;
1556 $retry = false;
1557 $newToken = false;
1558 if (!$this->getPlatform()->useOAuth1()) {
1559 $accessToken = $this->platform->getAccessToken();
1560 $retry = true;
1561 if (empty($accessToken)) {
1562 $accessToken = new AccessToken($this->platform);
1563 $this->platform->setAccessToken($accessToken);
1564 }
1565 if (!$accessToken->hasScope($scope) && (empty(Tool::$defaultTool) || !in_array(
1566 $scope,
1567 Tool::$defaultTool->requiredScopes
1568 ))) {
1569 $accessToken->expires = time();
1570 $accessToken->get($scope, true);
1571 $this->platform->setAccessToken($accessToken);
1572 $newToken = true;
1573 }
1574 }
1575 do {
1576 // Add message signature
1577 $signed = $this->getPlatform()->addSignature($url, $params, 'POST', 'application/x-www-form-urlencoded');
1578 // Connect to platform
1579 if (is_array($signed)) {
1580 $http = new HttpMessage($url, 'POST', $signed);
1581 } else {
1582 $http = new HttpMessage($url, 'POST', $params, $signed);
1583 }
1584 if ($http->send()) {
1585 // Parse XML response
1586 $this->extResponse = $http->response;
1587 $this->extResponseHeaders = $http->responseHeaders;
1588 try {
1589 $this->extDoc = new DOMDocument();
1590 $this->extDoc->loadXML($http->response);
1591 $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1592 if (isset($this->extNodes['statusinfo']['codemajor']) && ($this->extNodes['statusinfo']['codemajor'] === 'Success')) {
1593 $ok = true;
1594 }
1595 } catch (\Exception $e) {
1596 }
1597 }
1598 $retry = $retry && !$newToken && !$ok;
1599 if ($retry) { // Obtain a new access token just for the required scope
1600 $accessToken = $this->platform->getAccessToken();
1601 $accessToken->expires = time();
1602 $accessToken->get($scope, true);
1603 $this->platform->setAccessToken($accessToken);
1604 $newToken = true;
1605 }
1606 } while ($retry);
1607 $this->extRequest = $http->request;
1608 $this->extRequestHeaders = $http->requestHeaders;
1609 $this->lastServiceRequest = $http;
1610 }
1611
1612 return $ok;
1613 }
1614
1621 private function doResultService(UserResult $userResult, string $url): ?Outcome
1622 {
1623 $outcome = null;
1624 $this->extRequest = '';
1625 $this->extRequestHeaders = '';
1626 $this->extResponse = '';
1627 $this->extResponseHeaders = '';
1628 $this->lastServiceRequest = null;
1629 if (!empty($url)) {
1630 $resultService = new Service\Result($this->getPlatform(), $url);
1631 $outcome = $resultService->get($userResult);
1632 $http = $resultService->getHttpMessage();
1633 $this->extResponse = $http->response;
1634 $this->extResponseHeaders = $http->responseHeaders;
1635 $this->extRequest = $http->request;
1636 $this->extRequestHeaders = $http->requestHeaders;
1637 $this->lastServiceRequest = $http;
1638 }
1639
1640 return $outcome;
1641 }
1642
1650 private function doScoreService(Outcome $ltiOutcome, UserResult $userResult, string $url): bool
1651 {
1652 $ok = false;
1653 $this->extRequest = '';
1654 $this->extRequestHeaders = '';
1655 $this->extResponse = '';
1656 $this->extResponseHeaders = '';
1657 $this->lastServiceRequest = null;
1658 if (!empty($url)) {
1659 $scoreService = new Service\Score($this->getPlatform(), $url);
1660 $scoreService->submit($ltiOutcome, $userResult);
1661 $http = $scoreService->getHttpMessage();
1662 $this->extResponse = $http->response;
1663 $this->extResponseHeaders = $http->responseHeaders;
1664 $ok = $http->ok;
1665 $this->extRequest = $http->request;
1666 $this->extRequestHeaders = $http->requestHeaders;
1667 $this->lastServiceRequest = $http;
1668 }
1669
1670 return $ok;
1671 }
1672
1680 private function doLTI11Service(string $type, string $url, string $xml): bool
1681 {
1682 $ok = false;
1683 $this->extRequest = '';
1684 $this->extRequestHeaders = '';
1685 $this->extResponse = '';
1686 $this->extResponseHeaders = '';
1687 $this->lastServiceRequest = null;
1688 if (!empty($url)) {
1689 $id = uniqid();
1690 $xmlRequest = <<< EOD
1691<?xml version = "1.0" encoding = "UTF-8"?>
1692<imsx_POXEnvelopeRequest xmlns = "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
1693 <imsx_POXHeader>
1694 <imsx_POXRequestHeaderInfo>
1695 <imsx_version>V1.0</imsx_version>
1696 <imsx_messageIdentifier>{$id}</imsx_messageIdentifier>
1697 </imsx_POXRequestHeaderInfo>
1698 </imsx_POXHeader>
1699 <imsx_POXBody>
1700 <{$type}Request>
1701{$xml}
1702 </{$type}Request>
1703 </imsx_POXBody>
1704</imsx_POXEnvelopeRequest>
1705EOD;
1706 $scope = 'https://purl.imsglobal.org/spec/lti-bo/scope/basicoutcome';
1707 $retry = false;
1708 $newToken = false;
1709 if (!$this->getPlatform()->useOAuth1()) {
1710 $accessToken = $this->platform->getAccessToken();
1711 $retry = true;
1712 if (empty($accessToken)) {
1713 $accessToken = new AccessToken($this->platform);
1714 $this->platform->setAccessToken($accessToken);
1715 }
1716 if (!$accessToken->hasScope($scope) && (empty(Tool::$defaultTool) || !in_array(
1717 $scope,
1718 Tool::$defaultTool->requiredScopes
1719 ))) {
1720 $accessToken->expires = time();
1721 $accessToken->get($scope, true);
1722 $this->platform->setAccessToken($accessToken);
1723 $newToken = true;
1724 }
1725 }
1726 do {
1727 // Add message signature
1728 $header = $this->getPlatform()->addSignature($url, $xmlRequest, 'POST', 'application/xml');
1729 // Connect to platform
1730 $http = new \ILIAS\LTI\ToolProvider\Http\HttpMessage($url, 'POST', $xmlRequest, $header);
1731 if ($http->send()) {
1732 // Parse XML response
1733 $this->extResponse = $http->response;
1734 $this->extResponseHeaders = $http->responseHeaders;
1735 try {
1736 $this->extDoc = new DOMDocument();
1737 $this->extDoc->loadXML($http->response);
1738 $this->extNodes = $this->domnodeToArray($this->extDoc->documentElement);
1739 if (isset($this->extNodes['imsx_POXHeader']['imsx_POXResponseHeaderInfo']['imsx_statusInfo']['imsx_codeMajor']) &&
1740 ($this->extNodes['imsx_POXHeader']['imsx_POXResponseHeaderInfo']['imsx_statusInfo']['imsx_codeMajor'] === 'success')) {
1741 $ok = true;
1742 }
1743 } catch (\Exception $e) {
1744 }
1745 }
1746 $retry = $retry && !$newToken && !$ok;
1747 if ($retry) { // Obtain a new access token just for the required scope
1748 $accessToken = $this->platform->getAccessToken();
1749 $accessToken->expires = time();
1750 $accessToken->get($scope, true);
1751 $this->platform->setAccessToken($accessToken);
1752 $newToken = true;
1753 }
1754 } while ($retry);
1755 $this->extRequest = $http->request;
1756 $this->extRequestHeaders = $http->requestHeaders;
1757 $this->lastServiceRequest = $http;
1758 }
1759
1760 return $ok;
1761 }
1762
1768 private function getLineItemService()
1769 {
1770 $url = $this->getSetting('custom_lineitems_url');
1771 if (!empty($url)) {
1772 $lineItemService = new Service\LineItem($this->getPlatform(), $url);
1773 } else {
1774 $lineItemService = false;
1775 }
1776
1777 return $lineItemService;
1778 }
1779
1785 private function domnodeToArray($node)
1786 {
1787 $output = array();
1788 switch ($node->nodeType) {
1789 case XML_CDATA_SECTION_NODE:
1790 case XML_TEXT_NODE:
1791 $output = trim($node->textContent);
1792 break;
1793 case XML_ELEMENT_NODE:
1794 for ($i = 0; $i < $node->childNodes->length; $i++) {
1795 $child = $node->childNodes->item($i);
1796 $v = $this->domnodeToArray($child);
1797 if (isset($child->tagName)) {
1798 $output[$child->tagName][] = $v;
1799 } else {
1800 $s = (string) $v;
1801 if (!empty($s)) {
1802 $output = $s;
1803 }
1804 }
1805 }
1806 if (is_array($output)) {
1807 if ($node->hasAttributes()) {
1808 foreach ($node->attributes as $attrNode) {
1809 $output['@attributes'][$attrNode->name] = (string) $attrNode->value;
1810 }
1811 }
1812 foreach ($output as $t => $v) {
1813 if (is_array($v) && (count($v) === 1) && ($t !== '@attributes')) {
1814 $output[$t] = $v[0];
1815 }
1816 }
1817 }
1818 break;
1819 }
1820
1821 return $output;
1822 }
1823}
$comment
Definition: buildRTE.php:72
Class to represent an HTTP message.
Definition: AccessToken.php:33
Class to represent an assessment control action.
Class to represent a platform context.
Definition: Context.php:34
static fromRecordId(int $id, \ILIAS\LTI\ToolProvider\DataConnector\DataConnector $dataConnector)
Load the context from the database.
Definition: Context.php:664
getRecordId()
Get the context record ID.
Definition: Context.php:283
getDataConnector()
Get the data connector.
Definition: Context.php:302
Class to provide a connection to a persistent store for LTI objects.
Class to represent a line item.
Definition: LineItem.php:31
Class to represent an outcome.
Definition: Outcome.php:29
getValue()
Get the outcome value.
Definition: Outcome.php:158
getPointsPossible()
Get the points possible value.
Definition: Outcome.php:177
setValue(string $value)
Set the outcome value.
Definition: Outcome.php:167
Class to represent a platform.
Definition: Platform.php:36
getDataConnector()
Get the data connector.
Definition: Platform.php:328
Class to implement the Assessment Control service.
Class to implement the Membership service.
Definition: Membership.php:33
const MEDIA_TYPE_MEMBERSHIPS_V1
Media type for version 1 of Memberships service.
Definition: Membership.php:37
const MEDIA_TYPE_MEMBERSHIPS_NRPS
Media type for Names and Role Provisioning service.
Definition: Membership.php:42
Class to implement the Result service.
Definition: Result.php:32
static string $SCOPE
Access scope.
Definition: Result.php:36
Class to implement the Score service.
Definition: Score.php:31
static string $SCOPE
Access scope.
Definition: Score.php:35
Class to implement a service.
Definition: Service.php:34
Class to implement the Tool Settings service.
const MODE_CURRENT_LEVEL
Settings at current level mode.
static Tool $defaultTool
Default tool for use with service requests.
Definition: Tool.php:299
const ID_SCOPE_RESOURCE
Prefix the ID with the consumer key and resource ID.
Definition: Tool.php:66
Class to represent a platform user.
Definition: UserResult.php:31
static fromResourceLink(ResourceLink $resourceLink, string $ltiUserId)
Class constructor from resource link.
Definition: UserResult.php:265
getResourceLink()
Get resource link.
Definition: UserResult.php:133
Class to represent a platform user.
Definition: User.php:31
static logDebug(string $message, bool $showSource=false)
Log a debug message.
Definition: Util.php:363
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
$scope
Definition: ltiregstart.php:53
$service
Definition: ltiservices.php:43
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
$scopes
Definition: ltitoken.php:99
if($format !==null) $name
Definition: metadata.php:247
$format
Definition: metadata.php:235
$i
Definition: metadata.php:41
if( $orgName !==null) if($spconfig->hasValue('contacts')) $email
Definition: metadata.php:302
$xml
Definition: metadata.php:351
trait ApiHook
Trait to handle API hook registrations.
Definition: ApiHook.php:29
static string $OUTCOMES_SERVICE_HOOK
Outcomes service hook name.
Definition: ApiHook.php:53
static string $MEMBERSHIPS_SERVICE_HOOK
Memberships service hook name.
Definition: ApiHook.php:48
static getApiHook(string $hookName, string $familyCode)
Get the class name for an API hook.
Definition: ApiHook.php:88
static hasConfiguredApiHook(string $hookName, string $familyCode, $sourceObject)
Check if an API hook is registered and configured.
Definition: ApiHook.php:115
static string $TOOL_SETTINGS_SERVICE_HOOK
Tool Settings service hook name.
Definition: ApiHook.php:58
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: AccessToken.php:19
static parseRoles($roles, string $ltiVersion=Util::LTI_VERSION1)
Get an array of fully qualified user roles.
Definition: System.php:538
useOAuth1()
Determine whether this consumer is using the OAuth 1 security model.
Definition: System.php:698
$type
$url
$response
$http
Definition: raiseError.php:7