aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMoritz Strohm <strohm@data-quest.de>2025-04-24 10:48:24 +0000
committerMoritz Strohm <strohm@data-quest.de>2025-04-24 10:48:24 +0000
commitcd8222ba049eca136bb443410022d99dfbc5d0f2 (patch)
tree99b46288e335427b3f3bde5555b56497a80c1b88 /lib
parent3c783c028c229a3e6561500521800e1fac4383ba (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 'lib')
-rw-r--r--lib/classes/LTI13a/LineItemRepository.php32
-rw-r--r--lib/models/Grading/Definition.php27
-rw-r--r--lib/models/LtiDeployment.php52
-rw-r--r--lib/models/LtiResourceLink.php187
-rw-r--r--lib/models/LtiTool.php2
-rw-r--r--lib/modules/LtiToolModule.php6
6 files changed, 223 insertions, 83 deletions
diff --git a/lib/classes/LTI13a/LineItemRepository.php b/lib/classes/LTI13a/LineItemRepository.php
index add863e..c6c6ef1 100644
--- a/lib/classes/LTI13a/LineItemRepository.php
+++ b/lib/classes/LTI13a/LineItemRepository.php
@@ -110,22 +110,19 @@ class LineItemRepository implements LineItemRepositoryInterface
return $result;
}
- //$resourceLinkIdentifier contains the Stud.IP tool-ID, the deployment-ID and the course-ID,
- //separated by underscores.
- $id_parts = explode('_', $resourceLinkIdentifier);
- if (count($id_parts) !== 3) {
+ //Find the LTI resource link by its ID:
+ $resource_link = \LtiResourceLink::find($resourceLinkIdentifier);
+ if (!$resource_link) {
throw new LTIException('Invalid resource link identifier.');
}
- $tool_id = $id_parts[0];
- $deployment_id = $id_parts[1];
- $course_id = $id_parts[2];
+ $tool_id = $resource_link->deployment->tool_id ?? null;
$sql = '';
$sql_params = [];
- if ($tool_id && $course_id) {
+ if ($tool_id && $resource_link->course_id) {
$sql .= "`tool` = :tool AND `course_id` = :course_id";
- $sql_params['tool'] = self::getGradingToolName($tool_id, $deployment_id);
- $sql_params['course_id'] = $course_id;
+ $sql_params['tool'] = self::getGradingToolName($tool_id, $resource_link->deployment_id);
+ $sql_params['course_id'] = $resource_link->course_id;
} else {
//No tool-ID means no line item collection can be found.
return $result;
@@ -155,18 +152,17 @@ class LineItemRepository implements LineItemRepositoryInterface
*/
public function save(LineItemInterface $lineItem): LineItemInterface
{
- //The resource link identifier contains the Stud.IP tool-ID, deployment-ID and course-ID
- //separated by underscores.
- $studip_ids = explode('_', $lineItem->getResourceLinkIdentifier() ?? '');
- $tool_id = $studip_ids[0];
- $deployment_id = $studip_ids[1];
- $course_id = $studip_ids[2];
+ $resource_link_id = $lineItem->getResourceLinkIdentifier() ?? '';
+ $resource_link = \LtiResourceLink::find($resource_link_id);
+ if (!$resource_link) {
+ throw new LTIException('Invalid resource link identifier.');
+ }
$definition = new Definition();
$definition->id = $lineItem->getIdentifier();
$definition->name = $lineItem->getLabel();
- $definition->course_id = $course_id;
- $definition->tool = sprintf('lti-%s-%s', $tool_id, $deployment_id);
+ $definition->course_id = $resource_link->course_id;
+ $definition->tool = $resource_link->id;
$definition->weight = '1.0';
if ($definition->store()) {
return $definition->toLineItem();
diff --git a/lib/models/Grading/Definition.php b/lib/models/Grading/Definition.php
index 7fc2066..56417b2 100644
--- a/lib/models/Grading/Definition.php
+++ b/lib/models/Grading/Definition.php
@@ -72,21 +72,20 @@ class Definition extends \SimpleORMap
public function toLineItem() : LineItemInterface
{
- //Build the resource link identifier first:
- $studip_ids = explode('-', $this->tool ?? '');
- $tool_id = $studip_ids[1] ?? '';
- $deployment_id = $studip_ids[2] ?? '';
- $resource_link_identifier = sprintf('%s_%s_%s', $tool_id, $deployment_id, $this->course_id);
+ $resource_link_identifier = $this->tool ?? '';
+ $deployment_id = '';
+ if ($resource_link_identifier) {
+ $lti_resource_link = \LtiResourceLink::find($resource_link_identifier);
+ if ($lti_resource_link) {
+ $deployment_id = $lti_resource_link->deployment_id;
+ }
+ }
- $identifier = \URLHelper::getURL(
- 'dispatch.php/lti/ags/line_item',
- [
- 'cid' => $this->course_id,
- 'definition_id' => $this->id,
- 'deployment_id' => $deployment_id,
- 'tool_id' => $tool_id
- ]
- );
+ $identifier = \URLHelper::getURL(sprintf(
+ 'dispatch.php/lti/ags/line_item/%1$s/%2$s',
+ $resource_link_identifier,
+ $this->id
+ ));
return new LineItem(
PHP_FLOAT_MAX,
diff --git a/lib/models/LtiDeployment.php b/lib/models/LtiDeployment.php
index 0b66427..e59e738 100644
--- a/lib/models/LtiDeployment.php
+++ b/lib/models/LtiDeployment.php
@@ -12,8 +12,6 @@
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
*
* @property int $id database column
- * @property int $position database column
- * @property string $course_id database column
* @property string $title database column
* @property string $description database column
* @property int $tool_id database column
@@ -37,64 +35,24 @@ class LtiDeployment extends SimpleORMap
$config['serialized_fields']['options'] = JSONArrayObject::class;
- $config['belongs_to']['course'] = [
- 'class_name' => Course::class,
- 'foreign_key' => 'course_id'
- ];
$config['belongs_to']['tool'] = [
'class_name' => LtiTool::class,
'foreign_key' => 'tool_id'
];
-
+ $config['has_many']['resource_links'] = [
+ 'class_name' => LtiResourceLink::class,
+ 'assoc_foreign_key' => 'deployment_id',
+ 'on_delete' => 'delete'
+ ];
$config['has_many']['grades'] = [
'class_name' => LtiGrade::class,
'assoc_foreign_key' => 'link_id',
'on_delete' => 'delete'
];
- $config['registered_callbacks']['before_create'] = ['cbCalculatePosition'];
-
parent::configure($config);
}
- /**
- * Calculates the position of the new deployment in the course.
- */
- public function cbCalculatePosition() : void
- {
- $this->position = self::countBySql(
- 'JOIN `lti_tools` ON `tool_id` = `lti_tools`.`id`
- WHERE `lti_tools`.`range_id` = :range_id',
- ['range_id' => $this->tool->range_id]
- ) + 1;
- }
-
- /**
- * Find a single entry by course_id and position.
- *
- * @return static|null
- */
- public static function findByCourseAndPosition($course_id, $position)
- {
- return self::findOneBySQL('course_id = ? AND position = ?', [$course_id, $position]);
- }
-
- /**
- * Delete this entity.
- */
- public function delete()
- {
- $db = DBManager::get();
- $course_id = $this->course_id;
- $position = $this->position;
-
- if ($result = parent::delete()) {
- $db->execute('UPDATE `lti_deployments` SET `position` = position - 1 WHERE `course_id` = ? AND `position` > ?', [$course_id, $position]);
- }
-
- return $result;
- }
-
public function getToolLtiVersion() : string
{
return $this->tool->lti_version ?? '';
diff --git a/lib/models/LtiResourceLink.php b/lib/models/LtiResourceLink.php
new file mode 100644
index 0000000..785c350
--- /dev/null
+++ b/lib/models/LtiResourceLink.php
@@ -0,0 +1,187 @@
+<?php
+/*
+ * LtiResourceLink.php
+ * This file is part of Stud.IP.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author Moritz Strohm
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ */
+
+
+use OAT\Library\Lti1p3Core\Resource\LtiResourceLink\LtiResourceLinkInterface;
+use OAT\Library\Lti1p3Core\Util\Collection\Collection;
+use OAT\Library\Lti1p3Core\Util\Collection\CollectionInterface;
+
+/**
+ * The LtiResourceLink class is a model for the lti_resource_links table.
+ *
+ * @property int $id database column
+ * @property int $deployment_id database column
+ * @property string $course_id database column
+ * @property int $position database column
+ * @property int $mkdate database column
+ * @property int $chdate database column
+ * @property ?LtiDeployment $deployment related object
+ * @property ?Course $course related object
+ */
+class LtiResourceLink extends \SimpleORMap implements LtiResourceLinkInterface
+{
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'lti_resource_links';
+
+ $config['belongs_to']['course'] = [
+ 'class_name' => Course::class,
+ 'foreign_key' => 'course_id'
+ ];
+ $config['belongs_to']['deployment'] = [
+ 'class_name' => LtiDeployment::class,
+ 'foreign_key' => 'deployment_id'
+ ];
+
+ $config['registered_callbacks']['before_create'] = ['cbCalculatePosition'];
+
+ parent::configure($config);
+ }
+
+ /**
+ * Calculates the position for a new LTI resource link in the course.
+ */
+ public function cbCalculatePosition() : void
+ {
+ $this->position = self::countByCourse_id($this->course_id);
+ }
+
+ /**
+ * Delete this entity.
+ */
+ public function delete()
+ {
+ $course_id = $this->course_id;
+ $position = $this->position;
+ if ($result = parent::delete()) {
+ DBManager::get()->execute(
+ "UPDATE `lti_resource_links`
+ SET `position` = position - 1
+ WHERE `course_id` = :course_id AND `position` > :position",
+ [
+ 'course_id' => $course_id,
+ 'position' => $position
+ ]
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Find a single entry by course_id and position.
+ *
+ * @return static|null
+ */
+ public static function findByCourseAndPosition($course_id, $position)
+ {
+ return self::findOneBySQL('course_id = ? AND position = ?', [$course_id, $position]);
+ }
+
+ //OAT library LtiResourceLinkInterface and ResourceInterface implementation:
+
+ public function getUrl(): ?string
+ {
+ if ($this->deployment) {
+ return $this->deployment->getLaunchURL();
+ }
+ return null;
+ }
+
+ public function getIcon(): ?array
+ {
+ return null;
+ }
+
+ public function getThumbnail(): ?array
+ {
+ if ($this->course) {
+ return [$this->course->getItemAvatarURL()];
+ }
+ return null;
+ }
+
+ public function getIframe(): ?array
+ {
+ //Not supported.
+ return null;
+ }
+
+ public function getCustom(): ?array
+ {
+ //Not supported.
+ return null;
+ }
+
+ public function getLineItem(): ?array
+ {
+ // TODO: Implement getLineItem() method.
+ return null;
+ }
+
+ public function getAvailability(): ?array
+ {
+ // TODO: Implement getAvailability() method.
+ return null;
+ }
+
+ public function getSubmission(): ?array
+ {
+ // TODO: Implement getSubmission() method.
+ return null;
+ }
+
+ public function getIdentifier(): string
+ {
+ return strval($this->id);
+ }
+
+ public function getType(): string
+ {
+ return 'ltiResourceLink';
+ }
+
+ public function getTitle(): ?string
+ {
+ if ($this->deployment) {
+ return $this->deployment->title;
+ }
+ return null;
+ }
+
+ public function getText(): ?string
+ {
+ return null;
+ }
+
+ public function getProperties(): CollectionInterface
+ {
+ $collection = new Collection();
+ $collection->add([
+ 'url' => $this->getUrl(),
+ 'title' => $this->getTitle()
+ ]);
+ return $collection;
+ }
+
+ public function normalize(): array
+ {
+ return array_filter(
+ array_merge(
+ $this->getProperties()->all(),
+ ['type' => $this->getType()]
+ )
+ );
+ }
+}
diff --git a/lib/models/LtiTool.php b/lib/models/LtiTool.php
index 9839ac0..bde6203 100644
--- a/lib/models/LtiTool.php
+++ b/lib/models/LtiTool.php
@@ -48,7 +48,7 @@ class LtiTool extends SimpleORMap
{
$config['db_table'] = 'lti_tools';
- $config['has_many']['links'] = [
+ $config['has_many']['deployments'] = [ //formerly: links
'class_name' => LtiDeployment::class,
'assoc_foreign_key' => 'tool_id',
'on_delete' => 'delete'
diff --git a/lib/modules/LtiToolModule.php b/lib/modules/LtiToolModule.php
index 2a64e83..9d977be 100644
--- a/lib/modules/LtiToolModule.php
+++ b/lib/modules/LtiToolModule.php
@@ -27,7 +27,7 @@ class LtiToolModule extends CorePlugin implements StudipModule, SystemPlugin, Pr
LtiGrade::deleteBySQL('user_id = ?', [$user->id]);
});
NotificationCenter::on('CourseDidDelete', function ($event, $course) {
- LtiDeployment::deleteBySQL('course_id = ?', [$course->id]);
+ \LtiResourceLink::deleteBySQL('course_id = ?', [$course->id]);
});
}
@@ -40,7 +40,7 @@ class LtiToolModule extends CorePlugin implements StudipModule, SystemPlugin, Pr
return null;
}
- $changed = LtiDeployment::countBySQL('course_id = ? AND chdate > ?', [$course_id, $last_visit]);
+ $changed = \LtiResourceLink::countBySQL('course_id = ? AND chdate > ?', [$course_id, $last_visit]);
$icon = Icon::create('plugin', $changed ? Icon::ROLE_NEW : Icon::ROLE_CLICKABLE);
@@ -60,7 +60,7 @@ class LtiToolModule extends CorePlugin implements StudipModule, SystemPlugin, Pr
return [];
}
- $grades = LtiDeployment::countBySQL('course_id = ?', [$course_id]);
+ $grades = \LtiResourceLink::countBySQL('course_id = ?', [$course_id]);
$navigation = new Navigation(_('LTI-Tools'));
$navigation->setImage(Icon::create('link-extern', Icon::ROLE_INFO_ALT));