1<?php 2 3 4/** 5 * Class ilLTIAppEventListener 6 */ 7class ilLTIAppEventListener implements \ilAppEventListener 8{ 9 /** 10 * @var \ilLTIAppEventListener 11 */ 12 private static $instance = null; 13 14 /** 15 * @var \ilLogger 16 */ 17 private $logger = null; 18 19 /** 20 * @var ilLTIDataConnector|null 21 */ 22 private $connector = null; 23 24 25 /** 26 * ilLTIAppEventListener constructor. 27 */ 28 protected function __construct() 29 { 30 global $DIC; 31 32 $this->logger = $DIC->logger()->lti(); 33 $this->connector = new ilLTIDataConnector(); 34 } 35 36 /** 37 * @return \ilLTIAppEventListener 38 */ 39 protected static function getInstance() 40 { 41 if (!self::$instance instanceof \ilLTIAppEventListener) { 42 self::$instance = new self(); 43 } 44 return self::$instance; 45 } 46 47 48 /** 49 * Handle update status 50 */ 51 protected function handleUpdateStatus($a_obj_id, $a_usr_id, $a_status, $a_percentage) 52 { 53 $this->logger->debug('Handle update status'); 54 $auth_mode = ilObjUser::_lookupAuthMode($a_usr_id); 55 if (!$this->isLTIAuthMode($auth_mode)) { 56 $this->logger->debug('Ignoring update for non-LTI-user.'); 57 return false; 58 } 59 $ext_account = ilObjUser::_lookupExternalAccount($a_usr_id); 60 list($lti, $consumer) = explode('_', $auth_mode); 61 62 // iterate through all references 63 $refs = ilObject::_getAllReferences($a_obj_id); 64 foreach ((array) $refs as $ref_id) { 65 $resources = $this->connector->lookupResourcesForUserObjectRelation( 66 $ref_id, 67 $ext_account, 68 $consumer 69 ); 70 71 $this->logger->debug('Resources for update:'); 72 $this->logger->dump($resources, ilLogLevel::DEBUG); 73 74 foreach ($resources as $resource) { 75 $this->tryOutcomeService($resource, $ext_account, $a_status, $a_percentage); 76 } 77 } 78 } 79 80 81 /** 82 * @param ilDateTime $since 83 * @throws ilDateTimeException 84 */ 85 protected function doCronUpdate(ilDateTime $since) 86 { 87 $this->logger->debug('Starting cron update for lti outcome service'); 88 89 $resources = $this->connector->lookupResourcesForAllUsersSinceDate($since); 90 foreach ($resources as $consumer_ext_account => $user_resources) { 91 list($consumer, $ext_account) = explode('__', $consumer_ext_account, 2); 92 93 $login = ilObjUser::_checkExternalAuthAccount('lti_' . $consumer, $ext_account); 94 if (!$login) { 95 $this->logger->info('No user found for lti_' . $consumer . ' -> ' . $ext_account); 96 continue; 97 } 98 $usr_id = ilObjUser::_lookupId($login); 99 foreach ($user_resources as $resource_info) { 100 $this->logger->debug('Found resource: ' . $resource_info); 101 list($resource_id, $resource_ref_id) = explode('__', $resource_info); 102 103 // lookup lp status 104 $status = ilLPStatus::_lookupStatus( 105 ilObject::_lookupObjId($resource_ref_id), 106 $usr_id 107 ); 108 $percentage = ilLPStatus::_lookupPercentage( 109 ilObject::_lookupObjId($resource_ref_id), 110 $usr_id 111 ); 112 $this->tryOutcomeService($resource_id, $ext_account, $status, $percentage); 113 } 114 } 115 } 116 117 /** 118 * @param $a_usr_id 119 * @return bool 120 */ 121 protected function isLTIAuthMode($auth_mode) 122 { 123 return strpos($auth_mode, 'lti_') === 0; 124 } 125 126 127 /** 128 * try outcome service 129 */ 130 protected function tryOutcomeService($resource, $ext_account, $a_status, $a_percentage) 131 { 132 $resource_link = \IMSGlobal\LTI\ToolProvider\ResourceLink::fromRecordId($resource, $this->connector); 133 if (!$resource_link->hasOutcomesService()) { 134 $this->logger->debug('No outcome service available for resource id: ' . $resource); 135 return false; 136 } 137 $this->logger->debug('Trying outcome service with status ' . $a_status . ' and percentage ' . $a_percentage); 138 $user = \IMSGlobal\LTI\ToolProvider\User::fromResourceLink($resource_link, $ext_account); 139 140 if ($a_status == ilLPStatus::LP_STATUS_COMPLETED_NUM) { 141 $score = 1; 142 } elseif ( 143 $a_status == ilLPStatus::LP_STATUS_FAILED_NUM || 144 $a_status == ilLPStatus::LP_STATUS_NOT_ATTEMPTED_NUM 145 ) { 146 $score = 0; 147 } elseif (!$a_percentage) { 148 $score = 0; 149 } else { 150 $score = (int) $a_percentage / 100; 151 } 152 153 $this->logger->debug('Sending score: ' . (string) $score); 154 155 $outcome = new \IMSGlobal\LTI\ToolProvider\Outcome($score); 156 157 $resource_link->doOutcomesService( 158 \IMSGlobal\LTI\ToolProvider\ResourceLink::EXT_WRITE, 159 $outcome, 160 $user 161 ); 162 } 163 164 165 /** 166 * @inheritdoc 167 */ 168 public static function handleEvent($a_component, $a_event, $a_parameter) 169 { 170 global $DIC; 171 172 $logger = $DIC->logger()->lti()->debug('Handling event: ' . $a_event . ' from ' . $a_component); 173 174 switch ($a_component) { 175 case 'Services/Tracking': 176 if ($a_event == 'updateStatus') { 177 $listener = self::getInstance(); 178 $listener->handleUpdateStatus( 179 $a_parameter['obj_id'], 180 $a_parameter['usr_id'], 181 $a_parameter['status'], 182 $a_parameter['percentage'] 183 ); 184 } 185 break; 186 } 187 } 188 189 /** 190 * @param ilDateTime $since 191 * @return bool 192 * @throws ilDateTimeException 193 */ 194 public static function handleCronUpdate(ilDateTime $since) 195 { 196 $listener = self::getInstance(); 197 $listener->doCronUpdate($since); 198 return true; 199 } 200 201 202 public static function handleOutcomeWithoutLP($a_obj_id, $a_usr_id, $a_percentage) 203 { 204 global $DIC; 205 $score = 0; 206 207 $auth_mode = ilObjUser::_lookupAuthMode($a_usr_id); 208 if (strpos($auth_mode, 'lti_') === false) { 209 $DIC->logger()->lti()->debug('Ignoring outcome for non-LTI-user.'); 210 return false; 211 } 212 //check if LearningPress enabled 213 $olp = ilObjectLP::getInstance($a_obj_id); 214 if (ilLPObjSettings::LP_MODE_DEACTIVATED != $olp->getCurrentMode()) 215 { 216 $DIC->logger()->lti()->debug('Ignoring outcome if LP is activated.'); 217 return false; 218 } 219 220 if ($a_percentage && $a_percentage > 0) { 221 $score = round($a_percentage/100, 4); 222 } 223 224 $connector = new ilLTIDataConnector(); 225 $ext_account = ilObjUser::_lookupExternalAccount($a_usr_id); 226 list($lti, $consumer) = explode('_', $auth_mode); 227 228 // iterate through all references 229 $refs = ilObject::_getAllReferences($a_obj_id); 230 foreach ((array) $refs as $ref_id) { 231 $resources = $connector->lookupResourcesForUserObjectRelation( 232 $ref_id, 233 $ext_account, 234 $consumer 235 ); 236 237 $DIC->logger()->lti()->debug('Resources for update: '.$resources); 238 239 foreach ($resources as $resource) { 240 // $this->tryOutcomeService($resource, $ext_account, $a_status, $a_percentage); 241 $resource_link = \IMSGlobal\LTI\ToolProvider\ResourceLink::fromRecordId($resource, $connector); 242 if ($resource_link->hasOutcomesService()) { 243 $user = \IMSGlobal\LTI\ToolProvider\User::fromResourceLink($resource_link, $ext_account); 244 $DIC->logger()->lti()->debug('Sending score: ' . (string) $score); 245 $outcome = new \IMSGlobal\LTI\ToolProvider\Outcome($score); 246 247 $resource_link->doOutcomesService( 248 \IMSGlobal\LTI\ToolProvider\ResourceLink::EXT_WRITE, 249 $outcome, 250 $user 251 ); 252 } 253 } 254 } 255 } 256 257} 258