ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilLTIToolProvider.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
12 
13 
18 
26 class ilLTIToolProvider extends ToolProvider\ToolProvider
27 {
31  protected $logger = null;
32 
33 
34  public $debugMode = true; //ACHTUNG weg bei Produktiv-Umgebung
38  private static $LTI_VERSIONS = array(self::LTI_VERSION1, self::LTI_VERSION2);
42  private static $MESSAGE_TYPES = array('basic-lti-launch-request' => 'onLaunch',
43  'ContentItemSelectionRequest' => 'onContentItem',
44  'ToolProxyRegistrationRequest' => 'register');
50  private static $METHOD_NAMES = array('basic-lti-launch-request' => 'onLaunch',
51  'ContentItemSelectionRequest' => 'onContentItem',
52  'ToolProxyRegistrationRequest' => 'onRegister');
58  private static $LTI_CONSUMER_SETTING_NAMES = array('custom_tc_profile_url', 'custom_system_setting_url');
64  private static $LTI_CONTEXT_SETTING_NAMES = array('custom_context_setting_url',
65  'custom_lineitems_url', 'custom_results_url',
66  'custom_context_memberships_url');
72  private static $LTI_RESOURCE_LINK_SETTING_NAMES = array('lis_result_sourcedid', 'lis_outcome_service_url',
73  'ext_ims_lis_basic_outcome_url', 'ext_ims_lis_resultvalue_sourcedids',
74  'ext_ims_lis_memberships_id', 'ext_ims_lis_memberships_url',
75  'ext_ims_lti_tool_setting', 'ext_ims_lti_tool_setting_id', 'ext_ims_lti_tool_setting_url',
76  'custom_link_setting_url',
77  'custom_lineitem_url', 'custom_result_url');
83  private static $CUSTOM_SUBSTITUTION_VARIABLES = array('User.id' => 'user_id',
84  'User.image' => 'user_image',
85  'User.username' => 'username',
86  'User.scope.mentor' => 'role_scope_mentor',
87  'Membership.role' => 'roles',
88  'Person.sourcedId' => 'lis_person_sourcedid',
89  'Person.name.full' => 'lis_person_name_full',
90  'Person.name.family' => 'lis_person_name_family',
91  'Person.name.given' => 'lis_person_name_given',
92  'Person.email.primary' => 'lis_person_contact_email_primary',
93  'Context.id' => 'context_id',
94  'Context.type' => 'context_type',
95  'Context.title' => 'context_title',
96  'Context.label' => 'context_label',
97  'CourseOffering.sourcedId' => 'lis_course_offering_sourcedid',
98  'CourseSection.sourcedId' => 'lis_course_section_sourcedid',
99  'CourseSection.label' => 'context_label',
100  'CourseSection.title' => 'context_title',
101  'ResourceLink.id' => 'resource_link_id',
102  'ResourceLink.title' => 'resource_link_title',
103  'ResourceLink.description' => 'resource_link_description',
104  'Result.sourcedId' => 'lis_result_sourcedid',
105  'BasicOutcome.url' => 'lis_outcome_service_url',
106  'ToolConsumerProfile.url' => 'custom_tc_profile_url',
107  'ToolProxy.url' => 'tool_proxy_url',
108  'ToolProxy.custom.url' => 'custom_system_setting_url',
109  'ToolProxyBinding.custom.url' => 'custom_context_setting_url',
110  'LtiLink.custom.url' => 'custom_link_setting_url',
111  'LineItems.url' => 'custom_lineitems_url',
112  'LineItem.url' => 'custom_lineitem_url',
113  'Results.url' => 'custom_results_url',
114  'Result.url' => 'custom_result_url',
115  'ToolProxyBinding.memberships.url' => 'custom_context_memberships_url');
116 
117 
122  public function __construct(DataConnector $dataConnector)
123  {
124  global $DIC;
125 
126  $this->logger = $DIC->logger()->lti();
127  parent::__construct($dataConnector);
128  }
129 
130 
134  public function handleRequest()
135  {
136  if ($this->ok) {
137  if ($this->authenticate()) {
138  $this->doCallback();
139  }
140  }
141  // if return url is given, this redirects in case of errors
142  $this->result();
143  return $this->ok;
144  }
145 
146  ###
147  ### PROTECTED METHODS
148  ###
149 
155  protected function onLaunch()
156  {
157  // save/update current user
158  if ($this->user instanceof User) {
159  $this->user->save();
160  }
161 
162  if ($this->context instanceof Context) {
163  $this->context->save();
164  }
165 
166  if ($this->resourceLink instanceof ResourceLink) {
167  $this->resourceLink->save();
168  }
169  }
170 
176  protected function onContentItem()
177  {
178  $this->onError();
179  }
180 
186  protected function onRegister()
187  {
188  $this->onError();
189  }
190 
196  protected function onError()
197  {
198  // only return error status
199  return $this->ok;
200 
201  //$this->doCallback('onError');
202  // return parent::onError(); //Stefan M.
203  }
204 
205  ###
206  ### PRIVATE METHODS
207  ###
208 
216  private function doCallback($method = null)
217  {
218  $callback = $method;
219  if (is_null($callback)) {
220  $callback = self::$METHOD_NAMES[$_POST['lti_message_type']];
221  }
222  if (method_exists($this, $callback)) {
223  $result = $this->$callback(); // ACHTUNG HIER PROBLEM UK
224  } elseif (is_null($method) && $this->ok) {
225  $this->ok = false;
226  $this->reason = "Message type not supported: {$_POST['lti_message_type']}";
227  }
228  if ($this->ok && ($_POST['lti_message_type'] == 'ToolProxyRegistrationRequest')) {
229  $this->consumer->save();
230  }
231  }
232 
240  private function result()
241  {
242  $ok = false;
243  if (!$this->ok) {
244  $ok = $this->onError();
245  }
246  if (!$ok) {
247  if (!$this->ok) {
248  // If not valid, return an error message to the tool consumer if a return URL is provided
249  if (!empty($this->returnUrl)) {
250  $errorUrl = $this->returnUrl;
251  if (strpos($errorUrl, '?') === false) {
252  $errorUrl .= '?';
253  } else {
254  $errorUrl .= '&';
255  }
256  if ($this->debugMode && !is_null($this->reason)) {
257  $errorUrl .= 'lti_errormsg=' . urlencode("Debug error: $this->reason");
258  } else {
259  $errorUrl .= 'lti_errormsg=' . urlencode($this->message);
260  if (!is_null($this->reason)) {
261  $errorUrl .= '&lti_errorlog=' . urlencode("Debug error: $this->reason");
262  }
263  }
264  if (!is_null($this->consumer) && isset($_POST['lti_message_type']) && ($_POST['lti_message_type'] === 'ContentItemSelectionRequest')) {
265  $formParams = array();
266  if (isset($_POST['data'])) {
267  $formParams['data'] = $_POST['data'];
268  }
269  $version = (isset($_POST['lti_version'])) ? $_POST['lti_version'] : self::LTI_VERSION1;
270  $formParams = $this->consumer->signParameters($errorUrl, 'ContentItemSelection', $version, $formParams);
271  $page = self::sendForm($errorUrl, $formParams);
272  echo $page;
273  } else {
274  header("Location: {$errorUrl}");
275  }
276  exit; //ACHTUNG HIER PROBLEM UK
277  } else {
278  if (!is_null($this->errorOutput)) {
279  echo $this->errorOutput;
280  } elseif ($this->debugMode && !empty($this->reason)) {
281  echo "Debug error: {$this->reason}";
282  } else {
283  echo "Error: {$this->message}";
284  }
285  }
286  } elseif (!is_null($this->redirectUrl)) {
287  header("Location: {$this->redirectUrl}");
288  exit;
289  } elseif (!is_null($this->output)) {
290  echo $this->output;
291  }
292  }
293  }
294 
302  private function authenticate()
303  {
304 
305 // Get the consumer
306  $doSaveConsumer = false;
307  // Check all required launch parameters
308  $this->ok = isset($_POST['lti_message_type']) && array_key_exists($_POST['lti_message_type'], self::$MESSAGE_TYPES);
309  if (!$this->ok) {
310  $this->reason = 'Invalid or missing lti_message_type parameter.';
311  }
312  if ($this->ok) {
313  $this->ok = isset($_POST['lti_version']) && in_array($_POST['lti_version'], self::$LTI_VERSIONS);
314  if (!$this->ok) {
315  $this->reason = 'Invalid or missing lti_version parameter.';
316  }
317  }
318  if ($this->ok) {
319  if ($_POST['lti_message_type'] === 'basic-lti-launch-request') {
320  $this->ok = isset($_POST['resource_link_id']) && (strlen(trim($_POST['resource_link_id'])) > 0);
321  if (!$this->ok) {
322  $this->reason = 'Missing resource link ID.';
323  }
324  } elseif ($_POST['lti_message_type'] === 'ContentItemSelectionRequest') {
325  if (isset($_POST['accept_media_types']) && (strlen(trim($_POST['accept_media_types'])) > 0)) {
326  $mediaTypes = array_filter(explode(',', str_replace(' ', '', $_POST['accept_media_types'])), 'strlen');
327  $mediaTypes = array_unique($mediaTypes);
328  $this->ok = count($mediaTypes) > 0;
329  if (!$this->ok) {
330  $this->reason = 'No accept_media_types found.';
331  } else {
332  $this->mediaTypes = $mediaTypes;
333  }
334  } else {
335  $this->ok = false;
336  }
337  if ($this->ok && isset($_POST['accept_presentation_document_targets']) && (strlen(trim($_POST['accept_presentation_document_targets'])) > 0)) {
338  $documentTargets = array_filter(explode(',', str_replace(' ', '', $_POST['accept_presentation_document_targets'])), 'strlen');
339  $documentTargets = array_unique($documentTargets);
340  $this->ok = count($documentTargets) > 0;
341  if (!$this->ok) {
342  $this->reason = 'Missing or empty accept_presentation_document_targets parameter.';
343  } else {
344  foreach ($documentTargets as $documentTarget) {
345  $this->ok = $this->checkValue(
346  $documentTarget,
347  array('embed', 'frame', 'iframe', 'window', 'popup', 'overlay', 'none'),
348  'Invalid value in accept_presentation_document_targets parameter: %s.'
349  );
350  if (!$this->ok) {
351  break;
352  }
353  }
354  if ($this->ok) {
355  $this->documentTargets = $documentTargets;
356  }
357  }
358  } else {
359  $this->ok = false;
360  }
361  if ($this->ok) {
362  $this->ok = isset($_POST['content_item_return_url']) && (strlen(trim($_POST['content_item_return_url'])) > 0);
363  if (!$this->ok) {
364  $this->reason = 'Missing content_item_return_url parameter.';
365  }
366  }
367  } elseif ($_POST['lti_message_type'] == 'ToolProxyRegistrationRequest') {
368  $this->ok = ((isset($_POST['reg_key']) && (strlen(trim($_POST['reg_key'])) > 0)) &&
369  (isset($_POST['reg_password']) && (strlen(trim($_POST['reg_password'])) > 0)) &&
370  (isset($_POST['tc_profile_url']) && (strlen(trim($_POST['tc_profile_url'])) > 0)) &&
371  (isset($_POST['launch_presentation_return_url']) && (strlen(trim($_POST['launch_presentation_return_url'])) > 0)));
372  if ($this->debugMode && !$this->ok) {
373  $this->reason = 'Missing message parameters.';
374  }
375  }
376  }
377  $now = time();
378 
379  $this->logger->debug('Checking consumer key...');
380 
381  // Check consumer key
382  if ($this->ok && ($_POST['lti_message_type'] != 'ToolProxyRegistrationRequest')) {
383  $this->ok = isset($_POST['oauth_consumer_key']);
384  if (!$this->ok) {
385  $this->reason = 'Missing consumer key.';
386  }
387  if ($this->ok) {
388  // $this->consumer = new ToolConsumer($_POST['oauth_consumer_key'], $this->dataConnector);
389  $this->consumer = new ilLTIToolConsumer($_POST['oauth_consumer_key'], $this->dataConnector);
390  $this->ok = !is_null($this->consumer->created);
391  if (!$this->ok) {
392  $this->reason = 'Invalid consumer key.';
393  }
394  }
395  if ($this->ok) {
396  $today = date('Y-m-d', $now);
397  if (is_null($this->consumer->lastAccess)) {
398  $doSaveConsumer = true;
399  } else {
400  $last = date('Y-m-d', $this->consumer->lastAccess);
401  $doSaveConsumer = $doSaveConsumer || ($last !== $today);
402  }
403  $this->consumer->last_access = $now;
404  try {
405  $store = new OAuthDataStore($this);
406  $server = new OAuth\OAuthServer($store);
407  $method = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
408  $server->add_signature_method($method);
409  $request = OAuth\OAuthRequest::from_request();
410  $res = $server->verify_request($request);
411  } catch (\Exception $e) {
412  $this->ok = false;
413  if (empty($this->reason)) {
414  if ($this->debugMode) {
415  $consumer = new OAuth\OAuthConsumer($this->consumer->getKey(), $this->consumer->secret);
416  $signature = $request->build_signature($method, $consumer, false);
417  $this->reason = $e->getMessage();
418  if (empty($this->reason)) {
419  $this->reason = 'OAuth exception';
420  }
421  $this->details[] = 'Timestamp: ' . time();
422  $this->details[] = "Signature: {$signature}";
423  $this->details[] = "Base string: {$request->base_string}]";
424  } else {
425  $this->reason = 'OAuth signature check failed - perhaps an incorrect secret or timestamp.';
426  }
427  }
428  }
429  }
430  // $this->ok = true; //ACHTUNG Problem Signature bei M.
431  if ($this->ok) {
432  $today = date('Y-m-d', $now);
433  if (is_null($this->consumer->lastAccess)) {
434  $doSaveConsumer = true;
435  } else {
436  $last = date('Y-m-d', $this->consumer->lastAccess);
437  $doSaveConsumer = $doSaveConsumer || ($last !== $today);
438  }
439  $this->consumer->last_access = $now;
440  if ($this->consumer->protected) {
441  if (!is_null($this->consumer->consumerGuid)) {
442  $this->ok = empty($_POST['tool_consumer_instance_guid']) ||
443  ($this->consumer->consumerGuid === $_POST['tool_consumer_instance_guid']);
444  if (!$this->ok) {
445  $this->reason = 'Request is from an invalid tool consumer.';
446  }
447  } else {
448  $this->ok = isset($_POST['tool_consumer_instance_guid']);
449  if (!$this->ok) {
450  $this->reason = 'A tool consumer GUID must be included in the launch request.';
451  }
452  }
453  }
454  if ($this->ok) {
455  $this->ok = $this->consumer->enabled;
456  if (!$this->ok) {
457  $this->reason = 'Tool consumer has not been enabled by the tool provider.';
458  }
459  }
460  if ($this->ok) {
461  $this->ok = is_null($this->consumer->enableFrom) || ($this->consumer->enableFrom <= $now);
462  if ($this->ok) {
463  $this->ok = is_null($this->consumer->enableUntil) || ($this->consumer->enableUntil > $now);
464  if (!$this->ok) {
465  $this->reason = 'Tool consumer access has expired.';
466  }
467  } else {
468  $this->reason = 'Tool consumer access is not yet available.';
469  }
470  }
471  }
472  // Validate other message parameter values
473  if ($this->ok) {
474  if ($_POST['lti_message_type'] === 'ContentItemSelectionRequest') {
475  if (isset($_POST['accept_unsigned'])) {
476  $this->ok = $this->checkValue($_POST['accept_unsigned'], array('true', 'false'), 'Invalid value for accept_unsigned parameter: %s.');
477  }
478  if ($this->ok && isset($_POST['accept_multiple'])) {
479  $this->ok = $this->checkValue($_POST['accept_multiple'], array('true', 'false'), 'Invalid value for accept_multiple parameter: %s.');
480  }
481  if ($this->ok && isset($_POST['accept_copy_advice'])) {
482  $this->ok = $this->checkValue($_POST['accept_copy_advice'], array('true', 'false'), 'Invalid value for accept_copy_advice parameter: %s.');
483  }
484  if ($this->ok && isset($_POST['auto_create'])) {
485  $this->ok = $this->checkValue($_POST['auto_create'], array('true', 'false'), 'Invalid value for auto_create parameter: %s.');
486  }
487  if ($this->ok && isset($_POST['can_confirm'])) {
488  $this->ok = $this->checkValue($_POST['can_confirm'], array('true', 'false'), 'Invalid value for can_confirm parameter: %s.');
489  }
490  } elseif (isset($_POST['launch_presentation_document_target'])) {
491  $this->ok = $this->checkValue(
492  $_POST['launch_presentation_document_target'],
493  array('embed', 'frame', 'iframe', 'window', 'popup', 'overlay'),
494  'Invalid value for launch_presentation_document_target parameter: %s.'
495  );
496  }
497  }
498  }
499 
500  if ($this->ok && ($_POST['lti_message_type'] === 'ToolProxyRegistrationRequest')) {
501  $this->ok = $_POST['lti_version'] == self::LTI_VERSION2;
502  if (!$this->ok) {
503  $this->reason = 'Invalid lti_version parameter';
504  }
505  if ($this->ok) {
506  $http = new HTTPMessage($_POST['tc_profile_url'], 'GET', null, 'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
507  $this->ok = $http->send();
508  if (!$this->ok) {
509  $this->reason = 'Tool consumer profile not accessible.';
510  } else {
511  $tcProfile = json_decode($http->response);
512  $this->ok = !is_null($tcProfile);
513  if (!$this->ok) {
514  $this->reason = 'Invalid JSON in tool consumer profile.';
515  }
516  }
517  }
518  // Check for required capabilities
519  if ($this->ok) {
520  // $this->consumer = new ToolConsumer($_POST['reg_key'], $this->dataConnector);
521  $this->consumer = new ilLTIToolConsumer($_POST['oauth_consumer_key'], $this->dataConnector);
522  $this->consumer->profile = $tcProfile;
523  $capabilities = $this->consumer->profile->capability_offered;
524  $missing = array();
525  foreach ($this->resourceHandlers as $resourceHandler) {
526  foreach ($resourceHandler->requiredMessages as $message) {
527  if (!in_array($message->type, $capabilities)) {
528  $missing[$message->type] = true;
529  }
530  }
531  }
532  // smeyer: 21 Nov 2018 constraints are not supported in the moment and private in the base class.
547  }
548  // Check for required services
549  if ($this->ok) {
550  foreach ($this->requiredServices as $service) {
551  foreach ($service->formats as $format) {
552  if (!$this->findService($format, $service->actions)) {
553  if ($this->ok) {
554  $this->reason = 'Required service(s) not offered - ';
555  $this->ok = false;
556  } else {
557  $this->reason .= ', ';
558  }
559  $this->reason .= "'{$format}' [" . implode(', ', $service->actions) . ']';
560  }
561  }
562  }
563  }
564  if ($this->ok) {
565  if ($_POST['lti_message_type'] === 'ToolProxyRegistrationRequest') {
566  $this->consumer->profile = $tcProfile;
567  $this->consumer->secret = $_POST['reg_password'];
568  $this->consumer->ltiVersion = $_POST['lti_version'];
569  $this->consumer->name = $tcProfile->product_instance->service_owner->service_owner_name->default_value;
570  $this->consumer->consumerName = $this->consumer->name;
571  $this->consumer->consumerVersion = "{$tcProfile->product_instance->product_info->product_family->code}-{$tcProfile->product_instance->product_info->product_version}";
572  $this->consumer->consumerGuid = $tcProfile->product_instance->guid;
573  $this->consumer->enabled = true;
574  $this->consumer->protected = true;
575  $doSaveConsumer = true;
576  }
577  }
578  } elseif ($this->ok && !empty($_POST['custom_tc_profile_url']) && empty($this->consumer->profile)) {
579  $http = new HTTPMessage($_POST['custom_tc_profile_url'], 'GET', null, 'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
580  if ($http->send()) {
581  $tcProfile = json_decode($http->response);
582  if (!is_null($tcProfile)) {
583  $this->consumer->profile = $tcProfile;
584  $doSaveConsumer = true;
585  }
586  }
587  }
588  //ACHTUNG HIER TODO UWE
589  // Validate message parameter constraints
590  // if ($this->ok) {
591  // $invalidParameters = array();
592  // foreach ($this->constraints as $name => $constraint) {
593  // // if (empty($constraint['messages']) || in_array($_POST['lti_message_type'], $constraint['messages'])) {
594  // // $ok = true;
595  // // if ($constraint['required']) {
596  // // if (!isset($_POST[$name]) || (strlen(trim($_POST[$name])) <= 0)) {
597  // // $invalidParameters[] = "{$name} (missing)";
598  // // $ok = false;
599  // // }
600  // // }
601  // // if ($ok && !is_null($constraint['max_length']) && isset($_POST[$name])) {
602  // // if (strlen(trim($_POST[$name])) > $constraint['max_length']) {
603  // // $invalidParameters[] = "{$name} (too long)";
604  // // }
605  // // }
606  // // }
607  // }
608  // if (count($invalidParameters) > 0) {
609  // $this->ok = false;
610  // if (empty($this->reason)) {
611  // $this->reason = 'Invalid parameter(s): ' . implode(', ', $invalidParameters) . '.';
612  // }
613  // }
614  // }
615 
616  $this->logger->debug('Still ok: ' . ($this->ok ? '1' : '0'));
617  if (!$this->ok) {
618  $this->logger->debug('Reason: ' . $this->reason);
619  }
620 
621  if ($this->ok) {
622 
623 // Set the request context
624  if (isset($_POST['context_id'])) {
625  $this->context = Context::fromConsumer($this->consumer, trim($_POST['context_id']));
626  $title = '';
627  if (isset($_POST['context_title'])) {
628  $title = trim($_POST['context_title']);
629  }
630  if (empty($title)) {
631  $title = "Course {$this->context->getId()}";
632  }
633  $this->context->title = $title;
634  }
635 
636  // Set the request resource link
637  if (isset($_POST['resource_link_id'])) {
638  $contentItemId = '';
639  if (isset($_POST['custom_content_item_id'])) {
640  $contentItemId = $_POST['custom_content_item_id'];
641  }
642  $this->resourceLink = ResourceLink::fromConsumer($this->consumer, trim($_POST['resource_link_id']), $contentItemId);
643  if (!empty($this->context)) {
644  $this->resourceLink->setContextId($this->context->getRecordId());
645  }
646  $title = '';
647  if (isset($_POST['resource_link_title'])) {
648  $title = trim($_POST['resource_link_title']);
649  }
650  if (empty($title)) {
651  $title = "Resource {$this->resourceLink->getId()}";
652  }
653  $this->resourceLink->title = $title;
654  // Delete any existing custom parameters
655  foreach ($this->consumer->getSettings() as $name => $value) {
656  if (strpos($name, 'custom_') === 0) {
657  $this->consumer->setSetting($name);
658  $doSaveConsumer = true;
659  }
660  }
661  if (!empty($this->context)) {
662  foreach ($this->context->getSettings() as $name => $value) {
663  if (strpos($name, 'custom_') === 0) {
664  $this->context->setSetting($name);
665  }
666  }
667  }
668  foreach ($this->resourceLink->getSettings() as $name => $value) {
669  if (strpos($name, 'custom_') === 0) {
670  $this->resourceLink->setSetting($name);
671  }
672  }
673  // Save LTI parameters
674  foreach (self::$LTI_CONSUMER_SETTING_NAMES as $name) {
675  if (isset($_POST[$name])) {
676  $this->consumer->setSetting($name, $_POST[$name]);
677  } else {
678  $this->consumer->setSetting($name);
679  }
680  }
681  if (!empty($this->context)) {
682  foreach (self::$LTI_CONTEXT_SETTING_NAMES as $name) {
683  if (isset($_POST[$name])) {
684  $this->context->setSetting($name, $_POST[$name]);
685  } else {
686  $this->context->setSetting($name);
687  }
688  }
689  }
690  foreach (self::$LTI_RESOURCE_LINK_SETTING_NAMES as $name) {
691  if (isset($_POST[$name])) {
692  $this->resourceLink->setSetting($name, $_POST[$name]);
693  } else {
694  $this->resourceLink->setSetting($name);
695  }
696  }
697  // Save other custom parameters
698  foreach ($_POST as $name => $value) {
699  if ((strpos($name, 'custom_') === 0) &&
700  !in_array($name, array_merge(self::$LTI_CONSUMER_SETTING_NAMES, self::$LTI_CONTEXT_SETTING_NAMES, self::$LTI_RESOURCE_LINK_SETTING_NAMES))) {
701  $this->resourceLink->setSetting($name, $value);
702  }
703  }
704  }
705 
706  // Set the user instance
707  $userId = '';
708  if (isset($_POST['user_id'])) {
709  $userId = trim($_POST['user_id']);
710  }
711 
712  $this->user = User::fromResourceLink($this->resourceLink, $userId);
713 
714  // Set the user name
715  $firstname = (isset($_POST['lis_person_name_given'])) ? $_POST['lis_person_name_given'] : '';
716  $lastname = (isset($_POST['lis_person_name_family'])) ? $_POST['lis_person_name_family'] : '';
717  $fullname = (isset($_POST['lis_person_name_full'])) ? $_POST['lis_person_name_full'] : '';
718  $this->user->setNames($firstname, $lastname, $fullname);
719 
720  // Set the user email
721  $email = (isset($_POST['lis_person_contact_email_primary'])) ? $_POST['lis_person_contact_email_primary'] : '';
722  $this->user->setEmail($email, $this->defaultEmail);
723 
724  // Set the user image URI
725  if (isset($_POST['user_image'])) {
726  $this->user->image = $_POST['user_image'];
727  }
728 
729  // Set the user roles
730  if (isset($_POST['roles'])) {
731  $this->user->roles = self::parseRoles($_POST['roles']);
732  }
733 
734  // Initialise the consumer and check for changes
735  $this->consumer->defaultEmail = $this->defaultEmail;
736  if ($this->consumer->ltiVersion !== $_POST['lti_version']) {
737  $this->consumer->ltiVersion = $_POST['lti_version'];
738  $doSaveConsumer = true;
739  }
740  if (isset($_POST['tool_consumer_instance_name'])) {
741  if ($this->consumer->consumerName !== $_POST['tool_consumer_instance_name']) {
742  $this->consumer->consumerName = $_POST['tool_consumer_instance_name'];
743  $doSaveConsumer = true;
744  }
745  }
746  if (isset($_POST['tool_consumer_info_product_family_code'])) {
747  $version = $_POST['tool_consumer_info_product_family_code'];
748  if (isset($_POST['tool_consumer_info_version'])) {
749  $version .= "-{$_POST['tool_consumer_info_version']}";
750  }
751  // do not delete any existing consumer version if none is passed
752  if ($this->consumer->consumerVersion !== $version) {
753  $this->consumer->consumerVersion = $version;
754  $doSaveConsumer = true;
755  }
756  } elseif (isset($_POST['ext_lms']) && ($this->consumer->consumerName !== $_POST['ext_lms'])) {
757  $this->consumer->consumerVersion = $_POST['ext_lms'];
758  $doSaveConsumer = true;
759  }
760  if (isset($_POST['tool_consumer_instance_guid'])) {
761  if (is_null($this->consumer->consumerGuid)) {
762  $this->consumer->consumerGuid = $_POST['tool_consumer_instance_guid'];
763  $doSaveConsumer = true;
764  } elseif (!$this->consumer->protected) {
765  $doSaveConsumer = ($this->consumer->consumerGuid !== $_POST['tool_consumer_instance_guid']);
766  if ($doSaveConsumer) {
767  $this->consumer->consumerGuid = $_POST['tool_consumer_instance_guid'];
768  }
769  }
770  }
771  if (isset($_POST['launch_presentation_css_url'])) {
772  if ($this->consumer->cssPath !== $_POST['launch_presentation_css_url']) {
773  $this->consumer->cssPath = $_POST['launch_presentation_css_url'];
774  $doSaveConsumer = true;
775  }
776  } elseif (isset($_POST['ext_launch_presentation_css_url']) &&
777  ($this->consumer->cssPath !== $_POST['ext_launch_presentation_css_url'])) {
778  $this->consumer->cssPath = $_POST['ext_launch_presentation_css_url'];
779  $doSaveConsumer = true;
780  } elseif (!empty($this->consumer->cssPath)) {
781  $this->consumer->cssPath = null;
782  $doSaveConsumer = true;
783  }
784  }
785 
786  // Persist changes to consumer
787  if ($doSaveConsumer) {
788  $this->consumer->save();
789  }
790  if ($this->ok && isset($this->context)) {
791  $this->context->save();//ACHTUNG TODO UWE
792  }
793 
794  $this->logger->dump(get_class($this->context));
795 
796 
797  if ($this->ok && isset($this->resourceLink)) {
798 
799 // Check if a share arrangement is in place for this resource link
800  // $this->ok = $this->checkForShare();//ACHTUNG TODO UWE
801  // Persist changes to resource link
802  $this->resourceLink->save();
803 
804  // Save the user instance
805  if (isset($_POST['lis_result_sourcedid'])) {
806  if ($this->user->ltiResultSourcedId !== $_POST['lis_result_sourcedid']) {
807  $this->user->ltiResultSourcedId = $_POST['lis_result_sourcedid'];
808  $this->user->save();
809  }
810  } elseif (!empty($this->user->ltiResultSourcedId)) {
811  $this->user->ltiResultSourcedId = '';
812  $this->user->save();
813  }
814  }
815  // die ($this->reason.'---'.$this->ok);//ACHTUNG WEG!
816  return $this->ok;
817  }
818 
824  private function checkForShare()
825  {
826  $ok = true;
827  $doSaveResourceLink = true;
828 
829  $id = $this->resourceLink->primaryResourceLinkId;
830 
831  $shareRequest = isset($_POST['custom_share_key']) && !empty($_POST['custom_share_key']);
832  if ($shareRequest) {
833  if (!$this->allowSharing) {
834  $ok = false;
835  $this->reason = 'Your sharing request has been refused because sharing is not being permitted.';
836  } else {
837  // Check if this is a new share key
838  $shareKey = new ResourceLinkShareKey($this->resourceLink, $_POST['custom_share_key']);
839  if (!is_null($shareKey->primaryConsumerKey) && !is_null($shareKey->primaryResourceLinkId)) {
840  // Update resource link with sharing primary resource link details
841  $key = $shareKey->primaryConsumerKey;
842  $id = $shareKey->primaryResourceLinkId;
843  $ok = ($key !== $this->consumer->getKey()) || ($id != $this->resourceLink->getId());
844  if ($ok) {
845  $this->resourceLink->primaryConsumerKey = $key;
846  $this->resourceLink->primaryResourceLinkId = $id;
847  $this->resourceLink->shareApproved = $shareKey->autoApprove;
848  $ok = $this->resourceLink->save();
849  if ($ok) {
850  $doSaveResourceLink = false;
851  $this->user->getResourceLink()->primaryConsumerKey = $key;
852  $this->user->getResourceLink()->primaryResourceLinkId = $id;
853  $this->user->getResourceLink()->shareApproved = $shareKey->autoApprove;
854  $this->user->getResourceLink()->updated = time();
855  // Remove share key
856  $shareKey->delete();
857  } else {
858  $this->reason = 'An error occurred initialising your share arrangement.';
859  }
860  } else {
861  $this->reason = 'It is not possible to share your resource link with yourself.';
862  }
863  }
864  if ($ok) {
865  $ok = !is_null($key);
866  if (!$ok) {
867  $this->reason = 'You have requested to share a resource link but none is available.';
868  } else {
869  $ok = (!is_null($this->user->getResourceLink()->shareApproved) && $this->user->getResourceLink()->shareApproved);
870  if (!$ok) {
871  $this->reason = 'Your share request is waiting to be approved.';
872  }
873  }
874  }
875  }
876  } else {
877  // Check no share is in place
878  $ok = is_null($id);
879  if (!$ok) {
880  $this->reason = 'You have not requested to share a resource link but an arrangement is currently in place.';
881  }
882  }
883  // Look up primary resource link
884  if ($ok && !is_null($id)) {
885  // $consumer = new ToolConsumer($key, $this->dataConnector);
886  $consumer = new ilLTIToolConsumer($_POST['oauth_consumer_key'], $this->dataConnector);
887  $ok = !is_null($consumer->created);
888  if ($ok) {
889  $resourceLink = ResourceLink::fromConsumer($consumer, $id);
890  $ok = !is_null($resourceLink->created);
891  }
892  if ($ok) {
893  if ($doSaveResourceLink) {
894  $this->resourceLink->save();
895  }
896  $this->resourceLink = $resourceLink;
897  } else {
898  $this->reason = 'Unable to load resource link being shared.';
899  }
900  }
901 
902  return $ok;
903  }
909  private function checkValue($value, $values, $reason)
910  {
911  $ok = in_array($value, $values);
912  if (!$ok && !empty($reason)) {
913  $this->reason = sprintf($reason, $value);
914  }
915 
916  return $ok;
917  }
918 }
handleRequest()
Process an incoming request.
authenticate()
Check the authenticity of the LTI launch request.
if($orgName !==null) if($spconfig->hasValue('contacts')) $email
Definition: metadata.php:193
$format
Definition: metadata.php:141
Class to represent an OAuth Data Store.
$result
global $DIC
Definition: saml.php:7
onContentItem()
Process a valid content-item request.
checkValue($value, $values, $reason)
Validate a parameter value from an array of permitted values.
LTI provider for LTI launch.
onLaunch()
Process a valid launch request.
static $METHOD_NAMES
List of supported message types and associated class methods.
if(!array_key_exists('StateId', $_REQUEST)) $id
$service
Definition: login.php:15
static $MESSAGE_TYPES
List of supported message types and associated class methods.
if(! $oauthconfig->getBoolean('getUserInfo.enable', FALSE)) $store
Definition: getUserInfo.php:11
user()
Definition: user.php:4
onError()
Process a response to an invalid request.
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\+" &#(? foreach( $entity_files as $file) $output
if($format !==null) $name
Definition: metadata.php:146
static $LTI_CONTEXT_SETTING_NAMES
Names of LTI parameters to be retained in the context settings property.
catch(Exception $e) $message
LTI provider for LTI launch.
foreach($_POST as $key=> $value) $res
result()
Perform the result of an action.
date( 'd-M-Y', $objPHPExcel->getProperties() ->getCreated())
doCallback($method=null)
Call any callback function for the requested action.
Add a drawing to the header
Definition: 04printing.php:69
Class to provide a connection to a persistent store for LTI objects.
Class to represent a tool consumer resource link share key.
$http
Definition: raiseError.php:7
$consumer
Definition: demo.php:30
Create styles array
The data for the language used.
$server
Definition: getUserInfo.php:12
static $LTI_CONSUMER_SETTING_NAMES
Names of LTI parameters to be retained in the consumer settings property.
static $LTI_RESOURCE_LINK_SETTING_NAMES
Names of LTI parameters to be retained in the resource link settings property.
static $CUSTOM_SUBSTITUTION_VARIABLES
Names of LTI custom parameter substitution variables (or capabilities) and their associated default m...
__construct(DataConnector $dataConnector)
ilLTIToolProvider constructor.
checkForShare()
Check if a share arrangement is in place.
static $LTI_VERSIONS
Permitted LTI versions for messages.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
onRegister()
Process a valid tool proxy registration request.
Class to represent a tool consumer context.
Definition: Context.php:17
$key
Definition: croninfo.php:18
$_POST["username"]
Class to represent an HTTP message.
Definition: HTTPMessage.php:14
Class to represent a tool consumer user.
Definition: User.php:15