1<?php 2/* 3 +-----------------------------------------------------------------------------+ 4 | ILIAS open source | 5 +-----------------------------------------------------------------------------+ 6 | Copyright (c) 1998-2001 ILIAS open source, University of Cologne | 7 | | 8 | This program is free software; you can redistribute it and/or | 9 | modify it under the terms of the GNU General Public License | 10 | as published by the Free Software Foundation; either version 2 | 11 | of the License, or (at your option) any later version. | 12 | | 13 | This program is distributed in the hope that it will be useful, | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 | GNU General Public License for more details. | 17 | | 18 | You should have received a copy of the GNU General Public License | 19 | along with this program; if not, write to the Free Software | 20 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 21 +-----------------------------------------------------------------------------+ 22*/ 23 24use ILIAS\LTI\Screen\LtiViewLayoutProvider; 25 26/** 27 * @classDescription class for ILIAS ViewLTI 28 * 29 * @author Stefan Schneider <schneider@hrz.uni-marburg.de 30 * @version $id$ 31 * @ingroup ServicesLTI 32 * @ilCtrl_IsCalledBy ilLTIViewGUI: ilLTIRouterGUI 33 * 34 */ 35class ilLTIViewGUI 36{ 37 /** 38 * contstants 39 */ 40 const CHECK_HTTP_REFERER = true; 41 42 /** 43 * private variables 44 */ 45 private $dic = null; 46 private $user = null; 47 private $log = null; 48 private $link_dir = ""; 49 50 /** 51 * public variables 52 */ 53 public $lng = null; 54 55 public function __construct() 56 { 57 global $DIC; 58 $this->dic = $DIC; 59 $this->log = $this->dic->logger()->lti(); 60 $this->lng = $this->dic->language(); 61 $this->lng->loadLanguageModule('lti'); 62 } 63 64 /** 65 * Init LTI mode for lti authenticated users 66 */ 67 public function init() 68 { 69 $this->link_dir = (defined("ILIAS_MODULE")) ? "../" : ""; 70 if ($this->isLTIUser()) 71 { 72 $context = $this->dic->globalScreen()->tool()->context(); 73 $context->claim()->lti(); 74 $this->initGUI(); 75 } 76 } 77 78 /** 79 * for compatiblity with ilLTIRouterGUI 80 */ 81 public static function getInstance() 82 { 83 global $DIC; 84 return $DIC["lti"]; 85 } 86 87 /** 88 * get LTI Mode from Users->getAuthMode 89 * @return boolean 90 */ 91 private function isLTIUser() 92 { 93 if (!$this->dic->user() instanceof ilObjUser) { 94 return false; 95 } 96 return (strpos($this->dic->user()->getAuthMode(), 'lti_') === 0); 97 } 98 99 public function executeCommand() 100 { 101 global $ilCtrl; 102 $cmd = $ilCtrl->getCmd(); 103 switch ($cmd) { 104 case 'exit': 105 $this->exitLti(); 106 break; 107 } 108 } 109 110 public function isActive() : bool 111 { 112 return $this->isLTIUser(); 113 } 114 115 public function initGUI() 116 { 117 $this->log->debug("initGUI"); 118 $baseclass = strtolower($_GET['baseClass']); 119 $cmdclass = strtolower($_GET['cmdClass']); 120 switch ($baseclass) 121 { 122 case 'illtiroutergui' : 123 return; 124 break; 125 } 126 } 127 128 public function getContextId() { 129 global $ilLocator; 130 131 // forced lti_context_id for example request command in exitLTI 132 if (isset($_GET['lti_context_id']) && $_GET['lti_context_id'] !== '') { 133 $this->log->debug("find context_id by GET param: " . $_GET['lti_context_id']); 134 return $_GET['lti_context_id']; 135 } 136 137 $ref_id = $this->findEffectiveRefId(); 138 $this->log->debug("Effective ref_id: ". $ref_id); 139 // context_id = ref_id in request 140 if (isset($_SESSION['lti_' . $ref_id . '_post_data'])) { 141 $this->log->debug("lti context session exists for " . $ref_id); 142 return $ref_id; 143 } 144 145 // sub item request 146 $this->log->debug("ref_id not exists as context_id, walking tree backwards to find a valid context_id"); 147 $locator_items = $ilLocator->getItems(); 148 if (is_array($locator_items) && count($locator_items) > 0) { 149 for ($i = count($locator_items)-1;$i>=0;$i--) { 150 if (isset($_SESSION['lti_' . $locator_items[$i]['ref_id'] . '_post_data'])) { 151 $this->log->debug("found valid ref_id in locator: " . $locator_items[$i]['ref_id']); 152 return $locator_items[$i]['ref_id']; 153 } 154 } 155 } 156 $this->log->warning("no valid context_id found for ref_id request: " . $ref_id); 157 158 if (ilLTIViewGUI::CHECK_HTTP_REFERER) { 159 $ref_id = ''; 160 $obj_type = ''; 161 $context_id = ''; 162 $referer = ''; 163 164 // first try to get real http referer 165 if (isset($_SERVER['HTTP_REFERER'])) { 166 $referer = $this->findEffectiveRefId($_SERVER['HTTP_REFERER']); 167 } 168 else { // only fallback and not reliable on multiple browser LTi contexts 169 if (isset($_SESSION['referer_ref_id'])) { 170 $referer = $_SESSION['referer_ref_id']; 171 } 172 } 173 174 if ($referer != '') { 175 if (isset($_SESSION['lti_' . $referer . '_post_data'])) { 176 $ref_id =$referer; 177 $context_id = $referer; 178 $obj_type = ilObject::_lookupType($ref_id,true); 179 $this->log->debug("referer obj_type: " . $obj_type); 180 } 181 else { 182 $this->log->debug("search tree of referer..."); 183 if ($this->dic->repositoryTree()->isInTree($referer)) { 184 $path = $this->dic->repositoryTree()->getPathId($referer); 185 for ($i = count($path)-1;$i>=0;$i--) { 186 if (isset($_SESSION['lti_' . $path[$i] . '_post_data'])) { 187 // redirect to referer, because it is valid 188 $ref_id = $referer; 189 $context_id = $path[$i]; 190 $obj_type = ilObject::_lookupType($ref_id,true); 191 break; 192 } 193 } 194 } 195 } 196 } 197 if ($ref_id != '' && $obj_type != '') { 198 ilUtil::sendFailure($this->lng->txt('permission_denied'),true); 199 $redirect = $this->link_dir."goto.php?target=".$obj_type."_".$ref_id."<i_context_id=".$context_id; 200 $this->log->debug("redirect: " . $redirect); 201 ilUtil::redirect($redirect); 202 } 203 } 204 $lti_context_ids = $_SESSION['lti_context_ids']; 205 if (is_array($lti_context_ids) && count($lti_context_ids) > 0) { 206 if (count($lti_context_ids) == 1) { 207 $this->log->debug("using context_id from only LTI session"); 208 return $lti_context_ids[0]; 209 } 210 else { 211 $this->log->warning("Multiple LTI sessions exists. The context_id can not be clearly detected"); 212 } 213 } 214 return ''; 215 } 216 217 public function getPostData() { 218 $context_id = $this->getContextId(); 219 if ($context_id == '') { 220 $this->log->warning("could not find any valid context_id!"); 221 return null; 222 } 223 $post_data = $_SESSION['lti_' . $this->getContextId() . '_post_data']; 224 if (!is_array($post_data)) { 225 $this->log->warning("no session post_data: " . "lti_" . $this->getContextId() . "_post_data"); 226 return null; 227 } 228 return $post_data; 229 } 230 231 public function getExternalCss() { 232 $post_data = $this->getPostData(); 233 if ($post_data !== null) { 234 return (isset($post_data['launch_presentation_css_url'])) ? $post_data['launch_presentation_css_url'] : ''; 235 } 236 return ''; 237 } 238 239 public function getTitle() : string 240 { 241 $post_data = $this->getPostData(); 242 if ($post_data !== null) { 243 return (isset($post_data['resource_link_title'])) ? "LTI - " . $post_data['resource_link_title'] : "LTI"; 244 } 245 return "LTI"; 246 } 247 248 public function getTitleForExitPage() : string 249 { 250 return $this->lng->txt('lti_exited'); 251 } 252 253 public function getShortTitle() : string 254 { 255 return $this->lng->txt('lti_mode'); 256 } 257 258 /** 259 * exit LTI session and if defined redirecting to returnUrl 260 * ToDo: Standard Template with delos ... 261 */ 262 public function exitLti() 263 { 264 $this->dic->logger()->lti()->info("exitLTI"); 265 $force_ilias_logout = false; 266 $context_id = $this->getContextId(); 267 if ($context_id == '') { 268 $this->log->warning("could not find any valid context_id!"); 269 $force_ilias_logout = true; 270 } 271 $post_data = $this->getPostData(); 272 $return_url = ($post_data !== null) ? $post_data['launch_presentation_return_url'] : ''; 273 $this->removeContextFromSession($context_id); 274 275 if (isset($_SESSION['lti_' . $context_id . '_post_data'])) { 276 unset($_SESSION['lti_' . $context_id . '_post_data']); 277 $this->dic->logger()->lti()->debug('unset SESSION["' . 'lti_' . $context_id . '_post_data"]'); 278 } 279 if (!isset($return_url) || $return_url === '') { 280 $cc = $this->dic->globalScreen()->tool()->context()->current(); 281 $cc->addAdditionalData(LtiViewLayoutProvider::GS_EXIT_LTI, true); 282 $ui_factory = $this->dic->ui()->factory(); 283 $renderer = $this->dic->ui()->renderer(); 284 $content = [ 285 $ui_factory->messageBox()->info($this->lng->txt('lti_exited_info')) 286 ]; 287 $tpl = $this->dic["tpl"]; 288 $tpl->setContent($renderer->render($content)); 289 $this->logout($force_ilias_logout); 290 $tpl->printToStdout(); 291 } else { 292 $this->logout($force_ilias_logout); 293 header('Location: ' . $return_url); 294 } 295 } 296 297 /** 298 * logout ILIAS and destroys Session and ilClientId cookie if no consumer is still open in the LTI User Session 299 */ 300 public function logout($force_ilias_logout=false) 301 { 302 if ($force_ilias_logout) { 303 $this->log->warning("forcing logout ilias session, maybe a broken LTI context"); 304 } 305 else { 306 if (is_array($_SESSION['lti_context_ids']) && count($_SESSION['lti_context_ids']) > 0) { 307 $this->log->debug("there is another valid consumer session: ilias session logout refused."); 308 return; 309 } 310 } 311 $this->dic->logger()->lti()->info("logout"); 312 $GLOBALS['DIC']->user()->setAuthMode(AUTH_LOCAL); 313 //ilSession::setClosingContext(ilSession::SESSION_CLOSE_USER); // needed? 314 $auth = $GLOBALS['DIC']['ilAuthSession']; 315 //$auth->logout(); // needed? 316 $auth->setExpired($auth::SESSION_AUTH_EXPIRED,ilAuthStatus::STATUS_UNDEFINED); 317 session_destroy(); 318 $client_id = $_COOKIE["ilClientId"]; 319 ilUtil::setCookie("ilClientId", ""); 320 ilUtil::setCookie("PHPSESSID",""); 321 } 322 323 public function getCmdLink(String $cmd) : String 324 { 325 global $ilCtrl; 326 $lti_context_id = $this->getContextId(); 327 $lti_context_id_param = ($lti_context_id != '') ? "<i_context_id=".$lti_context_id : ''; 328 $targetScript = ($ilCtrl->getTargetScript() !== 'ilias.php') ? "ilias.php" : ""; 329 return $this->link_dir.$targetScript.$ilCtrl->getLinkTargetByClass(array('illtiroutergui',strtolower(get_class($this))),$cmd)."&baseClass=illtiroutergui".$lti_context_id_param; 330 } 331 332 private function getSessionValue(String $sess_key) : String 333 { 334 if (isset($_SESSION[$sess_key]) && $_SESSION[$sess_key] != '') { 335 return $_SESSION[$sess_key]; 336 } else { 337 return ''; 338 } 339 } 340 341 private function getCookieValue(String $cookie_key) : String 342 { 343 if (isset($_COOKIE[$cookie_key]) && $_COOKIE[$cookie_key] != '') { 344 return $_COOKIE[$cookie_key]; 345 } 346 else { 347 return ''; 348 } 349 } 350 351 private function removeContextFromSession($context_id) { 352 $lti_context_ids = $_SESSION['lti_context_ids']; 353 if (is_array($lti_context_ids) && in_array($context_id,$lti_context_ids)) { 354 array_splice($lti_context_ids,array_search($context_id,$lti_context_ids),1); 355 $_SESSION['lti_context_ids'] = $lti_context_ids; 356 } 357 } 358 359 /** 360 * Find effective ref_id for request 361 */ 362 private function findEffectiveRefId($url=null) 363 { 364 if ($url === null) { 365 $query = $_GET; 366 } 367 else { 368 parse_str(parse_url($url, PHP_URL_QUERY),$query); 369 } 370 if ((int) $query['ref_id']) { 371 return (int) $query['ref_id']; 372 } 373 $target_arr = explode('_', (string) $query['target']); 374 if (isset($target_arr[1]) and (int) $target_arr[1]) { 375 return (int) $target_arr[1]; 376 } 377 return ''; 378 } 379} 380