From a43c2749ba273f59f7f30d1cb5ecb083ecdcce4d Mon Sep 17 00:00:00 2001 From: Rob Falkenstein Date: Mon, 9 Feb 2026 16:00:20 +0100 Subject: [PATCH 001/141] added fwolf-ilias as authority for WAC and ActiveRecord and removed Context from unmaintaned components --- docs/development/maintenance.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/development/maintenance.md b/docs/development/maintenance.md index b0cddc26a8c3..0e2cbdb11d84 100755 --- a/docs/development/maintenance.md +++ b/docs/development/maintenance.md @@ -176,12 +176,12 @@ of ILIAS. The file contains the following fields: [//]: # (BEGIN ActiveRecord) * **ActiveRecord** - * Authority to Sign off on Conceptual Changes: MISSING - * Authority to Sign off on Code Changes: MISSING + * Authority to Sign off on Conceptual Changes: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) + * Authority to Sign off on Code Changes: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) * Authority to Curate Test Cases: MISSING - * Authority to (De-)Assign Authorities: MISSING - * Assignee for Issues: MISSING - * Assignee for Security Reports: MISSING + * Authority to (De-)Assign Authorities: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) + * Assignee for Issues: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) + * Assignee for Security Reports: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) * Unit-specific Guidelines, Rules, and Regulations: [LINK MISSING]('') [//]: # (END ActiveRecord) @@ -1558,12 +1558,12 @@ of ILIAS. The file contains the following fields: [//]: # (BEGIN WebAccessChecker) * **Web Access Checker** - * Authority to Sign off on Conceptual Changes: MISSING - * Authority to Sign off on Code Changes: [ukohnle](https://docu.ilias.de/go/usr/21855) + * Authority to Sign off on Conceptual Changes: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) + * Authority to Sign off on Code Changes: [fwolf-ilias](https://docu.ilias.de/go/usr/29018), [ukohnle](https://docu.ilias.de/go/usr/21855) * Authority to Curate Test Cases: [AUTHOR MISSING](https://docu.ilias.de/go/pg/64423_4793) - * Authority to (De-)Assign Authorities: MISSING - * Assignee for Issues: MISSING - * Assignee for Security Reports: MISSING + * Authority to (De-)Assign Authorities: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) + * Assignee for Issues: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) + * Assignee for Security Reports: [fwolf-ilias](https://docu.ilias.de/go/usr/29018) * Unit-specific Guidelines, Rules, and Regulations: [LINK MISSING]('') [//]: # (END WebAccessChecker) @@ -1650,7 +1650,6 @@ of ILIAS. The file contains the following fields: The following directories are currently unmaintained: -* ILIAS/Context * ILIAS/CSV * ILIAS/EventHandling From a0a524563f73741f9a72f135a810363c5a34ace2 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 14:45:58 +0100 Subject: [PATCH 002/141] 45544: No questions after import on same ILIAS installation --- components/ILIAS/COPage/PC/Question/QuestionManager.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/ILIAS/COPage/PC/Question/QuestionManager.php b/components/ILIAS/COPage/PC/Question/QuestionManager.php index e0fc3829caa7..10f4206b9c29 100755 --- a/components/ILIAS/COPage/PC/Question/QuestionManager.php +++ b/components/ILIAS/COPage/PC/Question/QuestionManager.php @@ -47,7 +47,11 @@ public function resolveQuestionReferences( foreach ($nodes as $node) { $qref = $node->getAttribute("QRef"); if (isset($a_mapping[$qref])) { - $node->setAttribute("QRef", "il__qst_" . $a_mapping[$qref]["pool"]); + $new_id = (int) ($a_mapping[$qref]["pool"] ?? 0); + if ($new_id === 0 && isset($a_mapping[$qref]["test"])) { // changed with 10 + $new_id = $a_mapping[$qref]["test"]; + } + $node->setAttribute("QRef", "il__qst_" . $new_id); $updated = true; } } From b363faba208c66251f784426f471d03dfb9eb150 Mon Sep 17 00:00:00 2001 From: mjansen Date: Wed, 11 Feb 2026 09:44:04 +0100 Subject: [PATCH 003/141] SAML: Add activation check in authentication process (cherry picked from commit bd4a80d8afa7672151194c6a5f48b0ab27482bf5) --- .../ILIAS/Saml/classes/class.ilAuthProviderSaml.php | 11 +++++++++++ lang/ilias_de.lang | 1 + lang/ilias_en.lang | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/components/ILIAS/Saml/classes/class.ilAuthProviderSaml.php b/components/ILIAS/Saml/classes/class.ilAuthProviderSaml.php index 7ad59282fe7e..81afe48f9496 100755 --- a/components/ILIAS/Saml/classes/class.ilAuthProviderSaml.php +++ b/components/ILIAS/Saml/classes/class.ilAuthProviderSaml.php @@ -25,6 +25,7 @@ class ilAuthProviderSaml extends ilAuthProvider implements ilAuthProviderAccount private const string LOG_COMPONENT = 'auth'; private const string ERR_WRONG_LOGIN = 'err_wrong_login'; + private const string ERR_PROVIDER_INACTIVE = 'auth_saml_idp_deactivated_auth_failed'; private const string SESSION_TMP_ATTRIBUTES = 'tmp_attributes'; private const string SESSION_TMP_RETURN_TO = 'tmp_return_to'; @@ -83,6 +84,16 @@ private function determineUidFromAttributes(): void public function doAuthentication(ilAuthStatus $status): bool { + if (!$this->idp->isActive()) { + $this->getLogger()->info( + 'SAML IdP with id {idp_id} is not active.', + ['idp_id' => $this->idp->getIdpId()] + ); + $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED); + $status->setTranslatedReason($this->lng->txt(self::ERR_PROVIDER_INACTIVE)); + return false; + } + if ([] === $this->attributes) { $this->getLogger()->warning('Could not parse any attributes from SAML response.'); $this->handleAuthenticationFail($status, self::ERR_WRONG_LOGIN); diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 3c0dda007fce..fa19408b418b 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -2125,6 +2125,7 @@ auth#:#auth_saml_deleted_idp#:#Der Identity-Provider wurde gelöscht. auth#:#auth_saml_enable#:#SAML-Unterstützung aktivieren auth#:#auth_saml_err_sqlite_driver#:#Die SAML-Authentifizierung benötigt die SQLite-Treiber für PHP. Bitte installieren Sie diese und versuchen Sie es erneut. auth#:#auth_saml_idp#:#IDP +auth#:#auth_saml_idp_deactivated_auth_failed#:#Der IDP ist nicht aktiviert, Authentifizierung gescheitert. auth#:#auth_saml_idp_selection_table_desc#:#Bitte wählen Sie den Identity-Provider aus, mit dem Sie sich einloggen möchten. auth#:#auth_saml_idp_selection_table_title#:#Identity-Provider auth#:#auth_saml_idp_settings#:#IDP Einstellungen diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 38f11d985a92..1e4458283d30 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -2125,7 +2125,8 @@ auth#:#auth_saml_deleted_idp#:#The Identity Provider has been deleted. auth#:#auth_saml_enable#:#Enable SAML Support auth#:#auth_saml_err_sqlite_driver#:#The SAML authentication requires the SQLite driver for PHP. Please install the SQLite package and try again. auth#:#auth_saml_idp#:#IDP -auth#:#auth_saml_idp_selection_table_desc#:#Bitte wählen Sie den Identity Provider mit dem Sie sich anmelden möchten. +auth#:#auth_saml_idp_deactivated_auth_failed#:#Authentication failed, the identity provider is disabled. +auth#:#auth_saml_idp_selection_table_desc#:#Please select the identity provider you want to log in with. auth#:#auth_saml_idp_selection_table_title#:#Identity Provider Selection auth#:#auth_saml_idp_settings#:#IDP Settings auth#:#auth_saml_idps#:#SAML IDP List From 9201b23d16c7b97972ea09d566522268cc2b5618 Mon Sep 17 00:00:00 2001 From: mjansen Date: Wed, 11 Feb 2026 09:17:59 +0100 Subject: [PATCH 004/141] OIDC: Add activation check in authentication process --- .../class.ilAuthProviderOpenIdConnect.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/components/ILIAS/OpenIdConnect/classes/class.ilAuthProviderOpenIdConnect.php b/components/ILIAS/OpenIdConnect/classes/class.ilAuthProviderOpenIdConnect.php index 8aba3b2b070d..59400714c05f 100755 --- a/components/ILIAS/OpenIdConnect/classes/class.ilAuthProviderOpenIdConnect.php +++ b/components/ILIAS/OpenIdConnect/classes/class.ilAuthProviderOpenIdConnect.php @@ -24,6 +24,9 @@ class ilAuthProviderOpenIdConnect extends ilAuthProvider { private const OIDC_AUTH_IDTOKEN = 'oidc_auth_idtoken'; + private const ERR_AUTH_FAILED = 'auth_oidc_failed'; + private const ERR_AUTH_WRONG_LOGIN = 'err_wrong_login'; + private readonly ilOpenIdConnectSettings $settings; /** @var array $body */ private readonly ilLogger $logger; @@ -65,6 +68,13 @@ public function handleLogout(): void public function doAuthentication(ilAuthStatus $status): bool { + if (!$this->settings->getActive()) { + $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED); + $status->setTranslatedReason($this->lng->txt(self::ERR_AUTH_FAILED)); + $this->logger->info('Authentication aborted, OIDC authentication is disabled'); + return false; + } + try { $oidc = $this->initClient(); $oidc->setRedirectURL(ILIAS_HTTP_PATH . '/openidconnect.php'); @@ -107,7 +117,7 @@ public function doAuthentication(ilAuthStatus $status): bool $this->logger->warning($e->getMessage()); $this->logger->warning((string) $e->getCode()); $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED); - $status->setTranslatedReason($this->lng->txt('auth_oidc_failed')); + $status->setTranslatedReason($this->lng->txt(self::ERR_AUTH_FAILED)); return false; } } @@ -121,7 +131,7 @@ private function handleUpdate(ilAuthStatus $status, $user_info): ilAuthStatus $this->logger->error('Received invalid user credentials: '); $this->logger->dump($user_info, ilLogLevel::ERROR); $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED); - $status->setReason('err_wrong_login'); + $status->setReason(self::ERR_AUTH_WRONG_LOGIN); return $status; } @@ -132,7 +142,7 @@ private function handleUpdate(ilAuthStatus $status, $user_info): ilAuthStatus $this->logger->error('Could not determine valid external account, value is empty or not a string.'); $this->logger->dump($user_info, ilLogLevel::ERROR); $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED); - $status->setReason('err_wrong_login'); + $status->setReason(self::ERR_AUTH_WRONG_LOGIN); return $status; } @@ -156,7 +166,7 @@ private function handleUpdate(ilAuthStatus $status, $user_info): ilAuthStatus //$_GET['target'] = $this->getCredentials()->getRedirectionTarget();// TODO PHP8-REVIEW Please eliminate this. Mutating the request is not allowed and will not work in ILIAS 8. } catch (ilOpenIdConnectSyncForbiddenException) { $status->setStatus(ilAuthStatus::STATUS_AUTHENTICATION_FAILED); - $status->setReason('err_wrong_login'); + $status->setReason(self::ERR_AUTH_WRONG_LOGIN); } return $status; From 636b5355e746a8dd007bce024c0c24796cfe3308 Mon Sep 17 00:00:00 2001 From: Matthias Kunkel Date: Wed, 11 Feb 2026 15:43:04 +0100 Subject: [PATCH 005/141] =?UTF-8?q?Typo=20Benachrichtigungs-Einstellungen?= =?UTF-8?q?=20=E2=86=92=20Benachrichtigungseinstellungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit a0d7c9a79174a4753ac1f2e9328d8f18bc71a222) --- lang/ilias_de.lang | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index fa19408b418b..5ca54927432f 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -3027,9 +3027,9 @@ chatroom#:#messages#:#Nachrichten chatroom#:#no_further_users#:#Keine weiteren Benutzer anwesend. chatroom#:#no_messages#:#Es sind keine Nachrichten für den ausgewählten Zeitraum verfügbar. chatroom#:#no_username_given#:#Bitte wählen Sie einen Anmeldenamen! -chatroom#:#osc_browser_noti_no_permission_error#:#Bitte entfernen Sie die ILIAS-Domain von der Liste der blockierten Websites in den Benachrichtigungs-Einstellungen Ihres Browsers oder Betriebssystems. Andernfalls kann ILIAS keine Browser-Benachrichtigungen zustellen. +chatroom#:#osc_browser_noti_no_permission_error#:#Bitte entfernen Sie die ILIAS-Domain von der Liste der blockierten Websites in den Benachrichtigungseinstellungen Ihres Browsers oder Betriebssystems. Andernfalls kann ILIAS keine Browser-Benachrichtigungen zustellen. chatroom#:#osc_browser_noti_no_support_error#:#Browser-Benachrichtigungen werden mit Ihrem aktuellem Browser nicht unterstützt. Bitte stellen Sie sicher, dass Sie via HTTPS auf ILIAS zugreifen, und Ihr Browser als unterstützter Browser aufgelistet wird. -chatroom#:#osc_browser_noti_req_permission_error#:#Die Browser-Benachrichtigungen konnten nicht aktiviert werden, da Sie den Zugriff in Ihrem Browser verweigert haben. Bitte entfernen Sie die ILIAS-Domain von der Liste der blockierten Websites in den Benachrichtigungs-Einstellungen Ihres Browsers oder Betriebssystems. +chatroom#:#osc_browser_noti_req_permission_error#:#Die Browser-Benachrichtigungen konnten nicht aktiviert werden, da Sie den Zugriff in Ihrem Browser verweigert haben. Bitte entfernen Sie die ILIAS-Domain von der Liste der blockierten Websites in den Benachrichtigungseinstellungen Ihres Browsers oder Betriebssystems. chatroom#:#osc_enable_browser_notifications_info#:#Wenn aktiviert, erhalten Sie beim Eintreffen neuer Chats oder Chat-Nachrichten eine Browser-Benachrichtigung, wenn zwischen zwei Chat-Beiträgen mehr als %s Minute(n) vergangen ist/sind. Befindet sich ILIAS im Hintergrund und arbeiten Sie in anderen Browser-Tabs, oder ist der Browser ausgeblendet, erhalten Sie ebenfalls eine Benachrichtigung. chatroom#:#osc_enable_browser_notifications_label#:#Browser-Benachrichtigungen chatroom#:#osc_noti_title#:#Neue Chat-Nachricht @@ -10154,7 +10154,7 @@ forum#:#new_post#:#Neuer Beitrag forum#:#new_thread_with_post#:#Neues Thema mit Beitrag forum#:#no_forum_selected#:#Kein Forum für die Verschiebeaktion ausgewählt! forum#:#not_allowed_to_merge_into_another_forum#:#Es ist nicht möglich Themen verschiedener Foren zusammenzuführen. -forum#:#notification_settings#:#Benachrichtigungs-Einstellungen +forum#:#notification_settings#:#Benachrichtigungseinstellungen forum#:#notify_censored#:#Zensierte Beiträge forum#:#notify_modified#:#Geänderte Beiträge forum#:#notify_post_deleted#:#Gelöschte Beiträge From b9a0ab7fcc7f8671fcee0c0bad557f2034cfcb1c Mon Sep 17 00:00:00 2001 From: Aleksandr Litvin Date: Wed, 11 Feb 2026 16:14:31 +0000 Subject: [PATCH 006/141] fix no role bug 0047137 (#11108) * fix no role bug 0047137 * remove comment --- .../Membership/classes/class.ilMembershipGUI.php | 12 ++++++++---- lang/ilias_de.lang | 1 + lang/ilias_en.lang | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/components/ILIAS/Membership/classes/class.ilMembershipGUI.php b/components/ILIAS/Membership/classes/class.ilMembershipGUI.php index 4d0b6609128a..e4022452aa3a 100755 --- a/components/ILIAS/Membership/classes/class.ilMembershipGUI.php +++ b/components/ILIAS/Membership/classes/class.ilMembershipGUI.php @@ -61,6 +61,7 @@ public function __construct(ilObjectGUI $repository_gui, ilObject $repository_ob $this->tpl = $DIC->ui()->mainTemplate(); $this->ctrl = $DIC->ctrl(); $this->lng->loadLanguageModule('trac'); + $this->lng->loadLanguageModule('mmbr'); $this->logger = $DIC->logger()->mmbr(); $this->access = $DIC->access(); $this->user = $DIC->user(); @@ -575,8 +576,13 @@ public function updateParticipants(): void $post_roles[$usr_id][] = $adminRoleId; } + if (!isset($post_roles[$usr_id]) || empty($post_roles[$usr_id])) { + $this->tpl->setOnScreenMessage('failure', $this->lng->txt('mmbr_role_error'), true); + $this->ctrl->redirect($this, 'participants'); + } + // Validate the role ids in the post data - foreach ((array) $post_roles[$usr_id] as $role_id) { + foreach ((array) ($post_roles[$usr_id] ?? []) as $role_id) { if (!array_key_exists($role_id, $assignableLocalRoles)) { $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_perm_perm'), true); $this->ctrl->redirect($this, 'participants'); @@ -618,7 +624,7 @@ public function updateParticipants(): void } foreach ($participants as $usr_id) { - $this->getMembersObject()->updateRoleAssignments($usr_id, (array) $post_roles[$usr_id]); + $this->getMembersObject()->updateRoleAssignments($usr_id, (array) ($post_roles[$usr_id] ?? [])); // Disable notification for all of them $this->getMembersObject()->updateNotification($usr_id, false); @@ -1186,7 +1192,6 @@ public function confirmRefuseSubscribers(): void $this->tpl->setOnScreenMessage('failure', $this->lng->txt("crs_no_subscribers_selected"), true); $this->ctrl->redirect($this, 'participants'); } - $this->lng->loadLanguageModule('mmbr'); $c_gui = new ilConfirmationGUI(); // set confirm/cancel commands $c_gui->setFormAction($this->ctrl->getFormAction($this, "refuseSubscribers")); @@ -1419,7 +1424,6 @@ public function confirmRefuseFromList(): void $this->tpl->setOnScreenMessage('failure', $this->lng->txt("no_checkbox"), true); $this->ctrl->redirect($this, 'participants'); } - $this->lng->loadLanguageModule('mmbr'); $c_gui = new ilConfirmationGUI(); // set confirm/cancel commands diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 5ca54927432f..588f6cedd46d 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -12806,6 +12806,7 @@ mmbr#:#mmbr_awrn_my_groups_courses_info#:#Alle Mitglieder von Gruppen und Kursen mmbr#:#mmbr_btn_mail_selected_users#:#Mail verschicken mmbr#:#mmbr_info_delete_sure_unsubscribe#:#Wollen Sie wirklich die Mitgliedschaft für die folgenden Kurse und Gruppen beenden? mmbr#:#mmbr_memberships#:#Mitgliedschaften +mmbr#:#mmbr_role_error#:#Mitglieder müssen mindestens eine Rolle haben. mmbr#:#mmbr_selected_users#:#Ausgewählte Teilnehmer mmbr#:#mmbr_unsubscribed_from_objs#:#Sie wurden von den ausgewählten Objekten abgemeldet. mme#:#add_languages#:#Sprache hinzufügen diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 1e4458283d30..06c2a6135ee2 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -12779,6 +12779,7 @@ mmbr#:#mmbr_awrn_my_groups_courses_info#:#All members of groups or courses of th mmbr#:#mmbr_btn_mail_selected_users#:#Send Mail mmbr#:#mmbr_info_delete_sure_unsubscribe#:#Are you sure you want to unsubscribe from the following objects? mmbr#:#mmbr_memberships#:#Memberships +mmbr#:#mmbr_role_error#:#Members must have at least one role assigned. mmbr#:#mmbr_selected_users#:#Selected Participants mmbr#:#mmbr_unsubscribed_from_objs#:#You have been unsubscribed from the selected objects. mme#:#add_languages#:#Add Language From 9dead2b1d54b6f5d103178d5fa6e00c6346d63a6 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 17:19:39 +0100 Subject: [PATCH 007/141] =?UTF-8?q?44522:=20Die=20gew=C3=A4hlte=20Option?= =?UTF-8?q?=20springt=20auf=20die=20erste=20Stelle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ILIAS/Repository/Service/Form/class.FormAdapterGUI.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/ILIAS/Repository/Service/Form/class.FormAdapterGUI.php b/components/ILIAS/Repository/Service/Form/class.FormAdapterGUI.php index 8a4d133db6b6..3715802c998f 100755 --- a/components/ILIAS/Repository/Service/Form/class.FormAdapterGUI.php +++ b/components/ILIAS/Repository/Service/Form/class.FormAdapterGUI.php @@ -410,10 +410,6 @@ public function radio( ): self { $this->values[$key] = $value; $field = $this->ui->factory()->input()->field()->radio($title, $description); - if (!is_null($value)) { - $field = $field->withOption($value, ""); // dummy to prevent exception, will be overwritten by radioOption - $field = $field->withValue($value); - } $this->addField( $key, $field @@ -425,6 +421,9 @@ public function radioOption(string $value, string $title, string $description = { if ($field = $this->getLastField()) { $field = $field->withOption($value, $title, $description); + if (($this->values[$this->last_key] ?? null) === $value) { + $field = $field->withValue($value); + } $this->replaceLastField($field); } return $this; From 38922adcd2d7f87804a92c9aad8d3c62f808f302 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 17:41:08 +0100 Subject: [PATCH 008/141] =?UTF-8?q?43942:=20Failed=20test:=20Read=20only?= =?UTF-8?q?=20f=C3=BCr=20Administration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ILIAS/Help/Administration/class.ilHelpModuleTableGUI.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/ILIAS/Help/Administration/class.ilHelpModuleTableGUI.php b/components/ILIAS/Help/Administration/class.ilHelpModuleTableGUI.php index e2fab9b5f5aa..7a0f4bc57477 100755 --- a/components/ILIAS/Help/Administration/class.ilHelpModuleTableGUI.php +++ b/components/ILIAS/Help/Administration/class.ilHelpModuleTableGUI.php @@ -60,9 +60,8 @@ public function __construct( $this->setFormAction($ilCtrl->getFormAction($a_parent_obj)); $this->setRowTemplate("tpl.help_module_row.html", "components/ILIAS/Help/Administration"); - $this->addCommandButton("saveOrdering", $lng->txt("sorting_save")); - if ($this->has_write_permission) { + $this->addCommandButton("saveOrdering", $lng->txt("sorting_save")); $this->addMultiCommand("confirmHelpModulesDeletion", $lng->txt("delete")); } } From 6d84ad35ee6e41a30ef3b6441419bf3141e2e27d Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 18:18:15 +0100 Subject: [PATCH 009/141] 45296: Missing lang var #exc_received_peer_feedback# --- .../ILIAS/Exercise/PeerReview/class.ilExPeerReviewGUI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ILIAS/Exercise/PeerReview/class.ilExPeerReviewGUI.php b/components/ILIAS/Exercise/PeerReview/class.ilExPeerReviewGUI.php index bc902b78aba9..5bd5d2eaef2b 100755 --- a/components/ILIAS/Exercise/PeerReview/class.ilExPeerReviewGUI.php +++ b/components/ILIAS/Exercise/PeerReview/class.ilExPeerReviewGUI.php @@ -378,7 +378,7 @@ public function buildSubmissionPropertiesAndActions(\ILIAS\Exercise\Assignment\P else { $builder->addProperty( $builder::SEC_PEER_FEEDBACK, - $lng->txt("exc_received_peer_feedback"), + $lng->txt("exc_received_feedback"), $lng->txt("exc_peer_review_show_received_none") ); } From 3afa6bd0e1c8d5cc847625d08769bcc5bd62a772 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 18:33:37 +0100 Subject: [PATCH 010/141] 44236: Exercise - Download Selected Submissions --- .../ILIAS/Exercise/Submission/SubmissionManager.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/ILIAS/Exercise/Submission/SubmissionManager.php b/components/ILIAS/Exercise/Submission/SubmissionManager.php index 9c02439198bb..d17a454deb79 100644 --- a/components/ILIAS/Exercise/Submission/SubmissionManager.php +++ b/components/ILIAS/Exercise/Submission/SubmissionManager.php @@ -620,10 +620,12 @@ protected function copySubmissionFilesToDir( $dir = $to_path . DIRECTORY_SEPARATOR . $targetdir; \ilFileUtils::makeDirParents($dir); $file = $dir . DIRECTORY_SEPARATOR . $targetfile; - file_put_contents( - $file, - $stream->getContents() - ); + if (!is_null($stream)) { + file_put_contents( + $file, + $stream->getContents() + ); + } // unzip blog/portfolio From 579a87b1688939a301044c0e2be44f7bb5732fd2 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 18:43:25 +0100 Subject: [PATCH 011/141] 33630: Duplicate 'variable_ID' in wsp#:#search_no_match#:#Your search did not match any results. --- .../PersonalWorkspace/classes/class.ilSingleUserShareGUI.php | 2 +- lang/ilias_de.lang | 2 +- lang/ilias_en.lang | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/ILIAS/PersonalWorkspace/classes/class.ilSingleUserShareGUI.php b/components/ILIAS/PersonalWorkspace/classes/class.ilSingleUserShareGUI.php index 8d989cb5d2e6..fc7e87106443 100755 --- a/components/ILIAS/PersonalWorkspace/classes/class.ilSingleUserShareGUI.php +++ b/components/ILIAS/PersonalWorkspace/classes/class.ilSingleUserShareGUI.php @@ -99,7 +99,7 @@ protected function saveShare(): void } $this->ctrl->returnToParent($this); } else { - $this->tpl->setOnScreenMessage('failure', $this->lng->txt('search_no_match'), true); + $this->tpl->setOnScreenMessage('failure', $this->lng->txt('wsp_search_no_match'), true); } } $this->ctrl->redirect($this); diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 588f6cedd46d..27e2b514f71f 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -17993,7 +17993,6 @@ wopi#:#wopi_url_byline#:#Vollständige URL des WOPI-Discovery, diese muss über wsp#:#element_already_shared#:#Die Ressource ist für diese Person bereits freigegeben. wsp#:#element_shared#:#Die Ressource wurde freigegeben. wsp#:#error_creating_certificate_pdf#:#Das Zertifikat konnte nicht erstellt werden. Bitte kontaktieren Sie Ihre technische Betreuung und bitten um die Prüfung des Zertifikatsservers. -wsp#:#search_no_match#:#Ihre Suche ergab keine Treffer. wsp#:#share#:#Freigeben wsp#:#share_content#:#Ressource freigeben wsp#:#share_with#:#Anmeldename @@ -18015,6 +18014,7 @@ wsp#:#wsp_permission_registered_info#:#Dieses Objekt ist für alle registrierten wsp#:#wsp_permission_removed#:#Der Eintrag wurde gelöscht. wsp#:#wsp_permissions#:#Freigabe wsp#:#wsp_personal_resources_description#:#Hier können Sie persönliche Dateien, Blogs und Nachweise verwalten. +wsp#:#wsp_search_no_match#:#Ihre Suche ergab keine Treffer. wsp#:#wsp_send_mail#:#Mail senden wsp#:#wsp_set_permission_all#:#Internet/WWW wsp#:#wsp_set_permission_all_password#:#Internet/WWW mit Passwort diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 06c2a6135ee2..1617f7f9bbce 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -17963,7 +17963,6 @@ wopi#:#wopi_url_byline#:#Complete URL of the WOPI Discovery, which must be acces wsp#:#element_already_shared#:#This object is already shared with this user. wsp#:#element_shared#:#Object shared. wsp#:#error_creating_certificate_pdf#:#The certificate could not be created. Please contact the administrator to check the certificate server. -wsp#:#search_no_match#:#Your search did not match any results. wsp#:#share#:#Share wsp#:#share_content#:#Share Object wsp#:#share_with#:#Username @@ -17985,6 +17984,7 @@ wsp#:#wsp_permission_registered_info#:#This object is shared with all registered wsp#:#wsp_permission_removed#:#Entry has been removed. wsp#:#wsp_permissions#:#Share wsp#:#wsp_personal_resources_description#:#Here you can manage your private files, blogs and artifacts. +wsp#:#wsp_search_no_match#:#Your search did not match any results. wsp#:#wsp_send_mail#:#Send Mail wsp#:#wsp_set_permission_all#:#World Wide Web wsp#:#wsp_set_permission_all_password#:#World Wide Web with Password From 14cd7b5f1d00e36d9bfdf8d07df438d6397facfe Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Wed, 11 Feb 2026 21:12:42 +0100 Subject: [PATCH 012/141] =?UTF-8?q?44687:=20Metadaten=20doppelt=20im=20Akt?= =?UTF-8?q?ionenmen=C3=BC=20im=20Kontextmen=C3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Page/class.PageQueryActionHandler.php | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/components/ILIAS/COPage/Page/class.PageQueryActionHandler.php b/components/ILIAS/COPage/Page/class.PageQueryActionHandler.php index 4e29040eea21..e75693a289df 100755 --- a/components/ILIAS/COPage/Page/class.PageQueryActionHandler.php +++ b/components/ILIAS/COPage/Page/class.PageQueryActionHandler.php @@ -370,39 +370,30 @@ public function getActionsDropDown(): \ILIAS\UI\Component\Dropdown\Standard $ctrl->getLinkTargetByClass([get_class($this->page_gui), "ilnewsitemgui"], "editNews") ); } + } + if ($this->page_gui->use_meta_data) { if (($md_link = $this->page_gui->getMetaDataLink()) !== "") { $items[] = $ui->factory()->link()->standard( $lng->txt("meta_data"), $md_link ); - } - } - - if ($this->page_gui->use_meta_data) { - $mdgui = new \ilObjectMetaDataGUI( - $this->page_gui->meta_data_rep_obj, - $this->page_gui->meta_data_type, - $this->page_gui->meta_data_sub_obj_id - ); - $mdtab = $mdgui->getTab(); - if ($mdtab) { - $items[] = $ui->factory()->link()->standard( - $lng->txt("meta_data"), - $mdtab + } else { + $mdgui = new \ilObjectMetaDataGUI( + $this->page_gui->meta_data_rep_obj, + $this->page_gui->meta_data_type, + $this->page_gui->meta_data_sub_obj_id ); + $mdtab = $mdgui->getTab(); + if ($mdtab) { + $items[] = $ui->factory()->link()->standard( + $lng->txt("meta_data"), + $mdtab + ); + } } } - - if ($this->page_gui->getEnabledNews()) { - $items[] = $ui->factory()->link()->standard( - $lng->txt("news"), - $ctrl->getLinkTargetByClass([get_class($this->page_gui), \ilNewsItemGUI::class], "editNews") - ); - } - - // additional page actions foreach ($this->page_gui->getAdditionalPageActions() as $item) { $items[] = $item; From 48f269301ef6c476931861223dcacde45cc78753 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Thu, 12 Feb 2026 08:15:12 +0100 Subject: [PATCH 013/141] tax: fixed taxonomy creation in categories --- .../ILIAS/Repository/classes/class.ilRepositoryGUI.php | 5 +---- .../GlobalScreen/classes/class.ilTaxonomyGSToolProvider.php | 4 +++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/components/ILIAS/Repository/classes/class.ilRepositoryGUI.php b/components/ILIAS/Repository/classes/class.ilRepositoryGUI.php index 8fdc66ff78c8..b69c69f693a1 100755 --- a/components/ILIAS/Repository/classes/class.ilRepositoryGUI.php +++ b/components/ILIAS/Repository/classes/class.ilRepositoryGUI.php @@ -134,14 +134,13 @@ public function executeCommand(): void ) { $this->ctrl->redirectToURL('./login.php?cmd=force_login'); } - $this->tool_context->claim()->repository(); // check creation mode // determined by "new_type" parameter $new_type = $this->request->getNewType(); - if ($new_type !== "" && $new_type !== "sty") { + if ($new_type !== "" && $new_type !== "sty" && $new_type !== "tax") { $this->creation_mode = true; $ilHelp->setScreenIdComponent($new_type); $ilHelp->setDefaultScreenId(ilHelpGUI::ID_PART_SCREEN, "create"); @@ -194,7 +193,6 @@ public function executeCommand(): void if ($cmd === "showRepTree") { $next_class = ""; } - switch ($next_class) { // forward asynchronous file uploads to the upload handler. // possible via dropzones in list guis or global template @@ -223,7 +221,6 @@ public function executeCommand(): void } $this->gui_obj->setCreationMode($this->creation_mode); $this->ctrl->setReturn($this, "return"); - $this->show(); } else { // $cmd = (string) $this->ctrl->getCmd(""); diff --git a/components/ILIAS/Taxonomy/GlobalScreen/classes/class.ilTaxonomyGSToolProvider.php b/components/ILIAS/Taxonomy/GlobalScreen/classes/class.ilTaxonomyGSToolProvider.php index 2cd06d51fded..e6bc46238de0 100755 --- a/components/ILIAS/Taxonomy/GlobalScreen/classes/class.ilTaxonomyGSToolProvider.php +++ b/components/ILIAS/Taxonomy/GlobalScreen/classes/class.ilTaxonomyGSToolProvider.php @@ -91,7 +91,9 @@ private function getEditTree(array $gui_path, int $tax_id, string $cmd, string $ $gui = $this->dic->taxonomy()->internal()->gui(); $params = $gui->http()->request()->getQueryParams(); $current_tax_node = (int) ($params["tax_node"] ?? null); - $tax_exp->setPathOpen($current_tax_node); + if ($current_tax_node > 0) { + $tax_exp->setPathOpen($current_tax_node); + } return $tax_exp->getHTML(); From ae5684986b493e30e28aab916894da021f47f671 Mon Sep 17 00:00:00 2001 From: Stephan Kergomard Date: Thu, 12 Feb 2026 09:57:06 +0100 Subject: [PATCH 014/141] User: Fix Duplicate Entries in Search See: https://mantis.ilias.de/view.php?id=47191 --- .../User/src/Search/class.EndpointGUI.php | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/components/ILIAS/User/src/Search/class.EndpointGUI.php b/components/ILIAS/User/src/Search/class.EndpointGUI.php index 64eea85b7c1a..e61ef4adb64e 100644 --- a/components/ILIAS/User/src/Search/class.EndpointGUI.php +++ b/components/ILIAS/User/src/Search/class.EndpointGUI.php @@ -100,18 +100,25 @@ private function buildResponse(): string ]) ); - $response = array_map( - static fn(AutocompleteItem $v) => $v->getTagArray(), - array_merge( - $this->endpoint_configurator->getAdditionalAnswerElements( - $this->current_user, - $autocomplete_query + $response = array_values( + array_reduce( + array_merge( + $this->endpoint_configurator->getAdditionalAnswerElements( + $this->current_user, + $autocomplete_query + ), + $this->profile_data_repository->searchUsers( + $this->settings_data_repository, + $this->field_configuration_repository, + $autocomplete_query + ) ), - $this->profile_data_repository->searchUsers( - $this->settings_data_repository, - $this->field_configuration_repository, - $autocomplete_query - ) + static function (array $c, AutocompleteItem $v): array { + $tag_array = $v->getTagArray(); + $c[$tag_array['display']] = $tag_array; + return $c; + }, + [] ) ); From 5768fd691b15e22c25a7cc48ea06756910282c1a Mon Sep 17 00:00:00 2001 From: Tim Schmitz Date: Thu, 12 Feb 2026 10:06:11 +0100 Subject: [PATCH 015/141] Poll: fix links written to navigation history (46936) --- components/ILIAS/Poll/classes/class.ilObjPollGUI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ILIAS/Poll/classes/class.ilObjPollGUI.php b/components/ILIAS/Poll/classes/class.ilObjPollGUI.php index 183c52b993c0..486177e82fef 100755 --- a/components/ILIAS/Poll/classes/class.ilObjPollGUI.php +++ b/components/ILIAS/Poll/classes/class.ilObjPollGUI.php @@ -274,7 +274,7 @@ public function executeCommand(): void // add entry to navigation history if (!$this->getCreationMode() && $this->getAccessHandler()->checkAccess("read", "", $this->node_id)) { - $link = $this->ctrl->getLinkTargetByClass("ilrepositorygui", "frameset"); + $link = $this->ctrl->getLinkTargetByClass("ilrepositorygui"); $this->nav_history->addItem($this->node_id, $link, "poll"); } From ac1c5f7415ff9e6442ccb4e916e50b7c6c9fa3d8 Mon Sep 17 00:00:00 2001 From: Marvin Beym <79150442+mBeym@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:09:20 +0100 Subject: [PATCH 016/141] ilSessionStatistics prepared statement & Generator (#10972) * Use PHP "Generators" * Use "Prepared Statements" --- .../classes/class.ilSessionStatistics.php | 139 ++++++++++++------ 1 file changed, 96 insertions(+), 43 deletions(-) diff --git a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php index 8ed679c53efe..87ef2573771e 100755 --- a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php +++ b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php @@ -22,6 +22,10 @@ class ilSessionStatistics { private const int SLOT_SIZE = 15; + protected static ?ilDBStatement $number_of_active_raw_sessions_statement = null; + protected static ?ilDBStatement $aggregated_raw_data_statement = null; + protected static ?ilDBStatement $raw_data_statement = null; + /** * Is session statistics active at all? */ @@ -171,35 +175,26 @@ protected static function getNumberOfActiveRawSessions(int $a_time): int $ilDB = $DIC['ilDB']; - $sql = 'SELECT COUNT(*) counter FROM usr_session_stats_raw' . - ' WHERE (end_time IS NULL OR end_time >= ' . $ilDB->quote($a_time, 'integer') . ')' . - ' AND start_time <= ' . $ilDB->quote($a_time, 'integer') . - ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, 'integer'); - $res = $ilDB->query($sql); - $row = $ilDB->fetchAssoc($res); + $statement = self::getNumberOfActiveRawSessionsPreparedStatement(); + $row = $DIC->database()->fetchAssoc($DIC->database()->execute($statement, [$a_time, $a_time])); return (int) $row['counter']; } /** * Read raw data for timespan + * @return Generator> */ - protected static function getRawData(int $a_begin, int $a_end): array + protected static function getRawData(int $a_begin, int $a_end): Generator { global $DIC; - $ilDB = $DIC['ilDB']; - - $sql = 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . - ' WHERE start_time <= ' . $ilDB->quote($a_end, 'integer') . - ' AND (end_time IS NULL OR end_time >= ' . $ilDB->quote($a_begin, 'integer') . ')' . - ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, 'integer') . - ' ORDER BY start_time'; - $res = $ilDB->query($sql); - $all = []; - while ($row = $ilDB->fetchAssoc($res)) { - $all[] = $row; + $res = $DIC->database()->execute( + self::getRawDataPreparedStatement(), + [$a_end, $a_begin] + ); + while ($row = $DIC->database()->fetchAssoc($res)) { + yield $row; } - return $all; } /** @@ -256,15 +251,76 @@ public static function aggretateRaw(int $a_now): void self::deleteAggregatedRaw($a_now); } - /** - * Aggregate statistics data for one slot - * - */ - public static function aggregateRawHelper(int $a_begin, int $a_end): void + protected static function getNumberOfActiveRawSessionsPreparedStatement(): ilDbStatement { - global $DIC; + if (self::$number_of_active_raw_sessions_statement === null) { + global $DIC; + self::$number_of_active_raw_sessions_statement = $DIC->database()->prepare( + 'SELECT COUNT(*) counter FROM usr_session_stats_raw ' + . 'WHERE (end_time IS NULL OR end_time >= ?) ' + . 'AND start_time <= ? ' + . 'AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, 'integer'), + [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] + ); + } - $ilDB = $DIC['ilDB']; + return self::$number_of_active_raw_sessions_statement; + } + + protected static function getAggregatedRawDataPreparedStatement(): ilDBStatement + { + if (!self::$aggregated_raw_data_statement) { + global $DIC; + self::$aggregated_raw_data_statement = $DIC->database()->prepareManip( + 'UPDATE usr_session_stats ' + . 'SET active_min = ?, ' + . 'active_max = ?, ' + . 'active_avg = ?, ' + . 'active_end = ?, ' + . 'opened = ?, ' + . 'closed_manual = ?, ' + . 'closed_expire = ?, ' + . 'closed_login = ?, ' + . 'closed_misc = ? ' + . 'WHERE slot_begin = ? AND slot_end = ?', + [ + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER + ] + ); + } + + return self::$aggregated_raw_data_statement; + } + + protected static function getRawDataPreparedStatement(): ilDBStatement + { + if (!self::$raw_data_statement) { + global $DIC; + self::$raw_data_statement = $DIC->database()->prepare( + 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . + ' WHERE start_time <= %s' . + ' AND (end_time IS NULL OR end_time >= %s)' . + ' AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, ilDBConstants::T_INTEGER) . + ' ORDER BY start_time', + [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] + ); + } + return self::$raw_data_statement; + } + + public static function aggregateRawHelper(int $a_begin, int $a_end) + { + global $DIC; // "relevant" closing types $separate_closed = [ @@ -355,24 +411,21 @@ public static function aggregateRawHelper(int $a_begin, int $a_end): void } unset($events); - // save aggregated data - $fields = [ - 'active_min' => ['integer', $active_min], - 'active_max' => ['integer', $active_max], - 'active_avg' => ['integer', $active_avg], - 'active_end' => ['integer', $active_end], - 'opened' => ['integer', $opened_counter], - 'closed_manual' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_USER] ?? 0)], - 'closed_expire' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_EXPIRE] ?? 0)], - 'closed_login' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0)], - 'closed_misc' => ['integer', (int) ($closed_counter[0] ?? 0)], - ]; - $ilDB->update( - 'usr_session_stats', - $fields, + + $DIC->database()->execute( + self::getAggregatedRawDataPreparedStatement(), [ - 'slot_begin' => ['integer', $a_begin], - 'slot_end' => ['integer', $a_end] + 'active_min' => $active_min, + 'active_max' => $active_max, + 'active_avg' => $active_avg, + 'active_end' => $active_end, + 'opened' => $opened_counter, + 'closed_manual' => (int) ($closed_counter[ilSession::SESSION_CLOSE_USER] ?? 0), + 'closed_expire' => (int) ($closed_counter[ilSession::SESSION_CLOSE_EXPIRE] ?? 0), + 'closed_login' => (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0), + 'closed_misc' => (int) ($closed_counter[0] ?? 0), + 'slot_begin' => $a_begin, + 'slot_end' => $a_end, ] ); } From 6bd783dac9a2b369400f2c5377726f4c585183fb Mon Sep 17 00:00:00 2001 From: Stephan Kergomard Date: Thu, 12 Feb 2026 12:01:34 +0100 Subject: [PATCH 017/141] Revert "ilSessionStatistics prepared statement & Generator (#10972)" This reverts commit ac1c5f7415ff9e6442ccb4e916e50b7c6c9fa3d8. --- .../classes/class.ilSessionStatistics.php | 139 ++++++------------ 1 file changed, 43 insertions(+), 96 deletions(-) diff --git a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php index 87ef2573771e..8ed679c53efe 100755 --- a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php +++ b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php @@ -22,10 +22,6 @@ class ilSessionStatistics { private const int SLOT_SIZE = 15; - protected static ?ilDBStatement $number_of_active_raw_sessions_statement = null; - protected static ?ilDBStatement $aggregated_raw_data_statement = null; - protected static ?ilDBStatement $raw_data_statement = null; - /** * Is session statistics active at all? */ @@ -175,26 +171,35 @@ protected static function getNumberOfActiveRawSessions(int $a_time): int $ilDB = $DIC['ilDB']; - $statement = self::getNumberOfActiveRawSessionsPreparedStatement(); - $row = $DIC->database()->fetchAssoc($DIC->database()->execute($statement, [$a_time, $a_time])); + $sql = 'SELECT COUNT(*) counter FROM usr_session_stats_raw' . + ' WHERE (end_time IS NULL OR end_time >= ' . $ilDB->quote($a_time, 'integer') . ')' . + ' AND start_time <= ' . $ilDB->quote($a_time, 'integer') . + ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, 'integer'); + $res = $ilDB->query($sql); + $row = $ilDB->fetchAssoc($res); return (int) $row['counter']; } /** * Read raw data for timespan - * @return Generator> */ - protected static function getRawData(int $a_begin, int $a_end): Generator + protected static function getRawData(int $a_begin, int $a_end): array { global $DIC; - $res = $DIC->database()->execute( - self::getRawDataPreparedStatement(), - [$a_end, $a_begin] - ); - while ($row = $DIC->database()->fetchAssoc($res)) { - yield $row; + $ilDB = $DIC['ilDB']; + + $sql = 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . + ' WHERE start_time <= ' . $ilDB->quote($a_end, 'integer') . + ' AND (end_time IS NULL OR end_time >= ' . $ilDB->quote($a_begin, 'integer') . ')' . + ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, 'integer') . + ' ORDER BY start_time'; + $res = $ilDB->query($sql); + $all = []; + while ($row = $ilDB->fetchAssoc($res)) { + $all[] = $row; } + return $all; } /** @@ -251,77 +256,16 @@ public static function aggretateRaw(int $a_now): void self::deleteAggregatedRaw($a_now); } - protected static function getNumberOfActiveRawSessionsPreparedStatement(): ilDbStatement - { - if (self::$number_of_active_raw_sessions_statement === null) { - global $DIC; - self::$number_of_active_raw_sessions_statement = $DIC->database()->prepare( - 'SELECT COUNT(*) counter FROM usr_session_stats_raw ' - . 'WHERE (end_time IS NULL OR end_time >= ?) ' - . 'AND start_time <= ? ' - . 'AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, 'integer'), - [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] - ); - } - - return self::$number_of_active_raw_sessions_statement; - } - - protected static function getAggregatedRawDataPreparedStatement(): ilDBStatement - { - if (!self::$aggregated_raw_data_statement) { - global $DIC; - self::$aggregated_raw_data_statement = $DIC->database()->prepareManip( - 'UPDATE usr_session_stats ' - . 'SET active_min = ?, ' - . 'active_max = ?, ' - . 'active_avg = ?, ' - . 'active_end = ?, ' - . 'opened = ?, ' - . 'closed_manual = ?, ' - . 'closed_expire = ?, ' - . 'closed_login = ?, ' - . 'closed_misc = ? ' - . 'WHERE slot_begin = ? AND slot_end = ?', - [ - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER, - ilDBConstants::T_INTEGER - ] - ); - } - - return self::$aggregated_raw_data_statement; - } - - protected static function getRawDataPreparedStatement(): ilDBStatement - { - if (!self::$raw_data_statement) { - global $DIC; - self::$raw_data_statement = $DIC->database()->prepare( - 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . - ' WHERE start_time <= %s' . - ' AND (end_time IS NULL OR end_time >= %s)' . - ' AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, ilDBConstants::T_INTEGER) . - ' ORDER BY start_time', - [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] - ); - } - return self::$raw_data_statement; - } - - public static function aggregateRawHelper(int $a_begin, int $a_end) + /** + * Aggregate statistics data for one slot + * + */ + public static function aggregateRawHelper(int $a_begin, int $a_end): void { global $DIC; + $ilDB = $DIC['ilDB']; + // "relevant" closing types $separate_closed = [ ilSession::SESSION_CLOSE_USER, @@ -411,21 +355,24 @@ public static function aggregateRawHelper(int $a_begin, int $a_end) } unset($events); - - $DIC->database()->execute( - self::getAggregatedRawDataPreparedStatement(), + // save aggregated data + $fields = [ + 'active_min' => ['integer', $active_min], + 'active_max' => ['integer', $active_max], + 'active_avg' => ['integer', $active_avg], + 'active_end' => ['integer', $active_end], + 'opened' => ['integer', $opened_counter], + 'closed_manual' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_USER] ?? 0)], + 'closed_expire' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_EXPIRE] ?? 0)], + 'closed_login' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0)], + 'closed_misc' => ['integer', (int) ($closed_counter[0] ?? 0)], + ]; + $ilDB->update( + 'usr_session_stats', + $fields, [ - 'active_min' => $active_min, - 'active_max' => $active_max, - 'active_avg' => $active_avg, - 'active_end' => $active_end, - 'opened' => $opened_counter, - 'closed_manual' => (int) ($closed_counter[ilSession::SESSION_CLOSE_USER] ?? 0), - 'closed_expire' => (int) ($closed_counter[ilSession::SESSION_CLOSE_EXPIRE] ?? 0), - 'closed_login' => (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0), - 'closed_misc' => (int) ($closed_counter[0] ?? 0), - 'slot_begin' => $a_begin, - 'slot_end' => $a_end, + 'slot_begin' => ['integer', $a_begin], + 'slot_end' => ['integer', $a_end] ] ); } From b8f2499fdec54745467e978d0ff1a39f36c0fdd5 Mon Sep 17 00:00:00 2001 From: Stephan Kergomard Date: Thu, 12 Feb 2026 11:51:10 +0100 Subject: [PATCH 018/141] Object/User: Fix Lookup of Name of Owner See: https://mantis.ilias.de/view.php?id=47238 --- components/ILIAS/ILIASObject/classes/class.ilObject.php | 2 +- components/ILIAS/User/src/Profile/DatabaseDataRepository.php | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/ILIAS/ILIASObject/classes/class.ilObject.php b/components/ILIAS/ILIASObject/classes/class.ilObject.php index 0c77757f1da0..45825a269908 100755 --- a/components/ILIAS/ILIASObject/classes/class.ilObject.php +++ b/components/ILIAS/ILIASObject/classes/class.ilObject.php @@ -503,7 +503,7 @@ final public static function _lookupOwnerName(int $owner_id): string $owner = null; if ($owner_id != -1) { - if (ilObject::_exists($owner_id)) { + if (ilObjUser::userExists([$owner_id])) { $owner = new ilObjUser($owner_id); } } diff --git a/components/ILIAS/User/src/Profile/DatabaseDataRepository.php b/components/ILIAS/User/src/Profile/DatabaseDataRepository.php index 1f4c9dd0cfbe..1a36f3d79079 100644 --- a/components/ILIAS/User/src/Profile/DatabaseDataRepository.php +++ b/components/ILIAS/User/src/Profile/DatabaseDataRepository.php @@ -69,7 +69,9 @@ public function getSingle(int $id): Data $base_data = $this->db->fetchObject($base_query); if ($base_data === null) { - return $this->getDefault()->withId($id); + throw \InvalidArgumentException( + 'This user does not exist.' + ); } return $this->buildFromData( From 1ac6595ad8e13e0ab9e7187f444f95b921aab4a2 Mon Sep 17 00:00:00 2001 From: Stefan Meyer Date: Thu, 12 Feb 2026 12:02:45 +0100 Subject: [PATCH 019/141] Merge pull request #11069 from leifos-gmbh/11_mantis_ilias_47072 DidacticTemplate: fix incorrect icon displaying --- ...s.ilDidacticTemplateSettingsTableDataRetrieval.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsTableDataRetrieval.php b/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsTableDataRetrieval.php index 4d8055fe8bcb..0fe63f943b97 100755 --- a/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsTableDataRetrieval.php +++ b/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsTableDataRetrieval.php @@ -129,12 +129,11 @@ protected function getRecords(Order $order, Range $range): array foreach ($tpl->getAssignments() as $obj_type) { $icon_label = $this->lng->txt('objs_' . $obj_type); } - if ($icon_path) { - $icon = $this->ui_factory->symbol()->icon()->custom( - $icon_path, - $icon_label - ); - } + + $icon = ($icon_path !== "") ? $this->ui_factory->symbol()->icon()->custom( + $icon_path, + $icon_label + ) : null; $icon_active = $this->ui_factory->symbol()->icon()->custom( $tpl->isEnabled() ? From 67b1ad11e03090939a0a11e4aa11b4383152b2a2 Mon Sep 17 00:00:00 2001 From: Stefan Meyer Date: Thu, 12 Feb 2026 12:07:03 +0100 Subject: [PATCH 020/141] Merge pull request #11068 from leifos-gmbh/11_mantis_ilias_47075 DidacticTemplate: fix edit-import --- .../classes/Setting/class.ilDidacticTemplateSettingsGUI.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsGUI.php b/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsGUI.php index c02dc74d5109..b3eee10d3251 100755 --- a/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsGUI.php +++ b/components/ILIAS/DidacticTemplate/classes/Setting/class.ilDidacticTemplateSettingsGUI.php @@ -792,9 +792,9 @@ public function editImportForm(): ilPropertyFormGUI public function editImport(ilDidacticTemplateSetting $a_settings): void { - ilDidacticTemplateObjSettings::transferAutoGenerateStatus($a_settings->getId(), $a_settings->getId()); - $assignments = ilDidacticTemplateObjSettings::getAssignmentsByTemplateID($a_settings->getId()); - $a_settings->delete(); + ilDidacticTemplateObjSettings::transferAutoGenerateStatus($this->setting->getId(), $a_settings->getId()); + $assignments = ilDidacticTemplateObjSettings::getAssignmentsByTemplateID($this->setting->getId()); + $this->setting->delete(); foreach ($assignments as $obj) { ilDidacticTemplateObjSettings::assignTemplate($obj["ref_id"], $obj["obj_id"], $a_settings->getId()); } From 94dadf59bbc8e0ff32673eaa96017b023d8cb82b Mon Sep 17 00:00:00 2001 From: Marvin Beym <79150442+mBeym@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:09:20 +0100 Subject: [PATCH 021/141] ilSessionStatistics prepared statement & Generator (#10972) * Use PHP "Generators" * Use "Prepared Statements" --- .../classes/class.ilSessionStatistics.php | 139 ++++++++++++------ 1 file changed, 96 insertions(+), 43 deletions(-) diff --git a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php index 8ed679c53efe..87ef2573771e 100755 --- a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php +++ b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php @@ -22,6 +22,10 @@ class ilSessionStatistics { private const int SLOT_SIZE = 15; + protected static ?ilDBStatement $number_of_active_raw_sessions_statement = null; + protected static ?ilDBStatement $aggregated_raw_data_statement = null; + protected static ?ilDBStatement $raw_data_statement = null; + /** * Is session statistics active at all? */ @@ -171,35 +175,26 @@ protected static function getNumberOfActiveRawSessions(int $a_time): int $ilDB = $DIC['ilDB']; - $sql = 'SELECT COUNT(*) counter FROM usr_session_stats_raw' . - ' WHERE (end_time IS NULL OR end_time >= ' . $ilDB->quote($a_time, 'integer') . ')' . - ' AND start_time <= ' . $ilDB->quote($a_time, 'integer') . - ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, 'integer'); - $res = $ilDB->query($sql); - $row = $ilDB->fetchAssoc($res); + $statement = self::getNumberOfActiveRawSessionsPreparedStatement(); + $row = $DIC->database()->fetchAssoc($DIC->database()->execute($statement, [$a_time, $a_time])); return (int) $row['counter']; } /** * Read raw data for timespan + * @return Generator> */ - protected static function getRawData(int $a_begin, int $a_end): array + protected static function getRawData(int $a_begin, int $a_end): Generator { global $DIC; - $ilDB = $DIC['ilDB']; - - $sql = 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . - ' WHERE start_time <= ' . $ilDB->quote($a_end, 'integer') . - ' AND (end_time IS NULL OR end_time >= ' . $ilDB->quote($a_begin, 'integer') . ')' . - ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, 'integer') . - ' ORDER BY start_time'; - $res = $ilDB->query($sql); - $all = []; - while ($row = $ilDB->fetchAssoc($res)) { - $all[] = $row; + $res = $DIC->database()->execute( + self::getRawDataPreparedStatement(), + [$a_end, $a_begin] + ); + while ($row = $DIC->database()->fetchAssoc($res)) { + yield $row; } - return $all; } /** @@ -256,15 +251,76 @@ public static function aggretateRaw(int $a_now): void self::deleteAggregatedRaw($a_now); } - /** - * Aggregate statistics data for one slot - * - */ - public static function aggregateRawHelper(int $a_begin, int $a_end): void + protected static function getNumberOfActiveRawSessionsPreparedStatement(): ilDbStatement { - global $DIC; + if (self::$number_of_active_raw_sessions_statement === null) { + global $DIC; + self::$number_of_active_raw_sessions_statement = $DIC->database()->prepare( + 'SELECT COUNT(*) counter FROM usr_session_stats_raw ' + . 'WHERE (end_time IS NULL OR end_time >= ?) ' + . 'AND start_time <= ? ' + . 'AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, 'integer'), + [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] + ); + } - $ilDB = $DIC['ilDB']; + return self::$number_of_active_raw_sessions_statement; + } + + protected static function getAggregatedRawDataPreparedStatement(): ilDBStatement + { + if (!self::$aggregated_raw_data_statement) { + global $DIC; + self::$aggregated_raw_data_statement = $DIC->database()->prepareManip( + 'UPDATE usr_session_stats ' + . 'SET active_min = ?, ' + . 'active_max = ?, ' + . 'active_avg = ?, ' + . 'active_end = ?, ' + . 'opened = ?, ' + . 'closed_manual = ?, ' + . 'closed_expire = ?, ' + . 'closed_login = ?, ' + . 'closed_misc = ? ' + . 'WHERE slot_begin = ? AND slot_end = ?', + [ + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER, + ilDBConstants::T_INTEGER + ] + ); + } + + return self::$aggregated_raw_data_statement; + } + + protected static function getRawDataPreparedStatement(): ilDBStatement + { + if (!self::$raw_data_statement) { + global $DIC; + self::$raw_data_statement = $DIC->database()->prepare( + 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . + ' WHERE start_time <= %s' . + ' AND (end_time IS NULL OR end_time >= %s)' . + ' AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, ilDBConstants::T_INTEGER) . + ' ORDER BY start_time', + [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] + ); + } + return self::$raw_data_statement; + } + + public static function aggregateRawHelper(int $a_begin, int $a_end) + { + global $DIC; // "relevant" closing types $separate_closed = [ @@ -355,24 +411,21 @@ public static function aggregateRawHelper(int $a_begin, int $a_end): void } unset($events); - // save aggregated data - $fields = [ - 'active_min' => ['integer', $active_min], - 'active_max' => ['integer', $active_max], - 'active_avg' => ['integer', $active_avg], - 'active_end' => ['integer', $active_end], - 'opened' => ['integer', $opened_counter], - 'closed_manual' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_USER] ?? 0)], - 'closed_expire' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_EXPIRE] ?? 0)], - 'closed_login' => ['integer', (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0)], - 'closed_misc' => ['integer', (int) ($closed_counter[0] ?? 0)], - ]; - $ilDB->update( - 'usr_session_stats', - $fields, + + $DIC->database()->execute( + self::getAggregatedRawDataPreparedStatement(), [ - 'slot_begin' => ['integer', $a_begin], - 'slot_end' => ['integer', $a_end] + 'active_min' => $active_min, + 'active_max' => $active_max, + 'active_avg' => $active_avg, + 'active_end' => $active_end, + 'opened' => $opened_counter, + 'closed_manual' => (int) ($closed_counter[ilSession::SESSION_CLOSE_USER] ?? 0), + 'closed_expire' => (int) ($closed_counter[ilSession::SESSION_CLOSE_EXPIRE] ?? 0), + 'closed_login' => (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0), + 'closed_misc' => (int) ($closed_counter[0] ?? 0), + 'slot_begin' => $a_begin, + 'slot_end' => $a_end, ] ); } From e9eb66b9350f68ef3c4820bd93f526d5710265cd Mon Sep 17 00:00:00 2001 From: mjansen Date: Thu, 12 Feb 2026 12:06:01 +0100 Subject: [PATCH 022/141] [Feature] Authentication: Add missing types / Simplify code / Privatize / Fix typos --- ...class.ilAuthDestroyExpiredSessionsCron.php | 2 +- .../classes/class.ilSession.php | 2 +- .../classes/class.ilSessionStatistics.php | 187 ++++++++++-------- .../classes/class.ilSessionStatisticsGUI.php | 2 +- 4 files changed, 103 insertions(+), 90 deletions(-) diff --git a/components/ILIAS/Authentication/classes/Cron/class.ilAuthDestroyExpiredSessionsCron.php b/components/ILIAS/Authentication/classes/Cron/class.ilAuthDestroyExpiredSessionsCron.php index 20e7b429c4d0..7f38fe762170 100755 --- a/components/ILIAS/Authentication/classes/Cron/class.ilAuthDestroyExpiredSessionsCron.php +++ b/components/ILIAS/Authentication/classes/Cron/class.ilAuthDestroyExpiredSessionsCron.php @@ -87,7 +87,7 @@ public function run(): JobResult $result->setStatus(JobResult::STATUS_OK); $num_destroyed_sessions = ilSession::_destroyExpiredSessions(); - ilSessionStatistics::aggretateRaw(time()); + ilSessionStatistics::aggregateRaw(time()); $result->setMessage('Number of destroyed sessions: ' . $num_destroyed_sessions); return $result; diff --git a/components/ILIAS/Authentication/classes/class.ilSession.php b/components/ILIAS/Authentication/classes/class.ilSession.php index 2279aef3d375..cf9cef39f946 100755 --- a/components/ILIAS/Authentication/classes/class.ilSession.php +++ b/components/ILIAS/Authentication/classes/class.ilSession.php @@ -195,7 +195,7 @@ public static function _writeData(string $a_session_id, string $a_data): bool if ($r->getInt(0, 50) === 2) { // get time _before_ destroying expired sessions self::_destroyExpiredSessions(); - ilSessionStatistics::aggretateRaw($now); + ilSessionStatistics::aggregateRaw($now); } } diff --git a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php index 87ef2573771e..9c0b97ede4c3 100755 --- a/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php +++ b/components/ILIAS/Authentication/classes/class.ilSessionStatistics.php @@ -22,29 +22,25 @@ class ilSessionStatistics { private const int SLOT_SIZE = 15; - protected static ?ilDBStatement $number_of_active_raw_sessions_statement = null; - protected static ?ilDBStatement $aggregated_raw_data_statement = null; - protected static ?ilDBStatement $raw_data_statement = null; + private static ?ilDBStatement $number_of_active_raw_sessions_statement = null; + private static ?ilDBStatement $aggregated_raw_data_statement = null; + private static ?ilDBStatement $raw_data_statement = null; - /** - * Is session statistics active at all? - */ public static function isActive(): bool { global $DIC; + /** @var ilSetting $ilSetting */ $ilSetting = $DIC['ilSetting']; return (bool) $ilSetting->get('session_statistics', '1'); } - /** - * Create raw data entry - */ public static function createRawEntry(string $a_session_id, int $a_session_type, int $a_timestamp, int $a_user_id): void { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; if (!$a_user_id || !$a_session_id || !self::isActive()) { @@ -53,31 +49,28 @@ public static function createRawEntry(string $a_session_id, int $a_session_type, // #9669: if a session was destroyed and somehow the session id is still // in use there will be a id-collision for the raw-entry - $ilDB->replace( 'usr_session_stats_raw', [ - 'session_id' => ['text', $a_session_id] + 'session_id' => [ilDBConstants::T_TEXT, $a_session_id] ], [ - 'type' => ['integer', $a_session_type], - 'start_time' => ['integer', $a_timestamp], - 'user_id' => ['integer', $a_user_id] + 'type' => [ilDBConstants::T_INTEGER, $a_session_type], + 'start_time' => [ilDBConstants::T_INTEGER, $a_timestamp], + 'user_id' => [ilDBConstants::T_INTEGER, $a_user_id] ] ); } /** - * Close raw data entry - * - * @param int|array $a_session_id - * @param int $a_context + * @param string|list $a_session_id * @param int|bool $a_expired_at */ public static function closeRawEntry($a_session_id, ?int $a_context = null, $a_expired_at = null): void { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; if (!self::isActive()) { @@ -92,22 +85,22 @@ public static function closeRawEntry($a_session_id, ?int $a_context = null, $a_e $end_time = time(); } $sql = 'UPDATE usr_session_stats_raw' . - ' SET end_time = ' . $ilDB->quote($end_time, 'integer'); + ' SET end_time = ' . $ilDB->quote($end_time, ilDBConstants::T_INTEGER); if ($a_context) { - $sql .= ',end_context = ' . $ilDB->quote($a_context, 'integer'); + $sql .= ', end_context = ' . $ilDB->quote($a_context, ilDBConstants::T_INTEGER); } - $sql .= ' WHERE session_id = ' . $ilDB->quote($a_session_id, 'text') . + $sql .= ' WHERE session_id = ' . $ilDB->quote($a_session_id, ilDBConstants::T_TEXT) . ' AND end_time IS NULL'; $ilDB->manipulate($sql); } // batch closing elseif (!$a_expired_at) { $sql = 'UPDATE usr_session_stats_raw' . - ' SET end_time = ' . $ilDB->quote(time(), 'integer'); + ' SET end_time = ' . $ilDB->quote(time(), ilDBConstants::T_INTEGER); if ($a_context) { - $sql .= ',end_context = ' . $ilDB->quote($a_context, 'integer'); + $sql .= ', end_context = ' . $ilDB->quote($a_context, ilDBConstants::T_INTEGER); } - $sql .= ' WHERE ' . $ilDB->in('session_id', $a_session_id, false, 'text') . + $sql .= ' WHERE ' . $ilDB->in('session_id', $a_session_id, false, ilDBConstants::T_TEXT) . ' AND end_time IS NULL'; $ilDB->manipulate($sql); } @@ -115,11 +108,11 @@ public static function closeRawEntry($a_session_id, ?int $a_context = null, $a_e else { foreach ($a_session_id as $id => $ts) { $sql = 'UPDATE usr_session_stats_raw' . - ' SET end_time = ' . $ilDB->quote($ts, 'integer'); + ' SET end_time = ' . $ilDB->quote($ts, ilDBConstants::T_INTEGER); if ($a_context) { - $sql .= ',end_context = ' . $ilDB->quote($a_context, 'integer'); + $sql .= ', end_context = ' . $ilDB->quote($a_context, ilDBConstants::T_INTEGER); } - $sql .= ' WHERE session_id = ' . $ilDB->quote($id, 'text') . + $sql .= ' WHERE session_id = ' . $ilDB->quote($id, ilDBConstants::T_TEXT) . ' AND end_time IS NULL'; $ilDB->manipulate($sql); } @@ -128,18 +121,17 @@ public static function closeRawEntry($a_session_id, ?int $a_context = null, $a_e /** * Get next slot to aggregate - * - * @return array begin, end + * @return array{0: int, 1: int}|null */ - protected static function getCurrentSlot(int $a_now): ?array + private static function getCurrentSlot(int $a_now): ?array { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; // get latest slot in db - $sql = 'SELECT MAX(slot_end) previous_slot_end' . - ' FROM usr_session_stats'; + $sql = 'SELECT MAX(slot_end) previous_slot_end FROM usr_session_stats'; $res = $ilDB->query($sql); $row = $ilDB->fetchAssoc($res); $previous_slot_end = $row['previous_slot_end']; @@ -166,46 +158,53 @@ protected static function getCurrentSlot(int $a_now): ?array if ($current_slot_end < $a_now) { return [$current_slot_begin, $current_slot_end]; } + return null; } - protected static function getNumberOfActiveRawSessions(int $a_time): int + private static function getNumberOfActiveRawSessions(int $a_time): int { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; - $statement = self::getNumberOfActiveRawSessionsPreparedStatement(); - $row = $DIC->database()->fetchAssoc($DIC->database()->execute($statement, [$a_time, $a_time])); - return (int) $row['counter']; + return (int) $ilDB->fetchAssoc( + $ilDB->execute( + self::getNumberOfActiveRawSessionsPreparedStatement(), + [$a_time, $a_time] + ) + )['counter']; } /** - * Read raw data for timespan - * @return Generator> + * @return Generator */ - protected static function getRawData(int $a_begin, int $a_end): Generator + private static function getRawData(int $a_begin, int $a_end): Generator { global $DIC; - $res = $DIC->database()->execute( + /** @var ilDBInterface $ilDB */ + $ilDB = $DIC['ilDB']; + + $res = $ilDB->execute( self::getRawDataPreparedStatement(), [$a_end, $a_begin] ); - while ($row = $DIC->database()->fetchAssoc($res)) { + while ($row = $ilDB->fetchAssoc($res)) { yield $row; } } /** * Create new slot (using table lock) - * - * @return array begin, end + * @return array{0: int, 1: int}|null */ - protected static function createNewAggregationSlot(int $a_now): ?array + private static function createNewAggregationSlot(int $a_now): ?array { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; $ilAtomQuery = $ilDB->buildAtomQuery(); @@ -221,8 +220,8 @@ protected static function createNewAggregationSlot(int $a_now): ?array // save slot to mark as taken $fields = [ - 'slot_begin' => ['integer', $slot[0]], - 'slot_end' => ['integer', $slot[1]], + 'slot_begin' => [ilDBConstants::T_INTEGER, $slot[0]], + 'slot_end' => [ilDBConstants::T_INTEGER, $slot[1]], ]; $ilDB->insert('usr_session_stats', $fields); }); @@ -232,10 +231,7 @@ protected static function createNewAggregationSlot(int $a_now): ?array return $slot; } - /** - * Aggregate raw session data (older than given time) - */ - public static function aggretateRaw(int $a_now): void + public static function aggregateRaw(int $a_now): void { if (!self::isActive()) { return; @@ -251,15 +247,19 @@ public static function aggretateRaw(int $a_now): void self::deleteAggregatedRaw($a_now); } - protected static function getNumberOfActiveRawSessionsPreparedStatement(): ilDbStatement + private static function getNumberOfActiveRawSessionsPreparedStatement(): ilDBStatement { if (self::$number_of_active_raw_sessions_statement === null) { global $DIC; - self::$number_of_active_raw_sessions_statement = $DIC->database()->prepare( + + /** @var ilDBInterface $ilDB */ + $ilDB = $DIC['ilDB']; + + self::$number_of_active_raw_sessions_statement = $ilDB->prepare( 'SELECT COUNT(*) counter FROM usr_session_stats_raw ' . 'WHERE (end_time IS NULL OR end_time >= ?) ' . 'AND start_time <= ? ' - . 'AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, 'integer'), + . 'AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, ilDBConstants::T_INTEGER), [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] ); } @@ -267,11 +267,15 @@ protected static function getNumberOfActiveRawSessionsPreparedStatement(): ilDbS return self::$number_of_active_raw_sessions_statement; } - protected static function getAggregatedRawDataPreparedStatement(): ilDBStatement + private static function getAggregatedRawDataPreparedStatement(): ilDBStatement { if (!self::$aggregated_raw_data_statement) { global $DIC; - self::$aggregated_raw_data_statement = $DIC->database()->prepareManip( + + /** @var ilDBInterface $ilDB */ + $ilDB = $DIC['ilDB']; + + self::$aggregated_raw_data_statement = $ilDB->prepareManip( 'UPDATE usr_session_stats ' . 'SET active_min = ?, ' . 'active_max = ?, ' @@ -302,15 +306,19 @@ protected static function getAggregatedRawDataPreparedStatement(): ilDBStatement return self::$aggregated_raw_data_statement; } - protected static function getRawDataPreparedStatement(): ilDBStatement + private static function getRawDataPreparedStatement(): ilDBStatement { if (!self::$raw_data_statement) { global $DIC; - self::$raw_data_statement = $DIC->database()->prepare( - 'SELECT start_time,end_time,end_context FROM usr_session_stats_raw' . - ' WHERE start_time <= %s' . - ' AND (end_time IS NULL OR end_time >= %s)' . - ' AND ' . $DIC->database()->in('type', ilSessionControl::$session_types_controlled, false, ilDBConstants::T_INTEGER) . + + /** @var ilDBInterface $ilDB */ + $ilDB = $DIC['ilDB']; + + self::$raw_data_statement = $ilDB->prepare( + 'SELECT start_time, end_time, end_context FROM usr_session_stats_raw' . + ' WHERE start_time <= ?' . + ' AND (end_time IS NULL OR end_time >= ?)' . + ' AND ' . $ilDB->in('type', ilSessionControl::$session_types_controlled, false, ilDBConstants::T_INTEGER) . ' ORDER BY start_time', [ilDBConstants::T_INTEGER, ilDBConstants::T_INTEGER] ); @@ -318,10 +326,13 @@ protected static function getRawDataPreparedStatement(): ilDBStatement return self::$raw_data_statement; } - public static function aggregateRawHelper(int $a_begin, int $a_end) + private static function aggregateRawHelper(int $a_begin, int $a_end): void { global $DIC; + /** @var ilDBInterface $ilDB */ + $ilDB = $DIC['ilDB']; + // "relevant" closing types $separate_closed = [ ilSession::SESSION_CLOSE_USER, @@ -330,7 +341,8 @@ public static function aggregateRawHelper(int $a_begin, int $a_end) ]; // gather/process data (build event timeline) - $closed_counter = $events = []; + $events = []; + $closed_counter = $events; $opened_counter = 0; foreach (self::getRawData($a_begin, $a_end) as $item) { // open/close counters are _not_ time related @@ -360,11 +372,14 @@ public static function aggregateRawHelper(int $a_begin, int $a_end) } } - // initialising active statistical values + // initializing active statistical values $active_begin = self::getNumberOfActiveRawSessions($a_begin - 1); - $active_end = $active_min = $active_max = $active_avg = $active_begin; + $active_avg = $active_begin; + $active_max = $active_begin; + $active_min = $active_begin; + $active_end = $active_begin; - // parsing events / building avergages + // parsing events / building averages if (count($events)) { $last_update_avg = $a_begin - 1; $slot_seconds = self::SLOT_SIZE * 60; @@ -411,8 +426,7 @@ public static function aggregateRawHelper(int $a_begin, int $a_end) } unset($events); - - $DIC->database()->execute( + $ilDB->execute( self::getAggregatedRawDataPreparedStatement(), [ 'active_min' => $active_min, @@ -425,18 +439,16 @@ public static function aggregateRawHelper(int $a_begin, int $a_end) 'closed_login' => (int) ($closed_counter[ilSession::SESSION_CLOSE_LOGIN] ?? 0), 'closed_misc' => (int) ($closed_counter[0] ?? 0), 'slot_begin' => $a_begin, - 'slot_end' => $a_end, + 'slot_end' => $a_end ] ); } - /** - * Remove already aggregated raw data - */ - protected static function deleteAggregatedRaw(int $a_now): void + private static function deleteAggregatedRaw(int $a_now): void { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; // we are rather defensive here - 7 days BEFORE current aggregation @@ -444,31 +456,33 @@ protected static function deleteAggregatedRaw(int $a_now): void $ilDB->manipulate( 'DELETE FROM usr_session_stats_raw' . - ' WHERE start_time <= ' . $ilDB->quote($cut, 'integer') + ' WHERE start_time <= ' . $ilDB->quote($cut, ilDBConstants::T_INTEGER) ); } /** - * Get session counters by type (opened, closed) + * @return array{opened: int, closed_manual: int, closed_expire: int, closed_login: int, closed_misc: int} */ public static function getNumberOfSessionsByType(int $a_from, int $a_to): array { global $DIC; + /** @var ilDBInterface $ilDB */ $ilDB = $DIC['ilDB']; $sql = 'SELECT SUM(opened) opened, SUM(closed_manual) closed_manual,' . ' SUM(closed_expire) closed_expire,' . ' SUM(closed_login) closed_login, SUM(closed_misc) closed_misc' . ' FROM usr_session_stats' . - ' WHERE slot_end > ' . $ilDB->quote($a_from, 'integer') . - ' AND slot_begin < ' . $ilDB->quote($a_to, 'integer'); + ' WHERE slot_end > ' . $ilDB->quote($a_from, ilDBConstants::T_INTEGER) . + ' AND slot_begin < ' . $ilDB->quote($a_to, ilDBConstants::T_INTEGER); $res = $ilDB->query($sql); + return $ilDB->fetchAssoc($res); } /** - * Get active sessions aggregated data + * @return list */ public static function getActiveSessions(int $a_from, int $a_to): array { @@ -479,18 +493,16 @@ public static function getActiveSessions(int $a_from, int $a_to): array $sql = 'SELECT slot_begin, slot_end, active_min, active_max, active_avg' . ' FROM usr_session_stats' . - ' WHERE slot_end > ' . $ilDB->quote($a_from, 'integer') . - ' AND slot_begin < ' . $ilDB->quote($a_to, 'integer') . + ' WHERE slot_end > ' . $ilDB->quote($a_from, ilDBConstants::T_INTEGER) . + ' AND slot_begin < ' . $ilDB->quote($a_to, ilDBConstants::T_INTEGER) . ' ORDER BY slot_begin'; $res = $ilDB->query($sql); + $all = []; while ($row = $ilDB->fetchAssoc($res)) { - $entry = []; - foreach ($row as $key => $value) { - $entry[$key] = (int) $value; - } - $all[] = $entry; + $all[] = array_map(intval(...), $row); } + return $all; } @@ -503,12 +515,13 @@ public static function getLastAggregation(): ?int $ilDB = $DIC['ilDB']; - $sql = 'SELECT max(slot_end) latest FROM usr_session_stats'; + $sql = 'SELECT MAX(slot_end) latest FROM usr_session_stats'; $res = $ilDB->query($sql); $row = $ilDB->fetchAssoc($res); - if ($row['latest']) { + if ($row['latest'] !== null) { return (int) $row['latest']; } + //TODO check if return null as timestamp causes issues return null; } diff --git a/components/ILIAS/Authentication/classes/class.ilSessionStatisticsGUI.php b/components/ILIAS/Authentication/classes/class.ilSessionStatisticsGUI.php index f376c171789f..890937ba001e 100755 --- a/components/ILIAS/Authentication/classes/class.ilSessionStatisticsGUI.php +++ b/components/ILIAS/Authentication/classes/class.ilSessionStatisticsGUI.php @@ -736,7 +736,7 @@ protected function adminSync(): void // see ilSession::_writeData() $now = time(); ilSession::_destroyExpiredSessions(); - ilSessionStatistics::aggretateRaw($now); + ilSessionStatistics::aggregateRaw($now); $this->tpl->setOnScreenMessage('success', $this->lng->txt('trac_sync_session_stats_success'), true); $this->ilCtrl->redirect($this); From 7e3d53ba3777f5e68f8b9ce8b2d9fbb58f708409 Mon Sep 17 00:00:00 2001 From: Alexander Killing Date: Thu, 12 Feb 2026 12:45:24 +0100 Subject: [PATCH 023/141] 45037: Description texts under the fields Upload File and External URL in Media Pools are interchanged --- .../PC/MediaObject/class.ilPCMediaObjectEditorGUI.php | 7 ++++++- .../MediaObjects/Creation/class.ilMediaCreationGUI.php | 4 ++-- .../MediaObjects/MediaType/class.MediaTypeManager.php | 8 ++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/components/ILIAS/COPage/PC/MediaObject/class.ilPCMediaObjectEditorGUI.php b/components/ILIAS/COPage/PC/MediaObject/class.ilPCMediaObjectEditorGUI.php index f3835e7b3115..92b181b8325e 100755 --- a/components/ILIAS/COPage/PC/MediaObject/class.ilPCMediaObjectEditorGUI.php +++ b/components/ILIAS/COPage/PC/MediaObject/class.ilPCMediaObjectEditorGUI.php @@ -221,6 +221,8 @@ public function getRenderedUrlForm( public function getUrlForm( ilLanguage $lng ): ilPropertyFormGUI { + global $DIC; + $media_types = $DIC->mediaObjects()->internal()->domain()->mediaType(); $form = new ilPropertyFormGUI(); $form->setShowTopButtons(false); @@ -240,8 +242,11 @@ public function getUrlForm( $form->addItem($hi3); // standard reference + $lng->loadLanguageModule("mob"); $ti = new ilTextInputGUI($lng->txt("url"), "standard_reference"); - $ti->setInfo($lng->txt("cont_url_info")); + $info = $lng->txt("mob_url_info1") . " " . implode(", ", iterator_to_array($media_types->getAllowedSuffixes())) . "."; + $info .= " " . $lng->txt("mob_url_info_video"); + $ti->setInfo($info); $ti->setRequired(true); $form->addItem($ti); diff --git a/components/ILIAS/MediaObjects/Creation/class.ilMediaCreationGUI.php b/components/ILIAS/MediaObjects/Creation/class.ilMediaCreationGUI.php index 7141d5766799..2e18477fb7e3 100755 --- a/components/ILIAS/MediaObjects/Creation/class.ilMediaCreationGUI.php +++ b/components/ILIAS/MediaObjects/Creation/class.ilMediaCreationGUI.php @@ -170,7 +170,7 @@ protected function getMimeTypes($local_only = false): array { $mimes = []; if (in_array(self::TYPE_ALL, $this->accept_types)) { - $mimes = iterator_to_array($this->type_manager->getAllowedMimeTypes()); + $mimes = iterator_to_array($this->type_manager->getAllowedMimeTypes($local_only)); } if (in_array(self::TYPE_VIDEO, $this->accept_types)) { $mimes = array_merge($mimes, iterator_to_array($this->type_manager->getAllowedVideoMimeTypes($local_only))); @@ -287,7 +287,7 @@ public function initUrlForm(): ilPropertyFormGUI // $ti = new \ilTextInputGUI($lng->txt("mob_url"), "url"); $info = $lng->txt("mob_url_info1") . " " . implode(", ", $this->getSuffixes()) . "."; - if (in_array(self::TYPE_VIDEO, $this->accept_types)) { + if (in_array(self::TYPE_VIDEO, $this->accept_types) || in_array(self::TYPE_ALL, $this->accept_types)) { $info .= " " . $lng->txt("mob_url_info_video"); } $ti->setInfo($info); diff --git a/components/ILIAS/MediaObjects/MediaType/class.MediaTypeManager.php b/components/ILIAS/MediaObjects/MediaType/class.MediaTypeManager.php index 0c5914549ab8..3bf78e2c7c99 100755 --- a/components/ILIAS/MediaObjects/MediaType/class.MediaTypeManager.php +++ b/components/ILIAS/MediaObjects/MediaType/class.MediaTypeManager.php @@ -220,9 +220,13 @@ public function getMimeTypes(): \Iterator } } - public function getAllowedMimeTypes(): \Iterator + public function getAllowedMimeTypes(bool $local_only = false): \Iterator { - return $this->getAllowedSubset($this->getMimeTypes()); + foreach ($this->getAllowedSubset($this->getMimeTypes()) as $mime) { + if (!$local_only || !in_array($mime, ["video/vimeo", "video/youtube"])) { + yield $mime; + } + } } public function getVideoMimeTypes(bool $local_only = false): \Iterator From 31926389aaa0b125a8685fb79eb45c119609fc01 Mon Sep 17 00:00:00 2001 From: Stefan Meyer Date: Thu, 12 Feb 2026 12:52:07 +0100 Subject: [PATCH 024/141] RPC: switched to log4j-bom --- components/ILIAS/WebServices/RPC/lib/pom.xml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/ILIAS/WebServices/RPC/lib/pom.xml b/components/ILIAS/WebServices/RPC/lib/pom.xml index 5921a80a0890..36ec37ed0f4b 100755 --- a/components/ILIAS/WebServices/RPC/lib/pom.xml +++ b/components/ILIAS/WebServices/RPC/lib/pom.xml @@ -5,7 +5,7 @@ 4.0.0 de.ilias.ilserver ilServer - 12.0.1 + 12.0.2 ilServer @@ -88,6 +88,13 @@ pom import + + org.apache.logging.log4j + log4j-bom + 2.25.3 + pom + import + @@ -101,31 +108,26 @@ org.apache.logging.log4j log4j-core - 2.23.1 org.apache.logging.log4j log4j-api - 2.23.1 org.apache.logging.log4j log4j-jcl - 2.23.1 org.apache.logging.log4j log4j-slf4j-impl - 2.23.1 org.apache.logging.log4j log4j-1.2-api - 2.23.1 From 0d5b417ed37c0f6c802a97d71ef2d67a652f26ba Mon Sep 17 00:00:00 2001 From: Stefan Meyer Date: Thu, 12 Feb 2026 13:14:07 +0100 Subject: [PATCH 025/141] Membership: Attendance list user defined fields are selected according to settings of parent group/course --- .../Membership/classes/class.ilAttendanceList.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/components/ILIAS/Membership/classes/class.ilAttendanceList.php b/components/ILIAS/Membership/classes/class.ilAttendanceList.php index 5a59245fa8a8..08b02216a4fb 100755 --- a/components/ILIAS/Membership/classes/class.ilAttendanceList.php +++ b/components/ILIAS/Membership/classes/class.ilAttendanceList.php @@ -37,6 +37,7 @@ class ilAttendanceList protected ilObject $parent_obj; protected ?ilParticipants $participants; protected ?ilWaitingList $waiting_list; + protected ilTree $tree; /** * @var ?callable */ @@ -67,7 +68,7 @@ public function __construct( $this->ctrl = $DIC->ctrl(); $this->tpl = $DIC->ui()->mainTemplate(); $this->profile = $DIC['user']->getProfile(); - + $this->tree = $DIC->repositoryTree(); $this->parent_gui = $a_parent_gui; $this->parent_obj = $a_parent_obj; $this->participants = $a_participants_object; @@ -137,10 +138,14 @@ protected function readOrderedExportableFields(): bool ); } + $parent_obj_type = $this->tree->checkForParentType($this->parent_obj->getRefId(), 'crs') ? 'crs' : ''; + $parent_obj_type = $this->tree->checkForParentType($this->parent_obj->getRefId(), 'grp') ? 'grp' : $parent_obj_type; + $user_defined_fields = $parent_obj_type === '' + ? $this->profile->getAllUserDefinedFields() + : $this->profile->getVisibleUserDefinedFields(Context::buildFromObjectType($parent_obj_type)); + // add udf fields - foreach ($this->profile->getVisibleUserDefinedFields( - Context::buildFromObjectType($this->parent_obj->getType()) - ) as $field) { + foreach ($user_defined_fields as $field) { $this->presets['udf_' . $field->getIdentifier()] = array( $field->getLabel($this->lng), false From 3d523a625fc00fed0767f121163baa067f19fc6c Mon Sep 17 00:00:00 2001 From: Tim Schmitz Date: Thu, 12 Feb 2026 13:16:34 +0100 Subject: [PATCH 026/141] Tracking: fix details action in lp progress block (46962) --- .../classes/class.ilLPProgressBlockGUI.php | 31 +++++++++++++++++++ lang/ilias_de.lang | 1 + lang/ilias_en.lang | 1 + 3 files changed, 33 insertions(+) diff --git a/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php b/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php index 4cdbcafa6542..f9027f5ec894 100644 --- a/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php +++ b/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php @@ -43,6 +43,7 @@ public function __construct() $this->setBlockId('lpprogress_' . $this->ctrl->getContextObjId()); $this->setTitle($this->lng->txt('trac_progress_block_title')); $this->setPresentation(self::PRES_SEC_LEG); + $this->setActions(); } public function getBlockType(): string @@ -55,6 +56,11 @@ protected function isRepositoryObject(): bool return false; } + protected function getListItemForData(array $data): ?\ILIAS\UI\Component\Item\Item + { + return parent::getListItemForData($data); // TODO: Change the autogenerated stub + } + protected function getLegacyContent(): string { $filter = $this->data_retrieval @@ -81,4 +87,29 @@ protected function getLegacyContent(): string $mode_and_status ]); } + + protected function setActions(): void + { + $read_only_allowed = true; + if ($this->supportsMembers($this->requested_ref_id)) { + $read_only_allowed = ilParticipants::_isParticipant($this->requested_ref_id, $this->user->getId()); + } + if (!ilLearningProgressAccess::checkAccess($this->requested_ref_id, $read_only_allowed)) { + return; + } + $this->ctrl->setParameterByClass(ilLearningProgressGUI::class, 'ref_id', $this->requested_ref_id); + $link = $this->ctrl->getLinkTargetByClass(ilLearningProgressGUI::class); + $this->ctrl->clearParameterByClass(ilLearningProgressGUI::class, 'ref_id'); + $this->addBlockCommand($link, $this->lng->txt('trac_progress_block_details')); + } + + protected function supportsMembers(int $ref_id): bool + { + try { + ilParticipants::getInstance($ref_id); + return true; + } catch (Exception) { + return false; + } + } } diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 27e2b514f71f..a1b3b04e1901 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -17405,6 +17405,7 @@ trac#:#trac_paths#:#Pfade trac#:#trac_percentage#:#Prozent trac#:#trac_periodic_system_load#:#Periodische Lastdarstellung trac#:#trac_progress#:#Persönlicher Lernfortschritt +trac#:#trac_progress_block_details#:#Details anzeigen trac#:#trac_progress_block_title#:#Ihr Lernfortschritt trac#:#trac_read_count#:#Zugriffe trac#:#trac_read_count_spent_seconds#:#Bearbeitungszeit / Zugriffe diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 1617f7f9bbce..0a515f577e4b 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -17375,6 +17375,7 @@ trac#:#trac_paths#:#Paths trac#:#trac_percentage#:#Percentage trac#:#trac_periodic_system_load#:#Periodic trac#:#trac_progress#:#Personal Learning Progress +trac#:#trac_progress_block_details#:#View Details trac#:#trac_progress_block_title#:#Your Learning Progress trac#:#trac_read_count#:#Access Number trac#:#trac_read_count_spent_seconds#:#Time Spent / Access From 7799980aa4a3c2fc6358b4d71824ec362f6bb799 Mon Sep 17 00:00:00 2001 From: Tim Schmitz Date: Thu, 12 Feb 2026 13:18:04 +0100 Subject: [PATCH 027/141] Tracking: fix last commit --- .../ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php b/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php index f9027f5ec894..80894e93a3c9 100644 --- a/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php +++ b/components/ILIAS/Tracking/classes/class.ilLPProgressBlockGUI.php @@ -56,11 +56,6 @@ protected function isRepositoryObject(): bool return false; } - protected function getListItemForData(array $data): ?\ILIAS\UI\Component\Item\Item - { - return parent::getListItemForData($data); // TODO: Change the autogenerated stub - } - protected function getLegacyContent(): string { $filter = $this->data_retrieval From 7a6f6aef50bc4f59f95a863dd676c6a86a129d6d Mon Sep 17 00:00:00 2001 From: "Alex Hartwig (Qualitus)" Date: Thu, 12 Feb 2026 13:44:06 +0100 Subject: [PATCH 028/141] Fix Mantis #45285: imsmanifest.xml not found (#11051) --- .../ScormAicc/classes/class.ilObjSAHSLearningModuleGUI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ILIAS/ScormAicc/classes/class.ilObjSAHSLearningModuleGUI.php b/components/ILIAS/ScormAicc/classes/class.ilObjSAHSLearningModuleGUI.php index 073295e1523c..f83c4081fe9b 100755 --- a/components/ILIAS/ScormAicc/classes/class.ilObjSAHSLearningModuleGUI.php +++ b/components/ILIAS/ScormAicc/classes/class.ilObjSAHSLearningModuleGUI.php @@ -429,7 +429,7 @@ public function uploadObject(): void $newObj->getDataDirectory(), false, false, - true + false ); } ilFileUtils::renameExecutables($newObj->getDataDirectory()); From 0d978768629710b425e4befd5c1fdcfdd6a146c1 Mon Sep 17 00:00:00 2001 From: Fabian Helfer <82493694+fhelfer@users.noreply.github.com> Date: Thu, 12 Feb 2026 14:13:39 +0100 Subject: [PATCH 029/141] [FIX] #46465 UI: remove duplicate `Input\Container\Form\Standard` buttons. (#10812) * Fixes https://mantis.ilias.de/view.php?id=46465 * Remove duplicate submit- and additional action-buttons from standard form headers * Update rendering unit tests --- .../Component/Input/Container/Form/Renderer.php | 6 ------ .../UI/src/templates/default/Input/tpl.standard.html | 6 ------ .../Input/Container/Form/StandardFormTest.php | 12 +----------- 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php b/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php index 5871981dfe04..0f3a5194229e 100755 --- a/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php +++ b/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php @@ -52,11 +52,6 @@ protected function renderStandard(Form\Standard $component, RendererInterface $d $additional_form_actions = $component->getAdditionalFormActions(); foreach ($additional_form_actions as $action => $label) { - $tpl->setCurrentBlock('with_additional_form_action_top'); - $tpl->setVariable('ACTION_TOP', $action); - $tpl->setVariable('ACTION_LABEL_TOP', $label); - $tpl->parseCurrentBlock(); - $tpl->setCurrentBlock('with_additional_form_action_bottom'); $tpl->setVariable('ACTION_BOTTOM', $action); $tpl->setVariable('ACTION_LABEL_BOTTOM', $label); @@ -74,7 +69,6 @@ protected function renderStandard(Form\Standard $component, RendererInterface $d "" ); - $tpl->setVariable("BUTTONS_TOP", $default_renderer->render($main_submit_button)); $tpl->setVariable("BUTTONS_BOTTOM", $default_renderer->render($main_submit_button)); $tpl->setVariable("INPUTS", $default_renderer->render($component->getInputGroup())); diff --git a/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html b/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html index 5c5a797b0c24..09b76eb03d0c 100755 --- a/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html +++ b/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html @@ -1,11 +1,5 @@
action="{URL}" method="post">
-
- - - - {BUTTONS_TOP} -
* {TXT_REQUIRED_TOP} diff --git a/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php b/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php index fc8bc1fa0b6e..f9db51cac6af 100755 --- a/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php @@ -126,12 +126,11 @@ public function testRender(): void ]); $r = $this->getDefaultRenderer(); - $html = $this->getDefaultRenderer()->render($form); + $html = $this->brutallyTrimHTML($this->getDefaultRenderer()->render($form)); $expected = $this->brutallyTrimHTML('
-
' . $this->getTextFieldHtml() . '