diff --git a/components/ILIAS/LearningSequence/classes/Player/class.ilLSLaunchlinksBuilder.php b/components/ILIAS/LearningSequence/classes/Player/class.ilLSLaunchlinksBuilder.php index 80b65fd879f4..6f694940cf15 100755 --- a/components/ILIAS/LearningSequence/classes/Player/class.ilLSLaunchlinksBuilder.php +++ b/components/ILIAS/LearningSequence/classes/Player/class.ilLSLaunchlinksBuilder.php @@ -25,9 +25,6 @@ */ class ilLSLaunchlinksBuilder { - public const PERM_PARTICIPATE = 'participate'; - public const PERM_UNPARTICIPATE = 'unparticipate'; - public const CMD_STANDARD = ilObjLearningSequenceLearnerGUI::CMD_STANDARD; public const CMD_EXTRO = ilObjLearningSequenceLearnerGUI::CMD_EXTRO; public const CMD_START = ilObjLearningSequenceLearnerGUI::CMD_START; @@ -49,7 +46,7 @@ public function __construct( protected function mayJoin(): bool { - return $this->access->checkAccess(self::PERM_PARTICIPATE, '', $this->lso_ref_id); + return $this->access->checkAccess('read', '', $this->lso_ref_id); } public function currentUserMayUnparticipate(): bool @@ -59,7 +56,7 @@ public function currentUserMayUnparticipate(): bool protected function mayUnparticipate(): bool { - return $this->access->checkAccess(self::PERM_UNPARTICIPATE, '', $this->lso_ref_id); + return $this->isMember() && $this->access->checkAccess('read', '', $this->lso_ref_id); } protected function isMember(): bool diff --git a/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceSetupAgent.php b/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceSetupAgent.php index cdff906c33ce..14821e4d7fd6 100755 --- a/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceSetupAgent.php +++ b/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceSetupAgent.php @@ -69,6 +69,9 @@ public function getUpdateObjective(?Setup\Config $config = null): Setup\Objectiv new ilDatabaseUpdateStepsExecutedObjective( new LSODropActivationDBUpdateSteps() ), + new ilDatabaseUpdateStepsExecutedObjective( + new ilLearningSequenceStreamlinePermissionsDBUpdateSteps() + ), ); } @@ -89,7 +92,8 @@ public function getStatusObjective(Setup\Metrics\Storage $storage): Setup\Object 'Component LearningSequence', true, new ilDatabaseUpdateStepsMetricsCollectedObjective($storage, new ilLearningSequenceRectifyPostConditionsTableDBUpdateSteps()), - new ilDatabaseUpdateStepsMetricsCollectedObjective($storage, new ilLearningSequenceRegisterNotificationType()) + new ilDatabaseUpdateStepsMetricsCollectedObjective($storage, new ilLearningSequenceRegisterNotificationType()), + new ilDatabaseUpdateStepsMetricsCollectedObjective($storage, new ilLearningSequenceStreamlinePermissionsDBUpdateSteps()) ); } diff --git a/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceStreamlinePermissionsDBUpdateSteps.php b/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceStreamlinePermissionsDBUpdateSteps.php new file mode 100644 index 000000000000..d35f72ef0034 --- /dev/null +++ b/components/ILIAS/LearningSequence/classes/Setup/class.ilLearningSequenceStreamlinePermissionsDBUpdateSteps.php @@ -0,0 +1,134 @@ +db = $db; + } + + public function step_1(): void + { + $read_ops_id = $this->getOperationId('read'); + $participate_ops_id = $this->getOperationId('participate'); + $unparticipate_ops_id = $this->getOperationId('unparticipate'); + + if ($read_ops_id === null) { + throw new \RuntimeException('Cannot migrate learning sequence permissions: RBAC operation "read" not found.'); + } + + $old_ops_ids = array_values( + array_filter([ + $participate_ops_id, + $unparticipate_ops_id + ]) + ); + if ($old_ops_ids === []) { + return; + } + + $res = $this->db->query( + "SELECT pa.rol_id, pa.ref_id, pa.ops_id " . + "FROM rbac_pa pa " . + "JOIN object_reference ref ON ref.ref_id = pa.ref_id " . + "JOIN object_data obj ON obj.obj_id = ref.obj_id " . + "WHERE obj.type = '" . self::TYPE_TITLE . "'" + ); + + while ($row = $this->db->fetchAssoc($res)) { + $serialized = $row['ops_id'] ?? null; + if (!is_string($serialized) || $serialized === '') { + continue; + } + + $ops = @unserialize($serialized, ['allowed_classes' => false]); + if (!is_array($ops)) { + continue; + } + + $ops = array_map('intval', $ops); + $has_old = false; + foreach ($old_ops_ids as $old_ops_id) { + if (in_array((int) $old_ops_id, $ops, true)) { + $has_old = true; + break; + } + } + if (!$has_old) { + continue; + } + + $ops[] = $read_ops_id; + $ops = array_values(array_unique(array_diff($ops, array_map('intval', $old_ops_ids)))); + sort($ops); + + $this->db->manipulateF( + 'UPDATE rbac_pa SET ops_id = %s WHERE rol_id = %s AND ref_id = %s', + [\ilDBConstants::T_TEXT, \ilDBConstants::T_INTEGER, \ilDBConstants::T_INTEGER], + [serialize($ops), (int) $row['rol_id'], (int) $row['ref_id']] + ); + } + } + + public function step_2(): void + { + $participate_ops_id = $this->getOperationId('participate'); + $unparticipate_ops_id = $this->getOperationId('unparticipate'); + + if ($participate_ops_id === null && $unparticipate_ops_id === null) { + return; + } + + $ops_ids = array_values(array_filter([(int) $participate_ops_id, (int) $unparticipate_ops_id])); + if ($ops_ids === []) { + return; + } + + $in = implode(',', array_map('intval', $ops_ids)); + $sql = + "DELETE FROM rbac_ta " . + "WHERE typ_id IN (" . + "SELECT obj_id FROM object_data " . + "WHERE type = 'typ' AND title = '" . self::TYPE_TITLE . "'" . + ") " . + "AND ops_id IN (" . $in . ")"; + + $this->db->manipulate($sql); + } + + private function getOperationId(string $operation): ?int + { + $res = $this->db->queryF( + 'SELECT ops_id FROM rbac_operations WHERE operation = %s', + [\ilDBConstants::T_TEXT], + [$operation] + ); + $row = $this->db->fetchAssoc($res); + if (!is_array($row) || !isset($row['ops_id'])) { + return null; + } + return (int) $row['ops_id']; + } +} diff --git a/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceAccess.php b/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceAccess.php index a7597c60a976..3250db2a68fa 100755 --- a/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceAccess.php +++ b/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceAccess.php @@ -48,11 +48,6 @@ public static function _getCommands(): array 'cmd' => ilObjLearningSequenceGUI::CMD_SETTINGS, 'permission' => 'write', 'lang_var' => 'settings' - ], - [ - 'cmd' => ilObjLearningSequenceGUI::CMD_UNPARTICIPATE, - 'permission' => 'unparticipate', - 'lang_var' => 'unparticipate' ] ); } diff --git a/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceGUI.php b/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceGUI.php index d2c237b77efe..e52425a91c18 100755 --- a/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceGUI.php +++ b/components/ILIAS/LearningSequence/classes/class.ilObjLearningSequenceGUI.php @@ -659,9 +659,11 @@ protected function afterSave(ilObject $new_object): void public function unparticipate(): void { - if ($this->checkAccess('unparticipate')) { + if ($this->checkAccess('read')) { $usr_id = $this->user->getId(); - $this->getObject()->getLSRoles()->leave($usr_id); + if ($this->getObject()->getLSRoles()->isMember($usr_id)) { + $this->getObject()->getLSRoles()->leave($usr_id); + } } $this->ctrl->redirectByClass('ilObjLearningSequenceLearnerGUI', self::CMD_LEARNER_VIEW); } @@ -880,6 +882,7 @@ function (array $c, ProfileData $v) use ($a_data, $udfs): array { $field_id = $field->getIdentifier(); $c[$v->getId()]['udf_' . $field_id] = (string) $v->getAdditionalFieldByIdentifier($field_id); } + return $c; }, [] );