ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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)) {
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  foreach ($this->constraints as $name => $constraint) {
533  if ($constraint['required']) {
534  if (!in_array($name, $capabilities) && !in_array($name, array_flip($capabilities))) {
535  $missing[$name] = true;
536  }
537  }
538  }
539  if (!empty($missing)) {
540  ksort($missing);
541  $this->reason = 'Required capability not offered - \'' . implode('\', \'', array_keys($missing)) . '\'';
542  $this->ok = false;
543  }
544  }
545  // Check for required services
546  if ($this->ok) {
547  foreach ($this->requiredServices as $service) {
548  foreach ($service->formats as $format) {
549  if (!$this->findService($format, $service->actions)) {
550  if ($this->ok) {
551  $this->reason = 'Required service(s) not offered - ';
552  $this->ok = false;
553  } else {
554  $this->reason .= ', ';
555  }
556  $this->reason .= "'{$format}' [" . implode(', ', $service->actions) . ']';
557  }
558  }
559  }
560  }
561  if ($this->ok) {
562  if ($_POST['lti_message_type'] === 'ToolProxyRegistrationRequest') {
563  $this->consumer->profile = $tcProfile;
564  $this->consumer->secret = $_POST['reg_password'];
565  $this->consumer->ltiVersion = $_POST['lti_version'];
566  $this->consumer->name = $tcProfile->product_instance->service_owner->service_owner_name->default_value;
567  $this->consumer->consumerName = $this->consumer->name;
568  $this->consumer->consumerVersion = "{$tcProfile->product_instance->product_info->product_family->code}-{$tcProfile->product_instance->product_info->product_version}";
569  $this->consumer->consumerGuid = $tcProfile->product_instance->guid;
570  $this->consumer->enabled = true;
571  $this->consumer->protected = true;
572  $doSaveConsumer = true;
573  }
574  }
575  } elseif ($this->ok && !empty($_POST['custom_tc_profile_url']) && empty($this->consumer->profile)) {
576  $http = new HTTPMessage($_POST['custom_tc_profile_url'], 'GET', null, 'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
577  if ($http->send()) {
578  $tcProfile = json_decode($http->response);
579  if (!is_null($tcProfile)) {
580  $this->consumer->profile = $tcProfile;
581  $doSaveConsumer = true;
582  }
583  }
584  }
585  //ACHTUNG HIER TODO UWE
586  // Validate message parameter constraints
587  // if ($this->ok) {
588  // $invalidParameters = array();
589  // foreach ($this->constraints as $name => $constraint) {
590  // // if (empty($constraint['messages']) || in_array($_POST['lti_message_type'], $constraint['messages'])) {
591  // // $ok = true;
592  // // if ($constraint['required']) {
593  // // if (!isset($_POST[$name]) || (strlen(trim($_POST[$name])) <= 0)) {
594  // // $invalidParameters[] = "{$name} (missing)";
595  // // $ok = false;
596  // // }
597  // // }
598  // // if ($ok && !is_null($constraint['max_length']) && isset($_POST[$name])) {
599  // // if (strlen(trim($_POST[$name])) > $constraint['max_length']) {
600  // // $invalidParameters[] = "{$name} (too long)";
601  // // }
602  // // }
603  // // }
604  // }
605  // if (count($invalidParameters) > 0) {
606  // $this->ok = false;
607  // if (empty($this->reason)) {
608  // $this->reason = 'Invalid parameter(s): ' . implode(', ', $invalidParameters) . '.';
609  // }
610  // }
611  // }
612 
613  $this->logger->debug('Still ok: ' . ($this->ok ? '1' : '0'));
614  if (!$this->ok) {
615  $this->logger->debug('Reason: ' . $this->reason);
616  }
617 
618  if ($this->ok) {
619 
620 // Set the request context
621  if (isset($_POST['context_id'])) {
622  $this->context = Context::fromConsumer($this->consumer, trim($_POST['context_id']));
623  $title = '';
624  if (isset($_POST['context_title'])) {
625  $title = trim($_POST['context_title']);
626  }
627  if (empty($title)) {
628  $title = "Course {$this->context->getId()}";
629  }
630  $this->context->title = $title;
631  }
632 
633  // Set the request resource link
634  if (isset($_POST['resource_link_id'])) {
635  $contentItemId = '';
636  if (isset($_POST['custom_content_item_id'])) {
637  $contentItemId = $_POST['custom_content_item_id'];
638  }
639  $this->resourceLink = ResourceLink::fromConsumer($this->consumer, trim($_POST['resource_link_id']), $contentItemId);
640  if (!empty($this->context)) {
641  $this->resourceLink->setContextId($this->context->getRecordId());
642  }
643  $title = '';
644  if (isset($_POST['resource_link_title'])) {
645  $title = trim($_POST['resource_link_title']);
646  }
647  if (empty($title)) {
648  $title = "Resource {$this->resourceLink->getId()}";
649  }
650  $this->resourceLink->title = $title;
651  // Delete any existing custom parameters
652  foreach ($this->consumer->getSettings() as $name => $value) {
653  if (strpos($name, 'custom_') === 0) {
654  $this->consumer->setSetting($name);
655  $doSaveConsumer = true;
656  }
657  }
658  if (!empty($this->context)) {
659  foreach ($this->context->getSettings() as $name => $value) {
660  if (strpos($name, 'custom_') === 0) {
661  $this->context->setSetting($name);
662  }
663  }
664  }
665  foreach ($this->resourceLink->getSettings() as $name => $value) {
666  if (strpos($name, 'custom_') === 0) {
667  $this->resourceLink->setSetting($name);
668  }
669  }
670  // Save LTI parameters
671  foreach (self::$LTI_CONSUMER_SETTING_NAMES as $name) {
672  if (isset($_POST[$name])) {
673  $this->consumer->setSetting($name, $_POST[$name]);
674  } else {
675  $this->consumer->setSetting($name);
676  }
677  }
678  if (!empty($this->context)) {
679  foreach (self::$LTI_CONTEXT_SETTING_NAMES as $name) {
680  if (isset($_POST[$name])) {
681  $this->context->setSetting($name, $_POST[$name]);
682  } else {
683  $this->context->setSetting($name);
684  }
685  }
686  }
687  foreach (self::$LTI_RESOURCE_LINK_SETTING_NAMES as $name) {
688  if (isset($_POST[$name])) {
689  $this->resourceLink->setSetting($name, $_POST[$name]);
690  } else {
691  $this->resourceLink->setSetting($name);
692  }
693  }
694  // Save other custom parameters
695  foreach ($_POST as $name => $value) {
696  if ((strpos($name, 'custom_') === 0) &&
697  !in_array($name, array_merge(self::$LTI_CONSUMER_SETTING_NAMES, self::$LTI_CONTEXT_SETTING_NAMES, self::$LTI_RESOURCE_LINK_SETTING_NAMES))) {
698  $this->resourceLink->setSetting($name, $value);
699  }
700  }
701  }
702 
703  // Set the user instance
704  $userId = '';
705  if (isset($_POST['user_id'])) {
706  $userId = trim($_POST['user_id']);
707  }
708 
709  $this->user = User::fromResourceLink($this->resourceLink, $userId);
710 
711  // Set the user name
712  $firstname = (isset($_POST['lis_person_name_given'])) ? $_POST['lis_person_name_given'] : '';
713  $lastname = (isset($_POST['lis_person_name_family'])) ? $_POST['lis_person_name_family'] : '';
714  $fullname = (isset($_POST['lis_person_name_full'])) ? $_POST['lis_person_name_full'] : '';
715  $this->user->setNames($firstname, $lastname, $fullname);
716 
717  // Set the user email
718  $email = (isset($_POST['lis_person_contact_email_primary'])) ? $_POST['lis_person_contact_email_primary'] : '';
719  $this->user->setEmail($email, $this->defaultEmail);
720 
721  // Set the user image URI
722  if (isset($_POST['user_image'])) {
723  $this->user->image = $_POST['user_image'];
724  }
725 
726  // Set the user roles
727  if (isset($_POST['roles'])) {
728  $this->user->roles = self::parseRoles($_POST['roles']);
729  }
730 
731  // Initialise the consumer and check for changes
732  $this->consumer->defaultEmail = $this->defaultEmail;
733  if ($this->consumer->ltiVersion !== $_POST['lti_version']) {
734  $this->consumer->ltiVersion = $_POST['lti_version'];
735  $doSaveConsumer = true;
736  }
737  if (isset($_POST['tool_consumer_instance_name'])) {
738  if ($this->consumer->consumerName !== $_POST['tool_consumer_instance_name']) {
739  $this->consumer->consumerName = $_POST['tool_consumer_instance_name'];
740  $doSaveConsumer = true;
741  }
742  }
743  if (isset($_POST['tool_consumer_info_product_family_code'])) {
744  $version = $_POST['tool_consumer_info_product_family_code'];
745  if (isset($_POST['tool_consumer_info_version'])) {
746  $version .= "-{$_POST['tool_consumer_info_version']}";
747  }
748  // do not delete any existing consumer version if none is passed
749  if ($this->consumer->consumerVersion !== $version) {
750  $this->consumer->consumerVersion = $version;
751  $doSaveConsumer = true;
752  }
753  } elseif (isset($_POST['ext_lms']) && ($this->consumer->consumerName !== $_POST['ext_lms'])) {
754  $this->consumer->consumerVersion = $_POST['ext_lms'];
755  $doSaveConsumer = true;
756  }
757  if (isset($_POST['tool_consumer_instance_guid'])) {
758  if (is_null($this->consumer->consumerGuid)) {
759  $this->consumer->consumerGuid = $_POST['tool_consumer_instance_guid'];
760  $doSaveConsumer = true;
761  } elseif (!$this->consumer->protected) {
762  $doSaveConsumer = ($this->consumer->consumerGuid !== $_POST['tool_consumer_instance_guid']);
763  if ($doSaveConsumer) {
764  $this->consumer->consumerGuid = $_POST['tool_consumer_instance_guid'];
765  }
766  }
767  }
768  if (isset($_POST['launch_presentation_css_url'])) {
769  if ($this->consumer->cssPath !== $_POST['launch_presentation_css_url']) {
770  $this->consumer->cssPath = $_POST['launch_presentation_css_url'];
771  $doSaveConsumer = true;
772  }
773  } elseif (isset($_POST['ext_launch_presentation_css_url']) &&
774  ($this->consumer->cssPath !== $_POST['ext_launch_presentation_css_url'])) {
775  $this->consumer->cssPath = $_POST['ext_launch_presentation_css_url'];
776  $doSaveConsumer = true;
777  } elseif (!empty($this->consumer->cssPath)) {
778  $this->consumer->cssPath = null;
779  $doSaveConsumer = true;
780  }
781  }
782 
783  // Persist changes to consumer
784  if ($doSaveConsumer) {
785  $this->consumer->save();
786  }
787  if ($this->ok && isset($this->context)) {
788  $this->context->save();//ACHTUNG TODO UWE
789  }
790 
791  // $this->logger->dump(get_class($this->context));
792 
793 
794  if ($this->ok && isset($this->resourceLink)) {
795 
796 // Check if a share arrangement is in place for this resource link
797  // $this->ok = $this->checkForShare();//ACHTUNG TODO UWE
798  // Persist changes to resource link
799  $this->resourceLink->save();
800 
801  // Save the user instance
802  if (isset($_POST['lis_result_sourcedid'])) {
803  if ($this->user->ltiResultSourcedId !== $_POST['lis_result_sourcedid']) {
804  $this->user->ltiResultSourcedId = $_POST['lis_result_sourcedid'];
805  $this->user->save();
806  }
807  } elseif (!empty($this->user->ltiResultSourcedId)) {
808  $this->user->ltiResultSourcedId = '';
809  $this->user->save();
810  }
811  }
812  // die ($this->reason.'---'.$this->ok);//ACHTUNG WEG!
813  return $this->ok;
814  }
815 
821  private function checkForShare()
822  {
823  $ok = true;
824  $doSaveResourceLink = true;
825 
826  $id = $this->resourceLink->primaryResourceLinkId;
827 
828  $shareRequest = isset($_POST['custom_share_key']) && !empty($_POST['custom_share_key']);
829  if ($shareRequest) {
830  if (!$this->allowSharing) {
831  $ok = false;
832  $this->reason = 'Your sharing request has been refused because sharing is not being permitted.';
833  } else {
834  // Check if this is a new share key
835  $shareKey = new ResourceLinkShareKey($this->resourceLink, $_POST['custom_share_key']);
836  if (!is_null($shareKey->primaryConsumerKey) && !is_null($shareKey->primaryResourceLinkId)) {
837  // Update resource link with sharing primary resource link details
838  $key = $shareKey->primaryConsumerKey;
839  $id = $shareKey->primaryResourceLinkId;
840  $ok = ($key !== $this->consumer->getKey()) || ($id != $this->resourceLink->getId());
841  if ($ok) {
842  $this->resourceLink->primaryConsumerKey = $key;
843  $this->resourceLink->primaryResourceLinkId = $id;
844  $this->resourceLink->shareApproved = $shareKey->autoApprove;
845  $ok = $this->resourceLink->save();
846  if ($ok) {
847  $doSaveResourceLink = false;
848  $this->user->getResourceLink()->primaryConsumerKey = $key;
849  $this->user->getResourceLink()->primaryResourceLinkId = $id;
850  $this->user->getResourceLink()->shareApproved = $shareKey->autoApprove;
851  $this->user->getResourceLink()->updated = time();
852  // Remove share key
853  $shareKey->delete();
854  } else {
855  $this->reason = 'An error occurred initialising your share arrangement.';
856  }
857  } else {
858  $this->reason = 'It is not possible to share your resource link with yourself.';
859  }
860  }
861  if ($ok) {
862  $ok = !is_null($key);
863  if (!$ok) {
864  $this->reason = 'You have requested to share a resource link but none is available.';
865  } else {
866  $ok = (!is_null($this->user->getResourceLink()->shareApproved) && $this->user->getResourceLink()->shareApproved);
867  if (!$ok) {
868  $this->reason = 'Your share request is waiting to be approved.';
869  }
870  }
871  }
872  }
873  } else {
874  // Check no share is in place
875  $ok = is_null($id);
876  if (!$ok) {
877  $this->reason = 'You have not requested to share a resource link but an arrangement is currently in place.';
878  }
879  }
880  // Look up primary resource link
881  if ($ok && !is_null($id)) {
882  // $consumer = new ToolConsumer($key, $this->dataConnector);
883  $consumer = new ilLTIToolConsumer($_POST['oauth_consumer_key'], $this->dataConnector);
884  $ok = !is_null($consumer->created);
885  if ($ok) {
886  $resourceLink = ResourceLink::fromConsumer($consumer, $id);
887  $ok = !is_null($resourceLink->created);
888  }
889  if ($ok) {
890  if ($doSaveResourceLink) {
891  $this->resourceLink->save();
892  }
893  $this->resourceLink = $resourceLink;
894  } else {
895  $this->reason = 'Unable to load resource link being shared.';
896  }
897  }
898 
899  return $ok;
900  }
906  private function checkValue($value, $values, $reason)
907  {
908  $ok = in_array($value, $values);
909  if (!$ok && !empty($reason)) {
910  $this->reason = sprintf($reason, $value);
911  }
912 
913  return $ok;
914  }
915 }
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:201
$format
Definition: metadata.php:141
Class to represent an OAuth Data Store.
$result
foreach($paths as $path) $request
Definition: asyncclient.php:32
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
static $MESSAGE_TYPES
List of supported message types and associated class methods.
$server
Definition: sabredav.php:48
if(! $oauthconfig->getBoolean('getUserInfo.enable', FALSE)) $store
Definition: getUserInfo.php:11
user()
Definition: user.php:4
$version
Definition: build.php:27
onError()
Process a response to an invalid request.
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.
$values
doCallback($method=null)
Call any callback function for the requested action.
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
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...
exit
Definition: backend.php:16
__construct(DataConnector $dataConnector)
ilLTIToolProvider constructor.
checkForShare()
Check if a share arrangement is in place.
static $LTI_VERSIONS
Permitted LTI versions for messages.
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