ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilLTIViewGUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
33 {
37  public const CHECK_HTTP_REFERER = true;
38 
42  private ?ILIAS\DI\Container $dic = null;
43  private ?int $user = null;
44  private ?ilLogger $log = null;
45  private string $link_dir = "";
46 
47  private ?int $effectiveRefId = null;
48  private \ILIAS\HTTP\Wrapper\WrapperFactory $wrapper;
51 
55  public ?ilLanguage $lng = null;
56 
60  public function __construct()
61  {
62  global $DIC;
63  $this->dic = $DIC;
64  $this->log = ilLoggerFactory::getLogger('ltis');
65  $this->lng = $this->dic->language();
66  $this->lng->loadLanguageModule('lti');
67  $this->wrapper = $DIC->http()->wrapper();
68  $this->kindlyTo = $DIC->refinery()->kindlyTo();
69  $this->locator = $DIC['ilLocator'];
70  }
71 
75  public function init(): void
76  {
77  $this->link_dir = (defined("ILIAS_MODULE")) ? "../" : "";
78  if ($this->isLTIUser()) {
79  $context = $this->dic->globalScreen()->tool()->context();
80  $context->claim()->lti();
81  $this->initGUI();
82  }
83  }
84 
89  public static function getInstance()
90  {
91  global $DIC;
92  return $DIC["lti"];
93  }
94 
99  private function isLTIUser(): bool
100  {
101  if (!$this->dic->user() instanceof ilObjUser) {
102  return false;
103  }
104  if ($this->dic->user()->getAuthMode() == null) {
105  return false;
106  }
107  return (strpos($this->dic->user()->getAuthMode(), 'lti_') === 0);
108  }
109 
113  public function executeCommand(): void
114  {
115  global $ilCtrl;
116  $cmd = $ilCtrl->getCmd();
117  switch ($cmd) {
118  case 'exit':
119  $this->exitLti();
120  break;
121  }
122  }
123 
127  public function isActive(): bool
128  {
129  return $this->isLTIUser();
130  }
131 
135  public function initGUI(): void
136  {
137  $this->log->debug("initGUI");
138  $baseclass = '';
139  if ($this->wrapper->query()->has('baseClass')) {
140  $baseclass = strtolower($this->wrapper->query()->retrieve('baseClass', $this->kindlyTo->string()));
141  }
142  if ($this->wrapper->query()->has('cmdClass')) {
143  $cmdclass = strtolower($this->wrapper->query()->retrieve('cmdClass', $this->kindlyTo->string()));
144  }
145  if ($baseclass == 'illtiroutergui') {
146  return;
147  }
148  }
149 
153  protected function getContextId(): ?int
154  {
155  global $DIC;
156 
157  // forced lti_context_id for example request command in exitLTI
158  if ($this->wrapper->query()->has('lti_context_id') &&
159  $this->wrapper->query()->retrieve('lti_context_id', $this->kindlyTo->string()) !== '') {
160  $contextId = (int) $this->wrapper->query()->retrieve('lti_context_id', $this->kindlyTo->int());
161  $this->log->debug("find context_id by GET param: " . (string) $contextId);
162  return $contextId;
163  }
164 
165  $this->findEffectiveRefId();
166  // ???
167  if (
168  (
169  $this->wrapper->query()->has('baseClass') &&
170  $this->wrapper->query()->retrieve('baseClass', $this->kindlyTo->string()) === 'ilDashboardGUI'
171  )
172  &&
173  (
174  $this->wrapper->query()->has('cmd') &&
175  $this->wrapper->query()->retrieve('cmd', $this->kindlyTo->string()) === 'jumpToSelectedItems'
176  )
177  ) {
178  $this->log->debug("jumpToSelectedItems");
179  if (ilSession::get('lti_ref_id_at_init') != "") {
180  $this->effectiveRefId = (int) ilSession::get('lti_ref_id_at_init');
181  ilSession::set('lti_ref_id_at_init', "");
182  }
183  }
184 
186  if (empty($ref_id)) {
187  $this->log->debug("empty ref_id");
188  return 0;
189  }
190 
191  $this->log->debug("Effective ref_id: " . $ref_id);
192  //check
193  ilSession::set('lti_ref_id_at_init', (string) $ref_id);
194 
195 
196  // context_id = ref_id in request
197  if (ilSession::has('lti_' . $ref_id . '_post_data')) {
198  $this->log->debug("lti context session exists for " . $ref_id);
199  // return $ref_id;
200  }
201  // sub item request
202  $this->log->debug("ref_id not exists as context_id, walking tree backwards to find a valid context_id");
203  $locator_items = $this->locator->getItems();
204  if (is_array($locator_items) && count($locator_items) > 0) {
205  for ($i = count($locator_items) - 1;$i >= 0;$i--) {
206  if (ilSession::has('lti_' . $locator_items[$i]['ref_id'] . '_post_data')) {
207  $this->log->debug("found valid ref_id in locator: " . $locator_items[$i]['ref_id']);
208  return $locator_items[$i]['ref_id'];
209  }
210  }
211  }
212  $this->log->warning("no valid context_id found for ref_id request: " . $ref_id);
213 
215  $ref_id = $this->effectiveRefId;
216  $obj_type = ilObject::_lookupType($ref_id, true);
217  $context_id = 0;
218  $referer = 0;
219 
220  // first try to get real http referer
221  if (isset($_SERVER['HTTP_REFERER'])) {
222  $this->findEffectiveRefId($_SERVER['HTTP_REFERER']);
223  } else { // only fallback and not reliable on multiple browser LTi contexts
224  if (ilSession::has('referer_ref_id')) {
225  $this->effectiveRefId = ilSession::get('referer_ref_id');
226  }
227  }
228 
229  $referer = (int) $this->effectiveRefId;
230 
231  if ($referer > 0) {
232  if (ilSession::has('lti_' . $referer . '_post_data')) {
233  $ref_id = $referer;
234  $context_id = $referer;
235  $obj_type = ilObject::_lookupType($ref_id, true);
236  $this->log->debug("referer obj_type: " . $obj_type);
237  } else {
238  $this->log->debug("search tree of referer...");
239  if ($this->dic->repositoryTree()->isInTree($referer)) {
240  $path = $this->dic->repositoryTree()->getPathId($referer);
241  for ($i = count($path) - 1;$i >= 0;$i--) {
242  if (ilSession::has('lti_' . $path[$i] . '_post_data')) {
243  // redirect to referer, because it is valid
244  $ref_id = $referer;
245  $context_id = $path[$i];
246  $obj_type = ilObject::_lookupType($ref_id, true);
247  break;
248  }
249  }
250  }
251  }
252  }
253 
254  if ($ref_id > 0 && $obj_type != '') {
255  if (
256  (
257  $this->wrapper->query()->has('baseClass') &&
258  $this->wrapper->query()->retrieve('baseClass', $this->kindlyTo->string()) === 'ilDashboardGUI'
259  )
260  &&
261  (
262  $this->wrapper->query()->has('cmdClass') &&
263  $this->wrapper->query()->retrieve('cmdClass', $this->kindlyTo->string()) === 'ilpersonalprofilegui'
264  )
265  ) {
266  return $context_id;
267  }
268  // $this->dic->ui()->mainTemplate()->setOnScreenMessage('failure', $this->lng->txt('permission_denied'), true);
269  $redirect = $this->link_dir . "goto.php?target=" . $obj_type . "_" . $ref_id . "&lti_context_id=" . $context_id;
270  $this->log->debug("redirect: " . $redirect);
271  ilUtil::redirect($redirect);
272  }
273  }
274  $lti_context_ids = ilSession::get('lti_context_ids');
275  if (is_array($lti_context_ids) && count($lti_context_ids) > 0) {
276  if (count($lti_context_ids) == 1) {
277  $this->log->debug("using context_id from only LTI session");
278  return $lti_context_ids[0];
279  } else {
280  $this->log->warning("Multiple LTI sessions exists. The context_id can not be clearly detected");
281  }
282  }
283  return null;
284  }
285 
289  public function getPostData(): ?array
290  {
291  $context_id = $this->getContextId();
292  if ($context_id == 0) {
293  $this->log->warning("could not find any valid context_id!");
294  return null;
295  }
296  $post_data = ilSession::get('lti_' . $context_id . '_post_data');
297  if (!is_array($post_data)) {
298  $this->log->warning("no session post_data: " . "lti_" . $context_id . "_post_data");
299  return null;
300  }
301  return $post_data;
302  }
303 
307  public function getExternalCss(): string
308  {
309  $post_data = $this->getPostData();
310  if ($post_data !== null) {
311  return (isset($post_data['launch_presentation_css_url'])) ? $post_data['launch_presentation_css_url'] : '';
312  }
313  return '';
314  }
315 
319  public function getTitle(): string
320  {
321  $post_data = $this->getPostData();
322  if ($post_data !== null) {
323  return (isset($post_data['resource_link_title'])) ? "LTI - " . $post_data['resource_link_title'] : "LTI";
324  }
325  return "LTI";
326  }
327 
331  public function getTitleForExitPage(): string
332  {
333  return $this->lng->txt('lti_exited');
334  }
335 
339  public function getShortTitle(): string
340  {
341  return $this->lng->txt('lti_mode');
342  }
343 
348  public function exitLti(): void
349  {
350  $this->log->info("exitLTI");
351  $force_ilias_logout = false;
352  $context_id = $this->getContextId();
353  if ($context_id == 0) {
354  $this->log->warning("could not find any valid context_id!");
355  $force_ilias_logout = true;
356  }
357  $post_data = $this->getPostData();
358 
359  $return_url = '';
360  if (isset($post_data['launch_presentation_return_url'])) {
361  $return_url = $post_data['launch_presentation_return_url'];
362  }
363  $this->removeContextFromSession((string) $context_id);
364 
365  if (ilSession::has('lti_' . $context_id . '_post_data')) {
366  ilSession::clear('lti_' . $context_id . '_post_data');
367  $this->log->debug('unset SESSION["' . 'lti_' . $context_id . '_post_data"]');
368  }
369 
370  if (!isset($return_url) || $return_url === '') {
371  $cc = $this->dic->globalScreen()->tool()->context()->current();
372  $cc->addAdditionalData(LtiViewLayoutProvider::GS_EXIT_LTI, true);
373  $ui_factory = $this->dic->ui()->factory();
374  $renderer = $this->dic->ui()->renderer();
375  $content = [
376  $ui_factory->messageBox()->info($this->lng->txt('lti_exited_info'))
377  ];
378  $tpl = $this->dic["tpl"];
379  $tpl->setContent($renderer->render($content));
380  $this->logout($force_ilias_logout);
381  $tpl->printToStdout();
382  } else {
383  $this->logout($force_ilias_logout);
384  header('Location: ' . $return_url);
385  }
386  }
387 
391  public function logout(bool $force_ilias_logout = false): void
392  {
393  if ($force_ilias_logout) {
394  $this->log->warning("forcing logout ilias session, maybe a broken LTI context");
395  } else {
396  if (is_array(ilSession::get('lti_context_ids')) && count(ilSession::get('lti_context_ids')) > 0) {
397  $this->log->debug("there is another valid consumer session: ilias session logout refused.");
398  return;
399  }
400  }
401  $this->log->info("logout");
402  $this->dic->user()->setAuthMode((string) ilAuthUtils::AUTH_LOCAL);
403  //ilSession::setClosingContext(ilSession::SESSION_CLOSE_USER); // needed?
404  $auth = $this->dic['ilAuthSession'];
405  //$auth->logout(); // needed?
406  // $auth->setExpired($auth::SESSION_AUTH_EXPIRED, ilAuthStatus::STATUS_UNDEFINED);
407  $auth->setExpired(true);
408  session_destroy();
409  ilUtil::setCookie("ilClientId", "");
410  ilUtil::setCookie("PHPSESSID", "");
411  }
412 
418  public function getCmdLink(String $cmd): String
419  {
420  global $ilCtrl;
421  $lti_context_id = $this->getContextId();
422  $lti_context_id_param = ($lti_context_id != '') ? "&lti_context_id=" . $lti_context_id : '';
423  $targetScript = "";
424  return $this->link_dir . $targetScript . $this->dic->ctrl()->getLinkTargetByClass(array('illtiroutergui',strtolower(get_class($this))), $cmd) . "&baseClass=illtiroutergui" . $lti_context_id_param;
425  }
426 
427  private function getSessionValue(string $sess_key): string
428  {
429  if (ilSession::has($sess_key) && ilSession::get($sess_key) != '') {
430  return ilSession::get($sess_key);
431  } else {
432  return '';
433  }
434  }
435 
436  private function getCookieValue(string $cookie_key): string
437  {
438  if ($this->dic->wrapper->cookie()->has($cookie_key) && $this->dic->wrapper->cookie()->retrieve($cookie_key, $this->dic->refinery()->kindlyTo()->string() != '')) {
439  return $this->dic->wrapper->cookie()->retrieve($cookie_key, $this->dic->refinery()->kindlyTo()->string());
440  } else {
441  return '';
442  }
443  }
444 
445  private function removeContextFromSession(string $context_id): void
446  {
447  $lti_context_ids = ilSession::get('lti_context_ids');
448  if (is_array($lti_context_ids) && in_array($context_id, $lti_context_ids)) {
449  array_splice($lti_context_ids, array_search($context_id, $lti_context_ids), 1);
450  ilSession::set('lti_context_ids', $lti_context_ids);
451  }
452  }
453 
458  private function findEffectiveRefId(?string $url = null): void
459  {
460  $query = [];
461  if ($url === null) {
462  if ($this->wrapper->query()->has('ref_id')) {
463  $query['ref_id'] = $this->wrapper->query()->retrieve('ref_id', $this->kindlyTo->string());
464  }
465  if ($this->wrapper->query()->has('target')) {
466  $query['target'] = $this->wrapper->query()->retrieve('target', $this->kindlyTo->string());
467  }
468  } else {
469  parse_str((string) parse_url($url, PHP_URL_QUERY), $query);
470  }
471  if (isset($query['ref_id']) && (int) $query['ref_id']) {
472  $this->effectiveRefId = (int) $query['ref_id'];
473  return;
474  }
475  if (ilSession::has('lti_init_target') && ilSession::get('lti_init_target') != "") {
476  $target_arr = explode('_', ilSession::get('lti_init_target'));
477  ilSession::set('lti_init_target', "");
478  } else {
479  if (isset($query['target'])) {
480  $target_arr = explode('_', (string) $query['target']);
481  }
482  }
483  if (isset($target_arr[1]) and (int) $target_arr[1]) {
484  $this->effectiveRefId = (int) $target_arr[1];
485  }
486  }
487 }
static get(string $a_var)
$context
Definition: webdav.php:31
logout(bool $force_ilias_logout=false)
logout ILIAS and destroys Session and ilClientId cookie if no consumer is still open in the LTI User ...
static getLogger(string $a_component_id)
Get component logger.
removeContextFromSession(string $context_id)
$path
Definition: ltiservices.php:32
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ILIAS HTTP Wrapper WrapperFactory $wrapper
getSessionValue(string $sess_key)
global $DIC
Definition: feed.php:28
isLTIUser()
get LTI Mode from Users->getAuthMode
$ref_id
Definition: ltiauth.php:67
init()
Init LTI mode for lti authenticated users.
static setCookie(string $a_cookie_name, string $a_cookie_value='', bool $a_also_set_super_global=true, bool $a_set_cookie_invalid=false)
$_SERVER['HTTP_HOST']
Definition: raiseError.php:10
exitLti()
exit LTI session and if defined redirecting to returnUrl ToDo: Standard Template with delos ...
ILIAS DI Container $dic
private variables
$url
Definition: ltiregstart.php:35
static has($a_var)
getCookieValue(string $cookie_key)
static redirect(string $a_script)
const CHECK_HTTP_REFERER
contstants
ilLanguage $lng
public variables
ILIAS Refinery KindlyTo Group $kindlyTo
getCmdLink(String $cmd)
static getInstance()
for compatiblity with ilLTIRouterGUI
static _lookupType(int $id, bool $reference=false)
class for ILIAS ViewLTI
static clear(string $a_var)
ilLocatorGUI $locator
static set(string $a_var, $a_val)
Set a value.
findEffectiveRefId(?string $url=null)
Find effective ref_id for request.