ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
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  $cmdclass = '';
140  if ($this->wrapper->query()->has('baseClass')) {
141  $baseclass = strtolower($this->wrapper->query()->retrieve('baseClass', $this->kindlyTo->string()));
142  }
143  if ($this->wrapper->query()->has('cmdClass')) {
144  $cmdclass = strtolower($this->wrapper->query()->retrieve('cmdClass', $this->kindlyTo->string()));
145  }
146 
147  if ($baseclass == 'illtiroutergui') {
148  return;
149  }
150  }
151 
155  protected function getContextId(): ?int
156  {
157  global $DIC;
158 
159  // forced lti_context_id for example request command in exitLTI
160  if ($this->wrapper->query()->has('lti_context_id') &&
161  $this->wrapper->query()->retrieve('lti_context_id', $this->kindlyTo->string()) !== '') {
162  $contextId = (int) $this->wrapper->query()->retrieve('lti_context_id', $this->kindlyTo->int());
163  $this->log->debug("find context_id by GET param: " . (string) $contextId);
164  return $contextId;
165  }
166 
167  $this->findEffectiveRefId();
169  // ???
170  if (empty($ref_id)) {
171  return 0;
172  }
173 
174  $this->log->debug("Effective ref_id: " . $ref_id);
175  // context_id = ref_id in request
176  if (ilSession::has('lti_' . $ref_id . '_post_data')) {
177  $this->log->debug("lti context session exists for " . $ref_id);
178  // return $ref_id;
179  }
180  // sub item request
181  $this->log->debug("ref_id not exists as context_id, walking tree backwards to find a valid context_id");
182  $locator_items = $this->locator->getItems();
183  if (is_array($locator_items) && count($locator_items) > 0) {
184  for ($i = count($locator_items) - 1;$i >= 0;$i--) {
185  if (ilSession::has('lti_' . $locator_items[$i]['ref_id'] . '_post_data')) {
186  $this->log->debug("found valid ref_id in locator: " . $locator_items[$i]['ref_id']);
187  return $locator_items[$i]['ref_id'];
188  }
189  }
190  }
191  $this->log->warning("no valid context_id found for ref_id request: " . $ref_id);
192 
195  $obj_type = ilObject::_lookupType($ref_id, true);
196  $context_id = 0;
197  $referer = 0;
198 
199  // first try to get real http referer
200  if (isset($_SERVER['HTTP_REFERER'])) {
201  $this->findEffectiveRefId($_SERVER['HTTP_REFERER']);
202  } else { // only fallback and not reliable on multiple browser LTi contexts
203  if (ilSession::has('referer_ref_id')) {
204  $this->effectiveRefId = ilSession::get('referer_ref_id');
205  }
206  }
207 
208  $referer = (int) $this->effectiveRefId;
209 
210  if ($referer > 0) {
211  if (ilSession::has('lti_' . $referer . '_post_data')) {
212  $ref_id = $referer;
213  $context_id = $referer;
214  $obj_type = ilObject::_lookupType($ref_id, true);
215  $this->log->debug("referer obj_type: " . $obj_type);
216  } else {
217  $this->log->debug("search tree of referer...");
218  if ($this->dic->repositoryTree()->isInTree($referer)) {
219  $path = $this->dic->repositoryTree()->getPathId($referer);
220  for ($i = count($path) - 1;$i >= 0;$i--) {
221  if (ilSession::has('lti_' . $path[$i] . '_post_data')) {
222  // redirect to referer, because it is valid
223  $ref_id = $referer;
224  $context_id = $path[$i];
225  $obj_type = ilObject::_lookupType($ref_id, true);
226  break;
227  }
228  }
229  }
230  }
231  }
232  if ($ref_id > 0 && $obj_type != '') {
233  if (
234  (
235  $this->wrapper->query()->has('baseClass') &&
236  $this->wrapper->query()->retrieve('baseClass', $this->kindlyTo->string()) === 'ilDashboardGUI'
237  )
238  &&
239  (
240  $this->wrapper->query()->has('cmdClass') &&
241  $this->wrapper->query()->retrieve('cmdClass', $this->kindlyTo->string()) === 'ilpersonalprofilegui'
242  )
243  ) {
244  return $context_id;
245  }
246  // $this->dic->ui()->mainTemplate()->setOnScreenMessage('failure', $this->lng->txt('permission_denied'), true);
247  $redirect = $this->link_dir . "goto.php?target=" . $obj_type . "_" . $ref_id . "&lti_context_id=" . $context_id;
248  $this->log->debug("redirect: " . $redirect);
249  $DIC->ctrl()->redirectToURL($redirect);
250  }
251  }
252  $lti_context_ids = ilSession::get('lti_context_ids');
253  if (is_array($lti_context_ids) && count($lti_context_ids) > 0) {
254  if (count($lti_context_ids) == 1) {
255  $this->log->debug("using context_id from only LTI session");
256  return $lti_context_ids[0];
257  } else {
258  $this->log->warning("Multiple LTI sessions exists. The context_id can not be clearly detected");
259  }
260  }
261  return null;
262  }
263 
267  public function getPostData(): ?array
268  {
269  $context_id = $this->getContextId();
270  if ($context_id == 0) {
271  $this->log->warning("could not find any valid context_id!");
272  return null;
273  }
274  $post_data = ilSession::get('lti_' . $context_id . '_post_data');
275  if (!is_array($post_data)) {
276  $this->log->warning("no session post_data: " . "lti_" . $context_id . "_post_data");
277  return null;
278  }
279  return $post_data;
280  }
281 
285  public function getExternalCss(): string
286  {
287  $post_data = $this->getPostData();
288  if ($post_data !== null) {
289  return (isset($post_data['launch_presentation_css_url'])) ? $post_data['launch_presentation_css_url'] : '';
290  }
291  return '';
292  }
293 
297  public function getTitle(): string
298  {
299  $post_data = $this->getPostData();
300  if ($post_data !== null) {
301  return (isset($post_data['resource_link_title'])) ? "LTI - " . $post_data['resource_link_title'] : "LTI";
302  }
303  return "LTI";
304  }
305 
309  public function getTitleForExitPage(): string
310  {
311  return $this->lng->txt('lti_exited');
312  }
313 
317  public function getShortTitle(): string
318  {
319  return $this->lng->txt('lti_mode');
320  }
321 
326  public function exitLti(): void
327  {
328  $this->log->info("exitLTI");
329  $force_ilias_logout = false;
330  $context_id = $this->getContextId();
331  if ($context_id == 0) {
332  $this->log->warning("could not find any valid context_id!");
333  $force_ilias_logout = true;
334  }
335  $post_data = $this->getPostData();
336 
337  $return_url = '';
338  if (isset($post_data['launch_presentation_return_url'])) {
339  $return_url = $post_data['launch_presentation_return_url'];
340  }
341  $this->removeContextFromSession((string) $context_id);
342 
343  if (ilSession::has('lti_' . $context_id . '_post_data')) {
344  ilSession::clear('lti_' . $context_id . '_post_data');
345  $this->log->debug('unset SESSION["' . 'lti_' . $context_id . '_post_data"]');
346  }
347 
348  if (!isset($return_url) || $return_url === '') {
349  $cc = $this->dic->globalScreen()->tool()->context()->current();
350  $cc->addAdditionalData(LtiViewLayoutProvider::GS_EXIT_LTI, true);
351  $ui_factory = $this->dic->ui()->factory();
352  $renderer = $this->dic->ui()->renderer();
353  $content = [
354  $ui_factory->messageBox()->info($this->lng->txt('lti_exited_info'))
355  ];
356  $tpl = $this->dic["tpl"];
357  $tpl->setContent($renderer->render($content));
358  $this->logout($force_ilias_logout);
359  $tpl->printToStdout();
360  } else {
361  $this->logout($force_ilias_logout);
362  header('Location: ' . $return_url);
363  }
364  }
365 
369  public function logout(bool $force_ilias_logout = false): void
370  {
371  if ($force_ilias_logout) {
372  $this->log->warning("forcing logout ilias session, maybe a broken LTI context");
373  } else {
374  if (is_array(ilSession::get('lti_context_ids')) && count(ilSession::get('lti_context_ids')) > 0) {
375  $this->log->debug("there is another valid consumer session: ilias session logout refused.");
376  return;
377  }
378  }
379  $this->log->info("logout");
380  $this->dic->user()->setAuthMode((string) ilAuthUtils::AUTH_LOCAL);
381  //ilSession::setClosingContext(ilSession::SESSION_CLOSE_USER); // needed?
382  $auth = $this->dic['ilAuthSession'];
383  //$auth->logout(); // needed?
384  // $auth->setExpired($auth::SESSION_AUTH_EXPIRED, ilAuthStatus::STATUS_UNDEFINED);
385  $auth->setExpired(true);
386  session_destroy();
387  ilUtil::setCookie("ilClientId", "");
388  ilUtil::setCookie("PHPSESSID", "");
389  }
390 
396  public function getCmdLink(String $cmd): String
397  {
398  global $ilCtrl;
399  $lti_context_id = $this->getContextId();
400  $lti_context_id_param = ($lti_context_id != '') ? "&lti_context_id=" . $lti_context_id : '';
401  $targetScript = "";
402  return $this->link_dir . $targetScript . $this->dic->ctrl()->getLinkTargetByClass(array('illtiroutergui',strtolower(get_class($this))), $cmd) . "&baseClass=illtiroutergui" . $lti_context_id_param;
403  }
404 
405  private function getSessionValue(string $sess_key): string
406  {
407  if (ilSession::has($sess_key) && ilSession::get($sess_key) != '') {
408  return ilSession::get($sess_key);
409  } else {
410  return '';
411  }
412  }
413 
414  private function getCookieValue(string $cookie_key): string
415  {
416  if ($this->dic->wrapper->cookie()->has($cookie_key) && $this->dic->wrapper->cookie()->retrieve($cookie_key, $this->dic->refinery()->kindlyTo()->string() != '')) {
417  return $this->dic->wrapper->cookie()->retrieve($cookie_key, $this->dic->refinery()->kindlyTo()->string());
418  } else {
419  return '';
420  }
421  }
422 
423  private function removeContextFromSession(string $context_id): void
424  {
425  $lti_context_ids = ilSession::get('lti_context_ids');
426  if (is_array($lti_context_ids) && in_array($context_id, $lti_context_ids)) {
427  array_splice($lti_context_ids, array_search($context_id, $lti_context_ids), 1);
428  ilSession::set('lti_context_ids', $lti_context_ids);
429  }
430  }
431 
436  private function findEffectiveRefId(?string $url = null): void
437  {
438  $query = [];
439  if ($url === null) {
440  if ($this->wrapper->query()->has('ref_id')) {
441  $query['ref_id'] = $this->wrapper->query()->retrieve('ref_id', $this->kindlyTo->string());
442  }
443  if ($this->wrapper->query()->has('target')) {
444  $query['target'] = $this->wrapper->query()->retrieve('target', $this->kindlyTo->string());
445  }
446  } else {
447  parse_str((string) parse_url($url, PHP_URL_QUERY), $query);
448  }
449  if (isset($query['ref_id']) && (int) $query['ref_id']) {
450  $this->effectiveRefId = (int) $query['ref_id'];
451  return;
452  }
453  if (ilSession::has('lti_init_target') && ilSession::get('lti_init_target') != "") {
454  $target_arr = explode('_', ilSession::get('lti_init_target'));
455  ilSession::set('lti_init_target', "");
456  } else {
457  if (isset($query['target'])) {
458  $target_arr = explode('_', (string) $query['target']);
459  }
460  }
461  if (isset($target_arr[1]) and (int) $target_arr[1]) {
462  $this->effectiveRefId = (int) $target_arr[1];
463  }
464  }
465 }
static get(string $a_var)
$renderer
$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.
$url
Definition: shib_logout.php:63
removeContextFromSession(string $context_id)
$path
Definition: ltiservices.php:30
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)
isLTIUser()
get LTI Mode from Users->getAuthMode
$ref_id
Definition: ltiauth.php:66
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 ...
global $DIC
Definition: shib_login.php:25
ILIAS DI Container $dic
private variables
static has($a_var)
getCookieValue(string $cookie_key)
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
header()
expected output: > ILIAS shows the rendered Component.
Definition: header.php:13
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.