diff options
| author | Moritz Strohm <strohm@data-quest.de> | 2025-04-24 10:48:24 +0000 |
|---|---|---|
| committer | Moritz Strohm <strohm@data-quest.de> | 2025-04-24 10:48:24 +0000 |
| commit | cd8222ba049eca136bb443410022d99dfbc5d0f2 (patch) | |
| tree | 99b46288e335427b3f3bde5555b56497a80c1b88 /app | |
| parent | 3c783c028c229a3e6561500521800e1fac4383ba (diff) | |
distinguish between LTI deployment IDs and LTI resource links in the database, fixes #5330
Closes #5330
Merge request studip/studip!4045
Diffstat (limited to 'app')
| -rw-r--r-- | app/controllers/course/lti.php | 183 | ||||
| -rw-r--r-- | app/controllers/lti/tool.php | 199 | ||||
| -rw-r--r-- | app/views/admin/lti/index.php | 18 | ||||
| -rw-r--r-- | app/views/course/lti/consent.php | 14 | ||||
| -rw-r--r-- | app/views/course/lti/iframe.php | 6 | ||||
| -rw-r--r-- | app/views/course/lti/index.php | 52 | ||||
| -rw-r--r-- | app/views/course/lti/select_tool.php | 14 | ||||
| -rw-r--r-- | app/views/lti/_tool_form_fields.php | 204 | ||||
| -rw-r--r-- | app/views/lti/_tool_info.php | 19 |
9 files changed, 385 insertions, 324 deletions
diff --git a/app/controllers/course/lti.php b/app/controllers/course/lti.php index 1560f6d..d167a9e 100644 --- a/app/controllers/course/lti.php +++ b/app/controllers/course/lti.php @@ -76,14 +76,18 @@ class Course_LtiController extends StudipController */ public function index_action() { - $this->lti_data_array = []; + $this->links = []; if ($this->edit_perm) { - $this->lti_data_array = LtiDeployment::findByCourse_id($this->course_id, 'ORDER BY position'); + $this->links = \LtiResourceLink::findByCourse_id($this->course_id, 'ORDER BY `position`'); } else { - //Only load those deployments that are fully configured: - $this->lti_data_array = LtiDeployment::findBySQL( - "`course_id` = :course_id AND (`options` IS NULL OR `options` NOT LIKE '%unfinished_deep_linking%') - ORDER BY `position`", + //Only load those LTI resource links that are fully configured: + $this->links = \LtiResourceLink::findBySQL( + "JOIN `lti_deployments` + 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`", ['course_id' => $this->course_id] ); } @@ -131,10 +135,13 @@ class Course_LtiController extends StudipController public function select_tool_action() { //The permission check is done in the before filter. + $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" + ); - $this->global_tools = LtiTool::findBySQL("`lti_version` = '1.3a' AND `range_id` = 'global' ORDER BY `name` ASC"); - - if (!$this->global_tools) { + if (!$this->global_tool_deployments) { if (!Config::get()->LTI_ALLOW_TOOL_CONFIG_IN_COURSE) { PageLayout::postError(_('Es sind keine globalen LTI-Tools konfiguriert, die in dieser Veranstaltung eingebunden werden können.')); return; @@ -143,10 +150,10 @@ class Course_LtiController extends StudipController $this->redirect('lti/tool/add/' . $this->course->id); } - $this->selected_tool_id = ''; - if (count($this->global_tools) >= 1) { + $this->selected_deployment_id = ''; + if (count($this->global_tool_deployments) >= 1) { //Preselect the first tool: - $this->selected_tool_id = $this->global_tools[0]->id; + $this->selected_deployment_id = $this->global_tool_deployments[0]->id; } } @@ -154,40 +161,50 @@ class Course_LtiController extends StudipController { if (Request::isPost()) { CSRFProtection::verifyUnsafeRequest(); - $selected_tool_id = Request::get('selected_tool_id'); - if ($selected_tool_id === 'new') { + $selected_deployment_id = Request::get('selected_deployment_id'); + if ($selected_deployment_id === 'new') { //Redirect to the page to configure an LTI tool for the course: $this->redirect('lti/tool/add/' . $this->course->id); } else { - //Load the selected tool and check if it can be used in the course. - $selected_tool = LtiTool::find($selected_tool_id); - if (!$selected_tool || $selected_tool->range_id !== 'global') { + //Load the selected deployment and check if it can be used in the course. + $selected_deployment = LtiDeployment::find($selected_deployment_id); + if (!$selected_deployment || $selected_deployment->tool->range_id !== 'global') { PageLayout::postError(_('Das ausgewählte LTI-Tool kann nicht genutzt werden.')); $this->redirect('course/lti/select_tool'); return; } - $this->redirect('lti/tool/add/' . $this->course->id . '/' . $selected_tool->id); + //Link the tool in the course: + $link = new \LtiResourceLink(); + $link->deployment_id = $selected_deployment->id; + $link->course_id = $this->course->id; + if ($link->store()) { + PageLayout::postSuccess(_('Das LTI-Tool wurde eingebunden.')); + } else { + PageLayout::postError(_('Das LTI-Tool konnte nicht eingebunden werden.')); + } + $this->relocate('course/lti', ['cid' => $this->course->id]); } } else { $this->redirect('course/lti/select_tool'); } } - public function consent_action(string $deployment_id) + public function consent_action(string $link_id) { - $this->deployment = LtiDeployment::find($deployment_id); - if (!$this->deployment) { + $this->resource_link = \LtiResourceLink::find($link_id); + if (!$this->resource_link) { PageLayout::postError(_('Die Einbindung eines LTI-Tools ist ungültig.')); return; } + $tool_id = $this->resource_link->deployment->tool_id; $this->privacy_settings = LtiToolPrivacySettings::findOneBySQL( 'tool_id = :tool_id AND user_id = :user_id', - ['tool_id' => $this->deployment->tool_id, 'user_id' => $GLOBALS['user']->id] + ['tool_id' => $tool_id, 'user_id' => $GLOBALS['user']->id] ); if (!$this->privacy_settings) { $this->privacy_settings = new LtiToolPrivacySettings(); - $this->privacy_settings->tool_id = $this->deployment->tool_id; + $this->privacy_settings->tool_id = $tool_id; $this->privacy_settings->user_id = $GLOBALS['user']->id; } @@ -219,7 +236,7 @@ class Course_LtiController extends StudipController $this->response->add_header('X-Dialog-Close', '1'); } elseif (Request::submitted('redirect_to_tool') && Request::submitted('save')) { //Redirect to the tool launch action, but only after the privacy settings have been saved: - $this->redirect('course/lti/iframe/' . $this->deployment->id); + $this->redirect('course/lti/iframe/' . $this->resource_link->id); } else { //Redirect to the LTI tool page of the course: $this->redirect('course/lti/index'); @@ -230,55 +247,47 @@ class Course_LtiController extends StudipController /** * Display the launch form for a tool as an iframe. */ - public function iframe_action(string $deployment_id) + public function iframe_action(string $link_id) { - $this->deployment = LtiDeployment::find($deployment_id); + $this->resource_link = \LtiResourceLink::find($link_id); $this->show_data_protection_info = !LtiToolPrivacySettings::countBySQL( "`tool_id` = :tool_id AND `user_id` = :user_id AND `accepted` = 1", - ['tool_id' => $this->deployment->tool_id, 'user_id' => $GLOBALS['user']->id] + ['tool_id' => $this->resource_link->deployment->tool_id, 'user_id' => $GLOBALS['user']->id] ); if ($this->show_data_protection_info) { - $this->redirect('course/lti/consent/' . $deployment_id, ['redirect_to_tool' => '1']); + $this->redirect('course/lti/consent/' . $this->resource_link->deployment_id, ['redirect_to_tool' => '1']); return; } if (!$this->show_data_protection_info) { //Redirect to the tool. $this->lti13a_mode = false; - $lti_version = $this->deployment->getToolLtiVersion(); + $lti_version = $this->resource_link->deployment->getToolLtiVersion(); if ($lti_version === '1.3a') { //LTI 1.3a $this->lti13a_mode = true; - $lti_resource_link = new LtiResourceLink( - $this->deployment->tool_id . '_' . $this->deployment->id . '_' . $this->course_id, - [ - 'url' => $this->deployment->getLaunchURL(), - 'title' => $this->deployment->title - ] - ); - - $return_url = !isset($this->deployment->options['document_target']) ? - URLHelper::getURL($GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/course/lti', ['deployment_id' => $deployment_id]) : ''; - $document_target = isset($this->deployment->options['document_target']) ? 'iframe' : 'window'; + $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'; $locale = str_replace('_', '-', $_SESSION['_language']); - $registration = new Registration($this->deployment->tool); + $registration = new Registration($this->resource_link->deployment->tool); $builder = new LtiResourceLinkLaunchRequestBuilder(); //The AGS URLs need several parameters: $ags_url_parameters = [ 'cid' => $this->course_id, - 'tool_id' => $this->deployment->tool_id, - 'deployment_id' => $this->deployment->id, + 'tool_id' => $this->resource_link->deployment->tool_id, + 'deployment_id' => $this->resource_link->deployment_id, 'cancel_login' => '1' ]; //Build the message: $this->message = $builder->buildLtiResourceLinkLaunchRequest( - $lti_resource_link, + $this->resource_link, $registration, $GLOBALS['user']->id, - $this->deployment->id, + $this->resource_link->deployment_id, [ PlatformManager::getLtiRoleClaimForStudipRole($GLOBALS['perm']->get_studip_perm($this->course_id)) ], @@ -307,11 +316,12 @@ class Course_LtiController extends StudipController $this->url_for('lti/ags/line_item', $ags_url_parameters) ) ], - $this->deployment->getCustomLtiParameterArray(), + $this->resource_link->deployment->getCustomLtiParameterArray(), ) ); } else { //LTI 1.0/1.1 + $this->deployment = $this->resource_link->deployment; $lti_link = $this->getLtiLink($this->deployment); $this->launch_url = $this->deployment->getLaunchURL(); $this->launch_data = $lti_link->getBasicLaunchData(); @@ -356,18 +366,18 @@ class Course_LtiController extends StudipController } /** - * Moves an LTI deployment in a course either up or down. + * Moves an LTI resource link up or down in a course. * - * @param string $deployment_id The ID of the deployment to be moved. + * @param string $link_id The ID of the resource link to be moved. * * @param string $direction 'up' for moving the deployment upwards or 'down' for downwards. */ - public function move_action(string $deployment_id, string $direction) + public function move_action(string $link_id, string $direction) { CSRFProtection::verifyUnsafeRequest(); - $deployment = LtiDeployment::find($deployment_id); - if (!$deployment) { + $link = \LtiResourceLink::find($link_id); + if (!$link) { //Redirect and do nothing: $this->redirect('course/lti'); return; @@ -376,19 +386,19 @@ class Course_LtiController extends StudipController $new_position = 0; if ($direction === 'up') { - $new_position = $deployment->position - 1; + $new_position = $link->position - 1; } else { - $new_position = $deployment->position + 1; + $new_position = $link->position + 1; } //Find the deployment with the new position: - $other_deployment = LtiDeployment::findByCourseAndPosition($this->course_id, $new_position); - if ($other_deployment) { - $other_deployment->position = $deployment->position; - $other_deployment->store(); + $other_link = \LtiResourceLink::findByCourseAndPosition($this->course_id, $new_position); + if ($other_link) { + $other_link->position = $link->position; + $other_link->store(); } - $deployment->position = $new_position; - $deployment->store(); + $link->position = $new_position; + $link->store(); $this->redirect('course/lti'); } @@ -402,8 +412,8 @@ class Course_LtiController extends StudipController { CSRFProtection::verifyUnsafeRequest(); - $deployment = LtiDeployment::findByCourseAndPosition($this->course_id, $position); - $deployment->delete(); + $link = \LtiResourceLink::findByCourseAndPosition($this->course_id, $position); + $link->delete(); PageLayout::postSuccess(_('Der Abschnitt wurde gelöscht.')); $this->redirect('course/lti'); @@ -600,16 +610,15 @@ class Course_LtiController extends StudipController if (count($lti_resource_links) > 0) { $use_first_link = true; foreach ($lti_resource_links as $lti_resource_link) { - $deployment = null; - if ($use_first_link) { - //Recycle the deployment that has been created before - //for the course. - $deployment = LtiDeployment::findOneBySQL( - "`tool_id` = :tool_id AND `course_id` = :course_id - AND `options` LIKE '%unfinished_deep_linking%=%true'" + $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 @@ -617,13 +626,17 @@ class Course_LtiController extends StudipController $deployment = new LtiDeployment(); $deployment->tool_id = $tool->id; $deployment->title = $tool->name; - $deployment->course_id = $this->course_id; } $deployment->launch_url = $lti_resource_link->getUrl(); if (!empty($deployment->options['unfinished_deep_linking'])) { unset($deployment->options['unfinished_deep_linking']); } - $deployment->store(); + if ($deployment->store()) { + $link = new \LtiResourceLink(); + $link->deployment_id = $deployment->id; + $link->course_id = $this->range_id; + $link->store(); + } } } } else { @@ -632,17 +645,15 @@ class Course_LtiController extends StudipController $content_items = Request::get('content_items'); $content_items = json_decode($content_items, true); - if (!Studip\OAuth1::verifyRequest($this->getPsrRequest(), $tool->consumer_secret, '')) { - throw new Exception('Could not verify request.'); - } + if (!Studip\OAuth1::verifyRequest($this->getPsrRequest(), $tool->consumer_secret, '')) { + throw new Exception('Could not verify request.'); + } if (is_array($content_items) && count($content_items['@graph'])) { // we only support selecting a single content item $item = $content_items['@graph'][0]; $lti_data = new LtiDeployment(); - $lti_data->course_id = $this->course_id; - $lti_data->position = LtiDeployment::countBySQL('course_id = ?', [$this->course_id]); $lti_data->title = (string) $item['title']; $lti_data->description = Studip\Markup::purifyHtml(Studip\Markup::markAsHtml($item['text'])); $lti_data->tool_id = $tool_id; @@ -662,6 +673,11 @@ class Course_LtiController extends StudipController $lti_data->options = $options; $lti_data->store(); + $link = new \LtiResourceLink(); + $link->deployment_id = $lti_data->id; + $link->course_id = $this->course_id; + $link->position = \LtiResourceLink::countBySQL('course_id = ?', [$this->course_id]); + $link->store(); PageLayout::postSuccess($lti_msg ?: _('Der Link wurde als neuer Abschnitt hinzugefügt.')); } } @@ -864,12 +880,21 @@ class Course_LtiController extends StudipController Navigation::activateItem('/course/lti/grades'); if ($this->edit_perm) { - $this->lti_data_array = LtiDeployment::findByCourse_id($this->course_id, 'ORDER BY position'); + $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`", + ['course_id' => $this->course_id] + ); } else { //Only load those deployments that are fully configured: $this->lti_data_array = LtiDeployment::findBySQL( - "`course_id` = :course_id AND (`options` IS NULL OR `options` NOT LIKE '%unfinished_deep_linking%') - ORDER BY `position`", + "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`", ['course_id' => $this->course_id] ); } diff --git a/app/controllers/lti/tool.php b/app/controllers/lti/tool.php index 0e7077e..fae276e 100644 --- a/app/controllers/lti/tool.php +++ b/app/controllers/lti/tool.php @@ -33,10 +33,13 @@ class Lti_ToolController extends AuthenticatedController public function index_action($range_id, $tool_id): void { - //$this->tool is created in the before-filter. + //$this->range_id and $this->tool are created in the before-filter. if ($this->range_id !== 'global') { $this->deployment = LtiDeployment::findOneBySQL( - '`tool_id` = :tool_id AND `course_id` = :range_id', + '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` = :range_id', ['tool_id' => $this->tool->id, 'range_id' => $this->range_id] ); } @@ -59,7 +62,6 @@ class Lti_ToolController extends AuthenticatedController if (!$this->tool) { return; } - $this->deployment = null; if ($this->tool->isNew()) { if (!Config::get()->LTI_ALLOW_TOOL_CONFIG_IN_COURSE && $this->range_id !== 'global') { throw new AccessDeniedException( @@ -70,21 +72,14 @@ class Lti_ToolController extends AuthenticatedController throw new AccessDeniedException(); } PageLayout::postWarning(_('Bitte beachten Sie das geltende europäische Datenschutzrecht (DSGVO)!')); - if ($this->tool->range_id !== 'global') { - $this->deployment = new LtiDeployment(); - $this->deployment->course_id = $this->tool->range_id; - } - } elseif ($this->range_id !== 'global') { + } 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 AND `course_id` = :range_id', - ['tool_id' => $this->tool->id, 'range_id' => $this->range_id] + "`tool_id` = :tool_id ORDER BY `mkdate` ASC", + ['tool_id' => $this->tool->id] ); - if (!$this->deployment) { - //Create a new deployment: - $this->deployment = new LtiDeployment(); - $this->deployment->tool_id = $this->tool->id; - $this->deployment->course_id = $this->range_id; - } } if (Request::isPost()) { @@ -98,81 +93,95 @@ class Lti_ToolController extends AuthenticatedController protected function saveTool(): void { CSRFProtection::verifyUnsafeRequest(); - if ($this->range_id === 'global') { - //The admin page for editing global tools. - $this->tool->name = trim(Request::get('name')); - $this->tool->launch_url = trim(Request::get('launch_url')); + //Note: $this->tool is created in the before_filter. + $new_tool = $this->tool->isNew(); + $this->tool->name = trim(Request::get('name')); + $this->tool->launch_url = trim(Request::get('launch_url')); + $this->tool->terms_of_use_url = trim(Request::get('terms_of_use_url')); + $this->tool->privacy_policy_url = trim(Request::get('privacy_policy_url')); + $this->tool->data_protection_notes = trim(Request::get('data_protection_notes')); + $this->tool->lti_version = Request::get('lti_version', '1.3a'); + if ($this->tool->lti_version === '1.3a') { + $this->tool->oauth_signature_method = 'sha256'; + $this->tool->oidc_init_url = trim(Request::get('oidc_init_url')); + $this->tool->jwks_url = trim(Request::get('jwks_url')); + $this->tool->jwks_key_id = trim(Request::get('jwks_key_id')); + $this->tool->deep_linking_url = trim(Request::get('deep_linking_url')); + $this->tool->deep_linking = (bool) $this->tool->deep_linking_url; } else { - //The page for editing tools configured in courses. - $this->deployment->title = trim(Request::get('name')); - $this->deployment->description = trim(Request::get('description')); - $this->deployment->launch_url = trim(Request::get('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']); - } + //LTI 1.0/1.1: + $this->tool->oauth_signature_method = 'sha1'; + $this->tool->consumer_key = trim(Request::get('consumer_key')); + $this->tool->consumer_secret = trim(Request::get('consumer_secret')); } + $this->tool->send_lis_person = Request::int('send_lis_person', 0); + $this->tool->custom_parameters = trim(Request::get('custom_parameters')); + $tool_public_key = trim(Request::get('tool_public_key')); - //If a deployment is present, the tool is not used in the global context. - //If a tool is not used in the global context and the range_id is not set to "global", - //it is a tool that is only used for one course. - if ( - !$this->deployment - || $this->tool->range_id !== 'global' - || $GLOBALS['perm']->have_perm('root') - ) { - $this->tool->name = trim(Request::get('name')); - $this->tool->launch_url = trim(Request::get('launch_url')); - $this->tool->terms_of_use_url = trim(Request::get('terms_of_use_url')); - $this->tool->privacy_policy_url = trim(Request::get('privacy_policy_url')); - $this->tool->data_protection_notes = trim(Request::get('data_protection_notes')); - $this->tool->lti_version = Request::get('lti_version', '1.3a'); - if ($this->tool->lti_version === '1.3a') { - $this->tool->oauth_signature_method = 'sha256'; - $this->tool->oidc_init_url = trim(Request::get('oidc_init_url')); - $this->tool->jwks_url = trim(Request::get('jwks_url')); - $this->tool->jwks_key_id = trim(Request::get('jwks_key_id')); - $this->tool->deep_linking_url = trim(Request::get('deep_linking_url')); - $this->tool->deep_linking = (bool) $this->tool->deep_linking_url; - } else { - //LTI 1.0/1.1: - $this->tool->oauth_signature_method = 'sha1'; - $this->tool->consumer_key = trim(Request::get('consumer_key')); - $this->tool->consumer_secret = trim(Request::get('consumer_secret')); - } - $this->tool->send_lis_person = Request::int('send_lis_person', 0); - $this->tool->custom_parameters = trim(Request::get('custom_parameters')); - $tool_public_key = trim(Request::get('tool_public_key')); - $errors = $this->tool->validate(); - if ($errors) { - PageLayout::postError( - _('Die folgenden Daten zum LTI-Tool sind fehlerhaft:'), - array_map('htmlReady', $errors) - ); - return; - } - if ($this->tool->lti_version === '1.3a' && !$tool_public_key && !$this->tool->jwks_url) { - PageLayout::postError( - _('Es wurde weder ein öffentlicher Schlüssel noch eine JWKS-URL zum Tool angegeben.') - ); - return; + //Check if the tool has a 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' => $this->tool->id] + ); + } + if (!$this->deployment) { + $this->deployment = new LtiDeployment(); + if (!$new_tool) { + $this->deployment->tool_id = $this->tool->id; } - if ($this->tool->store() !== false) { - if ($this->deployment) { - $this->deployment->tool_id = $this->tool->id; - } - } else { - PageLayout::postError(_('Das LTI-Tool konnte nicht gespeichert werden.')); - return; + } + $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) { + PageLayout::postError( + _('Die folgenden Daten zum LTI-Tool sind fehlerhaft:'), + array_map('htmlReady', $errors) + ); + return; + } + if ($this->tool->lti_version === '1.3a' && !$tool_public_key && !$this->tool->jwks_url) { + PageLayout::postError( + _('Es wurde weder ein öffentlicher Schlüssel noch eine JWKS-URL zum Tool angegeben.') + ); + return; } - if ($this->deployment) { + if ($this->tool->store() !== false) { + $this->deployment->tool_id = $this->tool->id; $this->deployment->store(); + } else { + PageLayout::postError(_('Das LTI-Tool konnte nicht gespeichert werden.')); + return; + } + if ($this->range_id !== 'global') { + $resource_link_exists = false; + if (!$new_tool) { + //Create an LTI resource link, if it doesn't exist yet: + $resource_link_exists = \LtiResourceLink::countBySQL( + "`deployment_id` = :deployment_id AND `course_id` = :course_id", + ['deployment_id' => $this->deployment->id, 'course_id' => $this->range_id] + ) > 0; + } + if (!$resource_link_exists) { + //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(); + } } if ($this->tool->lti_version === '1.3a' && $tool_public_key) { if (!$this->tool->updatePublicKey($tool_public_key)) { @@ -201,23 +210,31 @@ class Lti_ToolController extends AuthenticatedController $tool_name = $this->tool->name; if ($this->tool->range_id === 'global') { if ($range_id === 'global') { + //A global tool shall be deleted globally. + if (!$this->tool->isEditableByUser()) { + throw new AccessDeniedException(); + } $deleted = $this->tool->delete(); } else { - //A tool shall be deleted from a course: Delete the deployment instead. - $deployment = LtiDeployment::findOneBySQL( - "`tool_id` = :tool_id AND `course_id` = :course_id", + //A tool shall be deleted from a course: Delete the resource link instead. + $link = \LtiResourceLink::findOneBySQL( + "JOIN `lti_deployments` + ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id` + WHERE `lti_deployments`.`tool_id` = :tool_id AND `course_id` = :course_id", ['tool_id' => $this->tool->id, 'course_id' => $range_id] ); - if ($deployment) { - $tool_name = $deployment->title; - $deleted = $deployment->delete(); + if ($link) { + $deleted = $link->delete(); } else { PageLayout::postError(sprintf(_('Das LTI-Tool „%s“ ist in dieser Veranstaltung nicht vorhanden.'), htmlReady($this->tool->name))); return; } } } else { - //Delete the tool directly: + //A tool that is used inside a course shall be deleted. + if (!$this->tool->isEditableByUser()) { + throw new AccessDeniedException(); + } $deleted = $this->tool->delete(); } if ($deleted !== false) { diff --git a/app/views/admin/lti/index.php b/app/views/admin/lti/index.php index 54ebd94..f047add 100644 --- a/app/views/admin/lti/index.php +++ b/app/views/admin/lti/index.php @@ -16,6 +16,7 @@ <col style="width: 20%;"> <col style="width: 5%;"> <col style="width: 5%;"> + <col style="width: 5%;"> </colgroup> <thead> @@ -24,6 +25,7 @@ <th><?= _('URL der Anwendung') ?></th> <th><?= _('Consumer-Key') ?></th> <th><?= _('LTI-Version') ?></th> + <th><?= _('Deployment-ID') ?></th> <th><?= _('Links') ?></th> <th class="actions"><?= _('Aktionen') ?></th> </tr> @@ -44,7 +46,21 @@ </td> <td><?= htmlReady($tool->consumer_key) ?></td> <td><?= htmlReady($tool->getLtiVersionString()) ?></td> - <td><?= count($tool->links) ?></td> + <td> + <? + //Each tool should only have one deployment-ID: + $deployment = LtiDeployment::findOneByTool_id($tool->id); + ?> + <?= htmlReady($deployment->id ?? '') ?> + </td> + <td> + <?= \LtiResourceLink::countBySql( + "JOIN `lti_deployments` + ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id` + WHERE `lti_deployments`.`tool_id` = :tool_id", + ['tool_id' => $tool->id] + ) ?> + </td> <td class="actions"> <a href="<?= $controller->link_for('lti/tool/edit/global/' . $tool->id) ?>" title="<?= _('LTI-Tool konfigurieren') ?>" aria-label="<?= _('LTI-Tool konfigurieren') ?>" data-dialog> diff --git a/app/views/course/lti/consent.php b/app/views/course/lti/consent.php index 4c810a5..be78d5d 100644 --- a/app/views/course/lti/consent.php +++ b/app/views/course/lti/consent.php @@ -1,13 +1,13 @@ <?php /** * @var AuthenticatedController $controller - * @var LtiDeployment $deployment - * @var LtiDeploymentPrivacySettings $privacy_settings + * @var LtiResourceLink $resource_link + * @var LtiToolPrivacySettings $privacy_settings */ ?> -<? if ($deployment) : ?> +<? if ($resource_link) : ?> <form class="default" method="post" <?= $privacy_settings->isNew() ? 'data-dialog="reload-on-close"' : 'data-dialog' ?> - action="<?= $controller->link_for('course/lti/consent/' . $deployment->id) ?>"> + action="<?= $controller->link_for('course/lti/consent/' . $resource_link->id) ?>"> <?= CSRFProtection::tokenTag() ?> <? $data_protection_warning = CourseConfig::get(Context::getId())->LTI_DATA_PROTECTION_COURSE_WARNING; @@ -19,8 +19,8 @@ <legend><?= _('Datenschutzhinweise') ?></legend> <section> <p><?= htmlReady($data_protection_warning) ?></p> - <? if ($deployment->tool->data_protection_notes) : ?> - <p><?= formatReady($deployment->tool->data_protection_notes) ?></p> + <? if ($resource_link->deployment->tool->data_protection_notes) : ?> + <p><?= formatReady($resource_link->deployment->tool->data_protection_notes) ?></p> <? endif ?> </section> </fieldset> @@ -53,7 +53,7 @@ <?= _('Ihr Profilbild') ?> </label> </fieldset> - <?= $this->render_partial('lti/_deployment_user_info', ['deployment' => $deployment]) ?> + <?= $this->render_partial('lti/_deployment_user_info', ['deployment' => $resource_link->deployment]) ?> <fieldset> <legend><?= _('Bestätigung') ?></legend> <label> diff --git a/app/views/course/lti/iframe.php b/app/views/course/lti/iframe.php index c1a7de1..79cbd60 100644 --- a/app/views/course/lti/iframe.php +++ b/app/views/course/lti/iframe.php @@ -1,14 +1,14 @@ <?php /** * @var StudipController $controller - * @var LtiDeployment $deployment + * @var ?LtiResourceLink $resource_link * @var array $launch_data * @var string $signature * @var bool $lti13a_mode * @var \OAT\Library\Lti1p3Core\Message\LtiMessage $message */ ?> -<? if ($deployment) : ?> +<? if ($resource_link) : ?> <!DOCTYPE html> <html> <head> @@ -27,7 +27,7 @@ <?= _('Das LTI-Tool kann nicht aufgerufen werden.') ?> <? endif ?> <? else : ?> - <form name="ltiLaunchForm" method="post" action="<?= htmlReady($deployment->getLaunchUrl()) ?>"> + <form name="ltiLaunchForm" method="post" action="<?= htmlReady($resource_link->deployment->getLaunchUrl()) ?>"> <? foreach ($launch_data as $key => $value): ?> <input type="hidden" name="<?= htmlReady($key) ?>" value="<?= htmlReady($value, false) ?>"> <? endforeach ?> diff --git a/app/views/course/lti/index.php b/app/views/course/lti/index.php index 9c399d6..76eb307 100644 --- a/app/views/course/lti/index.php +++ b/app/views/course/lti/index.php @@ -1,28 +1,28 @@ <?php /** * @var Course_LtiController $controller - * @var LtiDeployment[] $lti_data_array + * @var \LtiResourceLink[] $links * @var bool $edit_perm */ ?> -<? if (empty($lti_data_array)): ?> +<? if (empty($links)): ?> <?= MessageBox::info(_('Es sind keine LTI-Tools konfiguriert.')) ?> <? endif ?> -<? foreach ($lti_data_array as $lti_data): ?> +<? foreach ($links as $link): ?> <? - $launch_url = $lti_data->getLaunchURL(); - $unfinished_deep_linking = !empty($lti_data->options['unfinished_deep_linking']); + $launch_url = $link->deployment->getLaunchURL(); + $unfinished_deep_linking = !empty($link->deployment->options['unfinished_deep_linking']); $no_consent = !LtiToolPrivacySettings::countBySql( '`tool_id` = :tool_id AND `user_id` = :user_id', - ['tool_id' => $lti_data->tool_id, 'user_id' => $GLOBALS['user']->id] + ['tool_id' => $link->deployment->tool_id, 'user_id' => $GLOBALS['user']->id] ); ?> <article class="studip"> <header> <h1> - <?= htmlReady($lti_data->title) ?> + <?= htmlReady($link->deployment->title) ?> <?= $unfinished_deep_linking ? '(' . _('LTI Deep Linking noch nicht fertig eingerichtet') . ')' : '' ?> </h1> @@ -30,16 +30,16 @@ <nav> <form action="" method="post"> <?= CSRFProtection::tokenTag() ?> - <? if ($lti_data->position > 0): ?> + <? if ($link->position > 0): ?> <?= Icon::create('arr_2up', Icon::ROLE_SORT)->asInput([ - 'formaction' => $controller->url_for('course/lti/move/' . $lti_data->id . '/up'), + 'formaction' => $controller->url_for('course/lti/move/' . $link->id . '/up'), 'title' => _('Nach oben verschieben'), 'aria-label' => _('Nach oben verschieben') ]) ?> <? endif ?> - <? if ($lti_data->position < count($lti_data_array) - 1): ?> + <? if ($link->position < count($links) - 1): ?> <?= Icon::create('arr_2down', Icon::ROLE_SORT)->asInput([ - 'formaction' => $controller->url_for('course/lti/move/' . $lti_data->id . '/down'), + 'formaction' => $controller->url_for('course/lti/move/' . $link->id . '/down'), 'title' => _('Nach unten verschieben'), 'aria-label' => _('Nach unten verschieben') ]) ?> @@ -47,34 +47,36 @@ <? $menu = ActionMenu::get(); - $show_admin_actions = $GLOBALS['perm']->have_studip_perm('tutor', $lti_data->course_id); + $show_admin_actions = $GLOBALS['perm']->have_studip_perm('tutor', $link->course_id); if ($show_admin_actions) { $menu->addLink( - $controller->url_for('lti/tool/index/' . $lti_data->course_id . '/' . $lti_data->tool->id), + $controller->url_for('lti/tool/index/' . $link->course_id . '/' . $link->deployment->tool->id), _('Konfiguration des LTI-Tools anzeigen'), Icon::create('info-circle'), ['data-dialog' => 'size=default'] ); } $menu->addLink( - $controller->url_for('course/lti/consent/' . $lti_data->id), + $controller->url_for('course/lti/consent/' . $link->id), _('Datenschutzeinstellungen'), Icon::create('privacy'), ['data-dialog' => 'size=default'] ); - if ($show_admin_actions) { + if ($link->deployment->tool->isEditableByUser()) { $menu->addLink( - $controller->url_for('lti/tool/edit/' . $lti_data->course_id . '/' . $lti_data->tool->id), + $controller->url_for('lti/tool/edit/' . $link->course_id . '/' . $link->deployment->tool_id), _('LTI-Tool konfigurieren'), Icon::create('edit'), ['data-dialog' => 'size=default'] ); + } + if ($show_admin_actions) { $menu->addLink( sprintf( 'javascript:void(STUDIP.Dialog.confirmAsPost(\'%s\', \'%s\'))', - sprintf(_('Wollen Sie das LTI-Tool "%s" wirklich entfernen?'), $lti_data->title), - $controller->url_for('lti/tool/delete/' . $lti_data->course_id . '/' . $lti_data->tool->id) + sprintf(_('Wollen Sie das LTI-Tool "%s" wirklich entfernen?'), $link->deployment->title), + $controller->url_for('lti/tool/delete/' . $link->course_id . '/' . $link->deployment->tool_id) ), _('LTI-Tool entfernen'), Icon::create('trash') @@ -90,29 +92,29 @@ <? if ($unfinished_deep_linking) : ?> <?= Studip\LinkButton::create( _('Einrichtung abschließen'), - $controller->url_for('course/lti/select_link/' . $lti_data->id, ['tool_id' => $lti_data->tool_id]), + $controller->url_for('course/lti/select_link/' . $link->id, ['tool_id' => $link->tool_id]), ['target' => '_blank'] ) ?> <? elseif ($no_consent) : ?> - <?= formatReady($lti_data->description) ?> + <?= formatReady($link->deployment->description) ?> <p><?= _('Sie haben der Datenweitergabe an das LTI-Tool noch nicht zugestimmt und können es deswegen noch nicht nutzen.') ?></p> <?= Studip\LinkButton::create( _('Datenschutzeinstellungen öffnen'), - $controller->url_for('course/lti/consent/' . $lti_data->id), + $controller->url_for('course/lti/consent/' . $link->id), ['data-dialog' => 'reload-on-close'] ) ?> <? elseif ($launch_url) : ?> <? - $document_target = $lti_data->options['document_target'] ?? ''; + $document_target = $link->deployment->options['document_target'] ?? ''; ?> - <?= formatReady($lti_data->description) ?> + <?= formatReady($link->deployment->description) ?> <? if ($document_target === 'iframe') : ?> <iframe style="border: none; height: 640px; width: 100%;" - src="<?= $controller->link_for('course/lti/iframe/' . $lti_data->id) ?>"></iframe> + src="<?= $controller->link_for('course/lti/iframe/' . $link->id) ?>"></iframe> <? else : ?> <?= Studip\LinkButton::create( _('Anwendung starten'), - $controller->url_for('course/lti/iframe/' . $lti_data->id), + $controller->url_for('course/lti/iframe/' . $link->id), ['target' => '_blank'] ) ?> <? endif ?> diff --git a/app/views/course/lti/select_tool.php b/app/views/course/lti/select_tool.php index b6163e0..118c1ae 100644 --- a/app/views/course/lti/select_tool.php +++ b/app/views/course/lti/select_tool.php @@ -1,7 +1,7 @@ <?php /** * @var StudipController $controller - * @var LtiTool[] $global_tools + * @var LtiDeployment[] $global_tool_deployments */ ?> <form class="default" method="post" action="<?= $controller->link_for('course/lti/select_tool_redirect') ?>" @@ -11,10 +11,14 @@ <legend><?= _('Auswahl des LTI-Tools') ?></legend> <label> <?= _('Bitte wählen Sie ein LTI-Tool aus.') ?> - <select name="selected_tool_id"> - <? foreach ($global_tools as $tool) : ?> - <option value="<?= htmlReady($tool->id) ?>"> - <?= htmlReady($tool->name) ?> + <select name="selected_deployment_id"> + <? foreach ($global_tool_deployments as $deployment) : ?> + <option value="<?= htmlReady($deployment->id) ?>"> + <? if ($deployment->title !== $deployment->tool->name) : ?> + <?= htmlReady(sprintf('%1$s (%2$s)', $deployment->tool->name, $deployment->title)) ?> + <? else : ?> + <?= htmlReady($deployment->title) ?> + <? endif ?> </option> <? endforeach ?> <? if (Config::get()->LTI_ALLOW_TOOL_CONFIG_IN_COURSE) : ?> diff --git a/app/views/lti/_tool_form_fields.php b/app/views/lti/_tool_form_fields.php index 7a7dfbf..0dcbd25 100644 --- a/app/views/lti/_tool_form_fields.php +++ b/app/views/lti/_tool_form_fields.php @@ -1,7 +1,7 @@ <?php /** * @var LtiTool $tool - * @var LtiDeployment $deployment + * @var ?LtiDeployment $deployment */ ?> <fieldset> @@ -10,118 +10,106 @@ <span class="textlabel"><?= _('Titel') ?></span> <span class="asterisk">*</span> <input type="text" name="name" required - value="<?= htmlReady(!empty($deployment) ? $deployment->title : $tool->name ?? '') ?>"> + value="<?= htmlReady($tool->name ?? '') ?>"> </label> - <? if (!empty($deployment)) : ?> + <label> + <?= _('Beschreibung') ?> + <textarea name="description" class="wysiwyg"><?= wysiwygReady($deployment->description ?? '') ?></textarea> + </label> + <label> + <?= _('Datenschutzhinweise') ?> + <textarea name="data_protection_notes" class="wysiwyg" + placeholder="<?= _('Bitte machen Sie Angaben zu dem angebundenen Werkzeug, soweit sie ihnen bekannt sind. Wie ist der Name, wer bietet es an, wozu wird es eingesetzt und welche Daten werden übertragen? (Beispiel: „Tool XY wird zur Durchführung von Sprachtests genutzt und Testergebnisse und ggf. Noten gespeichert. Zur Anmeldung werden Name und Nutzerkennung übertragen.“)') ?>"><?= wysiwygReady($tool->data_protection_notes) ?></textarea> + </label> + <label> + <?= _('URL zu den Nutzungsbedingungen des LTI-Tools (falls verfügbar)') ?> + <input type="url" name="terms_of_use_url" value="<?= htmlReady($tool->terms_of_use_url) ?>"> + </label> + <label> + <?= _('URL zur Datenschutzerklärung des LTI-Tools (falls verfügbar)') ?> + <input type="url" name="privacy_policy_url" value="<?= htmlReady($tool->privacy_policy_url) ?>"> + </label> +</fieldset> +<fieldset> + <legend><?= _('Konfiguration des LTI-Tools') ?></legend> + <label class="studiprequired"> + <span class="textlabel"><?= _('LTI-Version') ?></span> + <span class="asterisk">*</span> + <select name="lti_version" + data-shows=".lti11-field" data-hides=".lti13a-field" + data-triggering-value="1.1"> + <option value="1.1" <?= !empty($tool->lti_version) && $tool->lti_version === '1.1' ? 'selected' : '' ?>> + 1.0/1.1 + </option> + <option value="1.3a" <?= empty($tool->lti_version) || $tool->lti_version === '1.3a' ? 'selected' : '' ?>> + 1.3a + </option> + </select> + </label> + + <label class="studiprequired"> + <span class="textlabel"><?= _('LTI Launch-URL') ?></span> + <span class="asterisk">*</span> + <input type="text" name="launch_url" required + value="<?= htmlReady($tool->launch_url ?? '') ?>"> + </label> + + <div class="lti13a-field"> <label> - <?= _('Beschreibung') ?> - <textarea name="description" class="wysiwyg"><?= wysiwygReady($deployment->description ?? '') ?></textarea> + <?= _('OIDC Login-URL') ?> + <?= tooltipIcon(_('Die URL, mit der der Login via OpenID Connect stattfindet.')) ?> + <input type="text" name="oidc_init_url" value="<?= htmlReady($tool->oidc_init_url ?? '') ?>"> </label> <label> - <?= _('Datenschutzhinweise') ?> - <textarea name="data_protection_notes" class="wysiwyg" - placeholder="<?= _('Bitte machen Sie Angaben zu dem angebundenen Werkzeug, soweit sie ihnen bekannt sind. Wie ist der Name, wer bietet es an, wozu wird es eingesetzt und welche Daten werden übertragen? (Beispiel: „Tool XY wird zur Durchführung von Sprachtests genutzt und Testergebnisse und ggf. Noten gespeichert. Zur Anmeldung werden Name und Nutzerkennung übertragen.“)') ?>"><?= wysiwygReady($tool->data_protection_notes) ?></textarea> + <?= _('Deep-linking URL') ?> + <input type="url" name="deep_linking_url" value="<?= htmlReady($tool->deep_linking_url ?? '') ?>"> </label> - <? endif ?> - <? if ($tool->isEditableByUser()) : ?> <label> - <?= _('URL zu den Nutzungsbedingungen des LTI-Tools (falls verfügbar)') ?> - <input type="url" name="terms_of_use_url" value="<?= htmlReady($tool->terms_of_use_url) ?>"> + <?= _('JWKS-URL') ?> + <?= tooltipIcon(_('Die URL, mit der der der Austausch von JSON web keys stattfinden kann.')) ?> + <input type="text" name="jwks_url" + value="<?= htmlReady($tool->jwks_url ?? '') ?>"> </label> <label> - <?= _('URL zur Datenschutzerklärung des LTI-Tools (falls verfügbar)') ?> - <input type="url" name="privacy_policy_url" value="<?= htmlReady($tool->privacy_policy_url) ?>"> + <?= _('Schlüssel-ID') ?> + <?= tooltipIcon(_('Die ID des Schlüssels, der über die JWKS-URL geladen werden soll.')) ?> + <input type="text" name="jwks_key_id" value="<?= htmlReady($tool->jwks_key_id ?? '') ?>"> </label> - <? endif ?> -</fieldset> -<fieldset> - <legend><?= _('Konfiguration des LTI-Tools') ?></legend> - <? if ($tool->isEditableByUser()) : ?> + <label> + <?= _('Öffentlicher Schlüssel des LTI-Tools') ?> + <? + $keyring = null; + if ($tool && !$tool->isNew()) { + $keyring = $tool->getKeyring(); + } + $public_key_string = ''; + if ($keyring) { + $keychain = $keyring->toKeyChain(); + $public_key_string = $keychain->getPublicKey()->getContent(); + } + ?> + <textarea name="tool_public_key"><?= htmlReady($public_key_string) ?></textarea> + </label> + </div> + <div class="lti11-field"> <label class="studiprequired"> - <span class="textlabel"><?= _('LTI-Version') ?></span> + <span class="textlabel"><?= _('Consumer-Key des LTI-Tools') ?></span> <span class="asterisk">*</span> - <select name="lti_version" - data-shows=".lti11-field" data-hides=".lti13a-field" - data-triggering-value="1.1"> - <option value="1.1" <?= !empty($tool->lti_version) && $tool->lti_version === '1.1' ? 'selected' : '' ?>> - 1.0/1.1 - </option> - <option value="1.3a" <?= empty($tool->lti_version) || $tool->lti_version === '1.3a' ? 'selected' : '' ?>> - 1.3a - </option> - </select> + <input type="text" name="consumer_key" required + value="<?= htmlReady($tool->consumer_key ?? '') ?>"> </label> - <? endif ?> - - <label class="studiprequired"> - <span class="textlabel"><?= _('LTI Launch-URL') ?></span> - <span class="asterisk">*</span> - <input type="text" name="launch_url" required - value="<?= htmlReady( - !empty($deployment->launch_url) - ? $deployment->launch_url - : $tool->launch_url ?? '' - ) ?>"> - </label> - - <? if ($tool->isEditableByUser()) : ?> - <div class="lti13a-field"> - <label> - <?= _('OIDC Login-URL') ?> - <?= tooltipIcon(_('Die URL, mit der der Login via OpenID Connect stattfindet.')) ?> - <input type="text" name="oidc_init_url" value="<?= htmlReady($tool->oidc_init_url ?? '') ?>"> - </label> - <label> - <?= _('Deep-linking URL') ?> - <input type="url" name="deep_linking_url" value="<?= htmlReady($tool->deep_linking_url ?? '') ?>"> - </label> - <label> - <?= _('JWKS-URL') ?> - <?= tooltipIcon(_('Die URL, mit der der der Austausch von JSON web keys stattfinden kann.')) ?> - <input type="text" name="jwks_url" - value="<?= htmlReady($tool->jwks_url ?? '') ?>"> - </label> - <label> - <?= _('Schlüssel-ID') ?> - <?= tooltipIcon(_('Die ID des Schlüssels, der über die JWKS-URL geladen werden soll.')) ?> - <input type="text" name="jwks_key_id" value="<?= htmlReady($tool->jwks_key_id ?? '') ?>"> - </label> - <label> - <?= _('Öffentlicher Schlüssel des LTI-Tools') ?> - <? - $keyring = null; - if ($tool && !$tool->isNew()) { - $keyring = $tool->getKeyring(); - } - $public_key_string = ''; - if ($keyring) { - $keychain = $keyring->toKeyChain(); - $public_key_string = $keychain->getPublicKey()->getContent(); - } - ?> - <textarea name="tool_public_key"><?= htmlReady($public_key_string) ?></textarea> - </label> - </div> - <div class="lti11-field"> - <label class="studiprequired"> - <span class="textlabel"><?= _('Consumer-Key des LTI-Tools') ?></span> - <span class="asterisk">*</span> - <input type="text" name="consumer_key" required - value="<?= htmlReady($tool->consumer_key ?? '') ?>"> - </label> - <label class="studiprequired"> + <label class="studiprequired"> <span class="textlabel"><?= _('Consumer-Secret des LTI-Tools') ?></span> - <span class="asterisk">*</span> - <input type="text" name="consumer_secret" required - value="<?= htmlReady($tool->consumer_secret ?? '') ?>"> - </label> - </div> - <label> - <input type="checkbox" name="send_lis_person" value="1" <?= !empty($tool->send_lis_person) ? ' checked' : '' ?>> - <?= _('Personendaten an das LTI-Tool senden') ?> - <?= tooltipIcon(_('Personendaten dürfen nur an das externe Tool gesendet werden, wenn es keine Datenschutzbedenken gibt. Mit Setzen des Hakens bestätigen Sie, dass die Übermittlung der Daten zulässig ist.')) ?> + <span class="asterisk">*</span> + <input type="text" name="consumer_secret" required + value="<?= htmlReady($tool->consumer_secret ?? '') ?>"> </label> - <? endif ?> + </div> + <label> + <input type="checkbox" name="send_lis_person" value="1" <?= !empty($tool->send_lis_person) ? ' checked' : '' ?>> + <?= _('Personendaten an das LTI-Tool senden') ?> + <?= tooltipIcon(_('Personendaten dürfen nur an das externe Tool gesendet werden, wenn es keine Datenschutzbedenken gibt. Mit Setzen des Hakens bestätigen Sie, dass die Übermittlung der Daten zulässig ist.')) ?> + </label> <label> <?= _('Zusätzliche LTI-Parameter') ?> <?= tooltipIcon(_('Ein Wert pro Zeile, Beispiel: Review:Chapter=1.2.56')) ?> @@ -132,16 +120,14 @@ ) ?></textarea> </label> </fieldset> -<? if (!empty($deployment)) : ?> - <fieldset> - <legend><?= _('Anzeigeeinstellungen') ?></legend> - <label> - <input type="checkbox" name="document_target" value="iframe" <?= isset($deployment->options['document_target']) && $deployment->options['document_target'] === 'iframe' ? ' checked' : '' ?>> - <?= _('Anzeige im IFRAME auf der Seite') ?> - <?= tooltipIcon(_('Normalerweise wird das externe Tool in einem neuen Fenster angezeigt. Aktivieren Sie diese Option, wenn die Anzeige stattdessen in einem IFRAME erfolgen soll.')) ?> - </label> - </fieldset> -<? endif ?> +<fieldset> + <legend><?= _('Anzeigeeinstellungen') ?></legend> + <label> + <input type="checkbox" name="document_target" value="iframe" <?= isset($deployment->options['document_target']) && $deployment->options['document_target'] === 'iframe' ? ' checked' : '' ?>> + <?= _('Anzeige im IFRAME auf der Seite') ?> + <?= tooltipIcon(_('Normalerweise wird das externe Tool in einem neuen Fenster angezeigt. Aktivieren Sie diese Option, wenn die Anzeige stattdessen in einem IFRAME erfolgen soll.')) ?> + </label> +</fieldset> <footer data-dialog-button> <?= Studip\Button::createAccept(_('Speichern'), 'save') ?> diff --git a/app/views/lti/_tool_info.php b/app/views/lti/_tool_info.php index 452e8f4..0fafc9d 100644 --- a/app/views/lti/_tool_info.php +++ b/app/views/lti/_tool_info.php @@ -65,10 +65,21 @@ <? endif ?> <dt><?= _('Direktlink zum LTI-Tool') ?></dt> <dd> - <a href="<?= $controller->link_for('course/lti/iframe', $deployment->id) ?>"> - <?= Icon::create('link-extern')->asImg(['class' => 'text-bottom']) ?> - <?= $controller->link_for('course/lti/iframe', $deployment->id) ?> - </a> + <ul> + <? foreach ($tool->deployments as $deployment) : ?> + <? + $link = LtiResourceLink::findOneByDeployment_id($deployment->id); + ?> + <? if ($link) : ?> + <li> + <a href="<?= $controller->link_for('course/lti/iframe', $link->id) ?>"> + <?= Icon::create('link-extern')->asImg(['class' => 'text-bottom']) ?> + <?= $controller->link_for('course/lti/iframe', $link->id) ?> + </a> + </li> + <? endif ?> + <? endforeach ?> + </ul> </dd> </dl> </article> |
