aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/course/lti.php264
-rw-r--r--app/controllers/lti/tool.php62
-rw-r--r--app/views/admin/lti/index.php22
-rw-r--r--app/views/course/lti/add_link.php2
-rw-r--r--app/views/course/lti/consent.php2
-rw-r--r--app/views/course/lti/grades.php2
-rw-r--r--app/views/course/lti/grades_user.php2
-rw-r--r--app/views/course/lti/index.php18
-rw-r--r--app/views/course/lti/select_link.php6
-rw-r--r--app/views/course/lti/select_tool.php6
-rw-r--r--app/views/lti/_link_user_info.php (renamed from app/views/lti/_deployment_user_info.php)24
-rw-r--r--app/views/lti/_tool_form_fields.php12
-rw-r--r--app/views/lti/_tool_info.php53
-rw-r--r--app/views/lti/tool/add.php6
-rw-r--r--app/views/lti/tool/edit.php6
-rw-r--r--db/migrations/6.0.51_add_columns_to_lti_resource_links_table.php79
-rw-r--r--lib/classes/LTI13a/PlatformManager.php22
-rw-r--r--lib/classes/LTI13a/Registration.php43
-rw-r--r--lib/classes/LTI13a/RegistrationManager.php21
-rw-r--r--lib/models/LtiDeployment.php34
-rw-r--r--lib/models/LtiGrade.php3
-rw-r--r--lib/models/LtiResourceLink.php51
22 files changed, 453 insertions, 287 deletions
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 @@
<col style="width: 5%;">
<col style="width: 5%;">
<col style="width: 5%;">
+ <col style="width: 5%;">
</colgroup>
<thead>
@@ -26,6 +27,7 @@
<th><?= _('Consumer-Key') ?></th>
<th><?= _('LTI-Version') ?></th>
<th><?= _('Deployment-ID') ?></th>
+ <th><?= _('Deep Links') ?></th>
<th><?= _('Links') ?></th>
<th class="actions"><?= _('Aktionen') ?></th>
</tr>
@@ -49,9 +51,25 @@
<td>
<?
//Each tool should only have one deployment-ID:
- $deployment = LtiDeployment::findOneByTool_id($tool->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;
+ }
?>
- <?= htmlReady($deployment->id ?? '') ?>
+ <?= htmlReady(implode(', ', $deployment_ids)) ?>
+ <? if (count($deployment_ids) > 1) : ?>
+ <?= tooltipIcon(_('Dieses Tool hat mehrere Deployment-IDs zur generellen Nutzung!')) ?>
+ <? endif ?>
+ </td>
+ <td>
+ <?= htmlReady(LtiDeployment::countBySQL(
+ "`tool_id` = :tool_id AND `purpose` = 'deep_linking'",
+ ['tool_id' => $tool->id]
+ )) ?>
</td>
<td>
<?= \LtiResourceLink::countBySql(
diff --git a/app/views/course/lti/add_link.php b/app/views/course/lti/add_link.php
index dc479a2..c5fd555 100644
--- a/app/views/course/lti/add_link.php
+++ b/app/views/course/lti/add_link.php
@@ -3,7 +3,7 @@
<?= _('Auswahl des externen Tools') ?>
<select name="tool_id">
<? foreach ($tools as $tool): ?>
- <option value="<?= $tool->id ?>"><?= htmlReady($tool->name) ?></option>
+ <option value="<?= htmlReady($tool->id) ?>"><?= htmlReady($tool->name) ?></option>
<? endforeach ?>
</select>
</label>
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 @@
<?= _('Ihr Profilbild') ?>
</label>
</fieldset>
- <?= $this->render_partial('lti/_deployment_user_info', ['deployment' => $resource_link->deployment]) ?>
+ <?= $this->render_partial('lti/_link_user_info', ['link' => $resource_link]) ?>
<fieldset>
<legend><?= _('Bestätigung') ?></legend>
<label>
diff --git a/app/views/course/lti/grades.php b/app/views/course/lti/grades.php
index e9b0b40..48df9e9 100644
--- a/app/views/course/lti/grades.php
+++ b/app/views/course/lti/grades.php
@@ -27,7 +27,7 @@
</td>
<? foreach ($lti_data_array as $lti_data): ?>
<td style="text-align: right;">
- <? if ($grade = $lti_data->grades->findOneBy('user_id', $member->user_id)): ?>
+ <? if ($grade = $lti_data->deployment->grades->findOneBy('user_id', $member->user_id)): ?>
<?= sprintf('%.0f%%', $grade->score * 100) ?>
<? else: ?>
&ndash;
diff --git a/app/views/course/lti/grades_user.php b/app/views/course/lti/grades_user.php
index fb041c5..04ce429 100644
--- a/app/views/course/lti/grades_user.php
+++ b/app/views/course/lti/grades_user.php
@@ -16,7 +16,7 @@
<tr>
<td><?= htmlReady($lti_data->title) ?></td>
<td style="text-align: right;">
- <? if ($grade = LtiGrade::find([$lti_data->id, $GLOBALS['user']->id])): ?>
+ <? if ($grade = LtiGrade::find([$lti_data->deployment_id, $GLOBALS['user']->id])): ?>
<?= sprintf('%.0f%%', $grade->score * 100) ?>
<? else: ?>
&ndash;
diff --git a/app/views/course/lti/index.php b/app/views/course/lti/index.php
index 76eb307..f28dc5e 100644
--- a/app/views/course/lti/index.php
+++ b/app/views/course/lti/index.php
@@ -11,8 +11,8 @@
<? foreach ($links as $link): ?>
<?
- $launch_url = $link->deployment->getLaunchURL();
- $unfinished_deep_linking = !empty($link->deployment->options['unfinished_deep_linking']);
+ $launch_url = $link->getLaunchURL();
+ $unfinished_deep_linking = !empty($link->options['unfinished_deep_linking']);
$no_consent = !LtiToolPrivacySettings::countBySql(
'`tool_id` = :tool_id AND `user_id` = :user_id',
['tool_id' => $link->deployment->tool_id, 'user_id' => $GLOBALS['user']->id]
@@ -22,7 +22,7 @@
<article class="studip">
<header>
<h1>
- <?= htmlReady($link->deployment->title) ?>
+ <?= htmlReady($link->title) ?>
<?= $unfinished_deep_linking ? '(' . _('LTI Deep Linking noch nicht fertig eingerichtet') . ')' : '' ?>
</h1>
@@ -50,7 +50,7 @@
$show_admin_actions = $GLOBALS['perm']->have_studip_perm('tutor', $link->course_id);
if ($show_admin_actions) {
$menu->addLink(
- $controller->url_for('lti/tool/index/' . $link->course_id . '/' . $link->deployment->tool->id),
+ $controller->url_for('lti/tool/index/' . $link->course_id . '/' . $link->deployment->tool_id, ['link_id' => $link->id]),
_('Konfiguration des LTI-Tools anzeigen'),
Icon::create('info-circle'),
['data-dialog' => 'size=default']
@@ -75,7 +75,7 @@
$menu->addLink(
sprintf(
'javascript:void(STUDIP.Dialog.confirmAsPost(\'%s\', \'%s\'))',
- sprintf(_('Wollen Sie das LTI-Tool "%s" wirklich entfernen?'), $link->deployment->title),
+ sprintf(_('Wollen Sie das LTI-Tool "%s" wirklich entfernen?'), $link->title),
$controller->url_for('lti/tool/delete/' . $link->course_id . '/' . $link->deployment->tool_id)
),
_('LTI-Tool entfernen'),
@@ -92,11 +92,11 @@
<? if ($unfinished_deep_linking) : ?>
<?= Studip\LinkButton::create(
_('Einrichtung abschließen'),
- $controller->url_for('course/lti/select_link/' . $link->id, ['tool_id' => $link->tool_id]),
+ $controller->url_for('course/lti/select_link/' . $link->id, ['tool_id' => $link->deployment->tool_id]),
['target' => '_blank']
) ?>
<? elseif ($no_consent) : ?>
- <?= formatReady($link->deployment->description) ?>
+ <?= formatReady($link->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'),
@@ -105,9 +105,9 @@
) ?>
<? elseif ($launch_url) : ?>
<?
- $document_target = $link->deployment->options['document_target'] ?? '';
+ $document_target = $link->options['document_target'] ?? '';
?>
- <?= formatReady($link->deployment->description) ?>
+ <?= formatReady($link->description) ?>
<? if ($document_target === 'iframe') : ?>
<iframe style="border: none; height: 640px; width: 100%;"
src="<?= $controller->link_for('course/lti/iframe/' . $link->id) ?>"></iframe>
diff --git a/app/views/course/lti/select_link.php b/app/views/course/lti/select_link.php
index 370587e..b0a30f6 100644
--- a/app/views/course/lti/select_link.php
+++ b/app/views/course/lti/select_link.php
@@ -2,13 +2,13 @@
/**
* @var AuthenticatedController $controller
* @var LtiTool $tool
- * @var LtiDeployment $deployment
+ * @var \LtiResourceLink $link
*/
?>
<form class="default" method="post"
- action="<?= $controller->link_for('course/lti/process_select_link/' . htmlReady($deployment->id), ['tool_id' => $tool->id]) ?>">
+ action="<?= $controller->link_for('course/lti/process_select_link/' . htmlReady($link->id ?? ''), ['tool_id' => $tool->id]) ?>">
<?= CSRFProtection::tokenTag() ?>
- <?= $this->render_partial('lti/_tool_info', ['tool' => $tool, 'deployment' => $deployment]) ?>
+ <?= $this->render_partial('lti/_tool_info', ['tool' => $tool, 'deployment' => $link->deployment ?? null]) ?>
<div data-dialog-button>
<?= \Studip\Button::create(_('Weiter'), 'continue') ?>
</div>
diff --git a/app/views/course/lti/select_tool.php b/app/views/course/lti/select_tool.php
index 118c1ae..d2af241 100644
--- a/app/views/course/lti/select_tool.php
+++ b/app/views/course/lti/select_tool.php
@@ -14,10 +14,10 @@
<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)) ?>
+ <? if ($deployment->name) : ?>
+ <?= htmlReady(sprintf('%1$s (%2$s)', $deployment->tool->name, $deployment->name)) ?>
<? else : ?>
- <?= htmlReady($deployment->title) ?>
+ <?= htmlReady($deployment->tool->name) ?>
<? endif ?>
</option>
<? endforeach ?>
diff --git a/app/views/lti/_deployment_user_info.php b/app/views/lti/_link_user_info.php
index b43da8f..568aaf8 100644
--- a/app/views/lti/_deployment_user_info.php
+++ b/app/views/lti/_link_user_info.php
@@ -1,37 +1,37 @@
<?
/**
- * @var LtiDeployment $deployment
+ * @var \LtiResourceLink $link
*/
?>
-<? if (!empty($deployment)) : ?>
+<? if (!empty($link)) : ?>
<article class="studip">
- <header><h1><?= htmlReady($deployment->title) ?></h1></header>
+ <header><h1><?= htmlReady($link->title) ?></h1></header>
<section>
- <? if ($deployment->tool->range_id === 'global') : ?>
+ <? if ($link->deployment->tool->range_id === 'global') : ?>
<p>
<?= sprintf(
'Dies ist eine Einbindung des LTI-Tools „%s“.',
- htmlReady($deployment->tool->name)
+ htmlReady($link->deployment->tool->name)
) ?>
</p>
<? endif ?>
- <p><?= formatReady($deployment->description ?? '') ?></p>
+ <p><?= formatReady($link->description ?? '') ?></p>
<?
- $url_parts = parse_url($deployment->getLaunchURL());
+ $url_parts = parse_url($link->getLaunchURL());
?>
<? if (!empty($url_parts['host'])) : ?>
<p><?= _('Domain') ?>: <?= htmlReady($url_parts['host']) ?></p>
<? endif ?>
- <? if ($deployment->tool->terms_of_use_url || $deployment->tool->privacy_policy_url) : ?>
+ <? if ($link->deployment->tool->terms_of_use_url || $link->deployment->tool->privacy_policy_url) : ?>
<p>
- <? if ($deployment->tool->terms_of_use_url) : ?>
- <a href="<?= htmlReady($deployment->tool->terms_of_use_url) ?>">
+ <? if ($link->deployment->tool->terms_of_use_url) : ?>
+ <a href="<?= htmlReady($link->deployment->tool->terms_of_use_url) ?>">
<?= Icon::create('link-extern')->asImg(['class' => 'text-bottom']) ?>
<?= _('Nutzungsbedingungen') ?>
</a>
<? endif ?>
- <? if ($deployment->tool->privacy_policy_url) : ?>
- <a href="<?= htmlReady($deployment->tool->privacy_policy_url) ?>">
+ <? if ($link->deployment->tool->privacy_policy_url) : ?>
+ <a href="<?= htmlReady($link->deployment->tool->privacy_policy_url) ?>">
<?= Icon::create('link-extern')->asImg(['class' => 'text-bottom']) ?>
<?= _('Datenschutzerklärung') ?>
</a>
diff --git a/app/views/lti/_tool_form_fields.php b/app/views/lti/_tool_form_fields.php
index 0dcbd25..d1dccec 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 ?\LtiResourceLink $link
*/
?>
<fieldset>
@@ -10,11 +10,11 @@
<span class="textlabel"><?= _('Titel') ?></span>
<span class="asterisk">*</span>
<input type="text" name="name" required
- value="<?= htmlReady($tool->name ?? '') ?>">
+ value="<?= htmlReady($link->title ?? $tool->name ?? '') ?>">
</label>
<label>
<?= _('Beschreibung') ?>
- <textarea name="description" class="wysiwyg"><?= wysiwygReady($deployment->description ?? '') ?></textarea>
+ <textarea name="description" class="wysiwyg"><?= wysiwygReady($link->description ?? '') ?></textarea>
</label>
<label>
<?= _('Datenschutzhinweise') ?>
@@ -114,8 +114,8 @@
<?= _('Zusätzliche LTI-Parameter') ?>
<?= tooltipIcon(_('Ein Wert pro Zeile, Beispiel: Review:Chapter=1.2.56')) ?>
<textarea name="custom_parameters"><?= htmlReady(
- !empty($deployment->options['custom_parameters'])
- ? $deployment->options['custom_parameters']
+ !empty($link->options['custom_parameters'])
+ ? $link->options['custom_parameters']
: $tool->custom_parameters ?? ''
) ?></textarea>
</label>
@@ -123,7 +123,7 @@
<fieldset>
<legend><?= _('Anzeigeeinstellungen') ?></legend>
<label>
- <input type="checkbox" name="document_target" value="iframe" <?= isset($deployment->options['document_target']) && $deployment->options['document_target'] === 'iframe' ? ' checked' : '' ?>>
+ <input type="checkbox" name="document_target" value="iframe" <?= isset($link->options['document_target']) && $link->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>
diff --git a/app/views/lti/_tool_info.php b/app/views/lti/_tool_info.php
index 0fafc9d..fb9657e 100644
--- a/app/views/lti/_tool_info.php
+++ b/app/views/lti/_tool_info.php
@@ -1,15 +1,15 @@
<?php
/**
* @var LtiTool $tool
- * @var LtiDeployment $deployment
- * @var StudipControlle $controller
+ * @var \LtiResourceLink $link
+ * @var StudipController $controller
*/
?>
<? if (!empty($tool)) : ?>
<article class="studip">
<header>
- <? if ($deployment) : ?>
- <h1><?= htmlReady($deployment->title) ?></h1>
+ <? if ($link) : ?>
+ <h1><?= htmlReady($link->title) ?></h1>
<? else : ?>
<h1><?= htmlReady($tool->name) ?></h1>
<? endif ?>
@@ -17,10 +17,10 @@
<dl>
<dt><?= _('Launch-URL') ?></dt>
<dd>
- <? if ($deployment && $deployment->launch_url) : ?>
- <a href="<?= htmlReady($deployment->launch_url) ?>">
+ <? if ($link && $link->launch_url) : ?>
+ <a href="<?= htmlReady($link->launch_url) ?>">
<?= Icon::create('link-extern')->asImg(['class' => 'text-bottom']) ?>
- <?= htmlReady($deployment->launch_url) ?>
+ <?= htmlReady($link->launch_url) ?>
</a>
<? else : ?>
<a href="<?= htmlReady($tool->launch_url) ?>">
@@ -54,37 +54,32 @@
<dd><?= htmlReady($tool->id) ?></dd>
<? endif ?>
- <? if ($deployment) : ?>
+ <? if (!empty($link->deployment->id)) : ?>
<dt><?= _('Deployment-ID') ?></dt>
- <dd><?= htmlReady($deployment->id) ?></dd>
+ <dd><?= htmlReady($link->deployment->id) ?></dd>
- <? if ($parameters = $deployment->getCustomParameters()) : ?>
+ <? if ($parameters = $link->getCustomParameters()) : ?>
<dt><?= _('LTI custom parameters') ?></dt>
<dd><?= htmlReady($parameters) ?></dd>
<? endif ?>
<? endif ?>
- <dt><?= _('Direktlink zum LTI-Tool') ?></dt>
- <dd>
- <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>
+ <? if ($link) : ?>
+ <dt><?= _('Direktlink zum LTI-Tool') ?></dt>
+ <dd>
+ <ul>
+ <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>
+ </ul>
+ </dd>
+ <? endif ?>
</dl>
</article>
<article class="studip">
<header><h1><?= _('Plattform-Konfiguration') ?></h1></header>
- <?= $this->render_partial('lti/_platform_data', ['platform' => \Studip\LTI13a\PlatformManager::getPlatformConfiguration()]) ?>
+ <?= $this->render_partial('lti/_platform_data', ['platform' => \Studip\LTI13a\PlatformManager::getPlatformConfiguration($tool->id)]) ?>
</article>
<? endif ?>
diff --git a/app/views/lti/tool/add.php b/app/views/lti/tool/add.php
index fda0849..fd0c223 100644
--- a/app/views/lti/tool/add.php
+++ b/app/views/lti/tool/add.php
@@ -3,14 +3,14 @@
* @var AuthenticatedController $controller
* @var string $range_id
* @var LtiTool $tool
- * @var LtiDeployment $deployment
+ * @var ?\LtiResourceLink $link
*/
?>
<form class="default" method="post" data-dialog="reload-on-close"
- action="<?= $controller->link_for('lti/tool/add/' . $range_id . '/' . $tool->id) ?>">
+ action="<?= $controller->link_for('lti/tool/add/' . $range_id . '/' . $tool->id, ['link_id' => $link->id ?? '']) ?>">
<?= CSRFProtection::tokenTag() ?>
<?= $this->render_partial('lti/_tool_form_fields', [
'tool' => $tool,
- 'deployment' => $deployment,
+ 'link' => $link ?? null,
]) ?>
</form>
diff --git a/app/views/lti/tool/edit.php b/app/views/lti/tool/edit.php
index 5b29dd3..74f40c3 100644
--- a/app/views/lti/tool/edit.php
+++ b/app/views/lti/tool/edit.php
@@ -3,16 +3,16 @@
* @var AuthenticatedController $controller
* @var string $range_id
* @var LtiTool $tool
- * @var LtiDeployment $deployment
+ * @var ?\LtiResourceLink $link
*/
?>
<? if ($tool) : ?>
<form class="default" method="post" data-dialog="reload-on-close"
- action="<?= $controller->link_for('lti/tool/edit/' . $range_id . '/' . $tool->id) ?>">
+ action="<?= $controller->link_for('lti/tool/edit/' . $range_id . '/' . $tool->id, ['link_id' => $link->id ?? '']) ?>">
<?= CSRFProtection::tokenTag() ?>
<?= $this->render_partial('lti/_tool_form_fields', [
'tool' => $tool,
- 'deployment' => $deployment,
+ 'deployment' => $link ?? null,
]) ?>
</form>
<? endif ?>
diff --git a/db/migrations/6.0.51_add_columns_to_lti_resource_links_table.php b/db/migrations/6.0.51_add_columns_to_lti_resource_links_table.php
new file mode 100644
index 0000000..9385908
--- /dev/null
+++ b/db/migrations/6.0.51_add_columns_to_lti_resource_links_table.php
@@ -0,0 +1,79 @@
+<?php
+
+
+class AddColumnsToLtiResourceLinksTable extends Migration
+{
+ public function description()
+ {
+ return 'Add missing columns to the lti_resource_links table.';
+ }
+
+ protected function up()
+ {
+ $db = DBManager::get();
+
+ //Clone the launch_url and options column from lti_deployments:
+ $db->exec(
+ "ALTER TABLE `lti_resource_links`
+ ADD COLUMN `title` VARCHAR(255) NOT NULL DEFAULT '',
+ ADD COLUMN `description` TEXT NULL,
+ ADD COLUMN `launch_url` VARCHAR(255) NOT NULL DEFAULT '',
+ ADD COLUMN `options` TEXT"
+ );
+
+ $db->exec(
+ "UPDATE `lti_resource_links`
+ JOIN `lti_deployments`
+ ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id`
+ SET
+ `lti_resource_links`.`title` = `lti_deployments`.`title`,
+ `lti_resource_links`.`description` = `lti_deployments`.`description`,
+ `lti_resource_links`.`launch_url` = `lti_deployments`.`launch_url`,
+ `lti_resource_links`.`options` = `lti_deployments`.`options`"
+ );
+
+ $db->exec(
+ "ALTER TABLE `lti_deployments`
+ DROP COLUMN `title`,
+ DROP COLUMN `description`,
+ DROP COLUMN `launch_url`,
+ DROP COLUMN `options`,
+ ADD COLUMN `purpose` ENUM ('general', 'deep_linking') NOT NULL DEFAULT 'general',
+ ADD COLUMN `name` VARCHAR(255) NOT NULL DEFAULT ''"
+ );
+ }
+
+ protected function down()
+ {
+ $db = DBManager::get();
+
+ $db->exec(
+ "ALTER TABLE `lti_deployments`
+ DROP COLUMN `name`,
+ DROP COLUMN `purpose`,
+ ADD COLUMN `title` VARCHAR(255) NOT NULL DEFAULT '',
+ ADD COLUMN `description` TEXT NULL,
+ ADD COLUMN `launch_url` VARCHAR(255) NOT NULL DEFAULT '',
+ ADD COLUMN `options` TEXT"
+ );
+
+ $db->exec(
+ "UPDATE `lti_deployments`
+ JOIN `lti_resource_links`
+ ON `lti_deployments`.`id` = `lti_resource_links`.`deployment_id`
+ SET
+ `lti_deployments`.`title` = `lti_resource_links`.`title`,
+ `lti_deployments`.`description` = `lti_resource_links`.`description`,
+ `lti_deployments`.`launch_url` = `lti_resource_links`.`launch_url`,
+ `lti_deployments`.`options` = `lti_resource_links`.`options`"
+ );
+
+ $db->exec(
+ "ALTER TABLE `lti_resource_links`
+ DROP COLUMN `title`,
+ DROP COLUMN `description`,
+ DROP COLUMN launch_url,
+ DROP COLUMN `options`"
+ );
+ }
+}
diff --git a/lib/classes/LTI13a/PlatformManager.php b/lib/classes/LTI13a/PlatformManager.php
index 10c7cb1..9017267 100644
--- a/lib/classes/LTI13a/PlatformManager.php
+++ b/lib/classes/LTI13a/PlatformManager.php
@@ -31,17 +31,20 @@ class PlatformManager
* Generates an object containing the settings for using this Stud.IP
* as a platform that connects to an LTI tool via Deep Linking.
*
- * @param string $tool_id An optional LTI tool ID that is used to construct
+ * @param string $link_id The Stud.IP LTI Resource Link ID that is used to construct
* the platform return URL.
*
+ * @param string $course_id An optional Stud.IP course for which to get
+ * the deep linking configuration.
+ *
* @return DeepLinkingSettings The settings for deep linking.
*/
- public static function getDeepLinkingConfiguration(string $tool_id = '') : DeepLinkingSettings
+ public static function getDeepLinkingConfiguration(string $link_id, string $course_id = '') : DeepLinkingSettings
{
$c = \Config::get();
return new DeepLinkingSettings(
- self::getDeepLinkingReturnUrl($tool_id),
+ self::getDeepLinkingReturnUrl($link_id, $course_id),
[LtiResourceLinkInterface::TYPE],
['window', 'iframe'],
'text/html',
@@ -85,13 +88,20 @@ class PlatformManager
/**
* Generates the URL for returning from the tool in an LTI deep linking process.
*
- * @param string $tool_id The optional LTI Tool-ID to append to the URL.
+ * @param string $link_id The Stud.IP LTI Resource Link ID to append to the URL.
+ *
+ * @param string $course_id An optional Stud.IP course for which to generate
+ * the deep linking return URL.
*
* @return string The URL for returning from an LTI deep linking process.
*/
- public static function getDeepLinkingReturnUrl(string $tool_id = '') : string
+ public static function getDeepLinkingReturnUrl(string $link_id, string $course_id = '') : string
{
- return \URLHelper::getURL('dispatch.php/course/lti/save_link/' . $tool_id, null, true);
+ $params = ['link_id' => $link_id];
+ if ($course_id) {
+ $params['cid'] = $course_id;
+ }
+ return \URLHelper::getURL('dispatch.php/course/lti/save_link/' . $link_id, $params, true);
}
/**
diff --git a/lib/classes/LTI13a/Registration.php b/lib/classes/LTI13a/Registration.php
index c4b898f..5498ace 100644
--- a/lib/classes/LTI13a/Registration.php
+++ b/lib/classes/LTI13a/Registration.php
@@ -2,6 +2,7 @@
namespace Studip\LTI13a;
+use OAT\Library\Lti1p3Core\Exception\LtiException;
use OAT\Library\Lti1p3Core\Registration\RegistrationInterface;
use OAT\Library\Lti1p3Core\Tool\ToolInterface;
use OAT\Library\Lti1p3Core\Platform\PlatformInterface;
@@ -10,7 +11,8 @@ use OAT\Library\Lti1p3Core\Security\Key\KeyChainInterface;
class Registration implements RegistrationInterface
{
public function __construct(
- protected ?\LtiTool $tool
+ protected ?\LtiTool $tool,
+ protected ?\LtiResourceLink $link = null
) {
}
@@ -24,13 +26,27 @@ class Registration implements RegistrationInterface
return $this->tool;
}
+ public function setLtiResourceLink(\LtiResourceLink $link)
+ {
+ $this->link = $link;
+ }
+
+ public function getLtiResourceLink() : ?\LtiResourceLink
+ {
+ return $this->link;
+ }
+
#[\Override]
public function getIdentifier(): string
{
if (!$this->tool) {
return '';
}
- return $this->tool->id;
+ if ($this->link) {
+ return $this->tool->id . '_' . $this->link->id;
+ } else {
+ return $this->tool->id;
+ }
}
#[\Override]
@@ -63,7 +79,11 @@ class Registration implements RegistrationInterface
if (!$this->tool) {
return [];
}
- return \DBManager::get()->fetchFirst("SELECT `id` FROM `lti_deployments` WHERE `tool_id` = ?", [$this->tool->id]);
+ if ($this->link) {
+ return [$this->link->deployment_id];
+ } else {
+ return \DBManager::get()->fetchFirst("SELECT `id` FROM `lti_deployments` WHERE `tool_id` = ?", [$this->tool->id]);
+ }
}
#[\Override]
@@ -72,10 +92,14 @@ class Registration implements RegistrationInterface
if (!$this->tool) {
return false;
}
- return \LtiDeployment::countBySql(
- "`tool_id` = :tool_id AND `id` = :deployment_id",
- ['tool_id' => $this->tool->id, 'deployment_id' => $deploymentId]
- ) > 0;
+ if ($this->link) {
+ return $this->link->deployment_id == $deploymentId;
+ } else {
+ return \LtiDeployment::countBySql(
+ "`tool_id` = :tool_id AND `id` = :deployment_id",
+ ['tool_id' => $this->tool->id, 'deployment_id' => $deploymentId]
+ ) > 0;
+ }
}
#[\Override]
@@ -98,12 +122,13 @@ class Registration implements RegistrationInterface
#[\Override]
public function getToolKeyChain(): ?KeyChainInterface
{
- if (!$this->tool) {
+ if (!$this->tool || $this->tool->jwks_url) {
return null;
}
+
$keyring = $this->tool->getKeyring();
if (!$keyring) {
- $keyring = $this->tool->getKeyring(true);
+ throw new LtiException('Failed to load public key for tool ' . $this->tool->id);
}
return $keyring->toKeyChain();
}
diff --git a/lib/classes/LTI13a/RegistrationManager.php b/lib/classes/LTI13a/RegistrationManager.php
index 922cb58..05bcc2e 100644
--- a/lib/classes/LTI13a/RegistrationManager.php
+++ b/lib/classes/LTI13a/RegistrationManager.php
@@ -7,15 +7,29 @@ use OAT\Library\Lti1p3Core\Registration\RegistrationInterface;
class RegistrationManager implements RegistrationRepositoryInterface
{
+ protected ?\LtiResourceLink $link = null;
+
+ public function setResourceLink(\LtiResourceLink $link)
+ {
+ $this->link = $link;
+ }
+
#[\Override]
public function find(string $identifier): ?RegistrationInterface
{
//The identifier is the ID of a tool.
$tool = \LtiTool::find($identifier);
+ $link = null;
+ if (!$tool) {
+ //Attempt to find the tool and a resource link.
+ $id_parts = explode('_', $identifier);
+ $tool = \LtiTool::find($id_parts[0]);
+ $link = \LtiResourceLink::find($id_parts[1]);
+ }
if (!$tool) {
return null;
}
- return new Registration($tool);
+ return new Registration($tool, $link);
}
/**
@@ -42,7 +56,7 @@ class RegistrationManager implements RegistrationRepositoryInterface
}
$tool = \LtiTool::find($clientId);
if ($tool) {
- return new Registration($tool);
+ return new Registration($tool, $this->link);
}
return null;
}
@@ -51,7 +65,8 @@ class RegistrationManager implements RegistrationRepositoryInterface
public function findByPlatformIssuer(string $issuer, string $clientId = null): ?RegistrationInterface
{
//Only handle requests for registrations of this Stud.IP:
- if ($issuer !== \Config::get()->STUDIP_INSTALLATION_ID) {
+ $platform_config = \Studip\LTI13a\PlatformManager::getPlatformConfiguration();
+ if ($issuer !== $platform_config->getAudience()) {
//Invalid issuer.
return null;
}
diff --git a/lib/models/LtiDeployment.php b/lib/models/LtiDeployment.php
index e59e738..86f18a0 100644
--- a/lib/models/LtiDeployment.php
+++ b/lib/models/LtiDeployment.php
@@ -12,15 +12,10 @@
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
*
* @property int $id database column
- * @property string $title database column
- * @property string $description database column
* @property int $tool_id database column
- * @property string $launch_url database column
* @property int $mkdate database column
* @property int $chdate database column
- * @property JSONArrayObject|null $options database column
* @property SimpleORMapCollection<LtiGrade> $grades has_many LtiGrade
- * @property Course $course belongs_to Course
* @property LtiTool $tool belongs_to LtiTool
*/
@@ -33,8 +28,6 @@ class LtiDeployment extends SimpleORMap
{
$config['db_table'] = 'lti_deployments';
- $config['serialized_fields']['options'] = JSONArrayObject::class;
-
$config['belongs_to']['tool'] = [
'class_name' => LtiTool::class,
'foreign_key' => 'tool_id'
@@ -61,6 +54,8 @@ class LtiDeployment extends SimpleORMap
/**
* Get the launch_url of this entry.
+ *
+ * @deprecated
*/
public function getLaunchURL()
{
@@ -72,6 +67,8 @@ class LtiDeployment extends SimpleORMap
/**
* Get the consumer_key of this entry.
+ *
+ * @deprecated
*/
public function getConsumerKey()
{
@@ -80,6 +77,8 @@ class LtiDeployment extends SimpleORMap
/**
* Get the consumer_secret of this entry.
+ *
+ * @deprecated
*/
public function getConsumerSecret()
{
@@ -88,6 +87,8 @@ class LtiDeployment extends SimpleORMap
/**
* Get the oauth_signature_method of this entry.
+ *
+ * @deprecated
*/
public function getOauthSignatureMethod()
{
@@ -96,6 +97,8 @@ class LtiDeployment extends SimpleORMap
/**
* Get the custom_parameters of this entry.
+ *
+ * @deprecated
*/
public function getCustomParameters()
{
@@ -107,23 +110,6 @@ class LtiDeployment extends SimpleORMap
return $parameters;
}
- public function getCustomLtiParameterArray() : array
- {
- $parameter_str = $this->getCustomParameters();
- if (empty($parameter_str)) {
- return [];
- }
- $parameters = explode("\n", $parameter_str);
- $array = [];
- foreach ($parameters as $parameter) {
- $key_value_parts = explode('=', $parameter, 2);
- if (count($key_value_parts) === 2) {
- $array[trim($key_value_parts[0])] = trim($key_value_parts[1]);
- }
- }
- return ['https://purl.imsglobal.org/spec/lti/claim/custom' => $array];
- }
-
/**
* Get the send_lis_person attribute of this entry.
*/
diff --git a/lib/models/LtiGrade.php b/lib/models/LtiGrade.php
index a7a5dbc..b037f30 100644
--- a/lib/models/LtiGrade.php
+++ b/lib/models/LtiGrade.php
@@ -18,6 +18,9 @@
* @property int $chdate database column
* @property LtiDeployment $link belongs_to LtiDeployment
* @property User $user belongs_to User
+ *
+ * NOTE: LtiGrade is only for the LTI 1.0/1.1 interface.
+ * The LTI 1.3A interface uses the grade book tables for storing grades.
*/
class LtiGrade extends SimpleORMap
diff --git a/lib/models/LtiResourceLink.php b/lib/models/LtiResourceLink.php
index 785c350..0ea15aa 100644
--- a/lib/models/LtiResourceLink.php
+++ b/lib/models/LtiResourceLink.php
@@ -23,7 +23,11 @@ use OAT\Library\Lti1p3Core\Util\Collection\CollectionInterface;
* @property int $id database column
* @property int $deployment_id database column
* @property string $course_id database column
+ * @property string $title database column
+ * @property string $description database column
* @property int $position database column
+ * @property string $launch_url database column
+ * @property JSONArrayObject|null $options database column
* @property int $mkdate database column
* @property int $chdate database column
* @property ?LtiDeployment $deployment related object
@@ -35,6 +39,8 @@ class LtiResourceLink extends \SimpleORMap implements LtiResourceLinkInterface
{
$config['db_table'] = 'lti_resource_links';
+ $config['serialized_fields']['options'] = JSONArrayObject::class;
+
$config['belongs_to']['course'] = [
'class_name' => Course::class,
'foreign_key' => 'course_id'
@@ -89,14 +95,19 @@ class LtiResourceLink extends \SimpleORMap implements LtiResourceLinkInterface
return self::findOneBySQL('course_id = ? AND position = ?', [$course_id, $position]);
}
+ public function getLaunchURL()
+ {
+ if (!empty($this->deployment->tool) && empty($this->deployment->tool->allow_custom_url) && empty($this->deployment->tool->deep_linking) || empty($this->launch_url)) {
+ return $this->deployment->tool->launch_url;
+ }
+ return $this->launch_url;
+ }
+
//OAT library LtiResourceLinkInterface and ResourceInterface implementation:
public function getUrl(): ?string
{
- if ($this->deployment) {
- return $this->deployment->getLaunchURL();
- }
- return null;
+ return $this->getLaunchURL();
}
public function getIcon(): ?array
@@ -154,10 +165,7 @@ class LtiResourceLink extends \SimpleORMap implements LtiResourceLinkInterface
public function getTitle(): ?string
{
- if ($this->deployment) {
- return $this->deployment->title;
- }
- return null;
+ return $this->title ?? $this->deployment->tool->name ?? null;
}
public function getText(): ?string
@@ -184,4 +192,31 @@ class LtiResourceLink extends \SimpleORMap implements LtiResourceLinkInterface
)
);
}
+
+ public function getCustomParameters()
+ {
+ $parameters = '';
+ if (!empty($this->deployment->tool)) {
+ $parameters = $this->deployment->tool->custom_parameters;
+ }
+ $parameters .= $this->options['custom_parameters'] ?? '';
+ return $parameters;
+ }
+
+ public function getCustomLtiParameterArray() : array
+ {
+ $parameter_str = $this->getCustomParameters();
+ if (empty($parameter_str)) {
+ return [];
+ }
+ $parameters = explode("\n", $parameter_str);
+ $array = [];
+ foreach ($parameters as $parameter) {
+ $key_value_parts = explode('=', $parameter, 2);
+ if (count($key_value_parts) === 2) {
+ $array[trim($key_value_parts[0])] = trim($key_value_parts[1]);
+ }
+ }
+ return ['https://purl.imsglobal.org/spec/lti/claim/custom' => $array];
+ }
}