From f1613a24917bf32de90f042801d72b9fc18e43a2 Mon Sep 17 00:00:00 2001 From: Rasmus Fuhse Date: Fri, 4 Jul 2025 11:26:38 +0000 Subject: course/lti/process_select_link: redirect on error with deployments, fixes #5488 Closes #5488 Merge request studip/studip!4183 (cherry picked from commit d98e8f811ee969fc94f922a70ad270ff02d057fc) 6e3310be course/lti/process_select_link: redirect on error with deployments b1f72a9f began rewriting course/lti/select_link and course/lti/process_select_link actions 74f13cb3 use LtiResourceLink instead of LtiDeployment 8c9e7707 course/lti/process_select_link: made lti resource link ID mandatory 5ee5de11 Revert "course/lti/process_select_link: made lti resource link ID mandatory" c26e2605 Revert "use LtiResourceLink instead of LtiDeployment" d466a82c Revert "began rewriting course/lti/select_link and course/lti/process_select_link actions" ec9ebdd9 course/lti/process_select_link action: create LtiResourceLink objects bb8aadb6 fixed typos 7c399153 fixed errors c3d046b3 added redirects 6be413d1 Revert "added redirects" 3d4462f7 Revert "fixed errors" adb61667 Revert "fixed typos" fcd16406 Revert "course/lti/process_select_link action: create LtiResourceLink objects" 1f34f17d began rewriting course/lti/select_link and course/lti/process_select_link actions 5411e842 use LtiResourceLink instead of LtiDeployment 7d3b22fb course/lti/process_select_link: made lti resource link ID mandatory a92bec66 added migration 6f1d77ec changed code a08519ee changed more code, fixed errors ecfab68d extended migration 4b441ca9 extended migration 18f6a0bb added columns to lti_deployments table, rewrote code for new database structure 40776ead replaced attribute access of LtiDeployment with LtiResourceLink 1150e772 continued moving attribute access 28ac0808 fixed course/lti/select_link action a9189788 fixed lti/tool/index for tools in courses 14e29ec7 fixed errors d1cf3aae fixed errors, added warning for more than one general LTI deployment per tool 46f0ea9a added deep link count to admin/lti/index dc8058f3 removed unused code d6d1491d fixed errors a7f34412 removed debug code 944782dd fixed errors 7ee7716d fixed more errors 35563dc9 set course-ID as URL parameter for deep linking return URL a1af83e5 fixed error f2bd12cc course/lti/save_link: attempted to fix "no registration platform side" error 041d65de fixed error d77844e0 fixed errors 581630b5 added debug code, allow setting link in registration manager c9174799 removed debug code b39ddf23 test 1f99d252 removed extra claim 75a25a15 made LTI requests have the same registration 680e9945 Prioritize JWKS URL over static key chain 46f3f6f4 Return LTi exception on public key failure 9d97dbbf Make public key unsetable Co-authored-by: Moritz Strohm --- app/controllers/course/lti.php | 264 ++++++++++----------- app/controllers/lti/tool.php | 62 ++--- app/views/admin/lti/index.php | 22 +- app/views/course/lti/add_link.php | 2 +- app/views/course/lti/consent.php | 2 +- app/views/course/lti/grades.php | 2 +- app/views/course/lti/grades_user.php | 2 +- app/views/course/lti/index.php | 18 +- app/views/course/lti/select_link.php | 6 +- app/views/course/lti/select_tool.php | 6 +- app/views/lti/_deployment_user_info.php | 43 ---- app/views/lti/_link_user_info.php | 43 ++++ app/views/lti/_tool_form_fields.php | 12 +- app/views/lti/_tool_info.php | 53 ++--- app/views/lti/tool/add.php | 6 +- app/views/lti/tool/edit.php | 6 +- ....51_add_columns_to_lti_resource_links_table.php | 79 ++++++ lib/classes/LTI13a/PlatformManager.php | 22 +- lib/classes/LTI13a/Registration.php | 43 +++- lib/classes/LTI13a/RegistrationManager.php | 21 +- lib/models/LtiDeployment.php | 34 +-- lib/models/LtiGrade.php | 3 + lib/models/LtiResourceLink.php | 51 +++- 23 files changed, 484 insertions(+), 318 deletions(-) delete mode 100644 app/views/lti/_deployment_user_info.php create mode 100644 app/views/lti/_link_user_info.php create mode 100644 db/migrations/6.0.51_add_columns_to_lti_resource_links_table.php diff --git a/app/controllers/course/lti.php b/app/controllers/course/lti.php index d167a9e..4658589 100644 --- a/app/controllers/course/lti.php +++ b/app/controllers/course/lti.php @@ -13,6 +13,7 @@ use OAT\Library\Lti1p3DeepLinking\Message\Launch\Builder\DeepLinkingLaunchReques use Studip\LTI13a\PlatformManager; use Studip\LTI13a\Registration; use Studip\LTI13a\RegistrationManager; +use OAT\Library\Lti1p3Core\Message\Payload\MessagePayloadInterface\MessagePayloadInterface; /** * course/lti.php - LTI consumer API for Stud.IP @@ -119,14 +120,23 @@ class Course_LtiController extends StudipController } Helpbar::get()->addPlainText('', _('Auf dieser Seite können Sie externe Anwendungen einbinden, sofern diese den LTI-Standard (Version 1.x) unterstützen.')); - if (Request::get('deployment_id')) { + + //Check for error messages: + if (Request::get('deployment_id') && (Request::submitted('lti_msg') || Request::submitted('lti_errormsg'))) { $deployment = LtiDeployment::find(Request::get('deployment_id')); if ($deployment) { - if (Request::get('lti_msg')) { - PageLayout::postInfo(htmlReady($deployment->title . ': ' . Request::get('lti_msg'))); - } - if (Request::get('lti_errormsg')) { - PageLayout::postError(htmlReady($deployment->title . ': ' . Request::get('lti_errormsg'))); + //Get the resource link for the deployment and display the messages: + $link = \LtiResourceLink::findOneBySQL( + "`deployment_id` = :deployment_id AND `course_id` = :course_id", + ['deployment_id' => $deployment->id, 'course_id' => $this->course_id] + ); + if ($link) { + if (Request::get('lti_msg')) { + PageLayout::postInfo(htmlReady($link->title . ': ' . Request::get('lti_msg'))); + } + if (Request::get('lti_errormsg')) { + PageLayout::postError(htmlReady($link->title . ': ' . Request::get('lti_errormsg'))); + } } } } @@ -138,7 +148,11 @@ class Course_LtiController extends StudipController $this->global_tool_deployments = LtiDeployment::findBySQL( "JOIN `lti_tools` ON `lti_deployments`.`tool_id` = `lti_tools`.`id` - WHERE `lti_tools`.`lti_version` = '1.3a' AND `lti_tools`.`range_id` = 'global' ORDER BY `lti_tools`.`name` ASC" + WHERE + `lti_deployments`.`purpose` = 'general' + AND `lti_tools`.`lti_version` = '1.3a' + AND `lti_tools`.`range_id` = 'global' + ORDER BY `lti_tools`.`name` ASC" ); if (!$this->global_tool_deployments) { @@ -267,11 +281,15 @@ class Course_LtiController extends StudipController //LTI 1.3a $this->lti13a_mode = true; - $return_url = !isset($this->resource_link->deployment->options['document_target']) ? - URLHelper::getURL($GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/course/lti', ['deployment_id' => $this->resource_link->deployment_id]) : ''; - $document_target = isset($this->resource_link->deployment->options['document_target']) ? 'iframe' : 'window'; + $return_url = URLHelper::getURL($GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/course/lti', ['deployment_id' => $this->resource_link->deployment_id]); + $document_target = 'window'; + if (!empty($this->resource_link->options['document_target'])) { + $return_url = $this->resource_link->options['document_target']; + $document_target = 'iframe'; + } + $locale = str_replace('_', '-', $_SESSION['_language']); - $registration = new Registration($this->resource_link->deployment->tool); + $registration = new Registration($this->resource_link->deployment->tool, $this->resource_link); $builder = new LtiResourceLinkLaunchRequestBuilder(); //The AGS URLs need several parameters: @@ -316,7 +334,7 @@ class Course_LtiController extends StudipController $this->url_for('lti/ags/line_item', $ags_url_parameters) ) ], - $this->resource_link->deployment->getCustomLtiParameterArray(), + $this->resource_link->getCustomLtiParameterArray(), ) ); } else { @@ -420,7 +438,7 @@ class Course_LtiController extends StudipController } /** - * Select a tool for adding a block via ContentItemSelectionRequest. + * Offers tool selection for LTI deep linking. */ public function add_link_action() { @@ -434,67 +452,69 @@ class Course_LtiController extends StudipController } /** - * Dispatch a ContentItemSelectionRequest to a specified LTI tool. + * Prepares the tool selected in the add_link action for being included in the course + * and displays the platform configuration that must be added in the LTI tool. */ - public function select_link_action($deployment_id = '') + public function select_link_action() { - $this->deployment = null; - if ($deployment_id) { - $this->deployment = LtiDeployment::find($deployment_id); - if (!$this->deployment) { - PageLayout::postError(_('Die Einbindung des LTI-Tools wurde nicht gefunden!')); - return; - } - if ($this->deployment->course_id !== $this->course_id) { - PageLayout::postError(_('Die Einbindung des LTI-Tools ist nicht für diese Veranstaltung bestimmt.')); - return; - } - if (empty($this->deployment->options['unfinished_deep_linking'])) { - PageLayout::postError(_('Die Einbindung des LTI-Tools ist bereits abgeschlossen.')); - return; - } - } - $this->tool = LtiTool::find(Request::int('tool_id')); if (!$this->tool) { PageLayout::postError(_('Das ausgewählte LTI-Tool wurde nicht gefunden.')); - $this->redirect('course/lti/add_link'); + $this->relocate('course/lti/add_link'); return; } if (!$this->tool->deep_linking) { PageLayout::postError(_('Das ausgewählte LTI-Tool unterstützt kein Deep Linking.')); - $this->redirect('course/lti/add_link'); + $this->relocate('course/lti/add_link'); return; } + + //Create a deployment for deep linking: + $this->deployment = new LtiDeployment(); + $this->deployment->tool_id = $this->tool->id; + $this->deployment->purpose = 'deep_linking'; + if ($this->deployment->store()) { + //Create an LTI resource link for the course: + $this->link = new \LtiResourceLink(); + $this->link->deployment_id = $this->deployment->id; + $this->link->course_id = $this->course_id; + $this->link->options = ['unfinished_deep_linking' => 'true']; + if (!$this->link->store()) { + PageLayout::postError(_('Die Einbindung des LTI-Tools in die Veranstaltung ist fehlgeschlagen.')); + $this->relocate('course/lti/add_link'); + } + } else { + PageLayout::postError(_('Es konnte kein LTI-Deployment für LTI Deep Linking erstellt werden.')); + $this->relocate('course/lti/add_link'); + } } - public function process_select_link_action($deployment_id = '') + /** + * Proceeds after the select_link action by switching to the LTI tool for + * selecting the items from the deep linked tool that shall be available in the Stud.IP course. + */ + public function process_select_link_action($link_id = '') { CSRFProtection::verifyUnsafeRequest(); - $this->deployment = null; - if ($deployment_id) { - $this->deployment = LtiDeployment::find($deployment_id); - if (!$this->deployment) { - PageLayout::postError(_('Die Einbindung des LTI-Tools wurde nicht gefunden!')); - return; - } - if ($this->deployment->course_id !== $this->course_id) { - PageLayout::postError(_('Die Einbindung des LTI-Tools ist nicht für diese Veranstaltung bestimmt.')); - return; - } - if (empty($this->deployment->options['unfinished_deep_linking'])) { - PageLayout::postError(_('Die Einbindung des LTI-Tools ist bereits abgeschlossen.')); - return; - } + $this->link = \LtiResourceLink::find($link_id); + if (!$this->link) { + PageLayout::postError(_('Die Einbindung des LTI-Tools wurde nicht gefunden.')); + $this->relocate('course/lti/add_link'); + return; } - - $this->tool = null; - if ($this->deployment) { - $this->tool = $this->deployment->tool; - } else { - $this->tool = LtiTool::find(Request::int('tool_id')); + if ($this->link->course_id !== $this->course_id) { + PageLayout::postError(_('Die Einbindung des LTI-Tools ist nicht für diese Veranstaltung bestimmt.')); + $this->relocate('course/lti/add_link'); + return; + } + if (empty($this->link->options['unfinished_deep_linking'])) { + PageLayout::postError(_('Die Einbindung des LTI-Tools ist bereits abgeschlossen.')); + $this->relocate('course/lti/add_link'); + return; } + + $this->tool = $this->link->deployment->tool ?? null; if (!$this->tool) { PageLayout::postError(_('Das ausgewählte LTI-Tool wurde nicht gefunden.')); $this->redirect('course/lti/add_link'); @@ -508,37 +528,20 @@ class Course_LtiController extends StudipController if ($this->tool->lti_version === '1.3a') { //LTI 1.3a - if ($this->deployment) { - $builder = new DeepLinkingLaunchRequestBuilder(); - $message = $builder->buildDeepLinkingLaunchRequest( - PlatformManager::getDeepLinkingConfiguration($this->tool->id), - new Registration($this->deployment->tool), - $GLOBALS['user']->id, - null, - $this->deployment->id, - [PlatformManager::getLtiRoleClaimForStudipRole($GLOBALS['perm']->get_studip_perm($this->course_id))] - ); - $this->render_text($message->toHtmlRedirectForm()); - } else { - //Build an LTI deployment object and mark it as not configured - //so that it can be displayed differently in the UI. - $this->deployment = new LtiDeployment(); - $this->deployment->tool_id = $this->tool->id; - $this->deployment->course_id = $this->course_id; - $this->deployment->title = $this->tool->name; - $this->deployment->options = ['unfinished_deep_linking' => true]; - if ($this->deployment->store() !== false) { - //Display the tool deployment data so that the user can enter - //them in the LTI tool. - PageLayout::postInfo( - _('Bitte tragen Sie die Daten zur Einbindung im LTI-Tool ein bevor sie fortfahren.') - ); - } - } + $builder = new DeepLinkingLaunchRequestBuilder(); + $message = $builder->buildDeepLinkingLaunchRequest( + PlatformManager::getDeepLinkingConfiguration($this->link->id, $this->course_id), + new Registration($this->tool, $this->link), + $GLOBALS['user']->id, + null, + $this->link->deployment_id, + [PlatformManager::getLtiRoleClaimForStudipRole($GLOBALS['perm']->get_studip_perm($this->course_id))] + ); + $this->render_text($message->toHtmlRedirectForm()); } else { //LTI 1.0/1.1 $custom_parameters = explode("\n", $this->tool->custom_parameters); - $content_item_return_url = $this->url_for('course/lti/save_link/' . $this->tool->id); + $content_item_return_url = $this->url_for('course/lti/save_link/' . $this->link->id); // set up ContentItemSelectionRequest $lti_link = new LtiLink($this->tool->launch_url, $this->tool->consumer_key, $this->tool->consumer_secret, $this->tool->oauth_signature_method); @@ -570,14 +573,31 @@ class Course_LtiController extends StudipController } /** - * Create a new LTI content block for the specified tool id. + * Handles the jump back from the LTI tool into Stud.IP and finishes the integration + * of a deep linked LTI tool into a Stud.IP course. * - * @param int $tool_id tool id + * @param int $link_id tool id */ - public function save_link_action($tool_id) + public function save_link_action($link_id) { - $tool = LtiTool::find($tool_id); + $this->link = \LtiResourceLink::find($link_id); + if (!$this->link) { + PageLayout::postError(_('Die Einbindung des LTI-Tools wurde nicht gefunden!')); + $this->relocate('course/lti/add_link'); + return; + } + if ($this->link->course_id !== $this->course_id) { + PageLayout::postError(_('Die Einbindung des LTI-Tools ist nicht für diese Veranstaltung bestimmt.')); + $this->relocate('course/lti/add_link'); + return; + } + if (empty($this->link->options['unfinished_deep_linking'])) { + PageLayout::postError(_('Die Einbindung des LTI-Tools ist bereits abgeschlossen.')); + $this->relocate('course/lti/add_link'); + return; + } + $tool = $this->link->deployment->tool ?? null; if (!$tool) { PageLayout::postError(_('Das ausgewählte LTI-Tool wurde nicht gefunden.')); $this->redirect('course/lti/add_link'); @@ -592,14 +612,17 @@ class Course_LtiController extends StudipController if ($tool->lti_version === '1.3a') { //LTI 1.3a + $reg_man = new RegistrationManager(); + $reg_man->setResourceLink($this->link); + $validator = new PlatformLaunchValidator( - new RegistrationManager(), + $reg_man, new NonceRepository(Studip\Cache\Factory::getCache()) ); $result = $validator->validateToolOriginatingLaunch($this->getPsrRequest()); if ($result->hasError()) { PageLayout::postError($result->getError()); - $this->redirect('course/lti/add_link'); + $this->redirect('course/lti/index'); return; } $all_lti_resources = (new ResourceCollectionFactory())->createFromClaim( @@ -608,35 +631,12 @@ class Course_LtiController extends StudipController $lti_resource_links = $all_lti_resources->getByType(LtiResourceLinkInterface::TYPE); if (count($lti_resource_links) > 0) { - $use_first_link = true; foreach ($lti_resource_links as $lti_resource_link) { - $deployment = LtiDeployment::findOneBySQL( - "JOIN `lti_resource_links` - ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id` - WHERE - `lti_deployments`.`tool_id` = :tool_id AND `lti_resource_links`.`course_id` = :course_id - AND `lti_deployments`.`options` LIKE '%unfinished_deep_linking%=%true'", - ['tool_id' => $this->tool->id, 'course_id' => $this->range_id] - ); - $use_first_link = false; - if (!$deployment) { - //If this is the first link, the deployment has been removed. - //In that case and if it is not the first link, a new deployment - //has to be created. - $deployment = new LtiDeployment(); - $deployment->tool_id = $tool->id; - $deployment->title = $tool->name; - } - $deployment->launch_url = $lti_resource_link->getUrl(); - if (!empty($deployment->options['unfinished_deep_linking'])) { - unset($deployment->options['unfinished_deep_linking']); - } - if ($deployment->store()) { - $link = new \LtiResourceLink(); - $link->deployment_id = $deployment->id; - $link->course_id = $this->range_id; - $link->store(); + $this->link->launch_url = $lti_resource_link->getUrl(); + if (!empty($this->link->options['unfinished_deep_linking'])) { + unset($this->link->options['unfinished_deep_linking']); } + $this->link->store(); } } } else { @@ -656,7 +656,7 @@ class Course_LtiController extends StudipController $lti_data = new LtiDeployment(); $lti_data->title = (string) $item['title']; $lti_data->description = Studip\Markup::purifyHtml(Studip\Markup::markAsHtml($item['text'])); - $lti_data->tool_id = $tool_id; + $lti_data->tool_id = $tool->id; $lti_data->launch_url = (string) ($item['url'] ?? ''); $options = []; if (is_array($item['custom'])) { @@ -682,7 +682,7 @@ class Course_LtiController extends StudipController } } - if ($lti_errormsg) { + if (!empty($lti_errormsg)) { PageLayout::postError($lti_errormsg); } @@ -819,9 +819,9 @@ class Course_LtiController extends StudipController */ public function outcome_action($id) { - $lti_data = LtiDeployment::find($id); + $lti_data = \LtiResourceLink::find($id); - if (!Studip\OAuth1::verifyRequest($this->getPsrRequest(), $lti_data->getConsumerSecret(), '')) { + if (!Studip\OAuth1::verifyRequest($this->getPsrRequest(), $lti_data->deployment->getConsumerSecret(), '')) { throw new Exception('Could not verify request.'); } @@ -880,21 +880,17 @@ class Course_LtiController extends StudipController Navigation::activateItem('/course/lti/grades'); if ($this->edit_perm) { - $this->lti_data_array = LtiDeployment::findBySQL( - "JOIN `lti_resource_links` - ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id` - WHERE `lti_resource_links`.`course_id` = :course_id - ORDER BY `lti_resource_links`.`position`", + $this->lti_data_array = \LtiResourceLink::findBySQL( + "`course_id` = :course_id + ORDER BY `position`", ['course_id' => $this->course_id] ); } else { //Only load those deployments that are fully configured: - $this->lti_data_array = LtiDeployment::findBySQL( - "JOIN `lti_resource_links` - ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id` - WHERE `lti_resource_links`.`course_id` = :course_id - AND (`lti_deployments`.`options` IS NULL OR `lti_deployments`.`options` NOT LIKE '%unfinished_deep_linking%') - ORDER BY `lti_resource_links`.`position`", + $this->lti_data_array = \LtiResourceLink::findBySQL( + "`course_id` = :course_id + AND (`options` IS NULL OR `options` NOT LIKE '%unfinished_deep_linking%') + ORDER BY `position`", ['course_id' => $this->course_id] ); } @@ -926,10 +922,10 @@ class Course_LtiController extends StudipController public function export_grades_action() { if ($this->edit_perm) { - $lti_data_array = LtiDeployment::findByCourse_id($this->course_id, 'ORDER BY position'); + $lti_data_array = \LtiResourceLink::findByCourse_id($this->course_id, 'ORDER BY position'); } else { //Only load those deployments that are fully configured: - $lti_data_array = LtiDeployment::findBySQL( + $lti_data_array = \LtiResourceLink::findBySQL( "`course_id` = :course_id AND (`options` IS NULL OR `options` NOT LIKE '%unfinished_deep_linking%') ORDER BY `position`", ['course_id' => $this->course_id] diff --git a/app/controllers/lti/tool.php b/app/controllers/lti/tool.php index fae276e..61ca37b 100644 --- a/app/controllers/lti/tool.php +++ b/app/controllers/lti/tool.php @@ -35,12 +35,13 @@ class Lti_ToolController extends AuthenticatedController { //$this->range_id and $this->tool are created in the before-filter. if ($this->range_id !== 'global') { - $this->deployment = LtiDeployment::findOneBySQL( - 'JOIN `lti_resource_links` + $link_id = Request::get('link_id'); + $this->link = \LtiResourceLink::findOneBySQL( + 'JOIN `lti_deployments` ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id` WHERE - `lti_deployments`.`tool_id` = :tool_id AND `lti_resource_links`.`course_id` = :range_id', - ['tool_id' => $this->tool->id, 'range_id' => $this->range_id] + `lti_deployments`.`tool_id` = :tool_id AND `lti_resource_links`.`id` = :link_id', + ['tool_id' => $this->tool->id, 'link_id' => $link_id] ); } } @@ -74,12 +75,9 @@ class Lti_ToolController extends AuthenticatedController PageLayout::postWarning(_('Bitte beachten Sie das geltende europäische Datenschutzrecht (DSGVO)!')); } elseif (!$this->tool->isEditableByUser()) { throw new AccessDeniedException(); - } else { - //The tool is old and editable by the user. Check if a deployment exists and load it. - $this->deployment = LtiDeployment::findOneBySQL( - "`tool_id` = :tool_id ORDER BY `mkdate` ASC", - ['tool_id' => $this->tool->id] - ); + } elseif (Request::get('link_id')) { + //The tool is old and editable by the user. Check if a link exists and load it. + $this->link = \LtiResourceLink::find(Request::get('link_id')); } if (Request::isPost()) { @@ -93,6 +91,10 @@ class Lti_ToolController extends AuthenticatedController protected function saveTool(): void { CSRFProtection::verifyUnsafeRequest(); + $this->link = null; + if (Request::get('link_id')) { + $this->link = \LtiResourceLink::find(Request::get('link_id')); + } //Note: $this->tool is created in the before_filter. $new_tool = $this->tool->isNew(); $this->tool->name = trim(Request::get('name')); @@ -118,10 +120,10 @@ class Lti_ToolController extends AuthenticatedController $this->tool->custom_parameters = trim(Request::get('custom_parameters')); $tool_public_key = trim(Request::get('tool_public_key')); - //Check if the tool has a deployment. If so, use it. Otherwise, create a new deployment. + //Check if the tool has a general deployment. If so, use it. Otherwise, create a new deployment. if (!$new_tool) { $this->deployment = LtiDeployment::findOneBySQL( - "`tool_id` = :tool_id ORDER BY `mkdate` ASC", + "`tool_id` = :tool_id AND `purpose` = 'general' ORDER BY `mkdate` ASC", ['tool_id' => $this->tool->id] ); } @@ -131,18 +133,6 @@ class Lti_ToolController extends AuthenticatedController $this->deployment->tool_id = $this->tool->id; } } - $this->deployment->description = trim(Request::get('description')); - $this->deployment->title = $this->tool->name; - $this->deployment->launch_url = $this->tool->launch_url; - $document_target = trim(Request::get('document_target')); - if ($document_target === 'iframe') { - if (!is_array($this->deployment->options)) { - $this->deployment->options = []; - } - $this->deployment->options['document_target'] = $document_target; - } elseif (isset($this->deployment->options['document_target'])) { - unset($this->deployment->options['document_target']); - } $errors = $this->tool->validate(); if ($errors) { @@ -166,22 +156,34 @@ class Lti_ToolController extends AuthenticatedController return; } if ($this->range_id !== 'global') { - $resource_link_exists = false; + $resource_link = null; if (!$new_tool) { //Create an LTI resource link, if it doesn't exist yet: - $resource_link_exists = \LtiResourceLink::countBySQL( + $resource_link = \LtiResourceLink::findOneBySQL( "`deployment_id` = :deployment_id AND `course_id` = :course_id", ['deployment_id' => $this->deployment->id, 'course_id' => $this->range_id] - ) > 0; + ); } - if (!$resource_link_exists) { + if (!$resource_link) { //Either the tool has just been created or the tool existed and it wasn't yet //linked to the course. In those both cases, we have to create a new LTI resource link. $resource_link = new \LtiResourceLink(); $resource_link->deployment_id = $this->deployment->id; $resource_link->course_id = $this->range_id; - $resource_link->store(); } + $resource_link->description = trim(Request::get('description')); + $resource_link->title = $this->tool->name; + $resource_link->launch_url = $this->tool->launch_url; + $document_target = trim(Request::get('document_target')); + if ($document_target === 'iframe') { + if (!is_array($resource_link->options)) { + $resource_link->options = []; + } + $resource_link->options['document_target'] = $document_target; + } elseif (isset($resource_link->options['document_target'])) { + unset($resource_link->options['document_target']); + } + $resource_link->store(); } if ($this->tool->lti_version === '1.3a' && $tool_public_key) { if (!$this->tool->updatePublicKey($tool_public_key)) { @@ -189,6 +191,8 @@ class Lti_ToolController extends AuthenticatedController _('Der öffentliche Schlüssel des LTI-Tools konnte nicht gespeichert werden.') ); } + } else { + Keyring::deleteBySQL("`range_type` = 'lti_tool' AND `range_id` = :tool_id", ['tool_id' => $this->tool->id]); } PageLayout::postSuccess(_('Das LTI-Tool wurde gespeichert.')); diff --git a/app/views/admin/lti/index.php b/app/views/admin/lti/index.php index f047add..83faf3a 100644 --- a/app/views/admin/lti/index.php +++ b/app/views/admin/lti/index.php @@ -17,6 +17,7 @@ + @@ -26,6 +27,7 @@ + @@ -49,9 +51,25 @@ id); + $deployments = LtiDeployment::findBySQL( + "`tool_id` = :tool_id AND `purpose` = 'general'", + ['tool_id' => $tool->id] + ); + $deployment_ids = []; + foreach ($deployments as $deployment) { + $deployment_ids[] = $deployment->id; + } ?> - id ?? '') ?> + + 1) : ?> + + + + + $tool->id] + )) ?> diff --git a/app/views/course/lti/consent.php b/app/views/course/lti/consent.php index be78d5d..a6a07e9 100644 --- a/app/views/course/lti/consent.php +++ b/app/views/course/lti/consent.php @@ -53,7 +53,7 @@ - render_partial('lti/_deployment_user_info', ['deployment' => $resource_link->deployment]) ?> + render_partial('lti/_link_user_info', ['link' => $resource_link]) ?>